valsidalv
valsidalv

Reputation: 821

Passing Data from WinForm to WPF

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/

Upvotes: 4

Views: 2837

Answers (1)

Fede
Fede

Reputation: 44048

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.

Upvotes: 1

Related Questions