Использование WPF, MVVM и привязок с WinForm UserControl, как успешно интегрировать?

#wpf #winforms #mvvm #user-controls

#wpf #winforms #mvvm #пользовательские элементы управления

Вопрос:

У меня есть WinForm UserControl внутри окна WPF, и код WPF использует шаблон MVVM.

Каков наилучший способ успешной интеграции элемента управления WinForm в шаблон MVVM? Могу ли я использовать какую-либо форму привязки со стороны WPF?

Допустим, я хочу обработать некоторые события из элемента управления WF, есть ли способ полностью перейти на MVVM?

Спасибо.

Ответ №1:

Обратите внимание, что это на самом деле не отвечает на вопросы (я должен был читать лучше). Если вы заинтересованы в использовании элемента управления WPF в приложении WinForms, вот подход. Мой сценарий таков: 1) Иметь элемент управления WinForms, который используется во многих местах в моем приложении. 2) Хотите разработать реализацию WPF, которая будет использовать шаблон MVVM. 3) Хотите записать элемент управления как надлежащий элемент управления WPF в комплекте со свойствами зависимостей, чтобы его можно было правильно использовать, когда мое приложение в конечном итоге будет полностью WPF. 4) Хочу сохранить тот же WinForms control и API, чтобы не нарушать существующий клиентский код в моем приложении.

Почти все было просто, за исключением того, что мой элемент управления WinForms вызывал события при изменении свойств моего элемента управления WPF. Я хотел использовать привязку, но поскольку источником привязки должен быть DependencyObject и System.Windows.Формы.UserControl не является, мне пришлось создать простой вложенный класс. Я написал свой элемент управления WPF точно так, как если бы я интегрировал его в приложение WPF, и просто немного доработал, чтобы заставить мою оболочку WinForms работать.

Вот код для моего элемента управления WPF:

 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); }
  }
}
  

Вот код для моего элемента управления WinForms:

 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();
    }
  }
}
  

Ответ №2:

Лично я бы справился с этим, создав WPF UserControl, который оборачивает элемент управления Windows Forms. Это позволило бы вам инкапсулировать весь необходимый код в ваш элемент управления WPF, а затем использовать его в чистом виде MVVM.

Будет сложно оставаться «чистым» MVVM, используя элемент управления Windows Forms напрямую, поскольку элементы управления Windows Forms обычно требуют другой модели привязки, а также, как правило, требуют прямой обработки событий.

Комментарии:

1. Итак, имея WPF usercontrol, инкапсулирующий элемент управления WinForms, я мог бы обеспечить привязку WPF из WPF usercontrol, но внутренне он будет обрабатывать элемент управления WinForms? Если да, то это может быть именно так, как я хочу.

2. @Stecy: Да — внутренне, в конечном итоге это приведет к отображению зависимостей WPF для установки управляющих значений, получения событий и т.д. Однако со стороны WPF это имеет то преимущество, что выглядит точно так же, как обычный элемент управления WPF. Это действительно лучший способ в долгосрочной перспективе, IMO.

Ответ №3:

Вы могли бы взглянуть на адаптер WAF Windows Forms. Показан возможный способ использования Windows Forms вместе с MVVM.