我有一个第三方树包(ElXTree由LMD创新),我正在使用作为一个网格在我的程序。每当我选择一个单元格时,该行就会获得焦点,并将其突出显示,就像我想要的那样。
当我通过单击网格中的一个单元格来调用提供的内部编辑器时,该行将获得焦点。因为单元格是在编辑模式下选择的,所以只有单元格才会被高亮显示(而不是整行),这也是我想要的。
我不想要的是:当我在内部编辑一个单元格时,通过单击另一个单元格来调用另一个单元格的内部编辑器,首先给出带有旧单元格的行的焦点并突出显示。然后,它立即将其焦点移除,并且没有突出显示,新单元格中的行被指定为焦点并突出显示。然后,该新行将立即取消突出显示,除非单元格是在内部编辑的。这导致了一个恼人的双闪烁,我想摆脱它。
我有这个包的源代码,我一直在调试它。我确信,如果我能找到什么是调用双聚焦,我将能够找到如何做简单的修改,以防止它。
当我放置断点时,我发现我在表单单元中的TApplication.Run消息处理循环中。这个循环处理的许多消息中有两条是用来设置焦点的。我可以将程序逐行跟踪到类单元中的StdWndProc,在该类单元中发送消息。我有关于消息的所有信息(句柄、参数等)。
我没有也不知道的是消息是从哪里开始的。调用堆栈中没有ElXTree单元来提示我。其中一个例程必须发送了独立于当前调用堆栈的消息。
如果我能知道该消息是从哪里发送的(即哪个例程发送的),那么我就可以开始运行了。
有什么方法可以找到信息是从哪里发送的吗?或者,还有什么别的方法可以让我避开这个双重聚焦问题呢?
作为参考,我正在使用Delphi 2009。
更多信息:
ElXTree有几十条自己使用的Windows消息。就我而言,这两个相关的问题是:
procedure WMSetFocus(var Msg: TWMSetFocus); message WM_SETFOCUS;
procedure WMKillFocus(var Msg: TWMKillFocus); message WM_KILLFOCUS;
procedure TElXTreeView.WMSetFocus(var Msg: TWMSetFocus); { private }
begin
inherited;
FHasFocus := True;
if (FOwner.HideSelection or (FOwner.HideSelectColor <> FOwner.FocusedSelectColor) or (FOwner.HideSelectTextColor <> FOwner.FocusedSelectTextColor)) and
(FOwner.Items.Count > 0) then
Invalidate;
with FOwner do
if Flat or FUseCustomScrollBars or IsThemed then
UpdateFrame;
end; { WMSetFocus }
procedure TElXTreeView.WMKillFocus(var Msg: TWMKillFocus); { private }
begin
FMouseSel := False;
FPressed := False;
FHasFocus := False;
inherited;
FHintItemEx := nil;
DoHideLineHint;
if HandleAllocated then
begin
with FOwner do
if Flat or FUseCustomScrollBars or IsThemed then
begin
UpdateFrame;
DrawFlatBorder(False, False);
if FUseCustomScrollBars then
begin
HScrollBar.HideHint;
VScrollBar.HideHint;
end;
end;
if (FOwner.HideSelection or (FOwner.HideSelectColor <> FOwner.FocusedSelectColor) or (FOwner.HideSelectTextColor <> FOwner.FocusedSelectTextColor)) and
(FOwner.Items.Count > 0) then
Invalidate;
end;
end; { WMKillFocus }当我在WMSetFocus例程中放置一个断点时,我得到以下调用堆栈:

