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.

No comments: