4
votes

I have a WinForm UserControl inside a WPF window and the WPF code is using the MVVM pattern.

What is the best way to successfully integrate the WinForm control into the MVVM pattern? Can I use some form of binding from the WPF side?

Let's say that I want to handle some events from the WF control, is there a way to fully go MVVM?

Thanks.

3

3 Answers

2
votes

Note that this doesn't really answer the questions (I should have read better). If you're interested in using a WPF control in a WinForms app, here's an approach. My scenario is: 1) Have a WinForms control that is used many places in my app. 2) Want to develop a WPF implementation that will use the MVVM pattern. 3) Want to write the control as a proper WPF control complete with dependency properties so it can be used properly when my app is eventually all WPF. 4) Want to keep the same WinForms control and API to not break existing client code in my app.

Most everything was straightforward except for having my WinForms control raise events when properties of my WPF control changed. I wanted to use a binding but since the source of a binding must be a DependencyObject and a System.Windows.Forms.UserControl is not, I had to make a simple nested class. I wrote my WPF control exactly as if I was integrating it into a WPF application, and just did some extra thunking to get my WinForms wrapper to work.

Here's code for my WPF control:

public partial class MonkeySelector : UserControl
{
  public static readonly DependencyProperty SelectedMonkeyProperty =
    DependencyProperty.Register(
    "SelectedMonkey", typeof(IMonkey),
    typeof(MonkeySelector));

  public MonkeySelector()
  {
    InitializeComponent();
  }

  protected override void OnInitialized(EventArgs e)
  {
    base.OnInitialized(e);

    // Note: No code is shown for binding the SelectedMonkey dependency property
    // with the ViewModel's SelectedMonkey property. This is done by creating
    // a Binding object with a source of ViewModel (Path = SelectedMonkey) and
    // target of the SelectedMonkey dependency property. In my case, my
    // ViewModel was a resource declared in XAML and accessed using the
    // FindResource method.
  }

  public IMonkey SelectedMonkey
  {
    get { return (IMonkey)GetValue(SelectedMonkeyProperty); }
    set { SetValue(SelectedMonkeyProperty, value); }
  }
}

Here's the code for my WinForms control:

public partial class WinFormsMonkeySelector : UserControl
{
  public event EventHandler SelectedMonkeyChanged;

  private MonkeySelector _monkeySelector;
  private WpfThunker _thunker;

  public WinFormsMonkeySelector()
  {
    InitializeComponent();

    _monkeySelector = new MonkeySelector();
    _elementHost.Child = _monkeySelector;

    System.Windows.Data.Binding binding = new System.Windows.Data.Binding("SelectedMonkey");
    binding.Source = _monkeySelector;
    binding.Mode = System.Windows.Data.BindingMode.OneWay;

    _thunker = new WpfThunker(this);
    // Note: The second parameter here is arbitray since we do not actually
    // use it in the thunker. It cannot be null though. We could declare
    // a DP in the thunker and bind to that, but that isn't buying us anything.
    System.Windows.Data.BindingOperations.SetBinding(
      _thunker,
      MonkeySelector.SelectedMonkeyProperty,
      binding);
  }

  protected virtual void OnSelectedMonkeyChanged()
  {
    if (SelectedMonkeyChanged != null)
      SelectedMonkeyChanged(this, EventArgs.Empty);
  }

  public IMonkey SelectedMonkey
  {
    get { return _monkeySelector.SelectedMonkey; }
    set { _monkeySelector.SelectedMonkey = value; }
  }

  private class WpfThunker : System.Windows.DependencyObject
  {
    private WinFormsMonkeySelector _parent;

    public WpfThunker(WinFormsMonkeySelector parent)
    {
      _parent = parent;
    }

    protected override void OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)
    {
      base.OnPropertyChanged(e);

      // Only need to check the property here if we are binding to multiple
      // properties.
      if (e.Property == MonkeySelector.SelectedMonkeyProperty)
        _parent.OnSelectedMonkeyChanged();
    }
  }
}
1
votes

Personally, I would handle this by creating a WPF UserControl that wraps the Windows Forms control. This would allow you to encapsulate all of the required code-behind into your WPF Control, and then use it in a pure MVVM manner.

It will be difficult to stay "pure" MVVM using a Windows Forms control directly, as Windows Forms controls typically require a different binding model, as well as typically requiring direct event handling.

0
votes

You might have a look at the WAF Windows Forms Adapter. It shows a possible way to use Windows Forms together with MVVM.