Search code examples
c#wpfxamlscintillawindowsformshost

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


I have a C# WPF 4.51 app. As far as I can tell, you can not bind to a property belonging to an object that is the child of a WPF WindowsFormsHost control. (If I am wrong in this assumption please show me how to do it):

Bind with WindowsFormsHost

In my case, I have a page that contains a WindowsFormsHost control whose Child object is a ScintillaNET editor control:

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>

The child control works and displays fine. If it were a normal WPF control I would bind the Text property of the Scintilla editor control to some string property in my ViewModel, so that all I had to do to update the content of the Scintilla editor control is update that string property.

But since I can't bind to a property belonging to a WindowsFormsHost child object, I'm looking for a strategy/solution that not completely awkward or kludgy. Has anybody faced this scenario before and has a reasonable strategy that solves my binding/update problem?


Solution

  • 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.