WPF弹出窗口中的控件有时无法接收输入

时间:2018-01-26 21:45:10

标签: c# wpf input popup treeview

我在CheckBox中有一个WPF Popup,我发现它是否在TreeView的项目模板中,然后CheckBox没有响应用户输入。如果它在TreeView之外,则没有问题。

我在这里创建了一个相对最小的模型:

https://github.com/logiclrd/TestControlsInPopupsNotWorking

是否有人知道为什么无法检查CheckBox中弹出的TreeView控件?

2 个答案:

答案 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库中找到,其背后的代码可以在这里找到:

https://github.com/6A/Ryder

挂钩 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导入到我的项目中,以便更改一个成员。 : - )