Wednesday, July 18, 2012

Shared MaxLength of two WPF TextBoxes



WHY
In a data driven WPF application, we have got two WPF TextBoxes txtOne and txtTwo in a user control. The requirement is to control the maximum number of characters that can be entered in both of them to be limited to a certain value. For example the maximum number of characters in txtOne and txtTwo together should not exceed 150.

WHAT
To mutually bind the DependencyProperty of two controls and hook up into some event of the control which directly deals with that DependencyProperty.

HOW
In this post, the controls we'll be working with are two TextBoxes and the DependencyProperty is MaxLength. A DependencyProperty can be bound to a data source using a WPF Binding Expression. This data source could be a CLR Property (like a property of an entity) or another DependencyProperty.

Let's start with two TextBoxes in a WPF UserControl or Window:
<Window x:Class="wpfTextBoxMaxLength.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-assembly:wpfTextBoxMaxLength;assembly=wpfTextBoxMaxLength" x:Name="userControl"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel Orientation="Vertical">
        <TextBox x:Name="txtOne" HorizontalAlignment="Stretch" 
                VerticalAlignment="Stretch" Background="Pink" Height="50"  />

        <TextBox x:Name="txtTwo" HorizontalAlignment="Stretch" 
                VerticalAlignment="Stretch" Background="BlueViolet" Height="50"                 Margin="0 10 0 0"/>
    </StackPanel>
</Window>

Now, we need these two TextBoxes to share a combined MaxLength property. User should not be able to enter more than say sum of 150 characters in both of these TextBoxes.
For example, if txtOne contains a text of length 100, txtTwo can't allow a text of legth more than 50.

So, here is how we are going to achieve this-
  1. Create two DependencyProperties in the UserControl or Window, one for each for TextBox.
    const int TOTAL_LENGTH = 150;
    public static DependencyProperty TxtOneAllowedLengthProperty = DependencyProperty.Register("TxtOneAllowedLength", typeof(Int32), typeof(MainWindow), new FrameworkPropertyMetadata(TOTAL_LENGTH));
    
    public int TxtOneAllowedLength
    {
        get { return (int)GetValue(TxtOneAllowedLengthProperty); }
        set { SetValue(TxtOneAllowedLengthProperty, value); }
    }
    
    public static DependencyProperty TxtTwoAllowedLengthProperty = DependencyProperty.Register("TxtTwoAllowedLength", typeof(Int32), typeof(MainWindow), new FrameworkPropertyMetadata(TOTAL_LENGTH));
    
    public int TxtTwoAllowedLength
    {
        get { return (int)GetValue(TxtTwoAllowedLengthProperty); }
        set { SetValue(TxtTwoAllowedLengthProperty, value); }
    }
    
  2. Bind this DependencyProperty to MaxLength property of corresponding TextBox
    <TextBox x:Name="txtOne" Background="Pink" 
            MaxLength="{Binding ElementName=userControl, Path=TxtOneAllowedLength}"/>
    
    <TextBox x:Name="txtTwo" Background="BlueViolet"
            MaxLength="{Binding ElementName=userControl, Path=TxtTwoAllowedLength}"/>
    
  3. The DependencyProperty of one TextBox should be updated with any text changes in another TextBox. For this, we will need to hookup with an event of the TextBox which keeps track of any text changes in them. TextChanged event is exactly what we're looking for.
    <TextBox x:Name="txtOne" Background="Pink" TextChanged="txtOne_TextChanged"
       MaxLength="{Binding ElementName=userControl, Path=TxtOneAllowedLength}"/>
    
    <TextBox x:Name="txtTwo" Background="BlueViolet" TextChanged="txtTwo_TextChanged" 
       MaxLength="{Binding ElementName=userControl, Path=TxtTwoAllowedLength}"/>
    
    private void txtOne_TextChanged(object sender, TextChangedEventArgs e)
    {
        TxtTwoAllowedLength = TOTAL_LENGTH - txtOne.Text.Length;
    }
    
    private void txtTwo_TextChanged(object sender, TextChangedEventArgs e)
    {
        TxtOneAllowedLength = TOTAL_LENGTH - txtTwo.Text.Length;
    }
    
And we're done. These two TextBoxes now share their MaxLength Property with each other and their combined MaxLength can be set as a constant e.g. TOTAL_LENGTH or can also be dynamically calculated.

Leave your comments and encourage me to keep posting.

No comments:

Post a Comment