调用堆栈中唯一的其他ElXTree例程是第4行中的一个:
procedure TElXTreeView.WndProc(var Message: TMessage);
var P1: TPoint;
Item: TElXTreeItem;
HCol: Integer;
IP: TSTXItemPart;
begin
if (FHintItem <> nil) and (FOwner.FHideHintOnMove) then
begin
if ((Message.Msg >= WM_MOUSEMOVE) and (Message.Msg <= WM_MOUSELAST)) or (Message.Msg = WM_NCMOUSEMOVE) then
begin
GetCursorPos(P1);
P1 := ScreenToClient(P1);
Item := GetItemAt(P1.X, P1.Y, IP, HCol);
if Item <> FHintItem then
DoHideLineHint;
inherited;
Exit;
end
else
if
((Message.Msg >= WM_KEYFIRST) and (Message.Msg <= WM_KEYLAST)) or
((Message.Msg = CM_ACTIVATE) or (Message.Msg = CM_DEACTIVATE)) or
(Message.Msg = CM_APPKEYDOWN) or (Message.Msg = CM_APPSYSCOMMAND) or
(Message.Msg = WM_COMMAND) or
((Message.Msg > WM_MOUSEMOVE) and (Message.Msg <= WM_MOUSELAST))
or (Message.Msg = WM_NCMOUSEMOVE) then
DoHideLineHint;
end;
if (FHintItem <> nil) and ((Message.Msg = CM_ACTIVATE) or (Message.Msg = CM_DEACTIVATE))
or (Message.Msg = WM_NCMOUSEMOVE) then
DoHideLineHint;
inherited;
end;当我在这个例程中放置一个断点时,它似乎只传递到“继承”行,然后调用系统函数,最终到达处理消息的StdWndProc (正如我在最初的问题中所描述的)。
精确跟踪所涉及的问题是,我必须单击鼠标,并将鼠标指针保存在程序中的可视控件之上,同时还要通过代码进行调试。在调试时移动或使用我的鼠标的任何错误都会导致额外的鼠标事件影响处理,这使调试成为一个真正的错误。
但是,我可以小心地跟踪到StdWndProc,并看到被分派的事件,该事件使行的焦点突出。我不能做的似乎是找出什么问题的信息。
现在,为什么我不知道这个信息有什么问题呢?好吧,我假设它来自PostMessage或SendMessage命令,就像David所说的那样。当我在ElXTree中查找所有这些调用的位置时,我只找到以下10个:
Result := SendMessage(hWnd, SBM_SetScrollInfo, Integer(Redraw), Integer(@ScrollInfo));
SendMessage(hWnd, SBM_GetScrollInfo, 0, Integer(@ScrollInfo));
SendMessage(FHScrollBar.Handle, Message.Msg, Message.wParam, Message.lParam);
SendMessage(FVScrollBar.Handle, Message.Msg, Message.wParam, Message.lParam);
case Key of
VK_LEFT: begin
PostMessage(FOwner.Handle, WM_HSCROLL, SB_LINELEFT, 0);
Exit;
end;
VK_RIGHT: begin
PostMessage(FOwner.Handle, WM_HSCROLL, SB_LINERIGHT, 0);
Exit;
end;
end;
FScrollbarsInitialized := True;
if UseCustomScrollbars then
PostMessage(Handle, WM_UPDATESBFRAME, 0, 0);
end;
procedure TCustomElXTree.WMSysColorChange(var Msg: TWMSysColorChange);
begin
inherited;
PostMessage(FVScrollBar.Handle, Msg.Msg, TMessage(Msg).WParam, TMessage(Msg).LParam);
PostMessage(FHScrollBar.Handle, Msg.Msg, TMessage(Msg).WParam, TMessage(Msg).LParam);
PostMessage(FHeader.Handle, Msg.Msg, TMessage(Msg).WParam, TMessage(Msg).LParam);
end; { WMSysColorChange }第一个7处理滚动条。接下来的三个是ColorChange。
我已经查看了所有其他LMD组件例程,以及消息的发布,没有任何东西看起来很有希望。
所以我仍然被困住了,我需要一个提示或者一个线索来找到信息的发送者,这是要求线路被聚焦的。
解决办法:
嗯,一旦我意识到Windows启动了鼠标事件,我就能做一些事情来阻止大部分的闪烁。不过,这是个真正的黑客。如果有人知道更好的事情,我很想听听。
在TElXTreeView.WndProc中,我用以下语句替换了继承的语句:
if (Message.Msg = WM_SETFOCUS) or (Message.Msg = WM_KILLFOCUS) then begin
FOwner.Items.BeginUpdate;
inherited;
FOwner.Items.EndUpdate;
end
else
inherited;这样做是阻止多重聚焦发生在被调用的例程中。
除了一种情况外,它可以完成其他工作:在我单击可编辑条目的地方,它仍然会在进入编辑模式之前突出显示该条目。这是因为高亮显示发生在MouseDown上,而进入编辑模式则发生在MouseUp上。我也许能找到解决这个问题的方法,但最初的尝试没有成功。但它并没有双重闪光灯那么糟糕,如果有必要的话,我可以忍受它。
感谢你们帮助我的大脑。公认的答案是大卫,他给了我关键的线索。
..。也许我说得太早了。我发现了其他一些控件,例如,在控件之间分页时,带有网格的页面不会更新。我尝试在EndUpdate之后添加一个刷新命令。一旦我这么做了,我就再次得到了双闪烁。这是个非常麻烦的问题。
我可能能够找到分页的解决办法,但我希望该控件的开发人员能够更好地响应我的要求。
像这样的事情不是编程的乐趣之一。:-(
发布于 2011-06-23 07:16:13
这些消息被发送到消息队列,而不是同步发送。这一点很清楚,因为您要将它们追溯到TApplication.Run,这是一个例程,用于泵出主线程的消息队列。这就是为什么你没有看到堆栈上的呼叫站点。它们是通过调用PostMessage生成的,或者是在第三方组件中,也可能是由Windows生成的。
我不知道这些组件,所以我怀疑我能否帮助解决你的问题。我认为您应该与组件供应商联系,后者应该知道该做什么。
https://stackoverflow.com/questions/6449504
复制相似问题