在 C#/XAML 应用程序中无法绑定到属于 WindowsFormsHost 子对象的 属性 的解决方法?

Workaround for inability to bind to a property that belongs to a WindowsFormsHost Child object in C#/XAML app?

我有一个 C# WPF 4.51 应用程序。据我所知,您不能绑定到属于 WPF WindowsFormsHost 控件的子对象的 属性。 (如果我的假设有误,请告诉我怎么做):

Bind with WindowsFormsHost

就我而言,我有一个包含 WindowsFormsHost 控件的页面,其 Child 对象是 ScintillaNET 编辑器控件:

https://github.com/jacobslusser/ScintillaNET

    <WindowsFormsHost x:Name="wfhScintillaTest"
                      Width="625"
                      Height="489"
                      Margin="206,98,0,0"
                      HorizontalAlignment="Left"
                      VerticalAlignment="Top">
        <WindowsFormsHost.Child>
            <sci:Scintilla x:Name="scintillaCtl" />
        </WindowsFormsHost.Child>
    </WindowsFormsHost>

子控件可以正常工作并显示。如果它是一个普通的 WPF 控件,我会将 Scintilla 编辑器控件的 Text 属性 绑定到某个 字符串 属性 在我的 ViewModel 中,所以我只需要更新 Scintilla 编辑器控件的内容正在更新 string 属性。

但是因为我无法绑定到属于 WindowsFormsHost 子对象的 属性,所以我正在寻找一个 strategy/solution 不完全尴尬或笨拙。有没有人以前遇到过这种情况并且有解决我的 binding/update 问题的合理策略?

您只能通过 Windows 表单控件实现非常非常有限的绑定,因为它们的绑定不会收到更新通知,可能需要通过自定义 [=11] 显式轮询才能获得结果=] 方法或轮询每条数据的方法。

但是如果你只需要访问子控件,你应该在代码中进行绑定:

(WFH.Child as MyWinFormsControl).Text

如果您打算进行大量绑定,创建一个 WPF 包装器对象(可能 UserControl)可能会更容易,它具有您需要的所有属性作为 DependencyProperties 和基础代码,每个 属性 将手动轮询 WinForms 控件,就好像它是 属性 的支持字段一样。起初有点复杂,但比手动轮询每个 属性.

更容易

这里的一个简单方法是您可以创建一些专用的 class 来包含映射到 winforms 控件中的属性的附加属性。在这种情况下,我只是选择 Text 作为示例。使用这种方法,您仍然可以正常设置绑定,但附加属性将用于 WindowsFormsHost:

public static class WindowsFormsHostMap
{
    public static readonly DependencyProperty TextProperty
        = DependencyProperty.RegisterAttached("Text", typeof(string), typeof(WindowsFormsHostMap), new PropertyMetadata(propertyChanged));
    public static string GetText(WindowsFormsHost o)
    {
        return (string)o.GetValue(TextProperty);
    }
    public static void SetText(WindowsFormsHost o, string value)
    {
        o.SetValue(TextProperty, value);
    }
    static void propertyChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var t = (sender as WindowsFormsHost).Child as Scintilla;
        if(t != null) t.Text = Convert.ToString(e.NewValue);
    }
}

在XAML中的用法:

<WindowsFormsHost x:Name="wfhScintillaTest"
                  Width="625"
                  Height="489"
                  Margin="206,98,0,0"
                  HorizontalAlignment="Left"
                  VerticalAlignment="Top"
                  local:WindowsFormsHostMap.Text="{Binding yourTextProp}"
    >
    <WindowsFormsHost.Child>
        <sci:Scintilla x:Name="scintillaCtl"/>
    </WindowsFormsHost.Child>
</WindowsFormsHost>

Child当然应该是Scintilla,否则需要修改WindowsFormsHostMap的代码。无论如何,这只是为了展示这个想法,您可以随时对其进行调整以使其变得更好。

请注意,上面的代码仅适用于一种方式绑定(从视图模型到您的 winforms 控件)。如果您想要另一种方式,则需要为控件注册一些事件处理程序并将值更新回该处理程序中附加的 属性。那样就很复杂了。

创建类型为 Windows Form Host 的依赖对象。

using System.Windows.Forms.Integration;

namespace MainStartUp.DependencyObjects
{
    public class FormHostDependencyObject : WindowsFormsHost
    {
        public static readonly DependencyProperty ContentControlProperty =
            DependencyProperty.Register("ContentControl", typeof(System.Windows.Forms.Control), 
                typeof(FormHostDependencyObject),
                new PropertyMetadata(new System.Windows.Forms.Control(), PropertyChaged));       

        public static void SetContentControl(UIElement element, string value)
        {  
            element.SetValue(ContentControlProperty, value);          
        }

        public static string GetContentControl(UIElement element)
        {
            return (string)element.GetValue(ContentControlProperty);
        }

        private static void PropertyChaged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            ((FormHostDependencyObject)dependencyObject).Child = (System.Windows.Forms.Control)e.NewValue;
        }
    }
}