关于继承自TGraphicControl的控件在位置变动后无法正确显示的问题

alloutoflove 2007-12-13 03:48:14
我写了一个类继承自TGraphicControl, 在运行时动态生成该控件( 没有经过注册, 只是用标准C++的继承 )
控件显示没问题... 但当在运行时更改其Left/Top属性移动其位置, 则显示不完整, 显示区域被限制在0, 0, Width, Height这样的区域里...
想来是因为Paint里给的Canvas的区域被限制了, 但看了一下TWinControl的PaintControls里的处理又是正常的(根据控件的Left/Top/Width/Height来自动限制), 原Paint函数处理如下:

TRect rt( Left, Top, Left + Width, Top + Height );

Canvas->Brush->Color = clWhite;
Canvas->FrameRect( rt );

rt.left++;
rt.top++;
rt.right--;
rt.bottom--;
Canvas->Brush->Color = clBlack;
Canvas->FrameRect( rt );

rt.left++;
rt.top++;
rt.right--;
rt.bottom--;
Canvas->Brush->Color = clWhite;
Canvas->FrameRect( rt );


于是我又尝试用API来画, DC还是用的Canvas的Handle, 问题依旧.... 所以就直接用其父窗口的整个区域DC去绘制, 结果就OK了...代码如下

HDC dc = GetDC( Parent->Handle );
TBrush *pBrush = new TBrush;
pBrush->Style = bsClear;
TPen *pPen = new TPen;

pPen->Color = clWhite;
SelectObject( dc, pBrush->Handle );
SelectObject( dc, pPen->Handle );
Rectangle( dc,
Left,
Top,
Left + Width,
Top + Height );

pPen->Color = clBlack;
SelectObject( dc, pPen->Handle );
Rectangle( dc,
Left + 1,
Top + 1,
Left + Width - 1,
Top + Height - 1 );

pPen->Color = clWhite;
SelectObject( dc, pPen->Handle );
Rectangle( dc,
Left + 2,
Top + 2,
Left + Width - 2,
Top + Height - 2 );

ReleaseDC( Parent->Handle, dc );
delete pBrush;
delete pPen;



希望有朋友帮忙回答下是为什么...
...全文
156 8 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
alloutoflove 2007-12-13
  • 打赏
  • 举报
回复
额..我晕了,上面的话收回,已经转换过DC了...完全是坐标引起的错误-_-

结帖~~~~~~~~~~~
alloutoflove 2007-12-13
  • 打赏
  • 举报
回复
额,毛毛过奖了-_-...

是我对坐标理解错了, 不过我说一下我的理解过程,虽然是错的..........

在PaintControls里的上面这些代码被这些代码包裹着: with TControl(FControls[I]) do ... end
所以这里的Perform(WM_PAINT, DC, 0);是执行子控件的WM_PAINT消息处理.相当于TControl(FControls[I]).Perform(WM_PAINT, DC, 0);


这里Perform时WM_PAINT参数的WPARAM消息是DC, 这个DC应该是父控件的全区域吧?
然后在子控件TGraphicControl响应WM_PAINT函数时的WMPaint()中有一句
Canvas.Handle := Message.DC;

如果我没理解错的话这里的Handle应该是那之前的DC..

而实际绘制时应该已经是新的DC了...不知道为什么好像被限制了一样...

难道TControlCanvas有别的猫腻-_-

我哪里理解错了.......




Waiting4you 2007-12-13
  • 打赏
  • 举报
回复
另外想想看你使用Canvas自画控件时需要考虑控件本身所在的位置吗?当然不用啦,否则不是很烦?
Waiting4you 2007-12-13
  • 打赏
  • 举报
回复
楼主有偷懒之嫌哦,以楼主的实力找出问题所在应该不是问题啊:)

在PaintControls里的上面这些代码被这些代码包裹着: with TControl(FControls[I]) do ... end
所以这里的Perform(WM_PAINT, DC, 0);是执行子控件的WM_PAINT消息处理.相当于TControl(FControls[I]).Perform(WM_PAINT, DC, 0);

再看TGraphicControl的,它的构造里初始化了设置了Canvas,注意使用的是TControlCanvas
constructor TGraphicControl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FCanvas := TControlCanvas.Create;
TControlCanvas(FCanvas).Control := Self;
end;


然后跟进TControlCanvas代码,当要用它画图时,它要确保有有效DC,这时会调用CreateHandle方法
procedure TControlCanvas.CreateHandle;
begin
if FControl = nil then inherited CreateHandle else
begin
if FDeviceContext = 0 then
begin
with CanvasList.LockList do
try
if Count >= CanvasListCacheSize then FreeDeviceContext;
FDeviceContext := FControl.GetDeviceContext(FWindowHandle); // 这里调用了之前说的GetDeviceContext
Add(Self);
finally
CanvasList.UnlockList;
end;
end;
Handle := FDeviceContext;
UpdateTextFlags;
end;
end;


最后很自然的,TGraphicControl有一个WMPaint处理WM_PAINT,它直接调用了虚函数Paint()。 当你使用它的Canvas时,之前所述的一切开始了...

alloutoflove 2007-12-13
  • 打赏
  • 举报
回复
谢谢毛毛的回答...
那个GetDeviceContext是在什么时候调用的?
你看一下controls.pas中TWinControl.PaintControls()函数( 这函数在TWinControl的WM_PAINT响应处理中被调用 )中的

SaveIndex := SaveDC(DC);
MoveWindowOrg(DC, Left, Top);
IntersectClipRect(DC, 0, 0, Width, Height);
Perform(WM_PAINT, DC, 0);
RestoreDC(DC, SaveIndex);


依这个看Perform时给的DC应该是正常的...

还有我之前做过类似的东西,只不过是不自己继承, 是直接修改TLabel位置的,不会有那样的问题...TLabel同样继承自TGraphicControl..
-_-
Waiting4you 2007-12-13
  • 打赏
  • 举报
回复
试了一下,明白了

//只要把第一行
TRect rt( Left, Top, Left + Width, Top + Height );
//改成:
TRect rt = ClientRect;

就可以了, 因为这个Canvas是针对你的TGraphicControl的,也就是说不管这个控件在什么地方,它的左上角的DC总是(0,0).想来它应该使用了API SetViewportOrgEx重定位了DC的原点,用SetViewportOrgEx查找可以发现:

function TControl.GetDeviceContext(var WindowHandle: HWnd): HDC;
begin
if Parent = nil then
raise EInvalidOperation.CreateFmt(SParentRequired, [Name]);
Result := Parent.GetDeviceContext(WindowHandle);
SetViewportOrgEx(Result, Left, Top, nil);
IntersectClipRect(Result, 0, 0, Width, Height);
end;
Waiting4you 2007-12-13
  • 打赏
  • 举报
回复
做个标记,研究一下再回答:)
i_love_pc 2007-12-13
  • 打赏
  • 举报
回复
友情支持!

604

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder VCL组件使用和开发
社区管理员
  • VCL组件使用和开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