Tuesday, July 26, 2011

Multi Value Converters in WPF



Multiple Bindings
Multiple binding enables target to bind with multiple sources. It aggregate multiple bindings together and out single value for target. Multibinding requires converter class which is responsible to combine all different types and values from different binding class and return single type and value for target. The converter class must implement IMultiValueConverter interface. This interface quite similar to IValueConverter and contains two methods Convert and ConvertBack. Convert Method contains array of values as parameter and ConvertBack method contains array of types as parameter.

<Window.Resources>
    <local:LableVisibilityMultiValueConverter
        x:Key="LabelMultiValueConverter" />
</Window.Resources>
<StackPanel>
    <CheckBox Content="Show Name"
                x:Name="ShowNameCheckbox" Margin="5" />
    <StackPanel Orientation="Horizontal" Margin="5">
        <TextBlock Text="Enter Name: "
                    VerticalAlignment="Center" />
        <TextBox x:Name="NameTextBox"
                    Width="150"/>
    </StackPanel>
    <StackPanel Margin="5" Orientation="Horizontal">
        <TextBlock Text="Your Name: "
                    VerticalAlignment="Center" />
        <Label Content="{Binding ElementName=NameTextBox,
               Path=Text}">
            <Label.Visibility>
                <MultiBinding
                    Converter="{StaticResource LabelMultiValueConverter}">
                    <Binding ElementName="ShowNameCheckbox"
                             Path="IsChecked" />
                    <Binding ElementName="NameTextBox"
                             Path="Text" />
                </MultiBinding>
            </Label.Visibility>
        </Label>
    </StackPanel>
</StackPanel>

public class LableVisibilityMultiValueConverter :IMultiValueConverter
{

    public object Convert(object[] values, Type targetType,
        object parameter, System.Globalization.CultureInfo culture)
    {
        bool showCheckbox = (bool)values[0];
        string text = values[1].ToString();
        if (text.Length > 0 && showCheckbox)
            return Visibility.Visible;
        else
            return Visibility.Collapsed;
    }

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





Label’s Visibility property is bound with two sources one is Checkbox’s Checked property and another is Textbox’s Text property. If checkbox is checked and Textbox’s text length is greater than zero then only Label is visible. To achieve this I need to write one Multivalue converter. In converter method i am getting Checkbox’s checked status as well getting textbox’s text as an object array. Now i need to do some processing on values from array and after that need to  return single value for target object. In this example target object is Label and target property is Visibility so returned Visibility enum.

Converter Parameters
Converter Parameter can also be passed with Multibinding Converter similar to normal ValueConverter. Multibinding object consists of many binding and each binding contains all the properties which normal binding has. It can also take converter and converter parameters with it.

<Label Content="{Binding ElementName=NameTextBox,
        Path=Text}">
    <Label.Visibility>
        <MultiBinding
            Converter="{StaticResource LabelMultiValueConverter}"
            ConverterParameter="Test">
            <Binding ElementName="ShowNameCheckbox"
                        Path="IsChecked"
                        Converter="{StaticResource TestConverter}"
                        ConverterParameter="1"/>
            <Binding ElementName="NameTextBox"
                        Path="Text" />
        </MultiBinding>
    </Label.Visibility>
</Label>

Monday, July 25, 2011

Value Converters in WPF




Value converters are used in Data binding. When source object type and target object type are different at that time value converts are used to manipulate data between source and target. Converter class must implement IValueConverter interface. The IValueConverter interface consists of two methods Convert and ConvertBack. Convert Method gets called when source updates target object and ConvertBack method gets called when target updates source object. 

<StackPanel>
    <CheckBox x:Name="ShowBackgroundCheckbox"
                Content="Show Background" Margin="5" />
    <TextBox Text="Value Converter" Margin="5"
                Background="{Binding
        ElementName=ShowBackgroundCheckbox, Path=IsChecked}"/>
</StackPanel> 

In above example Textbox’s Background property is bound with Checkbox’s IsChecked property. This binding won't work because IsChecked property is Boolean and background is Brush type. So for successful data binding the source and target type must match which is not true in above XAML code. So we need to create one value converter which manipulates checkbox’s IsChecked property and textbox’s Background property.

<Window.Resources>
    <local:BooleanToBackgroundConverter x:Key="booleanToBackground" />
</Window.Resources>

<StackPanel>
    <CheckBox x:Name="ShowBackgroundCheckbox"
              Content="Show Background" Margin="5" />
    <TextBox Text="Value Converter" Margin="5"
        Background="{Binding
        ElementName=ShowBackgroundCheckbox, Path=IsChecked,
        Converter={StaticResource booleanToBackground}}"/>
</StackPanel>

public class BooleanToBackgroundConverter:IValueConverter
{
    public object Convert(object value, Type targetType,
        object parameter, System.Globalization.CultureInfo culture)
    {
        bool result = (bool)value;
        if (result)
        {
            return Brushes.LightBlue;
        }
        return null;
    }

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












When Show Background checkbox is checked the textbox get filled with LightBlue background. To achieve that I created one class named BooleanToBackgroundConverter which implements IValueConverter interface. In our example Checkbox is source for textbox. So whenever user changes checkbox’s value the Convert method will get called. The ConvertBack method converts target value to source value. This method will get called when twoway binding established between source and target.

Converter Parameters
Additional information can be passed to value converter using converter parameter.

<TextBox Text="Value Converter" Margin="5"
    Background="{Binding
    ElementName=ShowBackgroundCheckbox, Path=IsChecked,
    Converter={StaticResource booleanToBackground}, ConverterParameter=1}"/>

public object Convert(object value, Type targetType,
    object parameter, System.Globalization.CultureInfo culture)
{
    bool result = (bool)value;
    int param = Int32.Parse(parameter.ToString());
    if (result)
    {
        if (param == 1)
            return Brushes.Red;
        return Brushes.LightBlue;
    }
    return null;
}



Friday, July 22, 2011

Custom Markup Extension in WPF


WPF allows us to create our own custom Markup Extension. In XAML we can use markup extension inside curly braces { } for e.g. {Binding}, {StaticResource} etc. These markup extensions are provided by WPF. We create our own custom markup extension by deriving MarkupExtension class available in System.Windows.Markup namespace. In this post I will explain how to create a custom Markup Extension using simple example.

Simple steps to create custom markup extension
  1. Create a Class and derived it from MarkupExtension class.
  2. Override ProvideValue method of MarkupExtension class in your class.
  3. Return value from ProvideValue method after doing processing.

XAML
<TextBlock Margin="10" HorizontalAlignment="Center"
    Text="{local:FullNameExtension FirstName=Mitesh, LastName=Sureja}" />

Similarly this can be written as,

<TextBlock Margin="10" HorizontalAlignment="Center">
   <TextBlock.Text>
      <local:FullNameExtension FirstName="Mitesh" LastName="Sureja" />
   </TextBlock.Text>
</TextBlock>

Code
public class FullNameExtension : MarkupExtension
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public FullNameExtension() { }

    public FullNameExtension(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }
       
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return LastName + ", " + FirstName;
    }
}











ProvideValue method is not using serviceProvider (IServiceProvider) object. We can utilize this object to get Target Element and Target Property. GetService method of IServiceProvider interface returns IProvideValueTarget and IProvideValueTarget interface provides TargetObject and TargetProperty. Using this target property we can covert and return our value to target type. Let’s have a look on below modified code which uses IServiceProvider object inside ProvideValue method.

public override object ProvideValue(IServiceProvider serviceProvider)
{
    IProvideValueTarget ipValueTarget =
        serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
    if (ipValueTarget == null) return null;

    //Get target element
    FrameworkElement targetElement = ipValueTarget.TargetObject as FrameworkElement;

    object length = LastName.Length + FirstName.Length;

    //Get target property
    DependencyProperty dp = ipValueTarget.TargetProperty as DependencyProperty;
    length = Convert.ChangeType(length, dp.PropertyType);

    return length;
}

In above example, I demonstrated simple way to use IServiceProvider and IProvideValueTarget in ProvideValue method but more complex scenarios could be possible.


Related Links –

Tuesday, July 19, 2011

Binding ObservableCollection to ListBox in WPF

Listbox is derived from ItemsControl class and has ItemSource property. ItemSource property can accept any collection which implements IEnumerable interface. In this post I am going to bind ObservableCollection<T> with Listbox.

<ListBox x:Name="PeopleList" ItemsSource="{Binding}"
        DisplayMemberPath="FirstName"
        IsSynchronizedWithCurrentItem="True"
        Margin="10" Height="110"/>

public partial class BindingCollection : Window
{
    public ObservableCollection<People> PeopleLists =
           new ObservableCollection<People>();
    public BindingCollection()
    {
        InitializeComponent();
        LoadPeopleList();
        this.DataContext = PeopleLists;
    }
    private void LoadPeopleList()
    {
        PeopleLists.Add(new People("Mitesh", "Sureja", 29));
        PeopleLists.Add(new People("Pritesh", "Parmar", 30));
        PeopleLists.Add(new People("Dharmesh", "Bheda", 32));
        PeopleLists.Add(new People("Jatin", "Mishra", 28));
        PeopleLists.Add(new People("Paresh", "Bijvani", 30));
    }
}
public class People
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public People(string firstName, string lastName, int age)
    {
        FirstName = firstName;
        LastName = lastName;
        Age = age;
    }
}












First I bound ItemSource property in XAML than i set DisplayMemberPath to FirstName it means Listbox should display only values of FirstName property available in Collection. In Code behind, i created one class called People and created one ObservableCollection of People class. I added few data to that collection and set window’s DataContext property to ObserableCollection.

I also set IsSynchronizedWithCurrentItem property to true. This property ensures that the selected item always correspond to the CurrentItem property in ItemCollection. For example, I have two listbox and both are bound with same itemsource property. Now if I change selected item in any of the listbox it will automatically change selection in other listbox if the IsSynchronizedWithCurrentItem property is set to true in both Listbox. Let’s have a look on below XAML snippet.

<ListBox x:Name="PeopleList" ItemsSource="{Binding}"
        DisplayMemberPath="FirstName"
        IsSynchronizedWithCurrentItem="True"
        Margin="10" Height="110"/>
<ListBox x:Name="PeopleList1" ItemsSource="{Binding}"
        DisplayMemberPath="FirstName"
        IsSynchronizedWithCurrentItem="True"
        Margin="10" Height="110"/>