Showing posts with label wpf binding. Show all posts
Showing posts with label wpf binding. Show all posts

Saturday, September 29, 2012

Dynamically Generated Properties using CustomTypeDescriptor for WPF Binding

In the previous post we discussed how we can use DynamicObject to add dynamically generated properties to our types. This post would continue the discussion but here we are going to discuss ICustomTypeDescriptor to add dynamically generated properties. We will also discuss how we can use these properties for WPF Binding. In the last part of our discussion we will be adding the missing bit to support change notifications for these properties.

System.ComponentModel has a default implementation of ICustomTypeDescriptor. This provides the interface implementation requirements in the form of virtual methods which can be overridden in your custom type. We will be using this implementation as part of our discussion. Basically the following methods from the interface do all the magic. We can provide implementation of these methods to introduce our custom properties in order to fake the client that the type already has these properties.


This is also more to do with WPF Binding System. If the DataContext implements ICustomTypeDescriptor then WPF Binding system uses the Type Descriptor instead of relying on reflection. From msdn, this discussion is very useful to learn how binding references are resolved.

http://msdn.microsoft.com/en-us/library/bb613546.aspx

Both of the overloads of GetProperties() methods return PropertyDescriptorCollection. It is a collection of PropertyDescriptor. Overriding these methods and adding further items to the collection can give the impression to the WPF Binding system of having these properties by the type itself. Let's provide a descriptor inheriting from PropertyDescriptor.

In the above property descriptor, please look at GetValue and SetValue methods. The type is holding a field called propertyValue. These methods operate on this field. We don't need these methods for working with WPF Binding system and need them to work with the additional properties in our code. Let's use this descriptor to create a Type implementing ICustomTypeDescriptor. We are maintaining an internal collection _myProperties to hold the additional properties which are not provided by the type itself. This would create perception of dynamically generated properties. An additional method, called AddProperty, is provided to add an item to the collection. The method is generic to support a property with any supported type. The type is also implementing INotifyPropertyChanged interface providing change propagation support. This would raise familiar PropertyChanged event when a value is changed using SetValue method.

Now let's use this type and introduce a new type called StudentViewModel. It maintains a property called FirstName. Now here is the intersting part. We are adding another property LastName using AddProperty method of the base type ViewModelBase.

Similarly, we add a new type called MainViewModel. Here also we are adding a property called MainTitle. We are setting the value of this property as "Main App Window". It is maintaining a collection holding a collection of StudentViewModel. Here we are adding members to the collection. We are setting the property of FirstName directly. We are setting the other property LastName using SetPropertyValue method. Another property SelectedStudent is provided to hold the currently selected student.

Now let's use these view models for WPF Binding. We are using MainViewModel as the DataContext of MainWindow. The view mainly has a ListBox and DataGrid to show items from Students collection in the DataContext. There is no difference in the use of FirstName and LastName properties from StudentViewModel even when LastName is being dynamically generated. You can also notice MainTitle being used for App banner.

Since the basic setup is done. Now let's run the application now. We should see the following display:


Supporting Change Notifications
This looks perfect but this has one limitation. This doesn't support change notifications for newly added properties. Since we are using PropertyDescritptor to add dynamic properties, we can easily add this behavior using OnValueChanged from the type. We first need to update the ViewModelBase so that whenever a property is added, we add the ValueChanged to the property descriptor and call OnPropertyChanged from ViewModelBase similary to the existing properties from the inheriting types.

Next we need to add the support in the inheriting PropertyDesriptor type i.e. CustomPropertyDescriptor. This would cause the ValueChanged to be used when SetValue method is called. In turn, PropertyChanged from ViewModelBase would be raised for the same property.


Download


Monday, July 25, 2011

INotifyPropertyChanged and Magic Strings

One of the argument against INotifyPropertyChanged is the magic strings based notification. Although this is not as big an issue as many people think it is but I have seen people finding it difficult to explain to their fellow developers about this. As an alternate they make their View Models a DependencyObject and provide Dependency Properties for all properties that should be supporting change notifications.

In this post we are going to discuss how we can avoid the use of magic strings and still be able to use INotifyPropertyChanged. For those who needs some glimpse of the problem, INotifyPropertyChanged based properties appear something like this in general.
private string _firstName;
public string FirstName
{
    get { return _firstName; }
    set
    {
       _firstName = value;
       OnPropertyChanged("FirstName");
    }
}
And that is what we want to avoid. We don’t want an OnPropertyChanged with a magic string.

Let’s create a simple view with a simple TextBox for entering user information (First Name). In order to prove that the property change notifications are working, we add one TextBlock. This TextBlock is bound to the same property. This is a OneWay binding which is one-way from Source to Target property. We have set the UpdateSourceTrigger on TextBox’s binding to PropertyChanged so that we don’t have to lose focus from the TextBox in order to trigger the copying of text to the view model’s property.
<Window x:Class="WpfApp_INotifyWithoutMagicString.MainWindow"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:local="clr-namespace:WpfApp_INotifyWithoutMagicString"
       Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
       <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
       <Label Content="First Name" Height="28" HorizontalAlignment="Left"
              Margin="22,54,0,0" Name="label1" VerticalAlignment="Top" Width="80" />
       <TextBox Height="28" HorizontalAlignment="Left" Margin="108,54,0,0"
                Name="textBox1" VerticalAlignment="Top" Width="356"
                Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" />
       <TextBlock Height="121" HorizontalAlignment="Left" Margin="108,101,0,0"
                  Name="textBlock1" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
                  Background ="Thistle" VerticalAlignment="Top" Width="356" />        
   </Grid>
</Window>
We are instantiating MainWindowViewModel and using it as DataContext of the Window. The view model is assumed to have a property, named FirstName. You can find the definition of MainWindowViewModel as follows:
&class MainWindowViewModel : BaseViewModel<MainWindowViewModel>
{
    private string _firstName;
    public string FirstName
    {
       get { return _firstName; }
       set
       {
           _firstName = value;
           OnPropertyChanged(viewModel => viewModel.FirstName);
       }
    }
}
As expected, the above view model has a public property FirstName. The difference is how we have called OnPropertyChanged. As we mentioned in the start of this post, there is no magic string. The definition of OnPropertyChanged is inherited from ViewModelBase. But one thing is apparent, the definition of the method should be dealing with the handling of the lambda expression provided as method argument.
class MainWindowViewModel : ViewModelBase<MainWindowViewModel>
{
    private string _firstName;
    public string FirstName
    {
       get { return _firstName; }
       set
       {
           _firstName = value;
           OnPropertyChanged(viewModel => viewModel.FirstName);
       }
    }
}
The definition of ViewModelBase is as follows:
class ViewModelBase<T> : INotifyPropertyChanged where T : class
{
    #region Implementation INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    protected void OnPropertyChanged(Expression<Func<T, object>> expression)
    {
       if (expression == null || !(expression.Body is MemberExpression))
       {
           return;
       }

       PropertyChanged(this, new PropertyChangedEventArgs(((MemberExpression)expression.Body).Member.Name));
    }

