Practical .NET

Moving Data to the Form in Windows Forms

When you implement the Model-View-ViewModel pattern you need to tell your View (in this case, a Windows Form) when the ViewModel has new data. Here's how to do that, along with a warning about how to avoid a potential bug.

In another column, I looked at the issue of how to structure a Windows Forms application using the Model-View-ViewModel (MVVM) pattern so that I could do Test-Driven Development (TDD). In that column, I showed how using the ObservableCollection class simplified moving list data out of the ViewModel and into the form's controls. What I didn't address (and will take care of in this column) is how to get data from a single-valued property (properties declared as string or integer or an object type) out of the ViewModel and into the form.

Using the ViewModel
In my MVVM design, events fired by controls in the form move data into properties in my ViewModel or call methods in my ViewModel. When the form calls methods in my ViewModel, those methods update properties in the ViewModel. But I then need to notify the form that the ViewModel has new data so the form can move that data out of the ViewModel and into the controls on the form. The simplest way to do that is to have the ViewModel fire an event when its properties change, and have the form catch and process those events.

The first step in implementing this plan is to tweak the code that instantiates my ViewModel. I declare the variable that holds the ViewModel using the WithEvents keyword, and add a method to handle an event raised by my ViewModel:

Private WithEvents VM As InvoicePremimumVM
Private Sub New()
  MyBase.New

  VM = New InvoicePremimumVM
  AddHandler VM.PropertyChanged, AddressOf VMPropertyChanged

Now, I need to have my ViewModel raise this PropertyChanged event whenever the ViewModel has data about which the form should know. The .NET Framework has a specific event designed for just this purpose, and if you add the INotifyPropertyChanged interface to your class, Visual Studio will add the event to your class for you. That's what I did with my ViewModel class (I wrote the first two lines of code, and Visual Studio wrote the last one):

Public Class InvoicePremimumVM
  Implements INotifyPropertyChanged

Public Event PropertyChanged(sender As Object, 
  e As System.ComponentModel.PropertyChangedEventArgs) Implements   
    System.ComponentModel.INotifyPropertyChanged.PropertyChanged

My next step is to raise the PropertyChanged event when something changes in my ViewModel. When raising the PropertyChanged event, I have to pass the event a reference to my ViewModel and a PropertyChangedEventArgs object holding the name of the property that was changed. Because I'll be raising this event from multiple properties in my ViewModel class, I add a method to my ViewModel that handles creating that object and raising the event:

Private Sub RaisePropertyChanged(PropertyName As String)
Dim ev As PropertyChangedEventArgs

  ev = New PropertyChangedEventArgs(PropertyName)
  RaiseEvent PropertyChanged(Me, ev)
End Sub

Now, in my ViewModel, whenever I update a property, I raise this event. A typical property in my ViewModel looks like this:

Private _AuthCode As String
Friend Property AuthCode As String
  Get
    Return _AuthCode
  End Get
  Set(value As String)
    _AuthCode = value
    RaisePropertyChanged("AuthCode")
  End Set
End Property

In my form's VMPropertyChanged method, I check to see which property has been changed and do whatever I think best with the property. In this case, that's updating a textbox on the form:

Private Sub VMPropertyChanged(sender As Object, e As PropertyChangedEventArgs)
  Select Case e.PropertyName
    Case "AuthCode"
      Me.txtSInvoice.Text = VM.AuthCode
  End Select
End Sub

Silly Processing … and a Trap
The logic for updating the textbox works like this: When code in my ViewModel updates the AuthCode property, the property raises the PropertyChanged event; the form catches that PropertyChanged event and moves data from the AuthCode property into the txtSInvoice textbox on the form. This leads to some silly processing.

Assume, for example, that I update the ViewModel AuthCode property in the LostFocus event of the textbox:

Private Sub txtSInvoice_LostFocus(…) Handles txtSInvoice.LostFocus
  VM.AuthCode = txtSInvoice.Text
End Sub

Now, imagine what happens when the user changes the textbox contents and tabs out of the textbox. The code in the LostFocus event updates the ViewModel AuthCode property; the ViewModel then raises the PropertyChanged event; the PropertyChanged event causes the form to read the AuthCode property from the ViewModel; the form then updates the textbox … with the value the textbox already has. This is silly: I've made a roundtrip from my textbox to my ViewModel and back to the form to update the textbox with whatever the user just put in the textbox.

However, I'm not worried enough about this to change anything. This silly processing is just moving stuff around in memory, and as a result, the form's response time isn't appreciably affected. If I wanted to avoid this problem, I'd have to add more code to ensure the PropertyChanged event is only raised when the property is updated from within the ViewModel. I'm not sure how I'd do that. It would make either my form or my ViewModel (or both!) more complicated, and I'd have to do it (and do it correctly) for every property in my ViewModel. I don't like doing any of those things. The current processing is silly, but I can live with it because I like it better than the alternative.

I do have to be careful, though: I can't use an event on the textbox that will fire when the textbox is updated because I could end up with an endless loop. I could, for example, use the textbox TextChanged property to update my ViewModel property. Then, when the user typed in the textbox, the textbox would raise the TextChanged property; that would update the ViewModel property, which would raise the PropertyChanged event; that event would cause the form to update the textbox, which would cause the textbox to raise its TextChanged event; raising the TextChanged event would start the whole cycle over again … and again … and again.

I won't explain how I know this. Just live with the silly processing and pick the event on the control for updating your ViewModel carefully.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • AI for GitHub Collaboration? Maybe Not So Much

    No doubt GitHub Copilot has been a boon for developers, but AI might not be the best tool for collaboration, according to developers weighing in on a recent social media post from the GitHub team.

  • Visual Studio 2022 Getting VS Code 'Command Palette' Equivalent

    As any Visual Studio Code user knows, the editor's command palette is a powerful tool for getting things done quickly, without having to navigate through menus and dialogs. Now, we learn how an equivalent is coming for Microsoft's flagship Visual Studio IDE, invoked by the same familiar Ctrl+Shift+P keyboard shortcut.

  • .NET 9 Preview 3: 'I've Been Waiting 9 Years for This API!'

    Microsoft's third preview of .NET 9 sees a lot of minor tweaks and fixes with no earth-shaking new functionality, but little things can be important to individual developers.

  • Data Anomaly Detection Using a Neural Autoencoder with C#

    Dr. James McCaffrey of Microsoft Research tackles the process of examining a set of source data to find data items that are different in some way from the majority of the source items.

  • What's New for Python, Java in Visual Studio Code

    Microsoft announced March 2024 updates to its Python and Java extensions for Visual Studio Code, the open source-based, cross-platform code editor that has repeatedly been named the No. 1 tool in major development surveys.

Subscribe on YouTube