Я думал, что могу просто выбросить это и просто спросить: я видел элементы управления Delphi, которые безупречны с точки зрения графических эффектов. Значение: не мерцание, секционированные обновления (только перерисовка раздела элемента управления, отмеченного как грязная) и плавная прокрутка.
За эти годы я кодировал множество графических элементов управления, поэтому я знаю о двойной буферизации, dibs, bitblts и всех "общих" материалах (я всегда использую dibs, чтобы рисовать все, если это возможно, но есть накладные расходы). Также знайте о InvalidateRect и проверяете TCanvas.ClipRect для фактического прямоугольника, который необходимо обновить. Несмотря на все эти типичные решения, мне очень сложно создавать те же компоненты качества, что и говорить - Developer Express или Razed Components. Если графика гладкая, вы можете поставить мерцание полосы прокрутки (родной), и если полосы прокрутки и рамки будут плавными, вы можете поклясться, что фликеры фона во время прокрутки.
Есть ли стандартная настройка кода для обработки этого? Какая-то лучшая практика, которая обеспечивает плавное перерисовку всего элемента управления - включая неклиентскую область элемента управления?
Например, вот элемент управления "голой кости", который занимает высоту для сегментированных обновлений (только перерисовывайте то, что необходимо). Если вы создадите его на форме, попробуйте переместить окно над ним и посмотрите, как он заменяет детали цветами (см. Метод рисования).
Есть ли у кого-нибудь аналогичный базовый класс, который может обрабатывать не-клиентские области без изменения?
type
TMyControl = Class(TCustomControl)
private
(* TWinControl: Erase background prior to client-area paint *)
procedure WMEraseBkgnd(var Message: TWmEraseBkgnd);message WM_ERASEBKGND;
Protected
(* TCustomControl: Overrides client-area paint mechanism *)
Procedure Paint;Override;
(* TWinControl: Adjust Win32 parameters for CreateWindow *)
procedure CreateParams(var Params: TCreateParams);override;
public
Constructor Create(AOwner:TComponent);override;
End;
{ TMyControl }
Constructor TMyControl.Create(AOwner:TComponent);
Begin
inherited Create(Aowner);
ControlStyle:=ControlStyle - [csOpaque];
end;
procedure TMyControl.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
(* When a window has this style set, any areas that its
child windows occupy are excluded from the update region. *)
params.ExStyle:=params.ExStyle + WS_CLIPCHILDREN;
(* Exclude VREDRAW & HREDRAW *)
with Params.WindowClass do
Begin
(* When a window class has either of these two styles set,
the window contents will be completely redrawn every time it is
resized either vertically or horizontally (or both) *)
style:=style - CS_VREDRAW;
style:=style - CS_HREDRAW;
end;
end;
procedure TMyControl.Paint;
(* Inline proc: check if a rectangle is "empty" *)
function isEmptyRect(const aRect:TRect):Boolean;
Begin
result:=(arect.Right=aRect.Left) and (aRect.Bottom=aRect.Top);
end;
(* Inline proc: Compare two rectangles *)
function isSameRect(const aFirstRect:TRect;const aSecondRect:TRect):Boolean;
Begin
result:=sysutils.CompareMem(@aFirstRect,@aSecondRect,SizeOf(TRect))
end;
(* Inline proc: This fills the background completely *)
Procedure FullRepaint;
var
mRect:TRect;
Begin
mRect:=getClientRect;
AdjustClientRect(mRect);
Canvas.Brush.Color:=clWhite;
Canvas.Brush.Style:=bsSolid;
Canvas.FillRect(mRect);
end;
begin
(* A full redraw is only issed if:
1. the cliprect is empty
2. the cliprect = clientrect *)
if isEmptyRect(Canvas.ClipRect)
or isSameRect(Canvas.ClipRect,Clientrect) then
FullRepaint else
Begin
(* Randomize a color *)
Randomize;
Canvas.Brush.Color:=RGB(random(255),random(255),random(255));
(* fill "dirty rectangle" *)
Canvas.Brush.Style:=bsSolid;
Canvas.FillRect(canvas.ClipRect);
end;
end;
procedure TMyControl.WMEraseBkgnd(var Message: TWmEraseBkgnd);
begin
message.Result:=-1;
end;
Обновление
Я просто хотел добавить, что трюк был комбинацией:
- ExcludeClipRect() при рисовании non-clientarea, поэтому вы не накладываетесь на графику в клиентской зоне
-
Захват сообщения WMNCCalcSize, а не просто использование границ для измерений. Мне также приходилось брать высоту для размеров кромок:
XEdge := GetSystemMetrics(SM_CXEDGE); YEdge := GetSystemMetrics(SM_CYEDGE);
-
Вызов RedrawWindow() со следующими флагами, когда у вас есть перемещенные полосы прокрутки или изменение размера:
mRect:=ClientRect; mFlags:=rdw_Invalidate or RDW_NOERASE or RDW_FRAME or RDW_INTERNALPAINT or RDW_NOCHILDREN; RedrawWindow(windowhandle,@mRect,0,mFlags);
-
При обновлении фона во время метода Paint() избегайте рисования по возможным дочерним объектам, например (см. RDW_NOCHILDREN, упомянутое выше):
for x := 1 to ControlCount do begin mCtrl:=Controls[x-1]; if mCtrl.Visible then Begin mRect:=mCtrl.BoundsRect; ExcludeClipRect(Canvas.Handle, mRect.Left,mRect.Top, mRect.Right,mRect.Bottom); end; end;
Спасибо за помощь ребятам!