    #endregion Implementation INotifyPropertyChanged
}
ViewModelBase is implemented as generic type. This is to allow the user to avoid casting. The other implementation might have been as follows:
class ViewModelBase : INotifyPropertyChanged
{
    #region Implementation INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    protected void OnPropertyChanged(Expression<Func<object, object>> expression)
    {
       if (expression == null || !(expression.Body is MemberExpression))
       {
           return;
       }

       PropertyChanged(this, new PropertyChangedEventArgs(((MemberExpression)expression.Body).Member.Name));
    }

    #endregion Implementation INotifyPropertyChanged
}
But since this is based on object type of argument for lambda expression. In order to access the FirstName property from the argument, the developer need to typecast. Wrong typecasting might result in runtime exceptions if the object being type casted doesn’t have the property with same name.
public string FirstName
{
    get { return _firstName; }
    set
    {
       _firstName = value;
       OnPropertyChanged(viewModel => ((MainWindowViewModel)viewModel).FirstName);
    }
}
The developer might be just using the code similar to the following code. But this must be remembered that we are not passing the current instance of View Model. We are just passing the lambda expression as argument to OnPropertyChanged. Its evaluation would result in raising PropertyChanged event for the same property.
public string FirstName
{
    get { return _firstName; }
    set
    {
       _firstName = value;
       OnPropertyChanged(viewModel => FirstName);
    }
}
Which leads us to third implementation of ViewModelBase without even specifying the type of view model as the argument for lambda. I think this would be the simplest and easiest implementation of the Base View Model.
class ViewModelBase : INotifyPropertyChanged 
{
    #region Implementation INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    protected void OnPropertyChanged(Expression<Func<object>> expression)
    {
       if (expression == null || !(expression.Body is MemberExpression))
       {
           return;
       }

       PropertyChanged(this, new PropertyChangedEventArgs(((MemberExpression)expression.Body).Member.Name));
    }

    #endregion Implementation INotifyPropertyChanged
}
If we implement our ViewModel as above then it might result in simplest OnPropertyChanged as follows:
public string FirstName
{
    get { return _firstName; }
    set
    {
       _firstName = value;
       OnPropertyChanged(() => FirstName);
    }
}
No matter what implementation of ViewModelBase you are using, when you would run the application and enter text in the TextBox, it would update the FirstName property in the DataContext (MainWindowViewModel’s instance). This is proved through the same data being displayed in the TextBlock below.

Friday, February 4, 2011

WPF - Binding Converter Parameter [Including Discussion about Binding Reflector]

In this post we will be discussing how we can bind ConverterParameter used in WPF Binding. As we know that ConverterParameter is not a DependencyProperty, we can not bind it directly. We will be looking at the possible workarounds to achieve the similar results. As always, we will start with a simple window and then add necessary ingredients for this discussion. Let us create a sample Window with two text boxes for entering Gender and Name of a Student.

<Window x:Class="WpfApplication_ConverterParameterBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication_ConverterParameterBinding"
Title="Main Window" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<TextBox Height="30" HorizontalAlignment="Left" Margin="84,6,0,0" Name="textBoxGender"
VerticalAlignment="Top" Width="412" >
<TextBox.Text>
<Binding Path="Gender" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" />
</TextBox.Text>
</TextBox>
<Label Content="Gender" Height="28" HorizontalAlignment="Left" Margin="12,6,0,0"
Name="labelGender" VerticalAlignment="Top" Width="58" />
<TextBox Height="30" HorizontalAlignment="Left" Margin="84,40,0,0" Name="textBoxStudentName"
VerticalAlignment="Top" Width="412" >
<TextBox.Text>
<Binding Path="StudentName" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" />
</TextBox.Text>
</TextBox>
<Label Content="Name" Height="28" HorizontalAlignment="Left" Margin="12,40,0,0"
Name="labelStudentName" VerticalAlignment="Top" Width="58" />
</Grid>
</Window>

As you can see that we have used an instance of MainWindowViewModel as DataContext of the Window. MainWindowViewModel is presented as below:
namespace WpfApplication_ConverterParameterBinding
{
using System.Windows;

class MainWindowViewModel : DependencyObject
{
public static DependencyProperty GenderProperty =
DependencyProperty.Register("Gender", typeof(string),
typeof(MainWindowViewModel), new FrameworkPropertyMetadata() { AffectsRender = true });

public string Gender
{
get { return (string)GetValue(GenderProperty); }
set { SetValue(GenderProperty, value); }
}

public static DependencyProperty StudentNameProperty =
DependencyProperty.Register("StudentName", typeof(string),
typeof(MainWindowViewModel), new FrameworkPropertyMetadata() { AffectsRender = true });

public string StudentName
{
get { return (string)GetValue(StudentNameProperty); }
set { SetValue(StudentNameProperty, value); }
}
}
}

When we run this application, it appears as follows:



Now we want to display a Welcome message to the Student based on the information entered. If it is a Male student (Gender = M), the message should be read as "Welcome Mr. NAME OF STUDENT". If it is a female student (Gender = F), it should read as "Welcome Ms. NAME OF STUDENT".


In order to update the Window, let us add a TextBlock at the bottom as follows:

