Я подумал, что могу просто выбросить это и просто спросить: я видел элементы управления Delphi, которые безупречны с точки зрения графических эффектов. Значение: без мерцания, разделенные обновления (перерисовывать только ту часть элемента управления, которая помечена как грязная) и плавная прокрутка.
За эти годы я закодировал множество графических элементов управления, поэтому я знаю о двойной буферизации, dibs, bitblts и всем "общем" (я всегда использую dibs для рисования всего, если это возможно, но это накладные расходы). Также знаю о InvalidateRect и проверке TCanvas.ClipRect на фактический прямоугольник, который необходимо обновить. Несмотря на все эти типичные решения, мне очень трудно создайте компоненты такого же качества, как, скажем, - Developer Express или Razed Components. Если графика гладкая, вы можете сделать ставку на мерцание полос прокрутки (собственное), а если полосы прокрутки и рамка гладкие, вы можете ругаться, что фон мерцает во время прокрутки. там стандартный se набор кода, чтобы справиться с этим? Это своего рода передовой опыт, который обеспечивает плавное перерисовку всего элемента управления, включая неклиентскую область элемента управления?
Например, вот элемент управления «голая кость», который принимает высоту для сегментированных обновлений (перерисовывает только то, что есть необходимо). Если вы создаете его в форме, попробуйте переместить окно над ним и посмотреть, как он заменяет части цветами (см. Метод рисования).
Есть ли у кого-нибудь аналогичный базовый класс, который может обрабатывать перерисовку неклиентской области без мерцания?
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;
Обновлено
Я просто хотел добавить, что трюк был комбинацией:
Захват WMNCCalcSize сообщение, а не просто использовать размер границы для измерений. Мне также пришлось взять высоту для размеров ребер:
XEdge: = GetSystemMetrics (SM_CXEDGE);
YEdge: = GetSystemMetrics (SM_CYEDGE);
Вызов RedrawWindow () со следующими флагами всякий раз, когда у вас есть перемещенные полосы прокрутки или изменение размера:
mRect: = ClientRect;
mFlags:= rdw_Invalidate
или RDW_NOERASE
или RDW_FRAME
или RDW_INTERNALPAINT
или RDW_NOCHILDREN;
RedrawWindow (дескриптор окна, @ mRect, 0, mFlags);
При обновлении фона во время метода Paint () избегайте рисования поверх возможных дочерних объектов, как это (см. RDW_NOCHILDREN, упомянутый выше):
for x: = 1 to ControlCount do
начать
mCtrl: = Controls [x-1];
если mCtrl.Visible, то
Начать
mRect: = mCtrl.BoundsRect;
ExcludeClipRect (Canvas.Handle,
mRect.Left, mRect.Top,
mRect.Right, mRect.Bottom);
конец;
конец;
Спасибо за помощь, ребята!