20

我们正在 WPF 中开发一个 UI 控件,以便在现有的 Windows 窗体/MFC 应用程序引擎 (Rhino 3D) 中使用。

应用程序引擎公开了创建“Dockbar”的能力,它实质上允许您将 Windows 窗体控件放在可以停靠到引擎界面的子窗口中。

我试图将一个简单的 WPF TextBox 放在 ElementHost 控件中,该控件已添加到 Dockbar 中。乍一看,这似乎工作正常;但在尝试输入文本框后,只有某些序列实际显示在文本框中。DELETEBACKSPACECOPYPASTE和SELECTING TEXT工作。如果您键入 AZ、1-9 等,则不会显示这些键。

我已经在网上搜索过,并且听说过,ElementHost.EnableModelessKeyboardInterop()但这仅适用于从表单创建的 WPF Windows。我只是创建 WPF 用户控件并将它们托管在 ElementHost 控件中。

我看到一篇关于 Dispatcher.Run() 的帖子,它有点工作,但打破了表格的其余部分:

System.Windows.Threading.Dispatcher.Run();

PreviewKeyUpPreviewKeyDownKeyUpKeyDown事件都在 TextBox 上触发,但遗憾的是,TextBox 中没有显示任何文本。

我对 Windows 消息了解不多,但使用 WinSpector 我注意到没有 WM_GETTEXT 消息来自 TextBox(如果它们甚至应该是我不知道)。

我还创建了一个新的 Windows Forms 项目并在那里做了同样的事情,它工作正常,所以它必须是如何在 Rhino 3D 引擎中创建和停靠窗口的问题。

这是不起作用的示例代码:

ElementHost el = new ElementHost();
System.Windows.Controls.TextBox t = new System.Windows.Controls.TextBox();
t.Width = 100;
t.Text = "TEST";
el.Child = t;
panel1.Controls.Add(el);
4

4 回答 4

23

经过两天的抓头,我终于弄明白了……

MFC 对话框窗口正在接收WM_CHAR消息并阻止控件处理输入。因此,为了防止这种情况,我挂钩 HwndSource,每当我收到WM_GETDLGCODE消息时,我都会回复要接受的输入类型,然后将事件标记为已处理。

我创建了自己的 TextBox 以防止必须修复每个文本框(见下文):

    /// <summary>
    /// Interop Enabled TextBox : This TextBox will properly handle WM_GETDLGCODE Messages allowing Key Input
    /// </summary>
    class IOTextBox : 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 IOTextBox() : base()
        {
            Loaded += delegate
                          {
                              HwndSource s = HwndSource.FromVisual(this) as HwndSource;
                              if (s != null)
                                  s.AddHook(new HwndSourceHook(ChildHwndSourceHook));
                          };
        }

        IntPtr ChildHwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == WM_GETDLGCODE)
            {
                handled = true;
                return new IntPtr(DLGC_WANTCHARS | DLGC_WANTARROWS | DLGC_HASSETSEL);
            }
            return IntPtr.Zero;
        }
    }
于 2009-05-07T19:58:29.370 回答
12

看看我自己关于这件事的问题。最后,你所需要的只是这样的:

Window window1 = new Window();
ElementHost.EnableModelessKeyboardInterop(window1);
window1.Show();

为什么我的 WPF 文本框“有点”只读?

于 2009-05-08T13:30:49.050 回答
7

我对 wxWidgets 父窗口和嵌入的 WPF TextBox 控件有类似的问题。我发现虽然附加 ChildHwndSourceHook 确实解决了不接收键盘输入的问题,但我最终还是偶尔会出现重复的空格字符。似乎 WM_KEYDOWN 消息可靠地处理了空格字符,但是对于某些空格,也会收到重复的 WM_CHAR 消息。为了解决这个问题,我在 ChildHwndSourceHook 函数的主体中添加了以下子句,它只是忽略了 WM_CHAR 空格字符:

        const UInt32 WM_CHAR = 0x0102;

        if (msg == WM_CHAR)
        {
            // avoid duplicated spaces when parent window is a native window
            if (wParam.ToInt32() == 32)
                handled = true;
        }
于 2011-03-15T16:58:56.263 回答
7

不必创建派生的 TextBox。IOTextBox 的代码可用于承载文本框的 UserControl。我已经使用用于 VS2010 包中使用的自定义选项页面的 WPF 控件成功对其进行了测试。

于 2012-03-19T15:16:54.927 回答