如何在elementthost中托管一个没有窗口的WPF用户控件

本文关键字:一个 窗口 控件 用户 WPF elementthost | 更新日期: 2023-09-27 18:12:33

我使用ElementHost属性设置为自定义用户控件,在windows应用程序中嵌入了一个WPF用户控件。

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

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

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

这将不工作,因为我没有使用窗口。事实上,没有办法在元素宿主中驻留System.Windows.Window,或者使用该窗口作为宿主。

这个插件没有任何文本输入功能,直到开发到相当长的一段时间,所以事情已经完全停滞不前。

我怎样才能让输入被我的文本框接受?

如何在elementthost中托管一个没有窗口的WPF用户控件

我在网上找到了一个链接,子类TextBox和允许按键原创文章

我稍微改变了一些东西,但它几乎是一样的。它还没有完全测试过,但是按键进入了文本框。

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);
    }
}

另一个角度是创建元素宿主的子类并在那里添加代码。这样,如果有任何进一步的钩子定制,你可以在一个地方调整,它将级联到所有的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#说的。网络开发人员!