<Window x:Class="WpfApplication_ConverterParameterBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication_ConverterParameterBinding"
Title="Main Window" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<TextBox Height="30" HorizontalAlignment="Left" Margin="84,6,0,0" Name="textBoxGender"
VerticalAlignment="Top" Width="412" >
<TextBox.Text>
<Binding Path="Gender" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" />
</TextBox.Text>
</TextBox>
<Label Content="Gender" Height="28" HorizontalAlignment="Left" Margin="12,6,0,0"
Name="labelGender" VerticalAlignment="Top" Width="58" />
<TextBox Height="30" HorizontalAlignment="Left" Margin="84,40,0,0" Name="textBoxStudentName"
VerticalAlignment="Top" Width="412" >
<TextBox.Text>
<Binding Path="StudentName" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" />
</TextBox.Text>
</TextBox>
<Label Content="Name" Height="28" HorizontalAlignment="Left" Margin="12,40,0,0"
Name="labelStudentName" VerticalAlignment="Top" Width="58" />
<TextBlock Height="164" HorizontalAlignment="Left" Margin="14,135,0,0" Name="textBlockMessage"
Text="Hello Mr. Muhammad" VerticalAlignment="Top" Width="479" />
<Label Content="Message" Height="29" HorizontalAlignment="Left" Margin="11,102,0,0"
Name="label1" VerticalAlignment="Top" Width="129" />
</Grid>
</Window>

The text is hardcode "Hello Mr. Muhammad. We want it to be based on the Data entered. First let us update the Binding that it just shows "Hello NAME OF STUDENT" message in the notification box.

<TextBlock Height="164" HorizontalAlignment="Left" Margin="14,135,0,0" Name="textBlockMessage"
VerticalAlignment="Top" Width="479" >
<TextBlock.Text>
<Binding Path="StudentName" StringFormat="Hello {0}" />
</TextBlock.Text>
</TextBlock>

Now when we run the application, it shows a welcome message to the student.


Since we want to show the salutation (Mr. / Ms.) with name so we need to somehow use Gender. if it is M, we should show "Welcome Mr. NAME OF STUDENT". On the other hand if it is F, we should show "Hello Ms. NAME OF STUDENT".
Let us define an IValueConverter for this purpose:

