我认为这是TreeView设计上的疏忽。请看这个例子:
注:一些代码摘录已经被整理过,以避免换行。
internal void HandleMouseButtonDown()
{
if (!this.IsKeyboardFocusWithin)
{
if (_selectedContainer != null)
{
if (!_selectedContainer.IsKeyboardFocused)
_selectedContainer.Focus();
}
else
{
this.Focus();
}
}
}
该方法是从
TreeViewItem.OnMouseButtonDown
调用的,我们可以看到它是一个类级别的处理程序,被配置为
接收已处理事件:
EventManager.RegisterClassHandler(
typeof(TreeViewItem),
Mouse.MouseDownEvent,
new MouseButtonEventHandler(OnMouseButtonDown),
true);
我已经使用调试器核实过,当事件传递到TreeViewItem时,“Handled”已被设置为“true”。
当您按下鼠标左键时,复选框会开始进行推测性的“单击”操作,并将事件标记为已处理。通常,祖先元素不会看到已处理的事件冒泡,但在这种情况下,它明确要求这样做。
TreeView看到“this.IsKeyboardFocusWithin”解析为“false”,因为焦点元素位于另一个可视树中(弹出窗口),然后将焦点返回到TreeViewItem。
现在,如果您查看ButtonBase:
protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnLostKeyboardFocus(e);
if (ClickMode == ClickMode.Hover)
{
return;
}
if (e.OriginalSource == this)
{
if (IsPressed)
{
SetIsPressed(false);
}
if (IsMouseCaptured)
ReleaseMouseCapture();
IsSpaceKeyDown = false;
}
}
我们可以看到,当失去焦点时,
IsPressed
被设置为
false
。如果我们转到
OnMouseLeftButtonUp
,我们会看到这样的代码:
bool shouldClick = !IsSpaceKeyDown && IsPressed && ClickMode == ClickMode.Release
IsPressed
现在为false,单击操作永远不会完成,这是因为当您尝试单击按钮时,TreeViewItem
夺走了焦点。
Popup
实际上代表了可视树的一个新逻辑根 - 它是一个独立的顶级窗口,位于其所有者之上。这是否意味着事件无法从比Popup
更高的树上钩取?虽然我还没有测试过。 - Jonathan GilbertCheckBox
是ToggleButton
的子类,而TreeView
在内部使用ToggleButton
,所以我想知道它是否可能正在为ToggleButton
设置样式,但我通过将CheckBox
放入TreeView
的项模板中但在Popup
之外来排除了这种可能性 - 那个CheckBox
可以正常工作。Popup
似乎是导致问题的一个重要部分。 - Jonathan Gilbert