我已经编码了许多图形控制多年,所以我知道双缓冲,dibs,bitblts和所有的“常见”的东西(我总是使用dibs绘制一切,如果可能,但有一个开销)。也知道关于InvalidateRect和检查TCanvas.ClipRect为需要更新的实际rect。尽管所有这些典型的解决方案,我发现很难创建相同的质量组件,如说Developer Express或Razed组件。如果图形是平滑的,你可以下注滚动条(本机)闪烁,如果滚动条和框架是平滑的,你可以在滚动期间发出背景闪烁。
有没有标准的代码设置来处理这个?一种最佳实践,确保整个控制的平滑重绘 – 包括控制的非客户区域?
例如,这里是一个“裸骨”控件,为分段更新(只需要重绘需要的高度)。如果在表单上创建它,请尝试在其上移动窗口,并观察它用颜色替换部件(请参见paint方法)。
有没有人有类似的基类可以处理非客户区重绘,没有闪烁?
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()当绘制非客户端区域,所以你不会重叠的图形在clientarea
>捕获WMNCCalcSize消息,而不仅仅是使用borderize进行测量。我也不得不采取高度的边缘大小:
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,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;
感谢帮助家伙!
解决方法
For instance,here is a “bare bone” control which take height for segmented updates (only redraw what is needed). If you create it on a form,try moving a window over it,and watch it replace the parts with colors (see paint method).
Does anyone have a similar base class that can handle non client area redraws without flickering?
嗯,你的TMyControl没有非客户区(尚)。所以我添加BorderWidth:= 10;现在它有。