namespace WpfApplication_ConverterParameterBinding
{
using System;
using System.Windows.Data;

class MessageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string salutation = (parameter == null || parameter == "M") ? "Mr." : "Ms";

return string.Format("Hello {0} {1}", salutation, value);
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

Basically we are expecting that, somehow, we would be able to pass Gender as parameter of Converter. Let us instantiate the Converter in the Resources section of the window.

<Window.Resources>
<local:MessageConverter x:Key="messageConverter" />
</Window.Resources>

This is used for Binding the converter as follows:

<TextBlock.Text>
<Binding Path="StudentName" Converter="{StaticResource messageConverter}" ConverterParameter="{Binding Gender}" />
</TextBlock.Text>

This seems to pass Gender for ConverterParameter for Binding. But as we run the applcation, it results in an exception.



This is because of the limitation as mentioned in the beginning of this post. Basically ConverterParameter is not a dependency property so we can not bind it. Now let us discuss the alternative solutions.

Using MultiBinding:
One way to resolve the issue of Binding ConverterParameter is by using MultiBinding. Instead of Binding ConverterParameter, just have a separate binding for Target property using MultiBinding. We would be needing MultiValueConverter for this purpose. We are expecting that the following converter would be used for MultiBinding (implements IMultiValueConverter) of Gender and Name property. If the values of none of these are available then return empty string. If any of these is available then greet the person [Hello Mr. NAME_OF_PERSON for Gender = M and Hello Ms. NAME_OF_PERSON for Gender = F]

namespace WpfApplication_ConverterParameterBinding
{
using System;
using System.Windows.Data;

class MessageMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string name = (values[1] == null) ? string.Empty : values[1].ToString();
string gender = (values[0] == null) ? string.Empty : values[0].ToString();

string salutation;

switch(gender)
{
case "M":
salutation = "Mr";
break;
case "F":
salutation = "Ms";
break;
default:
salutation = string.Empty;
break;
}

string message = string.Empty;

if (!(name == string.Empty && gender == string.Empty))
{
message = string.Format("Hello {0} {1}", salutation, name);
}

return message;
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

Let's use this converter in a view. The only update is the usage of this new converter. We are instantiating it in the Resources section of Window. We are using the same converter with text block showing Message.

<Window x:Class="WpfApplication_ConverterParameterBinding.MainWindowMultiBinding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication_ConverterParameterBinding"
Title="Main Window" Height="350" Width="525">
<Window.Resources>
<local:MessageMultiConverter x:Key="messageConverter" />
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<TextBox Height="30" HorizontalAlignment="Left" Margin="84,6,0,0" Name="textBoxGender"
VerticalAlignment="Top" Width="412" >
<TextBox.Text>
<Binding Path="Gender" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" />
</TextBox.Text>
</TextBox>
<Label Content="Gender" Height="28" HorizontalAlignment="Left" Margin="12,6,0,0"
Name="labelGender" VerticalAlignment="Top" Width="58" />
<TextBox Height="30" HorizontalAlignment="Left" Margin="84,40,0,0" Name="textBoxStudentName"
VerticalAlignment="Top" Width="412" >
<TextBox.Text>
<Binding Path="StudentName" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" />
</TextBox.Text>
</TextBox>
<Label Content="Name" Height="28" HorizontalAlignment="Left" Margin="12,40,0,0"
Name="labelStudentName" VerticalAlignment="Top" Width="58" />
<TextBlock Height="164" HorizontalAlignment="Left" Margin="14,135,0,0" Name="textBlockMessage"
VerticalAlignment="Top" Width="479" >
<TextBlock.Text>
<MultiBinding Converter="{StaticResource messageConverter}">
<Binding Path="Gender" />
<Binding Path="StudentName" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<Label Content="Message" Height="29" HorizontalAlignment="Left" Margin="11,102,0,0"
Name="label1" VerticalAlignment="Top" Width="129" />
</Grid>
</Window>

When we run the project, it appears as follows:



As you can see that empty message is displayed. Let's enter F for Gender and Maria for name. It would update the message as follows:



This might or might not be a requirement but let me explain what is going on. The re-evaluation of Converter is being done when either of Gender or Name of Student is updated. Since Gender is supposed to be used just as a ConverterParameter originally this re-evaluation might not be a desired behavior. It might be desired that converter is re-evaulated just when the actual intended bound property is updated. This update should just use the current value of the property specified as ConverterParameter. In order to prove this point just update the Gender to M. This would update the message as Hello Mr. Maria. This might not be desired.



Introducing a Dependency Property in Converter and Using Binding Reflector:
ConverterParameter is a property of Binding. It is provided as a parameter to Convert and ConvertBack method of Converter whenever any update in Source or Target of Binding takes place.

Let us update define a new Converter. It is an IValueConverter like our first example. It just has an additional DependencyProperty. We name this as BindableConverterParameter.

namespace WpfApplication_ConverterParameterBinding
{
using System;
using System.Windows.Data;
using System.Windows;

class EnhancedMessageConverter : DependencyObject, IValueConverter
{
public static DependencyProperty BindableConverterParameterProperty =
DependencyProperty.Register("BindableConverterParameter", typeof(string),
typeof(EnhancedMessageConverter));

public string BindableConverterParameter
{
get { return (string)GetValue(BindableConverterParameterProperty); }
set { SetValue(BindableConverterParameterProperty, value); }
}

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string gender = BindableConverterParameter ?? string.Empty;
var name = value ?? string.Empty;

string salutation;

switch (gender)
{
case "M":
salutation = "Mr";
break;
case "F":
salutation = "Ms";
break;
default:
salutation = string.Empty;
break;
}

string message = string.Empty;

if (!(name == string.Empty && gender == string.Empty))
{
message = string.Format("Hello {0} {1}", salutation, name);
}

return message;

}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

Let's use this converter in the view.

<Window x:Class="WpfApplication_ConverterParameterBinding.MainWindowEnhanced"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication_ConverterParameterBinding"
Title="MainWindowEnhanced" Height="300" Width="300">
<Window.Resources>
<local:EnhancedMessageConverter x:Key="messageConverter"
BindableConverterParameter="{Binding Gender}" />
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<TextBox Height="30" HorizontalAlignment="Left" Margin="84,6,0,0" Name="textBoxGender"
VerticalAlignment="Top" Width="412" >
<TextBox.Text>
<Binding Path="Gender" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" />
</TextBox.Text>
</TextBox>
<Label Content="Gender" Height="28" HorizontalAlignment="Left" Margin="12,6,0,0"
Name="labelGender" VerticalAlignment="Top" Width="58" />
<TextBox Height="30" HorizontalAlignment="Left" Margin="84,40,0,0" Name="textBoxStudentName"
VerticalAlignment="Top" Width="412" >
<TextBox.Text>
<Binding Path="StudentName" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" />
</TextBox.Text>
</TextBox>
<Label Content="Name" Height="28" HorizontalAlignment="Left" Margin="12,40,0,0"
Name="labelStudentName" VerticalAlignment="Top" Width="58" />
<TextBlock Height="164" HorizontalAlignment="Left" Margin="14,135,0,0" Name="textBlockMessage"
VerticalAlignment="Top" Width="479" >
<TextBlock.Text>
<Binding Path="StudentName" Converter="{StaticResource messageConverter}" />
</TextBlock.Text>
</TextBlock>
<Label Content="Message" Height="29" HorizontalAlignment="Left" Margin="11,102,0,0"
Name="label1" VerticalAlignment="Top" Width="129" />
</Grid>
</Window>

Just look at the instantiation of the Converter in Window.Resources section. BindableConverterParameter in initialized to be bound to Gender property of the object. From our knowledge of Data Binding in WPF, we know that if we don't provide any Binding Source then it would be considered as DataContext.



Now keep changing the Gender and Name of Student in the view. No matter how many times we change the values entered, salutation title (Mr. / Ms.) never appears in the message text. As we see the definition of converter we know that this is only possible when BindableConverterParameter is null / Empty. Let's insert a break-point in the Convert method keep changing the data on the view.



This is as per the expectation. Basically BindableConverterParameter is always null. This is basically due to perceived over-simplification of the problem. We are binding to the DataContext in the Resources section. It must be remembered that DataContext is a DependencyProperty inherited through the Visual Tree. Since Resources section is not in the Visual Tree, it would not have the DataContext inherited resulting in no Binding at all. Because of same reason Relative Source Binding would also not work if we attempt to do it like this:

<Window.Resources>
<local:EnhancedMessageConverter x:Key="messageConverter"
BindableConverterParameter="{Binding RelativeSource={RelativeSource Self}, Path=DataContext.Gender}" />
</Window.Resources>

As we have discussed that Resources can not use DataContext property of the view. But we know that DataContext can utilize any view it desires. Let us try to Bind the BindableConverterParameter to Gender property of DataContext in such a way that Binding is defined on DataContext in reverse direction. This can be done as follows:

<Window.Resources>
<local:EnhancedMessageConverter x:Key="messageConverter" />
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel>
<local:MainWindowViewModel.Gender>
<Binding Source="{StaticResource messageConverter}" Path="BindableConverterParameter" Mode="OneWayToSource" />
</local:MainWindowViewModel.Gender>
</local:MainWindowViewModel>
</Window.DataContext>

Now let us run the application. If we enter data in the text boxes, we realize that it results in update of the message to user. The greatest thing is that updates in Gender (used as ConverterParameter) is not causing re-evaluation of message. Message is re-evaluated only when the actual bound value to the message text block (StudentName) is updated. When we run the application and enter data [Gender=F and Name = Maria], the message is displayed as follows:



Updating the Gender to M does not result in updating the display of message as follows:



As we update the name of student, the updated value of BindableConverterParameter is considered and view is updated with new message as follows:



Using Binding Reflector:
This is still not perfect solution because this is based on the assumption that view model is always instantiated by view. This is also possible that view model is instantiate separately and some other code assigns it to the DataContext property of view. Our logic would find limitation under this condition. In this case we are still instantiating the Converter in Resources section which is generally the norm.

The best solution could be to bind the Text property of Gender text box to BindableConverterParameter property of the Converter in OneWayToSource mode. But again, this is already bound to the Gender property of DataContext.

In WPF, a DependencyProperty might be the source for a number of Binding but it might be the Target of only one Binding. Although because of availability of various Binding modes, the difference between Source and Target of Binding in the real sense is really vague but Target is referred to the Property on which the binding is defined and Source is considered as the property which is actually being used for Binding to Target.

Based on the above discussion, this seems to be the end of world. But never lose hope! Let us invent a new tool. We name this tool as Binding Reflector. We will be using this to pass the updates in one dependency property to another Dependency Property. In our example we will be using it to Bind between Gender property of DataContext to BindableConverterParameter property of Converter instantiated in Resources section of Window. In order to use the DataContext, it must be an element in the Visual Tree of view. Let's define it as a FrameworkElement.

namespace WpfApplication_ConverterParameterBinding
{
using System.Windows;
using System.Windows.Data;

class BindingReflector : FrameworkElement
{
public static DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(object), typeof(BindingReflector),
new FrameworkPropertyMetadata()
{
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
PropertyChangedCallback = OnSourceChanged
});

public object Source
{
get { return GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}

public static DependencyProperty TargetProperty =
DependencyProperty.Register("Target", typeof(object), typeof(BindingReflector),
new FrameworkPropertyMetadata() { DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });

public object Target
{
get { return GetValue(TargetProperty); }
set { SetValue(TargetProperty, value); }
}

private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var reflector = (BindingReflector)d;
if (reflector.Source != reflector.Target)
{
reflector.Target = reflector.Source;
}
}
}
}

As you can see that it is a FrameworkElement with just two Dependency properties i.e. Source and Target. Source is registered with a PropertyChangedCallBack. In the callback handler, we are just copying the value of Source Property to Target property. This Binding Reflector will be used in the view. With appropriate Binding, we will be obtaining the actual desired result. Let us define a new view as follows:

<Window x:Class="WpfApplication_ConverterParameterBinding.MainWindowEnhancedReflector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication_ConverterParameterBinding"
Title="MainWindowEnhancedReflector" Height="300" Width="585">
<Window.Resources>
<local:EnhancedMessageConverter x:Key="messageConverter" />
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModelEnhanced />
</Window.DataContext>
<Grid>
<local:BindingReflector Target="{Binding Mode=OneWayToSource, Source = {StaticResource messageConverter}, Path=BindableConverterParameter}"
Source="{Binding Path=Gender, Mode=OneWay}" />
<TextBox Height="30" HorizontalAlignment="Left" Margin="84,6,0,0" Name="textBoxGender"
VerticalAlignment="Top" Width="412" >
<TextBox.Text>
<Binding Path="Gender" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" />
</TextBox.Text>
</TextBox>
<Label Content="Gender" Height="28" HorizontalAlignment="Left" Margin="12,6,0,0"
Name="labelGender" VerticalAlignment="Top" Width="58" />
<TextBox Height="30" HorizontalAlignment="Left" Margin="84,40,0,0" Name="textBoxStudentName"
VerticalAlignment="Top" Width="412" >
<TextBox.Text>
<Binding Path="StudentName" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" />
</TextBox.Text>
</TextBox>
<Label Content="Name" Height="28" HorizontalAlignment="Left" Margin="12,40,0,0"
Name="labelStudentName" VerticalAlignment="Top" Width="58" />
<TextBlock Height="164" HorizontalAlignment="Left" Margin="14,135,0,0" Name="textBlockMessage"
VerticalAlignment="Top" Width="479" >
<TextBlock.Text>
<Binding Path="StudentName" Converter="{StaticResource messageConverter}" />
</TextBlock.Text>
</TextBlock>
<Label Content="Message" Height="29" HorizontalAlignment="Left" Margin="11,102,0,0"
Name="label1" VerticalAlignment="Top" Width="129" />
</Grid>
</Window>

Just have a look at the first child of Grid. It is the same BindingReflector that we have created. You can see that we have used specific Binding modes for Source and Target Binding. For Source, it is One Way Binding so that it could receive value updates from Source. For Target property, it is set as OneWayToSource so that value changes as assigned in SourceChanged call back could be passed to the Target.

One important point is that we have bound Target property before Source property here. This is deliberate action. Basically as we know that using a FrameworkElement like this causes the default constructor of FramworkElement to be executed. It then assigns the specified properties to the values as specified. If we specify Source before Target here then the initial values of Source will be copied to the Target but they wouldn't be assigned to the Binding defined later. But if we first define binding for Target then as we would update the value of Target it would be available in BindableConverterParameter.

Now we run the application. The behavior is similar to our previous example. It is also not causing the Binding to re-evaluate when just BindableConverterParameter is updated. It is only updated when the actual Binding Source i.e. StudentName is updated. Let's run this to see this behavior:


Now update Gender followed by Name and see the message updated as follows:



Note:
In this post, we have introduced Binding Reflector. We can also use it to bind between dependency properties of two different Resources.

Download:

Wednesday, January 26, 2011

WPF - Performance Improvement for MVVM Applications - Part # 1

This is a series of posts about performance improvements of an MVVM based applications. You can find the other posts here:

- Part 2 : http://shujaatsiddiqi.blogspot.com/2011/02/wpf-performance-improvement-for-mvvm.html

- Part 3: http://shujaatsiddiqi.blogspot.com/2011/03/wpf-performance-improvement-for-mvvm.html

In this example we are discussing the various performance improvement for a WPF Binding developed using MVVM. This is for enhancing user experience by increasing the responsiveness of the system. MVVM based application is highly dependent upon the WPF Binding system. I think if we use certain features of Binding system then we can achieve this.
<Window x:Class="WpfApplication_ASync_Binding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication_ASync_Binding"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<TextBlock  Height="56" HorizontalAlignment="Left"
Margin="40,71,0,0" Name="label1" VerticalAlignment="Top"
Width="434" Background="Aquamarine" >
<TextBlock.Text>
<Binding Path="StudentName" StringFormat="testtt:{0}" />
</TextBlock.Text>
</TextBlock>
</Grid>
</Window>

We are not changing the code behind of the view so it should be as created by default. The view model:
namespace WpfApplication_ASync_Binding
{
using System.ComponentModel;

class MainWindowViewModel : INotifyPropertyChanged
{
private Student _student;
public MainWindowViewModel()
{
_student = new Student();
} 

private string _studentName;
public string StudentName
{
get
{
return _student.StudentName;
}
set
{
_student.StudentName = value;
OnPropertyChanged(StudentName);
}
} 

public event PropertyChangedEventHandler PropertyChanged; 

private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}

The view model is using _student as model. This is an instance of Student class. As you can see StudentName is just a proxy property for the model’s property named StudentName. The definition of Student is as follows:
namespace WpfApplication_ASync_Binding
{
class Student
{
public string StudentName { get; set; } 

public Student()
{
StudentName = "Muhammad";
}
}
}

Let’s run this application. The output should appear as follows:


Now let’s change this a bit. Let’s consider Student is using some complex logic for its property StudentName. It might be using a web service which might be slow. Now since we are using proxy property for binding this delay would slow down the binding and the form would become idle. Let’s create this scenarios by just introducing a delay using Thread.Sleep() as follows:
namespace WpfApplication_ASync_Binding
{
class Student
{
private string _studentName;
public string StudentName
{
get
{
//imaginary delay
Thread.Sleep(10000);
return _studentName;
}
set { _studentName = value; }
}

public Student()
{
StudentName = "Muhammad";
}
}
}

Now run the application. The same form would display but after a delay of atleast 10 seconds.

Basically when Binding system would access the getter for StudentName property in the DataContext. It would be delayed because the view model would be trying to get the Model’s property resulting in a delay of atleast 10 seconds (because of Thread.Sleep). So form instance is created. It assigns the DataContext as MainWindowViewModel in InitializeComponent. When it attempts to bind the Text property of TextBlock. It experiences this delay. So the delay is because of binding. If this binding becomes asynchronous then the performance could improve. You should be glad to learn that WPF Binding system supports making its binding asynchronous by providing IsAsync property in Binding markup extension. Let’s use this!

We update the view as follows:
<TextBlock.Text>
<Binding Path="StudentName" StringFormat="testtt:{0}" IsAsync="True" />
</TextBlock.Text>

Now run this. The form appears as follows:


This would be shown like this for atleast 10 seconds. During this time, it starts a ThreadPool thread on the background to get the Binding source. As soon as the source property becomes available, the binding updates the view as follows:


Well this is good but user sees nothing on the screen for atleast 10 seconds. We might want to show something else to the user until the data becomes available as a fallback mechanism. WPF binding system supports that too. You can achieve that by setting FallBackValue propery of Binding as follows:
<TextBlock.Text>
<Binding Path="StudentName" StringFormat="testtt:{0}"
IsAsync="True" FallbackValue="Test Fallback value" />   
</TextBlock.Text

Now run the application. The window is shown as follows:


This is shown like that for 10 seconds showing the Fallback value for Binding. You can see that StringFormat is not applied for FallbackValue. After 10 seconds, it updates itself as follows:


In order to have more sophisticated fallback mechanism, you might want to consider Priority Binding.

http://shujaatsiddiqi.blogspot.com/2010/09/wpf-priority-binding.html

The world is not simple. Let’s make it a little complex by updating the constructor of our model (Student) as follows:
public Student()
{
StudentName = "Muhammad";
Thread.Sleep(20000);
}

This is to simulate the 20 seconds further delay in Binding. Let’s run the application. For 20 seconds, nothing appears on the screen. After that the window appears with the fallback value. The fallback value remains on the view for 10 seconds. After that the view is updated with the finalized value for StudentName with applied StringFormat. Now the main question is why this window appears after 20 seconds when binding is asynchronous. Basically Binding.IsAsync just makes the Binding asynchronous. If there is a delay in the construction of DataContext, this would still be on the UI thread hence synchronous. The construction of DataContext (MainWindowViewModel) is causing this delay because of delay of instantiation of the Student (model) in the constructor of view model [Thread.Sleep(20000)].

In order to improve this behavior we can update the way DataContext is constructed. We can construct the object using ObjectDataProvider. This is to utilize the asynchronous instantiation feature of ObjectDataProvider. We can cause asynchronous construction by setting IsAsynchronous for ObjectDataProvider. Let’s update the definition of the section of XAML for view where we are assigning DataContext as follows:
<Window.DataContext>
<ObjectDataProvider ObjectType="{x:Type local:MainWindowViewModel}" IsAsynchronous="True"  />
</Window.DataContext>

Now run the application. As we run the application there is no delay and the following screen appears:


This is maintained for 30 seconds. The 20 seconds is for the construction of DataContext (causing construction of model) and the other 10 seconds is for the delay in the getter for StudentName property from DataContext. Basically the Fallback value is maintained until the value is available no matter the delay is called by the DataContext construction or the available of its property.

10 + 20 = 30 seconds

Eventually the window is updated as follows:


Download:

Monday, January 10, 2011

UpdateSourceTrigger = "Explicit" for MVVM Puritans

Recently, a new term has been evolved and established in WPF community. This is Pure MVVM. MVVM puritans is a belief system in MVVM. The believers of this faith have clean heart and no code behind :)

There are so many techniques developed by WPF community to adjust the believer of this faith in our society. One limitation that I have seen these people experiencing, is how to practice their religion with Explicit Binding. Just to recall, you can see this from msdn:

http://msdn.microsoft.com/en-us/library/system.windows.data.binding.updatesourcetrigger.aspx

With explicit binding, this has always been expected to update the binding source using BindingExpression. You might be familiar with this code:

private void SubmitButtonClick(object sender, RoutedEventArgs e)
{
textBoxName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

But the thing is that this code is written in code behind which is not allowed under pure MVVM methodology. In this post, we are going to look at one technique which could be used to overcome such situation. We will be using CommandParameters for our DelegateCommand in the DataContext (ViewModels) of our View.

Let's assume a sample view, StudentView. This form has two fields. They are student's First and Last names. This view should also have a button to update the Student details to the view model. The organization has zero tolerance to code behind so it is part of design requirement to avoid it.



We would be using RelayCommand for this post. You can get the code of RelayCommand from Josh Smith's article:

http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

The XAML definition of Student view is as follows:

<Window x:Class="WpfAppMVVM_Puritans_ExplicitBinding.StudentView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfAppMVVM_Puritans_ExplicitBinding"
Title="StudentView" Height="300" Width="555">
<Window.Resources>
<local:StudentConverter x:Key="studentParameterConverter" />
</Window.Resources>
<Window.DataContext>
<local:StudentViewModel />
</Window.DataContext>
<Grid>
<Label Content="First Name" Height="32" HorizontalAlignment="Left" Margin="13,11,0,0" Name="label1" VerticalAlignment="Top" Width="127" />
<TextBox Height="32" HorizontalAlignment="Left" Margin="148,10,0,0" Name="textBoxFirstName" VerticalAlignment="Top" Width="349" >
<TextBox.Text>
<Binding Path="FirstName" UpdateSourceTrigger="Explicit" />
</TextBox.Text>
</TextBox>
<Label Content="Last Name" Height="32" HorizontalAlignment="Left" Margin="13,49,0,0" Name="label2" VerticalAlignment="Top" Width="127" />
<TextBox Height="32" HorizontalAlignment="Left" Margin="148,48,0,0" Name="textBoxLastName" VerticalAlignment="Top" Width="349" >
<TextBox.Text>
<Binding Path="LastName" UpdateSourceTrigger="Explicit" />
</TextBox.Text>
</TextBox>
<Button Content="Save" Height="30" HorizontalAlignment="Left" Margin="148,86,0,0" Name="button1" VerticalAlignment="Top" Width="153"
Command="{Binding SaveCommand}" >
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource studentParameterConverter}">
<Binding ElementName="textBoxFirstName" Path="Text" />
<Binding ElementName="textBoxLastName" Path="Text" />
</MultiBinding>
</Button.CommandParameter>
</Button>
</Grid>
</Window>

As you can see the above view has two text boxes. The Text property of these text boxes are bound to FirstName and LastName property of the DataContext. Please notice UpdateSourceTrigger = Explicit with binding. An instance of StudentViewModel has been created and assigned as DataContext of this view. This also has a Update button. StudentViewModel is expected to have a command SaveCommand bound with the Command property of the button.

The most interesting thing and the reason of this post is the CommandParameter for the button. The CommandParameter has been bound with the Text properties of the two text boxes. We have to use MultiBinding for this. In order to get the value of this binding, we have to use converter. We have instantiate StudentConverter in the Resources section of the Window and used this as a StaticResource for this. Alternatively, We could have used StringFormat but WPF has this issue of StringFormat when bound to the property of System.Object type. StringFormat mainly works with string based properties for MultiBinding.

The definition of StudentViewModel is as follows:

class StudentViewModel : INotifyPropertyChanged
{
#region Private Fields
private string _firstName;
private string _lastName;
#endregion

#region Public Properties
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}

public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
OnPropertyChanged("LastName");
}
}
#endregion

