如何在没有带Ke​​yPresses的窗口的ElementHost中托管WPF UserControl

时间:2015-09-11 16:24:38

标签: c# wpf com wpf-controls elementhost

我已经使用 ElementHost 在Windows应用程序中嵌入了WPF用户控件,方法是将 Child 属性设置为自定义用户控件。

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

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

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

这不起作用,因为我没有使用窗口。实际上,无法在元素主机中托管System.Windows.Window或使用该窗口作为主机。

这个加载项在开发过程中没有任何文本输入功能,所以事情已经完全停滞不前了。

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

1 个答案:

答案 0 :(得分:1)

我在网上找到了一个分包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);
    }
}

另一个角度是创建元素主机的子类并在那里添加代码。这样,如果有任何进一步的钩子定制,你可以在一个地方进行调整,它将级联到你所有的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 AddIn并且不注意内存管理,那么对于那些不得不关心垃圾收集的人来说,您将获得全新的欣赏。从C#.Net开发人员那里说出来!