• homm © (04.03.07 19:32) [0]
    Делаю графические контролы, обязательное требование у которых - отсутствие дрожания. Естественно для этого сделано следующее:

    procedure TGRushControl.SetParent(AParent: TWinControl);
    begin
     if AParent<>nil then begin
       AParent.DoubleBuffered := TRUE;
     end;
     inherited SetParent(AParent);
    end;



    Но тогда получается что когда я у одного контрола вызываю Invalidate, все контролы того же родителя тоже перерисовываются (вызывается их обработчик Paint). Как на старался достать область, действительно учавстующую в перерисовке API вызовами в обработчике Paint так и не удалось.

    procedure TControl.InvalidateControl(IsVisible, IsOpaque: Boolean);
    var  Rect: TRect;
    .......
    begin
     if (IsVisible or (csDesigning in ComponentState) and
       not (csNoDesignVisible in ControlStyle)) and (Parent <> nil) and
       Parent.HandleAllocated then
     begin
       Rect := BoundsRect; // - Вот этот рэкт мне очень нужен
       InvalidateRect(Parent.Handle, @Rect, not (IsOpaque or
         (csOpaque in Parent.ControlStyle) or BackgroundClipped));
     end;
    end;



    А ведь, если я буду его знать я смогу исключить прорисовку тех контролов, которые реально все равно заклипаны вызовом BeginPaint в TWinControl.WMPaint родителя контрола, а значит их прорисовка не отображается.

    Я пробовал получить злосчастный рэкт с помощью вызова GetUpdateRect(Parent.Handle, PS.rcPaint, TRUE) из Paint, но почему-то он возващает одни нули, хотя InvalidateRect, как видно из приведенного куска кода выше, вызывается с BoundsRect в качестве области инвалидирования. Такой-же эффект (нули вместо BoundsRect) получается и без использования DoubleBuffering
  • homm © (04.03.07 23:14) [1]
    Кажется понял одну вешь: После вызова BeginPaint,  GetUpdateRect будет давать только пустую область, т.к. BeginPaint похоже валидатит ее.
    Вопрос становится еще сложне...
  • Макс Черных © (05.03.07 02:24) [2]
    Ну а зачем такие извращения? Почему не устраивает старый, проверенный способ, при котором наследник TGraphicControl отрисовывается на битмапе в памяти, а потом через BitBlt все копируется на Canvas. DoubleBuffered так, собственно, и работает.
  • homm © (05.03.07 07:09) [3]
    > [2] Макс Черных ©   (05.03.07 02:24)
    Подумай сам, как это может избавить от дрожания: есть 2 контрола, один частично перекрывает другой. Оба они забуферизованы предложенным тобой способом. Я делаю invalidate одного из них, при этом в область инвалидирования попадает и другой. Необходимость перерисовки становится известа родительскому контролу, он делает PaintControls, где ресуется и выводится на экран сначала первый, а затем второй контрол. Получаем еще то дрожание.

    В общем мои мзыскания продвинулись вот до чего: Если я ловлю нужный мне рект непосредственно в родителе, то все происходит так как мне нужно:

    unti1.pas:
    procedure TForm1.WMPaint(var Message: TWMPaint);
    begin
     GetUpdateRect(Handle, UpdateRect, FALSE);
     inherited;
    end;

    GRushcontrol.pas:
    procedure TGRushControl.Paint;
    begin
     IntersectRect(rct, BoundsRect, UpdateRect);
     if (IsRectEmpty(rct)=FALSE) then begin
       // Рисуем здесь!!!
     end;
    end;



    вопрос собственно как перехватить в родители WM_Paint средствами VCL. Про винапи средства (SetWindowLong) мне изсвестно.
  • DimaBr (05.03.07 09:47) [4]
    Что в вашем понятии означает "Дрожание"
  • homm © (05.03.07 09:54) [5]
    В общем, покритикуйте пожалуста мою реализацию:


    const
     PropWndProc:PChar = 'TWinControl_WndProc_OLD';
     PropUpdateRect:PChar = 'TWinControl_UpdateRect_OLD';
     
    function GRushWndProc(Window: HWND; Message, wParam, lParam: Longint): Longint; stdcall;
    var
     PrevWndProc: Pointer;
     UpdateRect: PRect;
    begin
     if (Message = WM_PAINT) then begin
       GetMem(UpdateRect, sizeof(TRect));
       GetUpdateRect(Window, UpdateRect^, FALSE);
       SetProp(Window, PropUpdateRect, DWORD(UpdateRect));
     end;
     PrevWndProc := Pointer(GetProp(Window, PropWndProc));
     if PrevWndProc<>nil then
       Result := CallWindowProc(PrevWndProc, Window, Message, wParam, lParam);

     if (Message = WM_PAINT) then begin
       FreeMem(UpdateRect);
       RemoveProp(Window, PropUpdateRect);
     end;
    end;

    procedure TGRushControl.SetParent(AParent: TWinControl);
    var WinProc: DWORD;
    begin
     if Parent<>nil then begin
       WinProc := RemoveProp(Parent.Handle, PropWndProc);;
       if (WinProc<>0) then
         SetWindowLong(Parent.Handle, GWL_WNDPROC, WinProc);
     end;
     inherited SetParent(AParent);
     if Parent<>nil then with Parent do begin
       DoubleBuffered := TRUE;
       WinProc := GetWindowLong(Handle, GWL_WNDPROC);
       if (WinProc <> DWORD(@GRushWndProc)) then begin
         SetProp(Handle, PropWndProc, WinProc);
         SetWindowLong(Handle, GWL_WNDPROC, DWORD(@GRushWndProc));
       end;
     end;
    end;

    procedure TGRushControl.Paint;
    var
     UpdateRect: PRect;
     TR: TRect;
    begin
     UpdateRect := Pointer(GetProp(Parent.Handle, PropUpdateRect));
     if UpdateRect<>nil then
       IntersectRect(TR, BoundsRect, UpdateRect^);
     if (IsRectEmpty(TR)=FALSE) OR (UpdateRect=nil) then begin
       // рисуем, как вы догадались, здесь :)
     end;
    end;


    вроде раболает, как в рантайм, так и в дезайне. Но может какие подвожные камни есть?

    Кстати, еще вопрос: чем inherited SetParent(AParent); отличается от просто inhirated; и что в данном случае следует применять?
  • homm © (05.03.07 09:56) [6]
    > Что в вашем понятии означает "Дрожание"
    когда прежде чем выводится компонент, на занимаемаемую им площадь рисуется другой или родитель, отсюда на долюсекундв, пока не прорисовался нужный компонет на занимаемой им площади отображается выведая не им картинка. я то думал что это уж совсем очевидно.
  • DimaBr (05.03.07 10:02) [7]
    Полный изврат, нафика козе баян ?
    В данном случае  inherited от  inherited SetParent(AParent); ничем не отличаются, например реальное отличие

    if тра-ля-ля
     then inherited
     else inherited SetParent(NewParent)


    но опят же можно просто
    inherited;
    AParent = NewParent


    а вот в такой конструкции только так
    procedure SetValue(const Value: integer);
    var NewValue: integer;
    begin
     NewValue := 123456789;
     inherited SetValue(NewValue);
    end;
  • homm © (05.03.07 10:08) [8]
    Так, об одном косяке уже подумал. Если кто-то поставит свой хук поверх моего, то WinProc <> DWORD(@GRushWndProc) всегдда даст TRUE, а значит я снова поставлю свой хук поверх и т.к. при этом затрется PropWndProc, то получится бесконечная рекурсия.

    Выход не в проверке адресса функции GWL_WNDPROC, а в проверки наличия PropWndProc.
    Вот так правильнее будет:

    procedure TGRushControl.SetParent(AParent: TWinControl);
    var WinProc: DWORD;
    begin
     if Parent<>nil then begin
       WinProc := RemoveProp(Parent.Handle, PropWndProc);;
       if (WinProc<>0) then
         SetWindowLong(Parent.Handle, GWL_WNDPROC, WinProc);
     end;
     inherited SetParent(AParent);
     if Parent<>nil then with Parent do begin
       DoubleBuffered := TRUE;
       if (GetProp(Handle, PropWndProc) = 0) then begin
         WinProc := SetWindowLong(Handle, GWL_WNDPROC, DWORD(@GRushWndProc));
         SetProp(Handle, PropWndProc, WinProc);
       end;
     end;
    end;

  • homm © (05.03.07 10:10) [9]
    > Полный изврат, нафика козе баян ?
    Увеличение производительности в разы при большом количестве контроллов. Что-бы вместо 40-а при движении мыши перерисовывался только один, который и изменяется при движении мыши.
  • DimaBr (05.03.07 10:11) [10]
    В этом то и отличие GraphicsControl от WinControl.
    Рассмотрите внимательнее
    TGraphicControl.WMPaint(var Message: TWMPaint);
    TWinControl.WMPaint(var Message: TWMPaint);
  • homm © (05.03.07 10:14) [11]
    > В этом то и отличие GraphicsControl от WinControl.

    Ну и отлично, и я от него избавился, кажется :)

    На самом деле отличие в возможности рисовать прозрачные и полу-прозрачные компопенты, т.к. все компоненты рисуются на одной канве.
  • DimaBr (05.03.07 10:46) [12]
    В таком случае возьмите за основу WinControl
  • homm © (05.03.07 10:49) [13]
    > В таком случае возьмите за основу WinControl

    я же уже ответил на этот вопрос заранее.

    > На самом деле отличие в возможности рисовать прозрачные
    > и полу-прозрачные компопенты, т.к. все компоненты рисуются
    > на одной канве.
  • homm © (05.03.07 10:54) [14]
    Все равно фигня получалась :) При удалении однго контрола весь механизм слетал, т.к. он снимался в принципе в SetParent. Правильнее перенсти этот механизм в WM_DESTROY.

    function GRushWndProc(Window: HWND; Message, wParam, lParam: Longint): Longint; stdcall;
    var
     PrevWndProc: Pointer;
     UpdateRect: PRect;
    begin
     PrevWndProc := Pointer(GetProp(Window, PropWndProc));
     case Message of
       WM_PAINT: begin
         GetMem(UpdateRect, sizeof(TRect));
         GetUpdateRect(Window, UpdateRect^, FALSE);
         SetProp(Window, PropUpdateRect, DWORD(UpdateRect));
       end;
       WM_DESTROY: begin
         if PrevWndProc<>nil then begin
           SetWindowLong(Window, GWL_WNDPROC, DWORD(PrevWndProc));
           RemoveProp(Window, PropWndProc);
         end;
       end;
     end;
     
     if PrevWndProc<>nil then
       Result := CallWindowProc(PrevWndProc, Window, Message, wParam, lParam);

     if (Message = WM_PAINT) then begin
       FreeMem(UpdateRect);
       RemoveProp(Window, PropUpdateRect);
     end;
    end;

    procedure TGRushControl.SetParent(AParent: TWinControl);
    var WinProc: DWORD;
    begin
     inherited SetParent(AParent);
     if Parent<>nil then with Parent do begin
       DoubleBuffered := TRUE;
       if (GetProp(Handle, PropWndProc) = 0) then begin
         WinProc := SetWindowLong(Handle, GWL_WNDPROC, DWORD(@GRushWndProc));
         SetProp(Handle, PropWndProc, WinProc);
       end;
     end;
    end;

  • DimaBr (05.03.07 11:28) [15]
     прозрачные  и полу-прозрачные компопенты
    Эта проблема просто решается, а вот положите рядом Image и перекройте частично его Panelю. Теперь порпробуйте поднять картинкувыше панели...
  • homm © (05.03.07 11:31) [16]
    > Эта проблема просто решается
    Да ну? И как интересно?


    > Теперь порпробуйте поднять картинкувыше панели...
    Зачем? Я и сам знаю что невозможно. Да мне это не нужно. Да и никому другому думаю не нужно.
  • DimaBr (05.03.07 12:27) [17]
    Просто
    1. Сдвигаем Наш контрол в сторону (Left := -Left);
    2. Даём паренту перерисоваться (Parent.Update)
    3. Копируем содержимое на наш контрол по старым координатам
    4. возвращаем контрол на место
  • homm © (05.03.07 12:41) [18]
    > Просто
    > 1. Сдвигаем Наш контрол в сторону (Left := -Left);
    > 2. Даём паренту перерисоваться (Parent.Update)
    > 3. Копируем содержимое на наш контрол по старым координатам
    > 4. возвращаем контрол на место
    И того полностью избавляемся от дрожания??? Ну-ну..
  • DimaBr (05.03.07 12:47) [19]
    Поменяйте монитор, дрожать перестанет.
  • homm © (05.03.07 12:50) [20]
    > Поменяйте монитор, дрожать перестанет.
    А если нет, ты оплатишь расходы? Тогда я уже в магазин пошел за плазменной панелью побольше :)

    А если серьезно, то неужели непонятно, что когда 2. Даём паренту перерисоваться (Parent.Update), то он и перерисовывается, соответсвенно изображение быстро меняется с пернта, на наш контрол в одном и то-же месте. Может хватит глупости говорить?
  • homm © (05.03.07 12:59) [21]
    > 1. Сдвигаем Наш контрол в сторону (Left := -Left);

    Ну был у меня Left = 0. Ну сделал я Left := -Left и чего? :)
  • DimaBr (05.03.07 13:03) [22]
    Поставьте Left := - Width
  • homm © (05.03.07 13:07) [23]
    Зачем? Я же сказал что предложенный способ получить прозрачность кривой.
  • homm © (05.03.07 13:07) [24]
    Хочеш продолжать флуд, постучи в аську.
  • DimaBr (05.03.07 14:25) [25]
    Разберёмся, почему "ДРОЖИТ".
    "Дрожит" потому что перерисовывается фон. Дабы фон не перерисовывался можно:
    1. Запретить перерисовку
    DoubleBuffered := true;

    ControlState := ControlState + [csOpaque];

    procedure WMEraseBkgnd(var Message: TWmEraseBkgnd); message WM_ERASEBKGND;
    begin
     Message.Result := 1;
    end;

  • homm © (05.03.07 15:05) [26]
    Так, смотрим :)

    http://lingvo.yandex.ru/en?text=Opaque&st_translate=1
    1. прил.
    1) непрозрачный; непроницаемый, темный



    А мы вроде прозрачный собрались делать :)
  • DimaBr (05.03.07 15:09) [27]
    Вы можете чётко объяснить с какой целью вы наследуетесь от TGraphicsControl.
  • homm © (05.03.07 15:18) [28]
    Могу.

    Все вопросы от Вас теперь только в аську, если не трудно.
  • DimaBr (05.03.07 15:25) [29]
    У меня нет аськи, если вы не хотите чтобы я пытался вам помочь, так и скажите.
  • homm © (05.03.07 15:31) [30]
    Хорошо, мне нужно сделать контрол, имеющий прозрачные А возможно полу-прозрачные) области, при этом при его тображении не должно быть видно никаких серцаний других компонентов находящимся за ним и прочих артефактов.
  • DimaBr (05.03.07 15:32) [31]
    Половина контролов библиотеки Raize унаследована от
     TRzCustomButton = class( TCustomControl )
    в котором реализовано свойство Transparent и не ДРОЖАТ. Может прислушаемся к профессионалам ?

    procedure TRzCustomButton.WMEraseBkgnd( var Msg: TWMEraseBkgnd );
    begin
     if FTransparent then
     begin
       DrawParentImage( Self, Msg.DC, True );
       Msg.Result := 1;
     end
     else
       inherited;
    end;

    procedure DrawParentImage( Control: TControl; DC: HDC; InvalidateParent: Boolean = False );
    var
     SaveIndex: Integer;
     P: TPoint;
    begin
     if Control.Parent = nil then
       Exit;
     SaveIndex := SaveDC( DC );
     GetViewportOrgEx( DC, P );

     SetViewportOrgEx( DC, P.X - Control.Left, P.Y - Control.Top, nil );
     IntersectClipRect( DC, 0, 0, Control.Parent.ClientWidth, Control.Parent.ClientHeight );

     if not ( csDesigning in Control.ComponentState ) then
     begin
       Control.Parent.Perform( wm_EraseBkgnd, DC, 0 );
       Control.Parent.Perform( wm_Paint, DC, 0 );
     end
     else
     begin
       try
         Control.Parent.Perform( wm_EraseBkgnd, DC, 0 );
         Control.Parent.Perform( wm_Paint, DC, 0 );
       except
       end;
     end;

     RestoreDC( DC, SaveIndex );

     if InvalidateParent then
     begin
       if not ( Control.Parent is TCustomControl ) and
          not ( Control.Parent is TCustomForm ) and
          not ( csDesigning in Control.ComponentState ) then
       begin
         Control.Parent.Invalidate;
       end;
     end;
    end;


  • DimaBr (05.03.07 15:38) [32]
    Те же нотки в DevExpress

    procedure DrawTransparentControlBackground(AControl: TWinControl;
     ADrawCanvas: TCanvas; ADrawRect: TRect);

     procedure PaintControlTo(ADrawingControl: TWinControl;
       ADrawCanvas: TCanvas; AX, AY, AWidth, AHeight: Integer);

       function ControlInDrawingRect(AControl: TWinControl; AOrg: TPoint): Boolean;
       var
         ARect: TRect;
       begin
         ARect.TopLeft := AOrg;
         ARect.Top := ARect.Top + AControl.Top;
         ARect.Left := ARect.Left + AControl.Left;
         ARect.Bottom := ARect.Top + AControl.Height;
         ARect.Right := ARect.Left + AControl.Width;
         Result := IntersectRect(ARect, ARect, Rect(0, 0, AWidth, AHeight));
       end;

       procedure DrawEdgesAndBorders(ADrawCanvas: TCanvas);
       var
         AEdgeFlags, ABorderFlags: Integer;
         R: TRect;
       begin
         ABorderFlags := 0;
         AEdgeFlags := 0;
         with ADrawingControl do
         begin
           if GetWindowLong(Handle, GWL_EXSTYLE) and WS_EX_CLIENTEDGE <> 0 then
           begin
             AEdgeFlags := EDGE_SUNKEN;
             ABorderFlags := BF_RECT or BF_ADJUST
           end
           else
             if GetWindowLong(Handle, GWL_STYLE) and WS_BORDER <> 0 then
             begin
               AEdgeFlags := BDR_OUTER;
               ABorderFlags := BF_RECT or BF_ADJUST or BF_MONO;
             end;
           if ABorderFlags <> 0 then
           begin
             R := Rect(0, 0, Width, Height);
             DrawEdge(ADrawCanvas.Handle, R, AEdgeFlags, ABorderFlags);
             MoveWindowOrg(ADrawCanvas.Handle, R.Left, R.Top);
             IntersectClipRect(ADrawCanvas.Handle, 0, 0,
               R.Right - R.Left, R.Bottom - R.Top);
           end;
         end;
       end;

     var
       AOrg: TPoint;
       ASavedDC, AWindowRgnType, I: Integer;
       AWindowRgn: HRGN;
     begin
       AWindowRgn := CreateRectRgnIndirect(cxEmptyRect);
       try
         AWindowRgnType := GetWindowRgn(ADrawingControl.Handle, AWindowRgn);
         if AWindowRgnType = NULLREGION then
           Exit;
         ASavedDC := SaveDC(ADrawCanvas.Handle);
         ADrawingControl.ControlState := ADrawingControl.ControlState + [csPaintCopy];
         try
           MoveWindowOrg(ADrawCanvas.Handle, AX, AY);
           GetWindowOrgEx(ADrawCanvas.Handle, AOrg);
           AOrg.X := -AOrg.X;
           AOrg.Y := -AOrg.Y;
           with ADrawingControl do
           begin
             if AWindowRgnType = ERROR then
               IntersectClipRect(ADrawCanvas.Handle, 0, 0, Width, Height)
             else
             begin
               OffsetRgn(AWindowRgn, AOrg.X, AOrg.Y);
               ExtSelectClipRgn(ADrawCanvas.Handle, AWindowRgn, RGN_AND);
             end;
             if ADrawingControl <> AControl.Parent then
               DrawEdgesAndBorders(ADrawCanvas);
             ADrawCanvas.Lock;
             try
               Perform(WM_ERASEBKGND, ADrawCanvas.Handle, 0);
               Perform(WM_PAINT, ADrawCanvas.Handle, 0);
             finally
               ADrawCanvas.Unlock;
             end;
             for I := 0 to ControlCount - 1 do
             begin
               if Controls[I] = AControl then
                 Break;
               if Controls[I] is TWinControl then
                 if TWinControl(Controls[I]).Visible and
                   ControlInDrawingRect(TWinControl(Controls[I]), AOrg) then
                   begin
                     ADrawCanvas.Lock;
                     try
                       with TWinControl(Controls[I]) do
                         PaintControlTo(TWinControl(ADrawingControl.Controls[I]),
                           ADrawCanvas, Left, Top, AWidth, AHeight);
                     finally
                       ADrawCanvas.Unlock;
                     end;
                   end;
             end;
           end;
         finally
           ADrawingControl.ControlState := ADrawingControl.ControlState - [csPaintCopy];
           RestoreDC(ADrawCanvas.Handle, ASavedDC);
         end;
       finally
         DeleteObject(AWindowRgn);
       end;
     end;

    var
     AX, AY: Integer;
    begin
     if AControl.Parent <> nil then
     begin
       OffsetRect(ADrawRect, AControl.Left, AControl.Top);
       AX := - ADrawRect.Left;
       AY := - ADrawRect.Top;
       PaintControlTo(AControl.Parent, ADrawCanvas, AX, AY,
         ADrawRect.Right - ADrawRect.Left, ADrawRect.Bottom - ADrawRect.Top);
     end;
    end;

  • DimaBr (05.03.07 15:40) [33]
    RxLib

    procedure CopyParentImage(Control: TControl; Dest: TCanvas);
    var
     I, Count, X, Y, SaveIndex: Integer;
     DC: HDC;
     R, SelfR, CtlR: TRect;
    begin
     if (Control = nil) or (Control.Parent = nil) then Exit;
     Count := Control.Parent.ControlCount;
     DC := Dest.Handle;
    {$IFDEF WIN32}
     with Control.Parent do ControlState := ControlState + [csPaintCopy];
     try
    {$ENDIF}
       with Control do begin
         SelfR := Bounds(Left, Top, Width, Height);
         X := -Left; Y := -Top;
       end;
       { Copy parent control image }
       SaveIndex := SaveDC(DC);
       try
         SetViewportOrgEx(DC, X, Y, nil);
         IntersectClipRect(DC, 0, 0, Control.Parent.ClientWidth,
           Control.Parent.ClientHeight);
         with TParentControl(Control.Parent) do begin
           Perform(WM_ERASEBKGND, DC, 0);
           PaintWindow(DC);
         end;
       finally
         RestoreDC(DC, SaveIndex);
       end;
       { Copy images of graphic controls }
       for I := 0 to Count - 1 do begin
         if Control.Parent.Controls[I] = Control then Break
         else if (Control.Parent.Controls[I] <> nil) and
           (Control.Parent.Controls[I] is TGraphicControl) then
         begin
           with TGraphicControl(Control.Parent.Controls[I]) do begin
             CtlR := Bounds(Left, Top, Width, Height);
             if Bool(IntersectRect(R, SelfR, CtlR)) and Visible then begin
    {$IFDEF WIN32}
               ControlState := ControlState + [csPaintCopy];
    {$ENDIF}
               SaveIndex := SaveDC(DC);
               try
                 SaveIndex := SaveDC(DC);
                 SetViewportOrgEx(DC, Left + X, Top + Y, nil);
                 IntersectClipRect(DC, 0, 0, Width, Height);
                 Perform(WM_PAINT, DC, 0);
               finally
                 RestoreDC(DC, SaveIndex);
    {$IFDEF WIN32}
                 ControlState := ControlState - [csPaintCopy];
    {$ENDIF}
               end;
             end;
           end;
         end;
       end;
    {$IFDEF WIN32}
     finally
       with Control.Parent do ControlState := ControlState - [csPaintCopy];
     end;
    {$ENDIF}
    end;

  • DimaBr (05.03.07 15:40) [34]
    Продолжить ?
  • homm © (05.03.07 15:42) [35]
    > if not ( csDesigning in Control.ComponentState ) then
    > begin
    >   Control.Parent.Perform( wm_EraseBkgnd, DC, 0 );
    >   Control.Parent.Perform( wm_Paint, DC, 0 );
    > end
    > else
    > begin
    >   try
    >     Control.Parent.Perform( wm_EraseBkgnd, DC, 0 );
    >     Control.Parent.Perform( wm_Paint, DC, 0 );
    >   except
    >   end;
    > end;


    Отлично. Родителя я увижу (за счет его, дополнительной перерисовки, потеря производительности). А как насчет других компонентов, которые находятся под моим?
  • homm © (05.03.07 15:44) [36]

    > Продолжить ?

    Лучше ссылку дай, где можно скачать исходник хоть однго прозрачного компонента без дрожания, а то я для кфшяу нашел только 30-и метровую ссылищу :)
  • Darvin © (05.03.07 15:49) [37]
    IMHO дрожание бывает двух видов:
    - при перерисовке, связанной с изменением внешнего вида компонента,
    - при перерисовке, связанной с перемещениями / ресайзами окна.

    Первую я у себя лече двойной буферизацией, методом Макс Черных ©   (05.03.07 02:24) [2] , а вторую, устанавливая DoubleBuffered Parent-а, но на Parent-е, а не из компонента.
  • DimaBr (05.03.07 15:50) [38]
  • GrayFace © (06.03.07 11:50) [39]
    Извращенец. По-моему, подобные штуки надо в самом Parent делать.
    А зачем тебе UpdateRect? И че говорит Canvas.ClipRect?
    В VCL аналог SetWindowLong - TControl.WindowProc.
  • homm © (07.03.07 01:25) [40]
    > По-моему, подобные штуки надо в самом Parent делать.
    Как? Исходники VCL править?

    > А зачем тебе UpdateRect?
    10 раз же сказано, что-бы проверять реально ли нуждается контрол в перерисовке, или нет.

    > И че говорит Canvas.ClipRect?
    Canvas.ClipRect равен ClientRect, т.к.
    1) этот дс не окна, которое перерисовывается(родителя), а бирмапа буфера
    2) Это все равно будут КЛИЕНТСКИЕ координаты графического контрола.

    > В VCL аналог SetWindowLong - TControl.WindowProc.
    А различия в чем? Если только в том что на апи я реализовал, а через эту штуку нет, то нафиг нада.
    К тому-же
    Use the WindowProc property to temporarily replace or subclass the window procedure of the control.

  • GrayFace © (07.03.07 21:27) [41]
    homm ©   (07.03.07 1:25) [40]
    Как? Исходники VCL править?

    Создать какой-нибудь TGraphicContainer от панели, за одно и другие (не твои) контролы не будут мерцать.

    homm ©   (07.03.07 1:25) [40]
    А различия в чем? Если только в том что на апи я реализовал, а через эту штуку нет, то нафиг нада.

    WindowProc при RecreateWnd не пострадает.

    homm ©   (07.03.07 1:25) [40]
    К тому-же Use the WindowProc property to temporarily replace or subclass the window procedure of the control.

    Это же относится и к SetWindowLong, т.к. если появятся другие контролы, желающие его изменить (в частности, другие твои), будут проблемы.
  • homm © (07.03.07 22:42) [42]
    > WindowProc при RecreateWnd не пострадает.
    Странно, я думал при пересоздании SetParent вызывается. Проверил - нифига.
    Буду переделывать под WindowProc.

    > Создать какой-нибудь TGraphicContainer от панели, за одно
    > и другие (не твои) контролы не будут мерцать.
    Любой WinControl итак ГрфикКонтайнер по идее. Мне не понятно только почему он не просто перерисовывает что ни поподя, а еще и не дает возможности исправить это самому. Просто вызова GetUpdateRgn для приходящей DC и SelectClipRgn для буферной ИМХО было бы достаточно, что-бы можно было проверять GetClipRgn из процедуры рисования самого контрола.
  • GrayFace © (09.03.07 16:00) [43]
    Потестил. D7. Проблема не наблюдается. Перерисовывается только то, что надо.
  • homm © (09.03.07 23:42) [44]
    > Потестил. D7. Проблема не наблюдается. Перерисовывается
    > только то, что надо.

    Какая проблема? С использованием моего варианта ограничения пррисовки? У меня тоже не наблюдается. Или имеется ввиду сама проблема выполнении процедуры переисовки того что нужно и нет? Если 2-е, то ...

    Протестил. D5, D7, BDS2006. Проблема наблюдается. Выполняется код перерисовки и того что не надо в том числе.
  • GrayFace © (10.03.07 12:07) [45]
    Я тестировал без DoubleBuffered. При Invalidate перерисовывался только прямоугольник, в котором находится контрол.
  • homm © (10.03.07 16:00) [46]
    > Я тестировал без DoubleBuffered.
    Непонятно зачем впустую тратил время...
  • BOBAH13 © (11.03.07 21:31) [47]
    вообщем читаю и удивляюсь. вся проблемы всего в

    procedure WMEraseBkgnd( var Msg: TWMEraseBkgnd ); message WM_EraseBkgnd  



    просто опишите ее и код в ней просто begin end; :)

    у меня после этого все на ура! а чтобы не было мерцания то рисуйте все в памяти на TBitmap а потом на окно выкладывайте так быстрей и мерцания нет. в моем компоненте списке вообще никакого мерцания нет
  • homm © (11.03.07 21:48) [48]
    > BOBAH13

    Ну еще один :(
    Ну не дурак я. И не с ветряными мельницами мне нужно было боротся. Если 2 твоих компонента лежат один над нругим, то они оба накладываются попорядку, а значит в этот момент мерцают. Усе?
  • GrayFace © (12.03.07 03:15) [49]
    Перепроверил, при DeoubleBuffered перерисовываются все контролы, но дрожжания нет и не может быть.
  • homm © (12.03.07 08:36) [50]
    > Перепроверил, при DeoubleBuffered перерисовываются все контролы,
    > но дрожжания нет и не может быть.
    Правильно. Верно. 12-раз об этом в этой ветке. Не надоело одно и то-же мусолить?
  • DimaBr (12.03.07 09:45) [51]
    Начнём сначала. (под словом КОНТРОЛ я подразумеваю TGRushControl)
    Насколько я понял из всего выше сканного - требуется создать прозрачный контрол, который перерисовывает только свою видимую часть, дабы не сложилась такая ситуация - один прозрачный контрол перекрывает другой, при перерисовке рисуется сначала нижний, а потом верхний - тем самым область перекрытия сначала заполняется первым контролом, а затем вторым. Получаем ДРОЖАНИЕ.
    Как достигнуть данного я не представляю. Поскольку представляем такую ситуацию: имеется контрол с произвольным графическим рисунком, поверх него лежит другой контрол с рисунком имеющим прозрачные области. Что нужно сделать ? Нужно отрисовать области совпадающие с прозрачными областями в нижнем контроле. Есть какие-нибудь решения ?
  • homm © (12.03.07 16:01) [52]
    > Начнём сначала.
    Народ, это развод, да?


    > Есть какие-нибудь решения ?
    ДА!!!!! Вся ветка об это. Прокрути окно вверх. Если интересно, могу ехе выслать, где это уже работает на простом примере.
  • homm © (12.03.07 16:07) [53]
    > Насколько я понял из всего выше сканного - требуется создать
    > прозрачный контрол, который перерисовывает только свою видимую
    > часть, дабы не сложилась такая ситуация - один прозрачный
    > контрол перекрывает другой
    Неправильно понял. "требуется создать прозрачный контрол, который перерисовывает только свою видимую часть", дабы перерисовывалась только видимая часть, а на перерисовку остального не тратились рессурсы.


    > Есть какие-нибудь решения ?
    [5], [8], [14], и еще один ньюанс: дабы механизм не слетал при RecrateWnd добавляем первой строчкой в
    TGRushControl.Paint
    SetParent(Parent);

  • GrayFace © (12.03.07 16:39) [54]
    > Правильно. Верно. 12-раз об этом в этой ветке. Не надоело
    > одно и то-же мусолить?

    Учись говрить. Перечитай свои посты - там не слово про то что при DoubleBuffered дрожжания не было.
    И ты наворотил подмену WindowProc родителя (далеко не безопасую) только ради уменьшения нагрузки на процессор?
  • DimaBr (12.03.07 16:52) [55]

    > Неправильно понял. "требуется создать прозрачный контрол,
    >  который перерисовывает только свою видимую часть", дабы
    > перерисовывалась только видимая часть, а на перерисовку
    > остального не тратились рессурсы.

    Моя твоя непонимай.  Должен ли рисоваться ВЕСЬ нижний контрол если он перекрыт верхним с прозрачными областями ? Или должны отрисоваться только перекрытые прозрачные области ???
  • homm © (12.03.07 16:58) [56]
    > Учись говрить. Перечитай свои посты - там не слово про то
    > что при DoubleBuffered дрожжания не было.
    Учись читать.
    > [0] homm ©   (04.03.07 19:32)
    > Делаю графические контролы, обязательное требование у которых
    > - отсутствие дрожания. Естественно для этого сделано следующее:
    > procedure TGRushControl.SetParent(AParent: TWinControl);
    > begin
    > if AParent<>nil then begin
    >   AParent.DoubleBuffered := TRUE;
    > end;
    > inherited SetParent(AParent);
    > end;


  • homm © (12.03.07 17:11) [57]
    > Моя твоя непонимай.  Должен ли рисоваться ВЕСЬ нижний контрол
    > если он перекрыт верхним с прозрачными областями ? Или должны
    > отрисоваться только перекрытые прозрачные области ???

    Поступило сообщение на перерисовку родителя всех контроллов. При этом где-то внутри системы хранится область, которую нужно перерисовать. ВинКонтрол, у которого установлен ойно буфер, создает этот буфер, отрисовывает на нем все дочерние контролы, дальше что-бы сбросить отрисованые контролы в свое окно он вызывает BeginPaint. Вот тут то и начинается действо! Тот регон, который реально требуется для перерисовки достается из недр системы, и клипается для оконной канвы. В результате когда ВинКонтрол блитит туда свой буфер, на оконную канву попадает далеко не все изображение. Зато подготавливается все. А после вызова BeginPaint получить ту магическую область, которую нужно перерисовать не получается (с чего и начался разговор). Выход был найден в том что-бы тащит эту область ДО вызова BeginPaint ставля хук на родительское окно компонента.


    > Должен ли рисоваться ВЕСЬ нижний контрол если он перекрыт
    > верхним с прозрачными областями ?
    Должен, если в область перерисовки перекрывает его хотя-бы частично.
  • homm © (12.03.07 17:13) [58]
    > И ты наворотил подмену WindowProc родителя (далеко не безопасую)
    > только ради уменьшения нагрузки на процессор?

    Я обошелся средствами АПИ в этом вопросе, как еще наши деды делали, у них все безопасно было. В топку такое "расширение функционала" средстваим ВСЛ.
  • GrayFace © (12.03.07 21:43) [59]
    homm ©   (12.03.07 17:13) [58]
    Я обошелся средствами АПИ в этом вопросе, как еще наши деды делали, у них все безопасно было.

    Какая разница API или VCL? Это не безопасно. Вот пример:

    Control1.SetHook:  Control1.LastHook:=SetWindowLong(Control1.Hook);
    Control2.SetHook:  Control2.LastHook:=SetWindowLong(Control2.Hook);
    Control1.Destroy:  SetWindowLong(Control1.LastHook);
    Control2.Destroy:  SetWindowLong(Control2.LastHook);



    В результате у Parent'а останется Control1.Hook, а контрола уже не будет.
  • homm © (12.03.07 22:12) [60]
    > В результате у Parent'а останется Control1.Hook, а контрола
    > уже не будет.
    Чего?? "Control1.Hook" это что за хрень? Как ты на апи окно объектно-ориентрованный хук поставишь? И нафиг тебе в самом хуке нужен объект, его установивший? Ты его просто не возьмешь никак (нет, ну есть функция которая по хендлу возвратит компонент, но мы сейчас как раз говорим что так делать ненадо). А раз ты его не будешь использовать, то какая нафиг разница есть он или нет? См [14] и медитировать над тем что там написано в GRushWndProc.
  • GrayFace © (13.03.07 00:00) [61]
    Вместо Control1.Hook подставь ObjectInstance или статический метод, имеющий отношение к Control1. А Control2 может быть чужим.

    homm ©   (12.03.07 22:12) [60]
    См [14]

    Вижу. Ничего хорошего сказать не могу. Вместо запихивания в SetProp UpdateRect лучше сразу устанавливать на HDC, который в wParam приходит. (Случай wParam = 0 надо игнорировать)
  • homm © (13.03.07 06:59) [62]
    > Вместо Control1.Hook подставь ObjectInstance или статический
    > метод, имеющий отношение к Control1.
    Да не имеет GRushWndProc никакого отношения к Control1. Она работает только с апишным хэндлом окна. До существования Control1 ей нет дела. Если есть апишное окно, то ошибки не может быть, т.к. оно и нужно, а если его нет то ошибки не может быть по опредилению, потому что эта функция никак не может быть вызвана.


    > А Control2 может быть чужим.
    Тем более до него нет дела.


    > Вижу. Ничего хорошего сказать не могу. Вместо запихивания
    > в SetProp UpdateRect лучше сразу устанавливать на HDC, который
    > в wParam приходит. (Случай wParam = 0 надо игнорировать)
    Рассмотрим 2 случая.
    1) wParam == 0
    Можем получить UpdateRect, но не имеем в распоряжении канвы, т.к. канва создается позже, когда дело доходит до WinControl.WM_PAINT.

    2) wParam <> 0
    Имеем в распоряжении канву, на которой будет выводится изображение, с которой потом можно будет легко содрать ClipRect, НО в этот момент уже вызвана BeginPaint, а значит мы уже не можем получить тот региоин (или рект) который потребовался для перерисовки.


    > Случай wParam = 0 надо игнорировать
    Тут я задумался, почему я не игнарирую случай wParam <> 0, ведь по идее там SetProp(PropUpdateRect) должа перекрывать реальный рект, при случае когда приходит уже буферная канва, потом взглянул в генофонд, и все встало на свои места:

    procedure TWinControl.WMPaint(var Message: TWMPaint);
    ....
         DC := BeginPaint(Handle, PS);
         Perform(WM_ERASEBKGND, MemDC, MemDC);
         Message.DC := MemDC;
         WMPaint(Message);
         Message.DC := 0;
         BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC, 0, 0, SRCCOPY);
         EndPaint(Handle, PS);
    ....
    end;



    Так что "Вместо запихивания в SetProp UpdateRect лучше сразу устанавливать на HDC, который в wParam приходит." не имеет никакого смысла.
  • GrayFace © (13.03.07 12:16) [63]
    Действительно, BeginPaint уже вызван.
    > WMPaint(Message);
    И на это не обратил внимания.
    Посмотрел еще.
    Тут я задумался, почему я не игнарирую случай wParam <> 0
    Зря не игноришь, канва может прийти от PaintTo.

    > Да не имеет GRushWndProc никакого отношения к Control1.
    Ты заменил оконную процедуру, потом еще одному разработчику пришла в голову эта "гениальная" идея. Control1 = TGRushControl, Control1.Hook = GRushWndProc. Все будет работать только если порядок установки WndProc будет равен порядку восстановления.
  • homm © (13.03.07 22:01) [64]
    Все будет работать только если порядок установки WndProc будет равен порядку восстановления.
    Это и так понятно (только не равен а противоположен). Но я не знаю что я могу еще сделать что-бы обезапасить свой код. По идее  снимаю хук в WM_DESTROY, если все остальные будут снимать так-же, то все хуки снимутся в том порядке, в каком нужно. Есть мысль дополнительно проверять текущую WndProc на равенство GRushWndProc и ставить PrevWndProc только если так и есть. Вроде ничего страшного не случится, если не снять GRushWndProc вообше, или если его снимит нижестоящий хук (тут правда вроде как утечка памяти в связи с неснятым SetProp возникает, опять-же до уничтожения контролла (а он так и так на уничтожение собирается)).


    > Зря не игноришь, канва может прийти от PaintTo.
    Верно, подправлю.
  • GrayFace © (15.03.07 18:25) [65]
    В общем, такое надо делать в самом родителе.
  • homm © (15.03.07 23:09) [66]
    > В общем, такое надо делать в самом родителе.

    Так то оно так, но что-же мне VCL переписывать что-ли?
  • GrayFace © (16.03.07 16:23) [67]
    Свою панельку сделать. Я в свою, наверное, добавлю.
  • TStas © (18.03.07 19:24) [68]
    А нельзя,
    1) Рисовать на битмапе, как уже сказали
    2) Почем зре не перерисовывать, а только при действительной необходимости?
    Я там делал в последнем компоненте, который на MouseMove реагировать был должен, (ссылки перекрашивать) ничего не мигало нигде
  • homm © (18.03.07 20:41) [69]
    > ничего не мигало нигде

    Это видимость в следствии не заинтерисованости Вами в надлежащем тестировании "не мигания", либо плохого зрения.
  • GrayFace © (19.03.07 20:36) [70]
    TStas ©   (18.03.07 19:24) [68]
    1) Рисовать на битмапе, как уже сказали

    Зачем? Это и так делает DoubleBuffered.

    TStas ©   (18.03.07 19:24) [68]
    ничего не мигало нигде

    Видимо, csOpaque или вообще наследник TWinControl.
  • Passer - by (31.07.07 18:39) [71]

    > В общем, покритикуйте пожалуста мою реализацию

    Всё правильно, верный подход
  • Игорь Шевченко © (01.08.07 11:38) [72]
    DimaBr   (05.03.07 15:38) [32]

    У DevExpress круче. Только по-моему ты нарушаешь их авторское право, выкладывая исходник, нет ?

    По сабжу - я на bitmap'е рисую и не мучаюсь.
Есть новые Нет новых   [134427   +38][b:0][p:0.009]