#region Commands
RelayCommand _saveCommand;
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
{
_saveCommand = new RelayCommand((param) => this.Save(param),
param => this.CanSave);
}
return _saveCommand;
}
}

public void Save(object parameter)
{
string[] Names = ((string)parameter).Split(new char[] { ':' });
FirstName = Names[0];
LastName = Names[1];
}

bool CanSave
{
get
{
return true;
}
}
#endregion Commands

#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion INotifyPropertyChanged implementation
}

As expected by StudentView. It has two properties FirstName and LastName. It also has a RelayCommand based property SaveCommand. The view model is implementing INotifyPropertyChanged to propagate change notifications to the view.

Please notice the definition of Execute method of SaveCommand (named Save). This is expecting FirstName and LastName joined together using colon ":". This code is just splitting the two from parameter (passed through CommandParameter) and assigning to FirstName and LastName properties. It is also possible that these values are not assigned directly but they are updated before being assigned. Whatever you would assign to the property bound to the view, the changes will be propagated to the view because of implementation of INotifyPropertyChanged.

As seen from above, the responsibility of CommandParameter's converter is to combine first and last name with a colon. The definition of converter is as follows:

public class StudentConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string firstName = (string)values[0];
string lastName = (string)values[1];

return string.Format("{0}:{1}", firstName, lastName);
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

