Feeds:
Posts
Comments

Archive for August, 2010

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 »

Attached properties are a beautiful thing. For a comprehensive introduction check out the MSDN  here. Anyway, the other day I was tasked with creating a button that has two distinct visual states, a normal state and mouse over state. The layout of the button itself was fairly simple. It was to be a rectangle with a stroke and fill. The rectangle had to contain an image and some text denoting what the purpose of the button was. Normally this would be no problem if I only had one button with one image. However, I needed two images and would prefer to have only one style that I could use for multiple buttons. For this example we are creating a “Save As” button.

When dropping the normal Windows button control on a form, it only exposes a single image property. The main problem was how to go about exposing multiple image properties on that button. One solution to this problem is to utilize attached properties. First I created a new WPF application in Visual Studio and added a class called CustomButton.cs.

Here are the contents of the CustomButton.cs class.

using System.Windows;
using System.Windows.Media;

namespace AttachedPropertyTesting
{
     public class CustomButton
     {
          static CustomButton()
          {
               ImageProperty = DependencyProperty.RegisterAttached("Image",
                    typeof(ImageSource), typeof(CustomButton), new FrameworkPropertyMetadata((ImageSource)null));

               ImageMouseOverProperty = DependencyProperty.RegisterAttached("ImageMouseOver",
                    typeof(ImageSource), typeof(CustomButton), new FrameworkPropertyMetadata((ImageSource)null));
          }

          public static readonly DependencyProperty ImageProperty;

          public static ImageSource GetImage(DependencyObject obj)
          {
               return (ImageSource)obj.GetValue(ImageProperty);
          }

          public static void SetImage(DependencyObject obj, ImageSource value)
          {
               obj.SetValue(ImageProperty, value);
          }

          public static readonly DependencyProperty ImageMouseOverProperty;

          public static ImageSource GetImageMouseOver(DependencyObject obj)
          {
               return (ImageSource)obj.GetValue(ImageMouseOverProperty);
          }

          public static void SetImageMouseOver(DependencyObject obj, ImageSource value)
          {
               obj.SetValue(ImageMouseOverProperty, value);
          }
     }
}

As you can see, we have declared two Dependency Properties, Image, and ImageMouseOver and registered them as attached properties. We can now access these properties from the button control itself. Then, with a little styling and template binding we have everything we need. I have placed our two images in a folder called Images. Here are the contents of the MainWindow.xaml.

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

<Window.Resources>
     <Style x:Key="btnSaveOrb" TargetType="{x:Type Button}">
          <Setter Property="Template">
               <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                         <Grid Background="LightBlue">
                              <Rectangle x:Name="rectangle" RadiusX="4" RadiusY="4" Stroke="Brown" Fill="Bisque"/>
                              <Label x:Name="label" Content="{TemplateBinding Content}" Margin="60,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" Foreground="Brown"/>
                              <Image x:Name="image" Source="{TemplateBinding aps:CustomButton.Image}" HorizontalAlignment="Left" VerticalAlignment="Top"/>
                              <Image x:Name="imageMouseOver" Source="{TemplateBinding aps:CustomButton.ImageMouseOver}" HorizontalAlignment="Left" VerticalAlignment="Top" Visibility="Hidden"/>
                         </Grid>
                         <ControlTemplate.Triggers>
                              <Trigger Property="IsMouseOver" Value="True">
                                   <Setter Property="Stroke" TargetName="rectangle" Value="Green"/>
                                   <Setter Property="Fill" TargetName="rectangle" Value="LightGreen"/>
                                   <Setter Property="Visibility" TargetName="imageMouseOver" Value="Visible"/>
                              </Trigger>
                         </ControlTemplate.Triggers>
                    </ControlTemplate>
               </Setter.Value>
          </Setter>
     </Style>
</Window.Resources>

     <Grid>
          <Button x:Name="buttonTwoImages"
                  aps:CustomButton.Image="Images/largeSaveIcon.png"
                  aps:CustomButton.ImageMouseOver="Images/largeSaveIconMouseOver.png"
                  Style="{StaticResource btnSaveOrb}"
                  Width="151"
                  Height="47"
                  Content="SaveAs">
          </Button>
     </Grid>
</Window>

One thing to note here is the reference to the clr-namespace at the top of the xaml file. Below are two screenshots showing the button in action. The first shows the button in its normal state while the second shows the button in its  mouse over state with the mouse over image. I hope you all realize there are limitless possibilities with attached properties. Enjoy!

Read Full Post »