如何在没有 Window 的情况下使用 KeyPresses 在 ElementHost 中托管 WPF UserControl

How to host a WPF UserControl in ElementHost without Window with KeyPresses

我使用 ElementHost 通过设置 Child [=32] 在 windows 应用程序中嵌入了 WPF 用户控件=] 到自定义用户控件。

不幸的是,我的文本框没有接收到任何按键输入,所以我根本无法使用我的加载项。

当我发现以下代码行时,我以为我找到了解决方法。

ElementHost.EnableModelessKeyboardInterop([System.Windows.Window]);

这将不起作用,因为我没有使用 window。其实没有办法在element host中托管一个System.Windows.Window或者用window作为host.

此插件在开发相当长一段时间之前没有任何文本输入功能,因此它已经完全停滞了。

如何让我的文本框接受输入?

我在网上找到了 link 子类 TextBox 并允许击键 original post

我稍作改动,但大体相同。它没有经过全面测试,但按键会进入文本框。

class TextInput : TextBox
{
    private const UInt32 DLGC_WANTARROWS = 0x0001;
    private const UInt32 DLGC_WANTTAB = 0x0002;
    private const UInt32 DLGC_WANTALLKEYS = 0x0004;
    private const UInt32 DLGC_HASSETSEL = 0x0008;
    private const UInt32 DLGC_WANTCHARS = 0x0080;
    private const UInt32 WM_GETDLGCODE = 0x0087;
    public TextInput()
    {
        Loaded += delegate
        {
            var s = PresentationSource.FromVisual(this) as HwndSource;
            s?.AddHook(ChildHwndSourceHook);
        };
    }
    static IntPtr ChildHwndSourceHook(IntPtr hwnd, int msg, 
         IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg != WM_GETDLGCODE) return IntPtr.Zero;
        handled = true;
        return new IntPtr(DLGC_WANTCHARS | DLGC_WANTARROWS | DLGC_HASSETSEL);
    }
}

另一个角度是创建元素主机的子class 并在那里添加代码。这样,如果有任何进一步的挂钩自定义,您可以在一个地方进行调整,它将级联到您所有的 wpf 控件。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using System.Windows.Interop;
using Automated.ToolWindow;

namespace Automated
{

    public class MyElementHost : ElementHost
    {
        protected override void Dispose(bool disposing)
        {
            if (_contentControl != null)
            {
                _contentControl.Loaded -= OnContentControlOnLoaded;
                _contentControl = null;
            }

            base.Dispose(disposing);
        }

        private ContentControl _contentControl;
        // Hide the child element.
        public new UIElement Child 
        {
            get { return base.Child; }
            set
            {
                _contentControl = new ContentControl();
                _contentControl.Loaded += OnContentControlOnLoaded;
                _contentControl.Content = value;
                base.Child = _contentControl;
            }
        }

        private void OnContentControlOnLoaded(object sender, RoutedEventArgs e)
        {
            var s = PresentationSource.FromVisual(_contentControl) as HwndSource;
            s?.AddHook(ChildHwndSourceHook);
        }

        private const UInt32 DLGC_WANTARROWS = 0x0001;
        private const UInt32 DLGC_WANTTAB = 0x0002;
        private const UInt32 DLGC_WANTALLKEYS = 0x0004;
        private const UInt32 DLGC_HASSETSEL = 0x0008;
        private const UInt32 DLGC_WANTCHARS = 0x0080;
        private const UInt32 WM_GETDLGCODE = 0x0087;

        static IntPtr ChildHwndSourceHook(IntPtr hwnd, 
          int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg != WM_GETDLGCODE) return IntPtr.Zero;
            handled = true;
            return new IntPtr(DLGC_WANTCHARS | DLGC_WANTARROWS | DLGC_HASSETSEL);
        }
    }
}

我添加主机的代码在执行后如下所示。

ElementHost = new MyElementHost()
{ 
    Child =  new RootXamlControl()
};

TaskPanes.Add(_host.TaskPanes.Add((int) ElementHost.Handle, "", 
      TaskPaneCaption, "Auto"));

旁注,如果您正在编写 COM 加载项并且不注意内存管理,那么对于那些不得不关心垃圾收集的人来说,您将获得全新的赞赏。来自 C#.Net 开发人员的演讲!