Now run the application. Enter first and last names in the assigned textboxes and click Save button. Those values will be converted and passed to the SaveCommand in the view model. Here the view model's properties will be updated and changes are propagated to the View.


Download:

Tuesday, November 23, 2010

WPF - Binding Radio Buttons

In this post we will be discussing how we can bind radio buttons to some property in the view model. Generally one property is bound to one control e.g. string property bound to the Text property of a control. The unique thing with binding RadioButton is that one property is needed to be bound a number of times to the corresponding radio buttons. Most of the times, we have enumeration or boolean based properties in View models which we need to bind to radio buttons.

In order to understand this, let us consider an example form. In this form, we are entering information of sales staff. We have divided our market in three different region. These regions are North, Central and South. A sales staff might belong to any of these regions. If Commission status is turned on for a staff then he will be getting a fixed commission of each transaction brought by him to the company.

In WPF, we can assign radio buttons to different groups. In any group, at most, only one radio button can be checked at a single time. We create two groups, one for region of operation and the other to specify whether it is a commission based staff or not. Let's see the view containing these two groups with radio buttons.
<Window x:Class="WPF_RadioButtonBinding.SalesStaffView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPF_RadioButtonBinding"
Title="Window1" Height="354" Width="481">
<Window.DataContext>
<local:SalesStaffViewModel />
</Window.DataContext>
<Window.Resources>
<local:AreaOfOperationConverter x:Key="ConverterAreaOfOperation" />
<local:CommissionStatusConverter x:Key="ConverterCommissionStatus" />
</Window.Resources>
<Grid>
<RadioButton Height="19" HorizontalAlignment="Left" Margin="25,82,0,0" Name="AreaOfOperationNorth"
IsChecked="{Binding Path=StaffAreaOfOperation, ConverterParameter=North, Converter={StaticResource ConverterAreaOfOperation}}"
GroupName="AreaOfOperation" VerticalAlignment="Top" Width="192">North Region</RadioButton>
<RadioButton Height="19" HorizontalAlignment="Left" Margin="25,107,0,0" Name="AreaOfOperationCentral"
IsChecked="{Binding Path=StaffAreaOfOperation, ConverterParameter=Central, Converter={StaticResource ConverterAreaOfOperation}}"
GroupName="AreaOfOperation" VerticalAlignment="Top" Width="192">Central Region</RadioButton>
<RadioButton Height="19" HorizontalAlignment="Left" Margin="25,132,0,0" Name="AreaOfOperationSouth"
IsChecked="{Binding Path=StaffAreaOfOperation, ConverterParameter=South, Converter={StaticResource ConverterAreaOfOperation}}"
GroupName="AreaOfOperation" VerticalAlignment="Top" Width="192">South Region</RadioButton>
<RadioButton HorizontalAlignment="Left" Margin="25,0,0,113" Name="CommisionStatusActive" Width="192" Height="19"
IsChecked="{Binding Path=StaffCommissionStatus, ConverterParameter=true, Converter={StaticResource ConverterCommissionStatus}}"
GroupName="CommissionStatus" VerticalAlignment="Bottom">Active</RadioButton>
<RadioButton HorizontalAlignment="Left" Margin="25,0,0,88" Name="CommissionStatusInactive" Width="192" Height="19"
IsChecked="{Binding Path=StaffCommissionStatus, ConverterParameter=false, Converter={StaticResource ConverterCommissionStatus}}"
GroupName="CommissionStatus" VerticalAlignment="Bottom">Inactive</RadioButton>
<TextBlock Height="21" HorizontalAlignment="Left" Margin="25,55,0,0" Name="textBlock1"
VerticalAlignment="Top" Width="151" Text="Area of Operation:" FontSize="13" FontWeight="Bold" />
<TextBlock FontSize="13" FontWeight="Bold" HorizontalAlignment="Left" Margin="25,0,0,138"
Name="textBlock2" Text="Commission Status" Width="151" Height="21" VerticalAlignment="Bottom" />
<TextBlock Height="36" Margin="84,9,76,0" Name="textBlock3" VerticalAlignment="Top"
Text="Sales Staff" FontSize="20" HorizontalAlignment="Center" FontWeight="Bold" />
</Grid>
</Window>

