Feeds:
Posts
Comments

Posts Tagged ‘MultiBinding’

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

Advertisements

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 »