我使用的程序中有一个第三方树形包(LMD Innovative的ElXTree)。我将其作为网格来使用。每当我选择单元格时,该行会获得焦点并突出显示,这是我想要的效果。
当我点击网格中的单元格并调用提供的 Inplace 编辑器时,该行会获得焦点。由于单元格处于编辑模式,因此仅有的突出显示是单元格而非整个行,这也是我想要的效果。
但当我正在对一个单元格进行 Inplace 编辑时,如果我点击另一个单元格调用 Inplace 编辑器,则先前单元格所在的行会被赋予焦点和突出显示,然后该行焦点立即被设为失去且不再突出显示,而新单元格所在的行则会被赋予焦点和突出显示。然后,新行立即变为失去焦点且不再突出显示,除了正在进行 Inplace 编辑的单元格。这会导致令人烦恼的双重闪烁,我想摆脱它。
我拥有这个程序包的源代码,我一直在调试它。我相信,如果我能找到是哪个部分引起了双重聚焦,我就能够弄清楚如何进行简单的修改,以防止它。
当我设置断点时,我发现自己在 Forms 单元中的 TApplication.Run 的消息处理循环中。该循环正在处理许多消息,其中包括设置焦点的两条消息。通过逐行跟踪程序代码,我可以一直跟踪到 Classes 单元中的 StdWndProc,消息在那里被分派。我拥有关于消息(Handle、参数等)的所有信息。
我没有并且不知道消息从哪里开始发送。调用堆栈中没有 ElXTree 单元来提示我。其中一个例程必须在当前调用堆栈之外独立发送了消息。
如果我能找出是哪个例程发送了该消息(即谁发送了它),那么我就可以解决这个问题。
有没有办法找出消息从哪里开始发送的呢?或者说,是否有其他方法可以处理我遇到的这个双重聚焦问题?
供参考,我使用的是 Delphi 2009。
进一步信息:
ElXTree 有几十个自己的 Windows 消息可以使用。在我的情况下,与此相关的两个是:
当我在WMSetFocus例程中设置断点时,我得到以下调用堆栈: 调用堆栈中仅有的另一个ElXTree例程在第四行。
当我在这个例程中设置一个断点时,它似乎只通过“inherited”行并调用系统函数,最终到达StdWndProc来处理消息(正如我在原始问题中所描述的)。
追踪这个问题的准确性在于,我必须在调试代码的同时,在程序中进行鼠标点击并将鼠标指针保持在视觉控件上。如果在调试过程中移动或使用鼠标时出现错误,可能会导致额外的鼠标事件影响处理。 这使得调试变得非常困难。
但是我可以仔细地跟踪StdWndProc,并查看被发送的聚焦行的事件。 我似乎无法找到发出消息的问题所在。
那么,为什么我不知道是什么发出了消息? 嗯,我假设这是从PostMessage或SendMessage命令中发出的,正如David所说。 当我寻找ElXTree中所有这些调用的位置时,我只找到了这10个:
前7个涉及滚动条。接下来的3个与ColorChange有关。
我已经查看了所有其他LMD组件例程,以寻找消息发出的方式,但是那里没有任何有希望的东西。
所以我仍然卡住了,需要提示或线索,以找到请求将行聚焦的消息的发送者。
解决方法:
好吧,一旦我意识到Windows正在启动鼠标事件,我就能做些事情来停止大部分闪烁。不过这是一个真正的hack。如果有人知道更好的方法,我会很乐意听取建议。
在TElXTreeView.WndProc中,我用以下语句替换了继承语句:
当我点击网格中的单元格并调用提供的 Inplace 编辑器时,该行会获得焦点。由于单元格处于编辑模式,因此仅有的突出显示是单元格而非整个行,这也是我想要的效果。
但当我正在对一个单元格进行 Inplace 编辑时,如果我点击另一个单元格调用 Inplace 编辑器,则先前单元格所在的行会被赋予焦点和突出显示,然后该行焦点立即被设为失去且不再突出显示,而新单元格所在的行则会被赋予焦点和突出显示。然后,新行立即变为失去焦点且不再突出显示,除了正在进行 Inplace 编辑的单元格。这会导致令人烦恼的双重闪烁,我想摆脱它。
我拥有这个程序包的源代码,我一直在调试它。我相信,如果我能找到是哪个部分引起了双重聚焦,我就能够弄清楚如何进行简单的修改,以防止它。
当我设置断点时,我发现自己在 Forms 单元中的 TApplication.Run 的消息处理循环中。该循环正在处理许多消息,其中包括设置焦点的两条消息。通过逐行跟踪程序代码,我可以一直跟踪到 Classes 单元中的 StdWndProc,消息在那里被分派。我拥有关于消息(Handle、参数等)的所有信息。
我没有并且不知道消息从哪里开始发送。调用堆栈中没有 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例程在第四行。
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;
当我在这个例程中设置一个断点时,它似乎只通过“inherited”行并调用系统函数,最终到达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个涉及滚动条。接下来的3个与ColorChange有关。
我已经查看了所有其他LMD组件例程,以寻找消息发出的方式,但是那里没有任何有希望的东西。
所以我仍然卡住了,需要提示或线索,以找到请求将行聚焦的消息的发送者。
解决方法:
好吧,一旦我意识到Windows正在启动鼠标事件,我就能做些事情来停止大部分闪烁。不过这是一个真正的hack。如果有人知道更好的方法,我会很乐意听取建议。
在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命令。一旦我这样做了,又出现了双重闪烁。这是一个真正混乱的问题。
我可能能够找到分页的解决方法,但我希望那个控件的开发人员能够回应我并提供更好的修复方法。
编程中这样的事情绝非乐趣所在。:-(