4

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?

Community
  • 1
  • 1
Robert Oschler
  • 14,153
  • 18
  • 94
  • 227
  • How was the WinForm's control property set? Maybe what-ever sets that can expose it in your VM? –  Oct 12 '15 at 06:20

3 Answers3

7

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.

King King
  • 61,710
  • 16
  • 105
  • 130
  • Thanks. Why use a static container class and underlying proopery instead of just a normal container class and property? (example: http://stackoverflow.com/questions/11226843/data-binding-in-wpf-user-controls ). In that SO post, the DependencyProperty declaration is static, but the property it exposes and the host class is not. I'm wondering why the difference between the two approaches? – Robert Oschler Oct 12 '15 at 18:04
  • 1
    @RobertOschler that's for UserControl and you have access to its class, you can add some instance property (a wrapper of dependencyProperty). But here the `WindowsFormsHost` is not your class, so using attached property is a convenient way. If you want, you can sub-class it and declare your property there, and of course use that class instead of `WindowsFormsHost`. – King King Oct 12 '15 at 18:09
1

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.

Dax Pandhi
  • 843
  • 4
  • 13
  • 1
    _"very limited binding with a Windows Forms control as they binding won't receive update notifications"_ - are you sure? `INotifyPropertyChanged` predates WPF. –  Oct 12 '15 at 06:21
  • I could be wrong (which would be GREAT in this case :) - but I'm mainly speaking from personal experience. While property notifications do exist, they weren't being updated properly, hence the wrapper we made. – Dax Pandhi Oct 12 '15 at 06:31
-2

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;
        }
    }
}
Rugmangathan
  • 3,186
  • 6
  • 33
  • 44
  • Please format your answer correct. It seems like some of your code is outside the `code box`. – L. Guthardt May 17 '18 at 10:12
  • You get an automatic live-updated preview while typing / editing questions and answers here on SO. Please look at that preview and check if everything looks okay before deciding to submit something. – Nyerguds May 17 '18 at 10:13