Friday, December 23, 2011

WPF 4.5 - Accessing DataBound Collections on Non-UI Thread

WPF has ItemsControl to show a collection of items. In MVVM, ItemsControl is data-bound with a collection in the view model. These collections might take a long time to load when the form is being shown. They might be constantly updated throughout the life cycle of the view. If we do it in the main application thread then it might cost the responsiveness of the application. Hence the need for a background thread for these operations. Well, the world has not been so simple.


As we know that the UI elements have affinity to the UI thread in WPF. It also does not allow playing with the elements collection bound as DataSource on any thread other than UI thread. And believe me, this really hurts !!! All we had were a few workarounds but no real solution. With WPF 4.5 Developer's preview, the situation improves a little. It is a step forward in the direction for providing these updates in some other thread. Although I do think that the way it is provided could be a little better than that but whatever makes my collection available to a non-UI thread. I don't really mind.

Let's understand this neat feature by creating a simple example. Let's have a simple view with a ListBox. The ListBox is supposed to display the history of signals received from a central server.


We can design the above view in XAML as follows:
<Window x:Class="MVVMCollectionNonUIThread.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MVVMCollectionNonUIThread"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Border Background="Navy" Grid.Row="0">
            <TextBlock Text="WPF 4.5 - Collections Access Across Threads" FontSize="20" 
                       Foreground="White" FontWeight="Bold"
                       TextAlignment="Center" VerticalAlignment="Center" />
        </Border>
        <ListBox Margin="3,5,3,5" Grid.Row="1" ItemsSource="{Binding RandomList}"  />
    </Grid>
</Window>
The above view has MainWindowViewModel set as the DataContext. It expects the DataContext to have a collection, named RandomList. This collection is data-bound to the ListBox so it should have the history of instances when the signal is received from the server. Now let's start defining the view model as per expectation.
namespace MVVMCollectionNonUIThread
{
    using System.Collections.ObjectModel;
    using System.Timers;
    using System.Windows.Data;

    class MainWindowViewModel
    {
        Timer _t1;

        ObservableCollection<string> _randomList;
        public ObservableCollection<string> RandomList
        {
            get
            {
                if (_randomList == null)
                {
                    _randomList = new ObservableCollection<string>();
                }

                return _randomList;
            }
        }

        public MainWindowViewModel()
        {
            _t1 = new Timer(300);
            _t1.Elapsed += new System.Timers.ElapsedEventHandler(_t1_Elapsed);
            _t1.Start();
        }

