Practical .NET

Integrating with the .NET Framework UI Controls

With a little bit of code (along with a .NET interface and collection), you can integrate the properties on your classes with the .NET user interface controls to simplify your presentation layer.

The .NET Framework team obviously assumes you'll build your applications and the classes that make up your application a certain way. If you leverage those assumptions you'll get lots of cool benefits (and, if you don't leverage those benefits…well, you'll get to do a lot more work).

For instance, the user interface controls you can drag onto your WPF or Silverlight forms (TextBoxes, DropDownList, and so on) assume that you'll bind class properties to them. Those controls look for specific features in your classes; if you build those features into your code, you'll get a lot of functionality for free. The benefit is that you can build those objects independent of your UI, which supports automated testing, parallel development and loose coupling between application components.

One example of how you can integrate your classes with a XAML UI is the INotifyPropertyChanged interface. Adding this interface (and some code) to your classes gives you some almost magical benefits. For instance, if you have an object whose property is bound to and displayed in some user interface control, the control will automatically update in your user interface if you update the property from code. There's no need to do anything to make the user interface display the property's latest value: you don't need to update the control or call a Refresh method on the control (or the form).

Adding the INotifyPropertyChanged interface to your class is easy:

Public Class Product
    Implements ComponentModel.INotifyPropertyChanged

Visual Studio will then add the interface's one required member, an event called PropertyChanged:

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

The .NET UI controls look for this interface, and when they find it, automatically subscribe to any PropertyChanged methods you fire. In those events, you need only pass the name of the property being changed to cause the .NET user interface controls to pick up and display the latest value for the property. The typical place to put that is in the setter of the property being changed:

Private _Description As String
  Public Property Description As String
    Get
      Return _Description
    End Get
    Set(value As String)
      _Description = value
      RaiseEvent PropertyChanged(Me, 
         New ComponentModel.PropertyChangedEventArgs("Description"))
    End Set
  End Property

Rather than repeat the RaiseEvent code in every property, it makes sense to centralize it in a method of your own, like this:

Private Sub RaisePropertyChanged(PropertyName As String)
    RaiseEvent PropertyChanged(Me,
       New ComponentModel.PropertyChangedEventArgs(PropertyName))
  End Sub

With properties that are dependent on other properties, you may want to raise an event in other locations than the setter of a property. For instance, let's say you have a readonly ExtendedPrice property that's calculated from two other properties, Price and Quantity:

Public ReadOnly Property ExtendedPrice As Decimal
    Get
      Return Me.Price * Me.Quantity
    End Get
  End Property

In that case, changing either the Price or Quantity properties not only changes the property being updated, but also changes the ExtendedPrice property. To support that you'd want to raise the property changed event for all the affected properties in the setter of the properties that drive ExtendedPrice. The Quantity property would look like this:

Private _Quantity As Integer
  Public Property Quantity As Integer
    Get
      Return _Quantity
    End Get
    Set(value As Integer)
      _Quantity = value
      RaisePropertyChanged("Quantity")
      RaisePropertyChanged("ExtendedPrice")
    End Set
  End Property

Now, when you update the Quantity property (or any property that calls your RaisePropertyChanged method), any UI control tied to your Quantity or ExtendedPrice will automatically display the change. The only entanglement between your classes and the UI is when you set the data source on the UI control to an instance of your class.

However, sometimes you bind your controls to collections of objects—DataGridViews, for instance. In those scenarios, it's not enough to notify the UI control of changes in properties—you also want to notify the control about additions or deletions in the list of objects to which the control is bound. Here again, the .NET Framework provides a solution: The ObservableCollection generic collection class. As you create the objects that will be displayed, just pass the collection to the grid's ItemSource property:

Dim prods As New ObjectModel.ObservableCollection(Of Product)
  prods.Add(New Product("A123"))
  prods.Add(New Product("B456"))
  Me.DataGrid1.ItemsSource = prods

Now, if in your code you add or remove an item from the custs collection, your UI will update automatically. You could write code to make that happen yourself; but by leveraging the INotifyPropertyChanged interface and the ObservableCollection class, you get all of the benefits, and minimize the entanglements between your UI and your classes.

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

Subscribe on YouTube