【问题标题】:Workaround for inability to bind to a property that belongs to a WindowsFormsHost Child object in C#/XAML app?无法绑定到属于 C#/XAML 应用程序中的 WindowsFormsHost 子对象的属性的解决方法?
【发布时间】:2016-01-09 12:31:45
【问题描述】:

我有一个 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 属性绑定到我的 ViewModelstring 属性/em>,所以我要更新 Scintilla 编辑器控件的内容所要做的就是更新 string 属性。

但由于我无法绑定到属于 WindowsFormsHost 子对象的属性,因此我正在寻找一种不完全笨拙或笨拙的策略/解决方案。有没有人遇到过这种情况并且有一个合理的策略来解决我的绑定/更新问题?

【问题讨论】:

  • WinForm 的控件属性是如何设置的?也许有什么可以在您的 VM 中公开它的设置?

标签: c# wpf xaml scintilla windowsformshost


【解决方案1】:

这里有一个简单的方法是您可以创建一些专用类来包含仅附加属性映射到您的 winforms 控件的属性。在这种情况下,我只是选择Text 作为示例。使用这种方法,您仍然可以正常设置 Binding,但附加的属性将用于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 控件)。如果您想要其他方式,则需要为控件注册一些事件处理程序并将值更新回该处理程序中的附加属性。那样就很复杂了。

【讨论】:

  • 谢谢。为什么要使用静态容器类和底层属性,而不仅仅是普通的容器类和属性? (例如:stackoverflow.com/questions/11226843/…)。在那个 SO 帖子中,DependencyProperty 声明是静态的,但它公开的属性和主机类不是。我想知道为什么这两种方法有区别?
  • @RobertOschler 用于 UserControl 并且您可以访问它的类,您可以添加一些实例属性(dependencyProperty 的包装器)。但是这里 WindowsFormsHost 不是你的类,所以使用附加属性是一种方便的方法。如果你愿意,你可以对它进行子类化并在那里声明你的属性,当然可以使用该类而不是WindowsFormsHost
【解决方案2】:

您只能通过 Windows 窗体控件实现非常非常有限的绑定,因为它们的绑定不会接收更新通知,并且可能需要通过自定义 RefreshValues() 方法或轮询每个方法来显式轮询以获取结果一条数据。

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

(WFH.Child as MyWinFormsControl).Text

如果您打算进行大量绑定,则创建一个 WPF 包装器对象(可能是UserControl)可能更容易,该对象具有您需要的所有属性作为 DependencyProperties,并且每个属性的底层代码将手动轮询 WinForms 控件好像它是属性的支持字段。一开始有点复杂,但比手动轮询每个属性更容易。

【讨论】:

  • “与 Windows 窗体控件的绑定非常有限,因为它们的绑定不会收到更新通知” - 你确定吗? INotifyPropertyChanged 早于 WPF。
  • 我可能是错的(在这种情况下会很棒:) - 但我主要是从个人经验中说的。虽然属性通知确实存在,但它们没有被正确更新,因此我们制作了包装器。
【解决方案3】:

创建一个 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;
        }
    }
}

【讨论】:

  • 请正确格式化您的答案。您的某些代码似乎在 code box 之外。
  • 在 SO 上键入/编辑问题和答案时,您将获得自动实时更新的预览。在决定提交之前,请查看该预览并检查一切是否正常。
猜你喜欢
  • 2016-11-26
  • 1970-01-01
  • 2015-03-25
  • 2014-08-04
  • 1970-01-01
  • 1970-01-01
  • 2012-12-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多