        void _t1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            RandomList.Add(string.Format("Signal Time : {0}", e.SignalTime));
        }

    }
}
In order to simulate the signal reception, we have used System.Timers.Timer. The timer would tick after at least 300 ms. This event is handled by _t1_Elapsed. We are just adding an item to RandomList each time the timer ticks. This event handler is to simulate the signal received from the server. Generally, this client / server communication is handled on a different thread like this handler. Since we are following through this post, if we run the application this results in the EXPECTED exception when the item is being added to RandomList in _t1_Elapsed (non-UI thread) [This is generally UNEXPECTED and comes as a surprise if developer doesn't know about it yet.]


FYI: In Winform, we can cause Elapsed event for System.Timers.Timer to be raised in UI thread by setting the SyncrhnizingObject property. We have discussed about this in our timers discussion [http://shujaatsiddiqi.blogspot.com/2010/10/timers-for-net-applications.html]

WPF 4.5 Developer's preview has provided certain new static methods in BindingOperations class to fix this behavior. The list of new methods I could find is as follows:
  1. AccessCollection
  2. DisableCollectionSynchronization
  3. EnableCollectionSynchronization
This also have some overloads. Let's see how we can fix our little example with these methods. We really can go around by just using EnableCollectionSynchronization. It is to report a collection to be accessible in non-UI threads. In order to provide thread synchronization, we need to provide the synchronization mechanism. Simply we can use the overload which needs lock object. Let's update the view model as follows:
namespace MVVMCollectionNonUIThread
{
    using System.Collections.ObjectModel;
    using System.Timers;
    using System.Windows.Data;

    class MainWindowViewModel
    {
        Timer _t1;
        object _lockObj = new object();

        ObservableCollection<string> _randomList;
        public ObservableCollection<string> RandomList
        {
            get
            {
                if (_randomList == null)
                {
                    _randomList = new ObservableCollection<string>();
                }

                return _randomList;
            }
        }

        public MainWindowViewModel()
        {
             BindingOperations.EnableCollectionSynchronization(RandomList, _lockObj);

            _t1 = new Timer(300);
            _t1.Elapsed += new System.Timers.ElapsedEventHandler(_t1_Elapsed);
            _t1.Start();

            
        }

        void _t1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            RandomList.Add(string.Format("Signal Time : {0}", e.SignalTime));
        }

    }
}
Here we just have added BindingOperations.EnableCollectionSynchronization to the view model's contructor for the RandomList collection. We have used the instance member _lockObj as the lock object. That's it! Let's run this now!


Just one thing. The feature is available through BindingOperations available in PresentationFramework assembly. Some developers like to keep their view models in a separate project and they don't like referencing this assembly in that project.


Download:

Wednesday, December 21, 2011

WPF 4.5 Developer's Preview - Delay Binding

In this post, let's discuss one great feature of Binding as in WPF 4.5 Developer's preview. This feature is called Delayed Binding. As a XAML technologies developers we are specially concerned about the timing when the values are copied between SOURCE and TARGET of binding.

From the very early stages, human has tried finding answer of this question. Who am I? Even famous eastern poet Bulleh Shah explained in one of his master piece.

Not a believer inside the mosque, am I
Nor a pagan disciple of false rites
Not the pure amongst the impure
Neither Moses, nor the Pharoh
Bulleh! to me, I am not known

Since WPF / Silverlight has very sophisticated Binding, the difference between Source and Target is blurred. Now the main question is what should be called Source and what should be considered as Binding Target. Although the question is not a philosophical one but we clearly need a way to identify the source and target of Binding. Charles Petzold makes it easier by calling Binding Target to be the one where the Binding is actually defined. Now the other party becomes the Binding Source. So if TextBox.Text is bound to FirstName property of the DataContext then TextBox.Text becomes the Binding target and hence DataContext.FirstName might be taken as Binding source.

From Target to Source, we control the timing by using UpdateSourceTrigger property of Binding. As we know that it has three possible modes. PropertyChanged, LostFocus and Explicit.

From Source to Target, this flow of update happens based on the nature of Source property. Generally, the view models implement INotifyPropertyChanged interface. Now setting the property causes PropertyChanged event to be raised. Binding Target listens to this event and update itself. The event has the details which property of the DataContext is updated which makes it easier for the runtime to update the view. The source property might be a DependencyProperty. As we know one of the feature of DependencyProperty is change notification. As WPF runtime receives such notifications, it propagates these changes to the Binding system, which causes updating the target's value.

The direction of flow of these updates are controlled by Binding mode. Different XAML technologies can have different default mode of binding. There are three different Binding modes in WPF / Silverlight. These Binding modes are as follows:

  1. TwoWay
  2. OneWay
  3. OneTime
  4. OneWayToSource
  5. Default
Except possibly Default option, the modes which support flowing the value updates from Target to Source are OneWayToSource and TwoWay. It must be noted that the feature we are discussing in this post only applies to this case when the updates are propagated from Target property to Source property.

Now the main question is why we need this delay? Basically it is to provide a compromise between two selections for UpdateSourceTrigger. These selections are PropertyChanged and LostFocus. With LostFocus, the binding system waits until focus is lost from the target control. So all these intermediate updates are lost. If this is a TextBox, the user might expect immediate response from the system. Since updates are not flowing from Target to source, no converters are applied. Similarly none of the validation rules are used. This might be frustrating for the application's user. On the other hand, if we change this to be using PropertyChanged, all the updates are propagated from Target to Source which might involve value conversion and validation. These operations might cause expensive logic to run. When we are using the control, we don't want to wait until we change focus to the other control. But we, definitely, don't want to run all of this with every key stroke or any other way in which user interaction is causing Target value to be updated. We need a mechanism similar to throttling which helps us in this regard. Binding Delay is just for the same purpose.

Let's create a simple WPF project MVVMDelayedBinding as follows:

Let's add a simple view model. The view model implements INotifyPropertyChanged interface. The view model just has one property FirstName. We will be binding this property to a TextBox in the view. Since we would just be binding this to support the flow of value update from Target to Source, it wouldn't really matter if this supports change notification or not. As a Binding Source, the only requirement is that the source should be a property, change notification support is just to update target from the property updates in the source.

namespace MVVMDelayedBinding
{
    using System.ComponentModel;

    class MainWindowViewModel : INotifyPropertyChanged
    {
        private string _firstName;
        public string FirstName
        {
            get { return _firstName; }
            set
            {
                _firstName = value;
                OnPropertyChanged("FirstName");
            }
        }

        #region INotifyPropertyChanged implementation

        public event PropertyChangedEventHandler PropertyChanged = delegate { };
        private void OnPropertyChanged(string propertyName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion
        
    }
}
Since this is a WPF 4.5 Developer's Preview feature, we need .net framework 4.5 installed on the machine. We also need Visual Studio 11 Developer's Preview. Let's open this in the IDE and update the framework to .net framework 4.5.

Now let's update MainWindow.xaml as follows:

<Window x:Class="MVVMDelayedBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MVVMDelayedBinding"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="45" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.20*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Border Background="Navy" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" >
            <TextBlock Text="Personal Information" Foreground="White" FontSize="20"
                       TextAlignment="Center" FontWeight="Bold" VerticalAlignment="Center"/>
        </Border>
        <Label Content="First Name" Grid.Row="1" Grid.Column="0" Margin="2,3,2,2"/>
        <TextBox Grid.Row="1" Grid.Column="1" Margin="2,3,2,2" >
            <TextBox.Text>
                <Binding Path="FirstName" 
                         Mode="OneWayToSource" 
                         UpdateSourceTrigger="PropertyChanged"
                         Delay="200" />
            </TextBox.Text>
        </TextBox>
        <Border BorderBrush="Silver" BorderThickness="1" 
                Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2">
            <TextBlock Text="Other Details" TextAlignment="Center"
                       VerticalAlignment="Center"
                       FontSize="20"/>
        </Border>
    </Grid>
</Window>
This is simple example of MVVM based view. We are using the new MainWindowViewModel instance as the DataContext of the view. The most interesting thing is the TextBox Binding. We are binding the TextBox with FirstName property from the DataContext. The mode is set as OneWayToSource supporting the flow of value updates only from Target to Source. The UpdateSourceTrigger is set as PropertyChanged, resulting in the updates in the Target properties to be copied to the Source without waiting for losing the focus. Now look at the Delay. We are setting the delay as 200. This is in milliseconds. It means the runtime should wait for 200 ms to copy a property update to the Source. This would throttle fast changes to the view and wait for the interaction to get settled before the view state gets updated. Let's run this and use the First Name as Muhammad. Instead of the regular behavior of PropertyChanged. It throttled my input, waited for 200ms and updated the view model's property. This is perfect!

Download: