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

安稳与你 提交于 2019-12-04 10:41:08

A simple approach here is you can create some dedicated class to contain just attached properties mapping to the properties from your winforms control. In this case I just choose Text as the example. With this approach, you can still set Binding normally but the attached properties will be used on the 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);
    }
}

Usage in 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>

The Child should of course be a Scintilla, otherwise you need to modify the code for the WindowsFormsHostMap. Anyway this is just to show the idea, you can always tweak it to make it better.

Note the code above works just for 1 way binding (from view-model to your winforms control). If you want the other way, you need to register some event handler for the control and update the value back to the attached property in that handler. It's quite complicated that way.

You can only achieve a very, very limited binding with a Windows Forms control as they binding won't receive update notifications and may need to be explicitly polled to get the results via a custom RefreshValues() method or something that polls each piece of data.

But if all you need is to access the child control, you should do the binding in code:

(WFH.Child as MyWinFormsControl).Text

If you intend to do a lot of binding, it might be easier to create a WPF wrapper object (UserControl perhaps) that has all the properties you need as DependencyProperties and the underlying code each property would manually poll the WinForms control as if it were the backing field for the property. It is a little complicated at first, but easier than manually polling each property.

Shivu Suputhra

Create a dependecy object of type 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;
        }
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!