Feeds:
Posts
Comments

Archive for the ‘Data Binding’ Category

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!!

Advertisements

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 »

If you have worked with Windows Presentation Foundation (WPF) you should be fully aware of the term data binding. As stated on the MSDN “data binding provides a simple and consistent way for applications to present and interact with data .” It also allows for a clean separation of business logic from the UI and should be used where applicable. A question I commonly see posted on forums is “How do I data bind to an enumeration?”. I hope to clearly address that question here so lets just jump into the code.

First you must create a new WPF project and declare an enumeration. For this example I have created a fruit enum as follows:

public enum Fruit
{
     apples,
     oranges,
     peaches,
     pears,
     strawberries,
}

My MainWindow.cs file will contain only the default constructor and one dependency property of type fruit. One thing to note about the DP is the UIPropertyMetadata. This provides the default value of apples for the DP since that is the first element in the enum. Here is the dependency property:


public Fruit Fruit
{
     get { return (Fruit)GetValue(FruitProperty); }
     set { SetValue(FruitProperty, value); }
}

public static readonly DependencyProperty FruitProperty =
     DependencyProperty.Register("Fruit", typeof(Fruit), typeof(MainWindow), new UIPropertyMetadata(new Fruit()));

Next is the EnumToBooleanConverter which can be seen below. If you are not familiar with binding converters have a look at this.

public class EnumToBooleanConverter : IValueConverter
{
     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
     {
          return value.Equals(parameter);
     }

     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
     {
          if (value.Equals(false))
               return Binding.DoNothing;
          else
               return parameter;
     }
}

Finally, here is all of the code contained in the MainWindow.xaml file. There are a few things to note here. First, we must include a reference to the local namespace so we can include the binding converter in the Windows.Resources tag. There are also two styles included in the Windows.Resources tag, one for the radio buttons and one for the stack panel. They are referenced using their x:Key.

You can see the binding syntax in the  IsChecked property of each radio button. It is important to note that we are NOT referring to the enums by simply using strings in xaml. We explicitly pass in the enum value using this syntax: x:Static local:Fruit.EnumValue.

<Window x:Class="DatabindingToAnEnumeration.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DatabindingToAnEnumeration"
        Title="MainWindow" Height="250" Width="300"
        x:Name="windowMain">

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

        <Style x:Key="radioButtonStyle" TargetType="RadioButton">
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="Margin" Value="15,0"/>
        </Style>

        <Style x:Key="stackPanelStyle" TargetType="StackPanel">
            <Setter Property="Orientation" Value="Horizontal"/>
            <Setter Property="Background" Value="LightGray"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="15,0"/>
        </Style>

    </Window.Resources>

    <Grid Background="LightYellow" Height="200">
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>

        <RadioButton Content="{x:Static local:Fruit.apples}" Grid.Row="0" Style="{StaticResource radioButtonStyle}"
                     IsChecked="{Binding Path=Fruit, ElementName=windowMain, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:Fruit.apples}}"/>

        <RadioButton Content="{x:Static local:Fruit.oranges}" Grid.Row="1" Style="{StaticResource radioButtonStyle}"
                     IsChecked="{Binding Path=Fruit, ElementName=windowMain, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:Fruit.oranges}}"/>

        <RadioButton Content="{x:Static local:Fruit.peaches}" Grid.Row="2" Style="{StaticResource radioButtonStyle}"
                     IsChecked="{Binding Path=Fruit, ElementName=windowMain, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:Fruit.peaches}}"/>

        <RadioButton Content="{x:Static local:Fruit.pears}" Grid.Row="3" Style="{StaticResource radioButtonStyle}"
                     IsChecked="{Binding Path=Fruit, ElementName=windowMain, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:Fruit.pears}}"/>

        <RadioButton Content="{x:Static local:Fruit.strawberries}" Grid.Row="4" Style="{StaticResource radioButtonStyle}"
                     IsChecked="{Binding Path=Fruit, ElementName=windowMain, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:Fruit.strawberries}}"/>

        <StackPanel Grid.Row="5" Style="{StaticResource stackPanelStyle}">
            <Label Content="Chosen Fruit: " VerticalAlignment="Center"/>
            <TextBlock Text="{Binding Fruit, ElementName=windowMain}" VerticalAlignment="Center" FontWeight="Bold"/>
        </StackPanel>
    </Grid>

</Window>

Just to show that everything is working as expected, I placed a stack panel at the bottom of the window. The panel has a TextBlock that is bound to our Fruit dependency property so you should see it update when you select different radio buttons. Here is an image of the running app.

Hopefully this clears things up. Enjoy!

Read Full Post »