The above view is expecting its view model to have two properties, StaffAreaOfOperation and CommissionStatus. As you can see that we have bound the same property with all radio buttons within the same group. We are registering these properties as DependencyProperty with specified default values.
class SalesStaffViewModel : DependencyObject
{
public static DependencyProperty StaffAreaOfOperationProperty =
DependencyProperty.Register("StaffAreaOfOperation", typeof(EnumAreaOfOperation),
typeof(SalesStaffViewModel),
new PropertyMetadata(EnumAreaOfOperation.North));

public EnumAreaOfOperation StaffAreaOfOperation
{
get { return (EnumAreaOfOperation)GetValue(StaffAreaOfOperationProperty); }
set { SetValue(StaffAreaOfOperationProperty, value); }
}

public static DependencyProperty StaffCommissionStatusProperty =
DependencyProperty.Register("StaffCommissionStatus", typeof(bool),
typeof(SalesStaffViewModel), new PropertyMetadata(false));

public bool StaffCommissionStatus
{
get { return (bool)GetValue(StaffCommissionStatusProperty); }
set { SetValue(StaffCommissionStatusProperty, value); }
}
}

The view discussed above is also expecting two converters in the same assembly. The first converter would be used by Area of operations group of radio buttons. This converter would be deciding which value is to be assigned to the DataContext's property based on ConverterParameter specified with binding. These parameters are passed to Converter to be used during conversion logic.
public class AreaOfOperationConverter : IValueConverter
{
#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
EnumAreaOfOperation param = (EnumAreaOfOperation)Enum.Parse(typeof(EnumAreaOfOperation), (string)parameter);
EnumAreaOfOperation valueToSet = (EnumAreaOfOperation)value;

return (param == valueToSet);
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Enum.Parse(targetType, (string)parameter);
}

