我在CheckBox
中有一个WPF Popup
,我发现它是否在TreeView
的项目模板中,然后CheckBox
没有响应用户输入。如果它在TreeView
之外,则没有问题。
我在这里创建了一个相对最小的模型:
https://github.com/logiclrd/TestControlsInPopupsNotWorking
是否有人知道为什么无法检查CheckBox
中弹出的TreeView
控件?
答案 0 :(得分:3)
我认为这是对TreeView
设计的疏忽。看看这个:
注意:为了避免包装,我们整理了一些代码摘录。
// This method is called when MouseButonDown on TreeViewItem and also listen
// for handled events too. The purpose is to restore focus on TreeView when
// mouse is clicked and focus was outside the TreeView. Focus goes either to
// selected item (if any) or treeview itself
internal void HandleMouseButtonDown()
{
if (!this.IsKeyboardFocusWithin)
{
if (_selectedContainer != null)
{
if (!_selectedContainer.IsKeyboardFocused)
_selectedContainer.Focus();
}
else
{
// If we don't have a selection - just focus the TreeView
this.Focus();
}
}
}
这个方法是从TreeViewItem.OnMouseButtonDown
调用的,我们可以看到它是一个类级别的处理程序,它被配置为接收处理过的事件:
EventManager.RegisterClassHandler(
typeof(TreeViewItem),
Mouse.MouseDownEvent,
new MouseButtonEventHandler(OnMouseButtonDown),
/* handledEventsToo: */ true);
我已向调试人员验证,当事件发送到Handled
时,true
设置为TreeViewItem
。
当您在CheckBox
上方按下鼠标左键时,CheckBox
开始投机点击'操作并将事件标记为已处理。通常情况下,祖先元素不会看到处理事件冒泡,但在这种情况下它明确要求它们。
TreeView
看到this.IsKeyboardFocusWithin
解析为false
,因为焦点元素位于另一个可视树(弹出窗口)中。然后它将重点放回TreeViewItem
。
现在,如果您查看ButtonBase
:
protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnLostKeyboardFocus(e);
if (ClickMode == ClickMode.Hover)
{
// Ignore when in hover-click mode.
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
会将注意力从您身边移开。
答案 1 :(得分:0)
作为一种解决方法,到目前为止,我使用NuGet库Ryder(看起来像是Microsoft Detours的可自由使用的开源(MIT许可证)版本)来截取TreeView
方法在HandleMouseButtonDown
。
Ryder库可以在NuGet库中找到,其背后的代码可以在这里找到:
挂钩 var realMethod = typeof(System.Windows.Controls.TreeView).GetMethod("HandleMouseButtonDown", BindingFlags.Instance | BindingFlags.NonPublic);
var replacementMethod = typeof(Program).GetMethod(nameof(TreeView_HandleMouseButtonDown_shim), BindingFlags.Static | BindingFlags.NonPublic);
Redirection.Redirect(realMethod, replacementMethod);
方法非常简单:
static void TreeView_HandleMouseButtonDown_shim(TreeView @this)
{
// Fix as seen in: https://developercommunity.visualstudio.com/content/problem/190202/button-controls-hosted-in-popup-windows-do-not-wor.html
if (!@this.IsKeyboardFocusWithin)
{
// BEGIN NEW LINES OF CODE
var keyboardFocusedControl = Keyboard.FocusedElement;
var focusPathTrace = keyboardFocusedControl as DependencyObject;
while (focusPathTrace != null)
{
if (ReferenceEquals(@this, focusPathTrace))
return;
focusPathTrace = VisualTreeHelper.GetParent(focusPathTrace) ?? LogicalTreeHelper.GetParent(focusPathTrace);
}
// END NEW LINES OF CODE
var selectedContainer = (System.Windows.Controls.TreeViewItem)TreeView_selectedContainer_field.GetValue(@this);
if (selectedContainer != null)
{
if (!selectedContainer.IsKeyboardFocused)
selectedContainer.Focus();
}
else
{
// If we don't have a selection - just focus the treeview
@this.Focus();
}
}
}
替换方法的垫片基本上可以做真正的方法所做的,但是使用一个检测跨视觉树焦点情况的修复:
TreeView
需要进行一些反思,因为这与私有字段相互作用,而私有字段没有从TreeView
类中暴露出来,但是随着解决方法的进行,这比我最初尝试的更少侵入,这是将整个-- HTML --
<div class-name="div-3"></div>
-- CSS --
.div-(x) {
width: 100px * x;
}
类(及相关类型)从Reference Source导入到我的项目中,以便更改一个成员。 : - )