August 26, 2009, 8:01 pm
Download the code here: WpfNullableComboBox.zip
A few months ago I wrote this article on making a nullable combo box control in WPF. I had a bunch of requests to see an actual implementation, so here is a sample Visual Studio 2008 project with the source code for the user control. It basically uses the same technique I described in my previous blog post. All the combo box properties (even the obscure ones no one will ever use) are implemented.
August 22, 2009, 9:03 pm
As I described in this previous article raising the PropertyChanged event for classes that implement INotifyPropertyChanged can be a real pain. The biggest problem is that PropertyChangedEventArgs takes the name of the property that changed as a string and as we all know, strings are the root of all evil. Here I will show how to use a simple PostSharp attribute on your properties that need to raise the PropertyChanged event when they are changed so that you don’t manually need to do it and hard code the name of the property as a string. PostSharp is a framework for .NET that allows for aspect oriented programming. You can read all about it at the PostSharp website.
First of all, let’s assume that the classes implementing INotifyPropertyChanged are model view classes in the MVVM pattern. We will use a base class for all model views called BaseModelView that looks like this:
Imports System.ComponentModel
''' <summary>
''' Parent class for all model views
''' </summary>
Public Class BaseModelView
Implements INotifyPropertyChanged
Public Event PropertyChanged( _
ByVal sender As Object, _
ByVal e As System.ComponentModel.PropertyChangedEventArgs) _
Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
''' <summary>
''' Raises the <c>PropertyChanged</c> event for the property with the given name.
''' </summary>
''' <param name="propertyName">The name of the property that has changed.</param>
''' <remarks>If there is no property on this class with the given name, then an
''' exception will be thrown.</remarks>
Public Sub OnPropertyChanged(ByVal propertyName As String)
' Throw an exception if the property doesn't exist
If Me.GetType().GetProperty(propertyName) Is Nothing Then
Throw New ArgumentException( _
String.Format("The property {0} doesn't exist on type {1}.", _
propertyName, _
Me.GetType().Name))
End If
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
This class is very important. There needs to be a method for property change notification (ie: OnPropertyChanged on BaseModelView) instead of just an event (ie: PropertyChanged on INotifyPropertyChanged) because the attribute cannot directly raise an event, but it can call a public method that raises the event. The PostSharp attribute looks like this:
Imports PostSharp.Laos
Imports System.ComponentModel
<Serializable()> _
Public Class NotifyAttribute
Inherits OnMethodBoundaryAspect
Public Overrides Sub OnExit(ByVal eventArgs As PostSharp.Laos.MethodExecutionEventArgs)
' Convert to BaseModelView
Dim notifier = TryCast(eventArgs.Instance, BaseModelView)
' If the instance is the wrong type then throw an exception
If notifier Is Nothing Then
Throw New InvalidOperationException("Cannot raise PropertyChanged event unless instance implements INotifyPropertyChanged.")
End If
' Ignore everything that's not a setter
If eventArgs.Method.Name.StartsWith("set_") Then
notifier.OnPropertyChanged(eventArgs.Method.Name.Substring(4))
End If
End Sub
End Class
Note that when you apply PostSharp attribute to a property, you are actually applying the attribute to the two methods that are generated for that property. For example, if you have a property called MyProperty then the compiler will actually generate two methods: get_MyProperty and set_MyProperty. Since OnExit() will actually get called for both of these methods when we apply the attribute to a property, the code has to check whether the getter or the setter was called. Using the attribute is very simple:
<Notify()> _
Public Property Text() As String
Get
Return _text
End Get
Set(ByVal value As String)
_text = value
End Set
End Property
The result is that the PropertyChanged event will automatically be raised after the setter finishes executing and there is no need to hard code any strings! Now you are free to change the name of your property and it won’t break any code.
August 22, 2009, 6:56 pm
Download the sample project here: WpfRenderingThreadSynchronization.zip
In most applications it is necessary to offload long running processes to an alternate thread so that the rest of the program does not lock up during that time. However, it’s not so simple when the long running process is the actual rendering. Separate windows can have their own UI threads (as explained here) but to my knowledge there is no way to use multiple rendering threads on a single window.
The second problem is that rendering is done in big chunks. For example, if you have an ItemsControl that is bound to an ObservableCollection and a loop that adds 1000 items to that collection, you will notice that the elements are not drawn one at a time. Instead the UI will stall for a moment and then every element will suddenly appear on screen. During the time that it is loading, the entire window will be completely unusable. Basically, what happens is that UI changes (like adding an element to an ItemsControl’s ObservableCollection) all get put into a queue and then the rendering thread deals with a whole bunch of them all at once.
There are two problems with this behaviour:
- The rest of the UI is unusable while this loading takes place
- It’s not obvious what is happening during the loading period. Since absolutely nothing is happening on screen, the user might think the app is broken.
It turns out that in cases like this where we have many small rendering operations that add up to a large amount of time, we can force the rendering thread to flush out the Windows message queue after each element is added to the collection. This will not only allow the user to see progress (ie: items appearing in the ItemsControl one at a time) but between each item being added other UI updates can take place giving the illusion that there are separate UI threads.
The included sample draws 1000 TextBoxes inside an ItemsControl, but after each element is added the Windows message queue is flushed out using the FlushWindowsMessageQueue function. All the functions does is tell the dispatcher to invoke a delegate that does nothing. The result is that the code blocks at that line until the specified delegate has been run. But since it is at the end of the queue, everything else has to be dealt with first. The function looks like this:
Private Sub FlushWindowsMessageQueue()
Application.Current.Dispatcher.Invoke( _
New Action(AddressOf DummySub), _
DispatcherPriority.Background, _
New Object() {})
End Sub
Private Sub DummySub()
End Sub
When the sample is run with the FlushWindowsMessageQueue() line commented out the whole UI will lock up for a couple of seconds after you click “Refresh data”. However, when the message queue is emptied after each element is added the UI never locks up, even when it is still drawing TextBoxes.
Unfortunately, there are some drawbacks to this method. The most obvious is that it makes the entire rendering operation actually take longer. The trade off is that the first items appear much earlier, but the last items appear later. The technique also cannot be used when the rendering cannot easily be split into many small chunks.