#endregion
}

Below is second converter. This converts the value to a boolean and assigns between view and view models using Convert and ConvertBack methods. This is also using Binding's ConverterParameter.
public class CommissionStatusConverter : IValueConverter
{

#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool.Parse((string)parameter) == (bool)value);
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool.Parse((string)parameter) == (bool)value);
}

#endregion
}

The enumeration for Area of operations is as follows:
public enum EnumAreaOfOperation
{
North,
Central,
South
}

When we run the application, it appears as follows:



You can see that default values are selected in both groups of radio buttons. When you start using this form by selecting these buttons the values are updated in the view models.

Sunday, November 14, 2010

WPF - Supporting Multiple DataSources for same View

I have been asked so many times about supporting multiple datasources for a single view. I have decided to write a separate post about it so that I could refer for the community. The issue is that WPF supports just one DataContext. Many a times we need to show data from different sources to the view.

Some purists might want to say that there should be one view models to the view. All these different datasources should be handled by the View models. The view should just know about its DataContext and bind its control with this DataContext. I agree to this view mostly but there are, sometimes, requirements in which we have to support multiple view models. So how should we bind these different View models to our views.

Since I have made a point now about the severity of the issue, I think that I can jump on the example of doing that. We are creating an example of a simple view in which we have to show CourseName and StudentName in a single TextBox. CourseNmae and StudentName would be from CourseVM and StudentVM respectively. As I discussed above that there can only be one DataContext, so we should be assigning one to the DataContext (CourseVM). We are instantiating the other as a resource to the View (StudentVM)

The definition of two view models are as follows:

CourseVM:
class CourseVM : DependencyObject
{
public static DependencyProperty CourseNameProperty =
DependencyProperty.Register("CourseName", typeof(string), typeof(CourseVM));
public string CourseName
{
get { return (string)GetValue(CourseNameProperty); }
set { SetValue(CourseNameProperty, value); }
}
}

StudentVM:
class StudentVM : DependencyObject
{
public static DependencyProperty StudentNameProperty =
DependencyProperty.Register("StudentName", typeof(string), typeof(CourseVM));
public string StudentName
{
get { return (string)GetValue(StudentNameProperty); }
set { SetValue(StudentNameProperty, value); }
}
}

We also define a Converter for combining StudentName and CourseName. This would be used for MultiBinding so we are defining it as MultiValueConverter. It has to implement IMultiValueConverter from System.Windows.Data namespace.

class CourseStudentConverter : IMultiValueConverter
{

#region IMultiValueConverter Members

public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
string CourseName = (string)values[0];
string StudentName = (string)values[1];

return string.Format("{0} : {1}", CourseName, StudentName);
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}

#endregion
}

In the following view, we are binding using the Converter defined above. We are also binding using StringFormat.
<Window x:Class="MultiBinding_DifferentVMs.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local ="clr-namespace:MultiBinding_DifferentVMs"
Title="Window1" Height="300" Width="300">
<Window.DataContext>
<local:CourseVM CourseName="Neural Networks" />
</Window.DataContext>
<Window.Resources>
<local:StudentVM x:Key="Student" StudentName="Muhammad" />
<local:CourseStudentConverter x:Key="CSConverter" />
</Window.Resources>
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Using StringFormat " />
<TextBox>
<TextBox.Text>
<MultiBinding StringFormat="{}{0} : {1}" >
<Binding Path="CourseName" />
<Binding Source="{StaticResource Student}" Path="StudentName" />
</MultiBinding>
</TextBox.Text>
</TextBox>
</StackPanel>
<Border Height="24" />
<StackPanel Orientation="Horizontal">
<Label Content="Using Converter " />
<TextBox>
<TextBox.Text>
<MultiBinding Converter="{StaticResource CSConverter}" >
<Binding Path="CourseName" />
<Binding Source="{StaticResource Student}" Path="StudentName" />
</MultiBinding>
</TextBox.Text>
</TextBox>
</StackPanel>
</StackPanel>
</Window>

When we run the application, the windows is loaded as follows: