4

I'm looking to pass data to a WPF window from a WinForm and receive a message back from the WPF window.

My code is a mix of random online tutorials and HighCore's log viewer. I have a WinForm that launches my new WPF window in the following fashion:

private void openTransactionViewToolStripMenuItem_Click(object sender, EventArgs e)
  {
     var transactionViewWindow = new TransactionViewer.MainWindow();
     ElementHost.EnableModelessKeyboardInterop(transactionViewWindow);
     transactionViewWindow.Show();
     transactionViewWindow.Test = "test";   // testing out data passing
     transactionViewWindow.AddTest();
  }

My MainWindow.xaml.cs looks like:

public partial class MainWindow : Window
{
  public ObservableCollection<Session> SessionList { get; set; }
  public string Test{ get; set; }

  public MainWindow()
  {
     InitializeComponent();

     SessionList = new ObservableCollection<Session>();

     SessionList.Add(new Session() { BeginLine = 0, EndLine = 1, Message = "some message" });
     SessionList.Add(new Session() { BeginLine = 2, EndLine = 3, Message = "another message" });

     SessionItems.ItemsSource = SessionList;  // the ItemsControl
  }

  public void AddTest()
  {
     SessionList.Add(new Session() { BeginLine = 4, EndLine = 5, Message = Test });
  }
}

public class Session : PropertyChangedBase
{
   public int BeginLine { get; set; }
   public int EndLine { get; set; }
   public string Message { get; set; }
}

where PropertyChangedBase inherits from INotifyPropertyChanged. I have an ItemsControl bound to Message. My output looks like:

some message
another message
test

"Data passing" is successful! Eventually, when the WPF window loads I want to pass a List<Session> from my WinForm that will be used to populate the ItemsControl. I also want to have a button on the WinForm that will send a List to repopulate/refresh the data in the WPF. From the current behaviour I think this will be possible even with my current, simple implementation (just updating SessionList).

Is there a more appropriate way of doing this? Events, for example? Do I need to fire off an event in order to tell my WinForm that the WPF has successfully added all Session objects, or whenever a user clicks on a specific one?
Any benefit to using MVVM here?

I've been developing for WinForms for a while and finding the transition to WPF quite confusing. Hopefully someone can help out with some guidance or code examples.

Edit: for future reference, a decent MVVM tutorial targeted to people like me can be found here: http://jpreecedev.com/2013/06/08/wpf-mvvm-for-winforms-devs-part-1-of-5/

Community
  • 1
  • 1
valsidalv
  • 761
  • 2
  • 19
  • 33

1 Answers1

1

You approach seems OK to me. It's not perfect, but it is workable enough.

An optimal approach, IMO, would be to create a ViewModel for the WPF Window, instead of directly referencing the Window itself when passing data back and forth.

The idea is:

public class MyForm: Form
{
   public TransactionViewerViewModel TransactionViewer {get;set;}

   //... other code...

   //Form constructor:
   public MyForm()
   {
       InitializeComponent();

      //Create ViewModel:
      TransactionViewer = new TransactionViewerViewModel();
   }

   private void openTransactionViewToolStripMenuItem_Click(object sender, EventArgs e)
   {
      //Create WPF View:
      var transactionViewWindow = new TransactionViewer.MainWindow();

      //Interop code
      ElementHost.EnableModelessKeyboardInterop(transactionViewWindow);

      //Set DataContext:
      transactionViewWindow.DataContext = TransactionViewer;     

      //Show Window:
      transactionViewWindow.Show();

      //Call methods on the ViewModel, rather than the View:
      TransactionViewer.Test = "test";   // testing out data passing
      TransactionViewer.AddTest();
   }
}

So, the ViewModel would be something like:

public class TransactionViewerViewModel : PropertyChangedBase
{
  public ObservableCollection<Session> SessionList { get; set; }
  public string Test{ get; set; }

  public TransactionViewerViewModel()
  {
     SessionList = new ObservableCollection<Session>();

     SessionList.Add(new Session() { BeginLine = 0, EndLine = 1, Message = "some message" });
     SessionList.Add(new Session() { BeginLine = 2, EndLine = 3, Message = "another message" });

  }

  public void AddTest()
  {
     SessionList.Add(new Session() { BeginLine = 4, EndLine = 5, Message = Test });
  }
}

This achieves a perfect separation between the WPF UI and the actual data / business logic, to the point that you can even create Unit Tests for your ViewModel.

And, since you're setting the ViewModel as the Window's DataContext, you will need to access all your ViewModel properties via DataBinding, rather than procedural code:

<ItemsControl ItemsSource="{Binding SessionList}"/>

Then, you may want to introduce delegates or events in the ViewModel, and listen to these in your Form, thus achieving WPF => winforms communication.

Federico Berasategui
  • 43,562
  • 11
  • 100
  • 154
  • 1
    HighCore to the rescue! Thanks, this is exactly what I needed. (I've basically learned WPF from you :P) – valsidalv Nov 15 '13 at 14:53
  • A followup... is the solution described [here](http://blogmarq.co.uk/index.php/launch-wpf-window-from-winforms-host/) acceptable for use with the MVVM for WPF -> WinForm communication? – valsidalv Nov 27 '13 at 00:29
  • @valsidalv yes, it uses parts of the Prism Framework (which is the official MVVM Framework from Microsoft), to send "messages" between WPF and winforms in a decoupled way. That is the best solution, IMO, though it adds some overhead in terms of architecture and structural design. – Federico Berasategui Nov 27 '13 at 01:32