我有一个第三方树包(LMD Innovative的ElXTree),我在程序中用作网格。每当我选择一个单元格时,该行就会获得焦点并突出显示,就像我想要的那样。如何查找在Delphi中发送消息的位置?
当我通过单击网格中的单元格调用提供的Inplace编辑器时,该行获得焦点。因为单元格是在编辑模式下选择的,所以只有单元格被突出显示(不是整行),也正如我所希望的那样。
我不想要的是这样的:当我就地编辑一个单元格时,通过单击它来调用另一个单元格的就地编辑器,首先将具有旧单元格的行赋予焦点并突出显示。然后立即将其焦点带走并且不被强调,并且具有新单元格的行被给予焦点和突出显示。然后,除了在位编辑的单元格之外,该新行立即变得不突出。这导致一个恼人的双闪烁,我想摆脱它。
我有包的源代码,我一直在通过它进行调试。我确定如果我能找到调用双重对焦的东西,我将能够弄清楚如何进行简单的修改以防止它。
当我放置断点时,我发现我在Forms单元中的TApplication.Run的消息处理循环中。这个循环处理的许多消息中的两个是设置焦点的消息。我可以将程序一行一行地追溯到StdWndProc的Classes单元,其中消息是Dispatched。我拥有关于消息的所有信息(Handle,Parameters等)。
我没有,也不知道消息是从哪里发起的。在调用堆栈中没有ElXTree单元来提示我。其中一个例程必须独立于当前调用堆栈发送消息。
如果我可以找出发送信息的位置(即发送的是哪个例程),那么我将关闭并运行。
有什么方法可以找到邮件的发送地点吗?或者,还有其他方法可以解决我遇到的这个双重焦点问题吗?
仅供参考,我使用德尔福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命令。当我寻找所有这些电话在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处理滚动条。接下来的3个是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上。我可能能够找到解决办法,但最初的尝试不成功。但它没有双闪一样坏,如果必须的话,我可以忍受它。
感谢那些帮助我推动大脑的人。接受的答案是David给了我关键线索。
......也许我说得太快了。我发现了一些其他控件,例如带有网格的页面在控件之间分页时不会更新。我试过在EndUpdate之后添加一个Refresh命令。一旦我做到了,我又重新闪了一次。这是一个真正的混乱问题。
我可能能够得到解决方法,但我希望该控件的开发人员能够对我做出更好的修复。
这样的事情不是编程的乐趣之一。 :-(
如果您有源代码,请在消息标识符(WM_Whatever)上执行grep,将日志记录添加到执行/发送它的例程中;为OnFocus *事件添加事件处理程序并在其中添加日志记录。这应该让你开始缩小范围。 –
需要做调试或运行时? –
@Marjan执行/发送不通过TApplication.Run –