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:

No comments: