Feeds:
Posts
Comments

Posts Tagged ‘DataBinding’

This article is intended for people who already have a solid understanding of data binding, data templates, converters, and WPF in general. In this article I will attempt to show you how to display a collection of items in multiple ways based on some user feedback. Fore example, say you had a collection of person objects and a toggle button on the UI to show a basic or detailed view of the people. Lets say the basic view will only show a person’s first name and photo, while the detailed view would show their last name, address, and special interests (no photo). How could something like this be easily accomplished? Try using DataTriggers and DataTemplates. The following example won’t demonstrate the situation described exactly as above but it will give you the foundation to just that (as well as some other pretty cool things). Now lets get to the code.

First is the MainWindow.cs where I will create two properties. One is a collection of people and the other is an enum value that will be bound to three radio buttons that will allow us to switch between our views of the person collection. For this example, we will allow the user to display each person item as a styled RadioButton, simple TextBlock, or simple CheckBox. Here is all of the code for the MainWindow.cs file.

namespace DataTemplateSwitchingTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
       public ObservableCollection<Person> People
       {
            get
            {
                if (_people == null)
                {
                    _people = new ObservableCollection<Person>()
                    {
                        new Person("Doe", "John", "Johnny"),
                        new Person("Doe", "Jane", "Janie"),
                        new Person("Smith", "Mike", "Big Mike"),
                        new Person("Simpson", "Mark", "El Capitan"),
                    };
                }
                return _people;
            }
        }private ObservableCollection<Person> _people;
        public Element ElementView
        {
            get { return _element; }
            set
            {
                if (value == _element) return;

                _element = value;
                this.OnPropertyChanged("ElementView");
            }
        }private Element _element;

        public MainWindow()
        {
            InitializeComponent();
            ElementView = Element.RadioButtons;
            mainWindow.DataContext = this;
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        #endregion
    }
}

Our Element enum consists of three values, RadioButtons, TextBlocks, and CheckBoxes. This enum allows us to switch the view of our ItemsControl’s content. Below is Xaml for the layout of the page. It is very simple and contains only three radio buttons and the ItemsControl to display the people collection. I will comment on the DataTemplates following the code. I have also not included the SquareRadioButton style here as it is not pertinent to the article but you can find it in the link to the sample project at the end of the article. Here is the MainWindow.xaml.

<Window.Resources>
    <Local:EnumToBooleanConverter x:Key="EnumToBooleanConverter"/>

    <DataTemplate x:Key="RadioButtonTemplate">
        <RadioButton Style="{StaticResource SquareRadioButtonStyle}"
                     Content="{Binding FirstName}"
                     GroupName="People"/>
    </DataTemplate>

    <DataTemplate x:Key="TextBlockTemplate">
        <TextBlock Text="{Binding LastName}"
                   HorizontalAlignment="Center"/>
    </DataTemplate>

    <DataTemplate x:Key="CheckBoxTemplate">
        <CheckBox Content="{Binding Nickname}"
                  HorizontalAlignment="Center"
                  Margin="3">
        </CheckBox>
    </DataTemplate>

    <DataTemplate x:Key="PersonItemTemplate">
        <ContentPresenter x:Name="PeoplePresenter"
                          Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"
                          ContentTemplate="{StaticResource RadioButtonTemplate}"/>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding ElementName=mainWindow, Path=DataContext.ElementView, UpdateSourceTrigger=PropertyChanged}"
                         Value="{x:Static Local:Element.RadioButtons}">
                <Setter TargetName="PeoplePresenter"
                        Property="ContentTemplate"
                        Value="{StaticResource RadioButtonTemplate}"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding ElementName=mainWindow, Path=DataContext.ElementView, UpdateSourceTrigger=PropertyChanged}"
                         Value="{x:Static Local:Element.TextBlocks}">
                <Setter TargetName="PeoplePresenter"
                        Property="ContentTemplate"
                        Value="{StaticResource TextBlockTemplate}"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding ElementName=mainWindow, Path=DataContext.ElementView, UpdateSourceTrigger=PropertyChanged}"
                         Value="{x:Static Local:Element.CheckBoxes}">
                <Setter TargetName="PeoplePresenter"
                        Property="ContentTemplate"
                        Value="{StaticResource CheckBoxTemplate}"/>
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>

</Window.Resources>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="50" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <RadioButton Content="RadioButtons"
                 Style="{StaticResource SquareRadioButtonStyle}"
                 IsChecked="{Binding ElementView, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Local:Element.RadioButtons}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <RadioButton Grid.Column="1"
                 Content="TextBlocks"
                 Style="{StaticResource SquareRadioButtonStyle}"
                 IsChecked="{Binding ElementView, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Local:Element.TextBlocks}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <RadioButton Grid.Column="2"
                 Content="CheckBoxes"
                 Style="{StaticResource SquareRadioButtonStyle}"
                 IsChecked="{Binding ElementView, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Local:Element.CheckBoxes}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

    <Grid Grid.Row="1" Grid.ColumnSpan="3" VerticalAlignment="Center" HorizontalAlignment="Center">
        <ItemsControl Width="200" Height="200" Margin="0"
                      ItemsSource="{Binding People}"
                      ItemTemplate="{StaticResource PersonItemTemplate}"/>
    </Grid>

</Grid>

As you can see, there are four data templates. The RadioButtonTemplate, TextBlockTemplate, and CheckBoxTemplate will  change the way a person item is displayed to a RadioButton, TextBlock, and CheckBox (obviously ;o)). Now comes the important part, the PersonItemTemplate. This template has a ContentPresenter that sets the default DataTemplate for a person item to the RadioButtonTemplate. Now, using DataTriggers and property setters, we can change the template to be used when the ElementView enum property changes. Thus, when the user clicks on the CheckBoxes radio button, the people items will then be displayed as check boxes showing just a nickname. The user can click on the TextBoxes radio button and will see only plain text showing last names. Finally, the default template shows each person item as styled radio button showing only first names on the button. Here are some screenshots showing the three views.

Here is a link to the sample project. Happy coding!!

Read Full Post »

Most of us have used ObjectDataProvider to create an object to use as a binding source in Xaml. However, many of you may have never used the XmlDataProvider to do the same. In this post I am going to explain how to use an external XML file to populate a list of ComboBox options. I will then bind a property to the currently selected item of the ComboBox.

The reason I find this so useful is when your application’s end user may need things to frequently change in the future, but has no clue as to how in the present. For example, lets say they have a list of colors that need to be available for their automobile paint. Well next week maroon is the new thing. Do you really want to update your app and re-publish? Wouldn’t you rather give them the ability to just add a new node in an XML file that is sitting on a network drive (or the web) that they have access to? Well now I’m going to show you how.

First we must make an XML file to contain our list of color options. We’ll call the file PaintColors.xml and place it on the root C:\ drive for now (it could be on a network drive, the web, etc.). Here are the contents of the file.


<?xml version="1.0" encoding="utf-8"?>
<PaintColors>
   <Color Name="None"/>
   <Color Name="Red"/>
   <Color Name="White"/>
   <Color Name="Blue"/>
   <Color Name="Maroon"/>
</PaintColors>

Next we must create a new WPF Application. For now we’ll call it XmlDataProviderTest. Once the application is created, we’ll add some code to the MainWindow.xaml.cs file. We need to create a CurrentColor property and give it property change notification by implementing the INotifyPropertyChanged interface. We are going to assign the DataContext as well as the initial CurrentColor value inside the MainWindow constructor. Here is all of the code for the aforementioned.


using System.Windows;
using System.ComponentModel;

namespace XmlDataProviderTest
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
            CurrentColor = "Blue";
        }

        public string CurrentColor
        {
            get {  return _currentColor; }
            set
            {
                if (value != _currentColor)
                {
                    _currentColor = value;
                    OnPropertyChanged("CurrentColor");
                }
            }
        }private string _currentColor;

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

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

        #endregion

   }
}

Finally we must place a ComboBox and TextBox on our MainWindow.xaml file. This is also where the XmlDataProvider will be declared. In the following code you’ll notice the Source property of the XmlDataProvider points to the location of the PaintColors.xml file on the C:\ drive. As mentioned before, this could point to a network drive, URL, embedded XML file, etc. The XPath property is set here as well. Please note that the XmlDataProvider’s default value for IsAsynchronous is True. This is because when retrieving XML data from RSS feeds or external XML files it is expected to take a while. This is the opposite of an ObjectDataProvider whose default IsAsynchronous value is set to false. The reason for this default is when retrieving data for an ObjectDataProvider we expect retrieval of the source to be very quick. I am setting IsAsynchronous here to false because our XML file is on a network share and contains very little information and thus should be quick to access.

Please note the binding in the ComboBox. The ItemsSource is bound to our PaintColors XmlData. We have chosen to display the Name attribute in the ComboBox and make it the selected value. Finally we are binding our SelectedValue to the CurrentColor string property we previously created in the MainWindow.xaml.cs file. To show everything is working well, I have bound the CurrentColor to a TextBox so we can see it update on the fly.


<Window x:Class="XmlDataProviderTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <XmlDataProvider x:Key="PaintColors" Source="C:\PaintColors.xml" XPath="PaintColors" IsAsynchronous="False" />
    </Window.Resources>

    <StackPanel HorizontalAlignment="Center">
        <ComboBox Grid.Column="4" Grid.Row="2" Width="200" Height="25" Margin="5"
                  ItemsSource="{Binding Source={StaticResource PaintColors}, XPath=./Color}"
                  DisplayMemberPath="@Name"
                  SelectedValuePath="@Name"
                  SelectedValue="{Binding CurrentColor}"/>
        <StackPanel Orientation="Horizontal">
            <Label Content="Current Color:"/>
            <TextBox Text="{Binding CurrentColor, UpdateSourceTrigger=PropertyChanged}" Width="120"/>
        </StackPanel>
    </StackPanel>
</Window>

I hope this provides you all with a clean and simple introduction to XmlDataProviders. For more on synchronous and asynchronous binding have a look at Bea Stollnitz’s blog post here. Enjoy!!

Read Full Post »

Well I hope the title of this post is fairly self explanatory. In this example I am going to show you how to first populate a ComboBox with the values in an enumeration. I am then going to databind a property of type enum to the SelectedItem of the ComboBox. If you would like to extend the sample and sort the values displayed in the ComboBox I suggest you check out this post by the almighty Josh Smith. Now on to the code.

First off, here is my animal enumeration:

public enum Animal
{
     Bear,
     Cheetah,
     Deer,
     Elehpant,
}

Here is my MainWindow.cs.

public partial class MainWindow : Window, INotifyPropertyChanged
{
     public Animal MyAnimal
     {
          get { return _myAnimal; }
          set
          {
               if (_myAnimal != value)
               {
                    _myAnimal = value;
                    OnPropertyChanged("MyAnimal");
               }
          }
     }private Animal _myAnimal;

     public MainWindow()
     {
          InitializeComponent();
     }

     #region INotifyPropertyChanged Members

     public event PropertyChangedEventHandler PropertyChanged;

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

     #endregion
}

Finally, here is the MainWindow.xaml where I am using an ObjectDataProvider and GetValues to provide the content for my ComboBox’s ItemsSource. When you run the app and change the SelectedItem in the ComboBox, you should see the TextBox update with the newly selected animal. Enjoy!

<Window x:Class="ComboBoxBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ComboBoxBinding"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="125" Width="200"
        x:Name="mainWindow">

     <Window.Resources>
          <ObjectDataProvider x:Key="Animals" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
               <ObjectDataProvider.MethodParameters>
                    <x:Type TypeName="local:Animal" />
               </ObjectDataProvider.MethodParameters>
          </ObjectDataProvider>
     </Window.Resources>

     <Grid Background="LightBlue" DataContext="{Binding ElementName=mainWindow}">
          <StackPanel HorizontalAlignment="Center">
               <ComboBox ItemsSource="{Binding Source={StaticResource Animals}}"
                         SelectedItem="{Binding MyAnimal}"
                         Margin="10"/>

               <TextBox Text="{Binding MyAnimal}" Width="100"/>
          </StackPanel>
     </Grid>
</Window>

Read Full Post »

The BindingBase.StringFormat property was included with the .NET framework 4.0 and is quite a handy feature. I recently found myself going back through old projects to remove binding converters and replace their logic with the StringFormat property. There is a decent example on the MSDN here. However, I felt like expanding on it a bit so here goes.

First I have created a Person class that has some basic properties like FirstName, LastName, Age, etc. Here is the code for the Person class and enum it uses.

using System;
using System.ComponentModel;

namespace BindingStringFormat
{
     public enum Sex
     {
          male,
          female
     }

     public class Person : INotifyPropertyChanged
     {
          #region Properties

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

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

          public int Age
          {
               get { return _age; }
               set { _age = value; OnPropertyChanged("Age"); }
          }private int _age;

          public DateTime BirthDate
          {
               get { return _birthDate; }
               set { _birthDate = value; OnPropertyChanged("BirthDate"); }
          }private DateTime _birthDate;

          public Sex Sex
          {
               get { return _sex; }
               set { _sex = value; OnPropertyChanged("Sex"); }
          }private Sex _sex;

          #endregion

          #region Constructor(s)

          public Person() { }

          public Person(string lastName, string firstName, int age, DateTime birthDate, Sex sex)
          {
               this.FirstName = firstName;
               this.LastName = lastName;
               this.Age = age;
               this.BirthDate = birthDate;
               this.Sex = sex;
          }

          #endregion

          #region INotifyPropertyChanged Members

          public event PropertyChangedEventHandler PropertyChanged;

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

          #endregion
     }
}

Next is the MainWindow. I have created a public Person property in the code behind and set the DataContext of the MainWindow.xaml to the Person property. Here is the code for that.

using System;
using System.Windows;

namespace BindingStringFormat
{
     /// <summary>
     /// Interaction logic for MainWindow.xaml
     /// </summary>
     public partial class MainWindow : Window
     {
          public Person Person
          {
               get
               {
                    if (_person == null)
                         _person = new Person("Bob", "Billy", 29, new DateTime(1978, 3, 9), Sex.male);

                    return _person;
               }
               set
               {
                    _person = value;
               }
          }private Person _person;

          public MainWindow()
          {
               InitializeComponent();
               this.DataContext = Person;
          }
     }
}

Last but not least is the MainWindow.xaml which is where the StringFormat syntax lies. In this example you will notice I have bound a string, integer, enum, and date to some TextBlocks and customized their display with the StringFormat property. I especially like the ability to use StringFormat with MultiBinding, and as I said before, this should help you get rid of some now obsolete BindingConverters. Enjoy!


<Window x:Class="BindingStringFormat.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">

   <Grid Margin="25">
        <StackPanel>
             <TextBlock Margin="5">
                  <TextBlock.Text>
                       <MultiBinding StringFormat="Hello {0} {1}!">
                            <Binding Path="FirstName"/>
                            <Binding Path="LastName"/>
                       </MultiBinding>
                  </TextBlock.Text>
             </TextBlock>
             <TextBlock Text="{Binding Path=Age, StringFormat='You are {0} years old.'}" Margin="5"/>
             <TextBlock Text="{Binding Path=Sex, StringFormat='You are a {0}.'}" Margin="5"/>
             <TextBlock Text="{Binding Path=BirthDate, StringFormat='You were born on {0:MM/dd/yyyy}'}" Margin="5"/>
        </StackPanel>
   </Grid>

</Window>

Here is what you should see when you run the application:

BindingBase.StringFormat

Read Full Post »

Today I’m going to show how to bind a collection of objects to a TreeView while using HierarchicalDataTemplates. The root nodes in the TreeView will be Continents. Each continent will have a collection of countries, while each country will have a collection of cities. There are a few additional properties associated with each of these object. For example, cities have populations, continents have a size in square miles, etc. Here are the three classes I created for the project.

public class Continent
{
    public Continent() { }

    public string Name { get; set; }
    public int Size { get; set; }   //in sq miles
    public ObservableCollection<Country> Countries { get; set; }
}

public class Country
{
    public Country() { }

    public string Name { get; set; }
    public int Population { get; set; }
    public ObservableCollection<City> Cities { get; set; }
}

public class City
{
    public City() { }

    public string Name { get; set; }
    public int Population { get; set; }
}

Now, here is all of the code for my MainWindow.cs which contains the code for populating my ObservableCollection<Continent>.

public partial class MainWindow : Window
{
    public ObservableCollection<Continent> Continents { get; set; }

    public MainWindow()
    {
       InitializeComponent();
       Continents = CreateWorld();
       this.DataContext = Continents;
    }

    private ObservableCollection<Continent> CreateWorld()
    {
       ObservableCollection<Continent> world = new ObservableCollection<Continent>();

       Continent northAmerica = new Continent()
       {
          Name = "North America",
          Size = 9540000,
          Countries = new ObservableCollection<Country>()
          {
             new Country()
             {
                Name = "Canada",
                Population = 15000000,
                Cities = new ObservableCollection<City>()
                {
                   new City() { Name = "Ottowa", Population = 812179 },
                   new City() { Name = "Vancouver", Population = 347009 },
                   new City() { Name = "Quebec", Population = 457339 }
                }
             },
             new Country()
             {
                Name = "United States",
                Population = 3456700,
                Cities = new ObservableCollection<City>()
                {
                   new City() { Name = "Denver", Population = 124897 },
                   new City() { Name = "Boston", Population = 234659 },
                   new City() { Name = "New York", Population = 354730 },
                   new City() { Name = "Los Angeles", Population = 423100 }
                }
             },
             new Country()
             {
                Name = "Mexico",
                Population = 34000234,
                Cities = new ObservableCollection<City>()
                {
                   new City() { Name = "Mexico City", Population = 21000000 },
                   new City() { Name = "Tijuana", Population = 1594000 },
                   new City() { Name = "Puebla", Population = 1590256 }
                }
             }
          }
       };

       Continent southAmerica = new Continent()
       {
          Name = "South America",
          Size = 6879000,
          Countries = new ObservableCollection<Country>()
          {
             new Country()
             {
                Name = "Brazil",
                Population = 4210000,
                Cities = new ObservableCollection<City>()
                {
                   new City() { Name = "Sao Paulo", Population = 11500000 },
                   new City() { Name = "Rio de Janeiro", Population = 7186000 },
                   new City() { Name = "Fortaleza", Population = 2505054 }
                }
             },
             new Country()
             {
                Name = "Argentina",
                Population = 5673420,
                Cities = new ObservableCollection<City>()
                {
                   new City() { Name = "Buenos Aires", Population = 5698000 },
                   new City() { Name = "Chaco", Population = 7845000 }
                }
             }
          }
       };

       world.Add(northAmerica);
       world.Add(southAmerica);
       return world;
    }
}

Finally, here is the MainWindow.xaml that uses the HierarchicalDataTemplates to show content at each level of TreeViewItems in the tree. Please note how the multibinding utilizes Binding.StringFormat to show cities and their population in the deepest node.

<Window x:Class="TreeViewBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="331" Width="289"
        xmlns:local="clr-namespace:TreeViewBinding">

    <Window.Resources>
        <DataTemplate DataType="{x:Type local:City}">
            <TextBlock>
                <TextBlock.Text>
                    <MultiBinding StringFormat="{}{0} ({1})">
                        <Binding Path="Name"/>
                        <Binding Path="Population"/>
                    </MultiBinding>
                </TextBlock.Text>
           </TextBlock>
       </DataTemplate>
       <HierarchicalDataTemplate DataType="{x:Type local:Country}"
                                 ItemsSource="{Binding Cities}">
            <TextBlock Text="{Binding Name}"/>
       </HierarchicalDataTemplate>
       <HierarchicalDataTemplate DataType="{x:Type local:Continent}"
                                 ItemsSource="{Binding Countries}">
            <TextBlock Text="{Binding Name}"/>
       </HierarchicalDataTemplate>
    </Window.Resources>

    <Grid>
        <TreeView x:Name="myTreeView" ItemsSource="{Binding}" Margin="0,0,12,12" />
    </Grid>

</Window>

Here is a screenshot of what you should see when you run the application. Enjoy!

Read Full Post »

After a few months of doing very simple data binding with my MVVM, I had a problem where I needed to enable or disable a control based on multiple conditions. I could easily perform this task with an event handler and some logic in my code behind but knew it would be cleaner if I could somehow handle it all with binding.  After doing a little digging I ran across a very powerful binding technique called, yep you guessed it, MultiBinding.

The easiest way for me to demonstrate MulitBinding and its capabilities is to explain a small working sample. I have decided to create a small form with only a TextBox, ComboBox, and Submit button. I only want the submit button to be enabled when some text has been entered AND something was chosen from the ComboBox. Since this example is so concise, I am just going to give you all of the xaml at once. Here are the contents of my MainWindow.xaml file. I have highlighted the MultiBinding syntax for your convenience.

<Window x:Class="MultiBindingTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Local="clr-namespace:MultiBindingTest"
        Title="MainWindow" Height="200" Width="300">

    <Window.Resources>
        <Local:FormFilledMultiConverter x:Key="FormFilledMultiConverter"/>
    </Window.Resources>

    <Grid>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" Margin="10">
            <StackPanel Orientation="Horizontal">
                <Label x:Name="labelName" Content="Name:"/>
                <TextBox x:Name="textBoxName" Width="110"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
            <Label x:Name="labelLetter" Content="Letter:" VerticalAlignment="Center"/>
            <ComboBox x:Name="comboBoxLetter" Width="110" Margin="10">
                <ComboBoxItem Content="Alpha"/>
                <ComboBoxItem Content="Beta"/>
                <ComboBoxItem Content="Delta"/>
                <ComboBoxItem Content="Gamma"/>
            </ComboBox>
            </StackPanel>
            <Button x:Name="buttonSubmit" Width="50" Height="30" Content="Submit">
                <Button.IsEnabled>
                    <MultiBinding Converter="{StaticResource FormFilledMultiConverter}">
                        <Binding Path="Text" ElementName="textBoxName"/>
                        <Binding Path="SelectedIndex" ElementName="comboBoxLetter"/>
                    </MultiBinding>
                </Button.IsEnabled>
            </Button>
        </StackPanel>
    </Grid>
</Window>

As you can see from the xaml, there is a MultiBinding converter, FormFilledMultiConverter.cs, that we are utilizing to perform the logic used to determine when the Submit button is enabled. Here is the code for the converter.

using System;
using System.Globalization;
using System.Windows.Data;

namespace MultiBindingTest
{
    public class FormFilledMultiConverter : IMultiValueConverter
    {
        #region IMultiValueConverter Members

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            for (int i = 0; i < values.Length; i++)
                if (values[i] == null)
                    return false;

            string name = values[0].ToString();
            int selectedIndex = System.Convert.ToInt32(values[1]);

            //if some text was entered for then name AND a letter was chosen from the combobox, return true, else return false.
            return (name.Length > 0 && selectedIndex != -1) ? true : false;
        }

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

        #endregion
    }
}

The contents of the converter are pretty straightforward. Now, lets say we had anther ComboBox on the form where the user needed to select a color. To make the Submit button dependent on this new ComboBox we would do the following in xaml:

<Button x:Name="buttonSubmit" Width="50" Height="30" Content="Submit">
    <Button.IsEnabled>
        <MultiBinding Converter="{StaticResource FormFilledMultiConverter}">
            <Binding Path="Text" ElementName="textBoxName"/>
            <Binding Path="SelectedIndex" ElementName="comboBoxLetter"/>
            <Binding Path="SelectedIndex" ElementName="myNewComboBox"/>
        </MultiBinding>
    </Button.IsEnabled>
</Button>

We could then grab the new ComboBox’s SelectedIndex in the converter by using this:

int newSelectedIndex = System.Convert.ToInt32(values[2]);

I hope this clearly demonstrates the fundamentals of MultiBinding, if anyone would like a link to the full sample code just post your request in the comments. Enjoy!

<Window x:Class=”MultiBindingTest.MainWindow”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221;
xmlns:Local=”clr-namespace:MultiBindingTest”
Title=”MainWindow” Height=”200″ Width=”300″><Window.Resources>
<Local:FormFilledMultiConverter x:Key=”FormFilledMultiConverter”/>
</Window.Resources> 

<Grid>
<StackPanel Orientation=”Vertical” HorizontalAlignment=”Center” Margin=”10″>
<StackPanel Orientation=”Horizontal”>
<Label x:Name=”labelName” Content=”Name:”/>
<TextBox x:Name=”textBoxName” Width=”110″/>
</StackPanel>
<StackPanel Orientation=”Horizontal”>
<Label x:Name=”labelLetter” Content=”Letter:” VerticalAlignment=”Center”/>
<ComboBox x:Name=”comboBoxLetter” Width=”110″ Margin=”10″>
<ComboBoxItem Content=”Alpha”/>
<ComboBoxItem Content=”Beta”/>
<ComboBoxItem Content=”Delta”/>
<ComboBoxItem Content=”Gamma”/>
</ComboBox>
</StackPanel>
<Button x:Name=”buttonSubmit” Width=”50″ Height=”30″ Content=”Submit”>
<Button.IsEnabled>
<MultiBinding Converter=”{StaticResource FormFilledMultiConverter}”>
<Binding Path=”Text” ElementName=”textBoxName”/>
<Binding Path=”SelectedIndex” ElementName=”comboBoxLetter”/>
</MultiBinding>
</Button.IsEnabled>
</Button>
</StackPanel>
</Grid>
</Window>

Read Full Post »

When developing web or desktop applications, data entry is a necessity. In order to ensure things run according to plan, we always want to validate our user data. For example, we may want to restrict the length of a user name field, or verify that a person’s first or last name only contains letters. A good way to ensure such restrictions is to implement data binding validation. For this simple example I am going to set up a form with two text boxes. The first text box may only contain letters, while the second may only contain numbers and have a maximum length of 5.

Lets get started by creating a new WPF application and declaring two dependency properties in our MainWindow.xaml.cs file like so. We will be binding these to their respective TextBlock’s Text property. Please note the default values being given to the properties (“TEXT”, and “123”).

public string UserText
{
    get { return (string)GetValue(UserTextProperty); }
    set { SetValue(UserTextProperty, value); }
}

public static readonly DependencyProperty UserTextProperty =
    DependencyProperty.Register("UserText", typeof(string), typeof(MainWindow), new UIPropertyMetadata("TEXT"));

public string UserNumbers
{
    get { return (string)GetValue(UserNumbersProperty); }
    set { SetValue(UserNumbersProperty, value); }
}

public static readonly DependencyProperty UserNumbersProperty =
    DependencyProperty.Register("UserNumbers", typeof(string), typeof(MainWindow), new UIPropertyMetadata("123"));

Now when binding to our TextBlocks without binding validation our syntax would look something like this:

<TextBox x:Name="textBoxUserText" Text="{Binding UserText, UpdateSourceTrigger=PropertyChanged}"/>

However, if we are going to use binding validation our syntax will be a little different. First, we need a way to visually convey to the user that their data entry failed our validation. This is the purpose of the reference to the textBoxInError style that will be described later. We also need to specify a validation rule that will be used to test that the data entered is in the correct format. In this case our validation rule is called TextOnlyValidationRule. Here is the updated syntax.

<TextBox x:Name="textBoxUserText" Style="{StaticResource textBoxInError}">
    <TextBox.Text>
        <Binding Path="UserText" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <Local:TextOnlyValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Not to worry, I haven’t forgotten about the validation rule itself. Actually, in many cases, a regular expression will be all you need to validate your data. Here is our TextOnlyValidationRule.cs file.

using System.Globalization;
using System.Text.RegularExpressions;
using System.Windows.Controls;

namespace BindingValidation
{
    public class TextOnlyValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            string parameter = value as string;

            Regex regexAlpha = new Regex(@"^[a-zA-Z]+$");

            if (parameter.Length != 0)
            {
                if (regexAlpha.IsMatch(parameter))
                    return new ValidationResult(true, null);
                else
                    return new ValidationResult(false, "Input must contain only letters!");
            }
            else
                return new ValidationResult(false, "You must enter some letters!");
        }
    }
}

From the rule you can see the regular expression is verifying that only letters are entered. You can also see that we are making sure at least some text has been entered (remember the default is “TEXT”). The strings in the ValidationResults are what is displayed when you mouse over the text box when it is in an error state. This is handled by the style and wil be shown later.

Now, what if we wanted to use this single validation rule for many text boxes and specify a different maximum length for different text boxes? This could easily be done by adding a MaxLength property to our validation rule and assigning to it in xaml. I have done this for our NumbersOnlyValidationRule.cs file which you can see below:

using System.Globalization;
using System.Text.RegularExpressions;
using System.Windows.Controls;

namespace BindingValidation
{
    public class NumbersOnlyValidationRule : ValidationRule
    {
        public int MaxLength { get; set; }

        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            string parameter = value as string;

            Regex regexNumeric = new Regex(@"^[0-9]+$");

            if(parameter.Length == 0)
                return new ValidationResult(false, "You must enter some numbers!");

            if (parameter.Length > MaxLength)
                return new ValidationResult(false, "Length must be less than " + MaxLength + "!");

            if (regexNumeric.IsMatch(parameter))
                return new ValidationResult(true, null);
            else
                return new ValidationResult(false, "Input must contain only numbers!");
        }
    }
}

Now in xaml when you want to assign to the MaxLength property you do the following:

<TextBox x:Name="textBoxUserNumbers" Style="{StaticResource textBoxInError}">
    <TextBox.Text>
        <Binding Path="UserNumbers" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <Local:NumbersOnlyValidationRule MaxLength="5" />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Finally, here is the style for when the text box is in error. The relative source binding (self) points to the first error in the error array of the current element. If there is an error it will be displayed as a tooltip.

<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
        </Trigger>
    </Style.Triggers>
</Style>

Here is a screenshot of the application showing a tooltip error.

Post a comment if you would like a link to the entire sample application.

Snippet

public string UserText
        {
            get { return (string)GetValue(UserTextProperty); }
            set { SetValue(UserTextProperty, value); }
        }

        public static readonly DependencyProperty UserTextProperty =
            DependencyProperty.Register("UserText"typeof(string), typeof(MainWindow), new UIPropertyMetadata("TEXT"));

        public string UserNumbers
        {
            get { return (string)GetValue(UserNumbersProperty); }
            set { SetValue(UserNumbersProperty, value); }
        }

        public static readonly DependencyProperty UserNumbersProperty =
            DependencyProperty.Register("UserNumbers"typeof(string), typeof(MainWindow), new UIPropertyMetadata("123"));

Read Full Post »

Older Posts »