Wednesday, October 16, 2013

WPF - Attached Property in WPF

An attached property is a concept defined by XAML. An attached property is intended to be used as a type of global property that is settable on any object. In Windows Presentation Foundation (WPF), attached properties are typically defined as a specialized form of dependency property that does not have the conventional property "wrapper".

Why and When we create an Attached Property??? Big Question....

One purpose of an attached property is to allow different child elements to specify unique values for a property that is actually defined in a parent element. A specific application of this scenario is having child elements inform the parent element of how they are to be presented in the user interface (UI).

You might create an attached property when there is a reason to have a property setting mechanism available for classes other than the defining class.

An Attached Behavior is simply an Attached Property.Using attached behaviors, you can bundle up a piece of event-handling logic in a small, specific, reusable package which you can then apply to controls using XAML even we can use attached Property in Style.

Lets take a simple example,suppose i want to create a textbox that allow to user to enter digit only. In traditionally way we use Textbox PreviewTextInput event to block non digit value.

Let see how can we achieve this task.

Step 1
Create a WPF Application and give the solution name as SolAttachedBehavior.

Step 2
Add a Textbox control in window,it is look like this

<Grid>
<TextBox x:Name="txtUser" Height="23" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" PreviewTextInput="txtUser_PreviewTextInput"></TextBox>
</Grid>

Step 3
Now write the following Non digit blocking logic to Textbox PreviewTextInput Event,it is look like this
private void txtUser_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            try
            {
                if (!Char.IsDigit(e.Text.FirstOrDefault<char>()))
                {
                    e.Handled = true;
                    return;
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

This approach works fine if we have one textbox but what about when we have multiple textboxes for blocking non digit value.it is not reusable code then what is the solution??? The solution is to make a reusable code with Attached Property.

Step 4
Remove TextBox PreviewTextInput event from XAML and Comment PreviewTextInput event code in Code Behind.
Now we will create Attached Property for TextBox which block Non digit value.it is look like this.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace SolAttachedBehavior
{
    public static class TextBoxBehavior
    {

        #region Dependency Property

        // Boolean Attache Proeprty with Getter and Setter 
        public static readonly DependencyProperty IsDigitInputProperty =
            DependencyProperty.RegisterAttached("IsDigitInput", typeof(Boolean), typeof(TextBoxBehavior), 
            new UIPropertyMetadata(false, TextBox_IsDigitInput));

        #endregion

        #region Get and Set Accessor Static Methods of IsDigitInput Property

        public static Boolean GetIsDigitInput(DependencyObject DObj)
        {
            return (Boolean)DObj.GetValue(IsDigitInputProperty);
        }

        public static void SetIsDigitInput(DependencyObject Dobj, Boolean Value)
        {
            Dobj.SetValue(IsDigitInputProperty, Value);
        }

        #endregion

        #region Event of Attached property

        public static void TextBox_IsDigitInput(Object sender, DependencyPropertyChangedEventArgs e)
        {
            try
            {
                TextBox txtInput = (TextBox)sender;

                if (txtInput != null)
                {
                    Boolean IsDigitInputValue = (Boolean)e.NewValue;

                    if (IsDigitInputValue)
                    {
                        // Wire Up TextBox_PreviewTextInput Event to Execute IsDigit Logic
                        txtInput.PreviewTextInput += (s, tc) => {

                            if (!char.IsDigit(tc.Text.FirstOrDefault<char>()))
                            {
                                tc.Handled = true;
                                return; // Stop Event if Condition true
                            }
                               
                            
                        };
                    }
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message); 
            }
        }
        
        #endregion



       
    }
}

An attached behavior in WPF is normally a static class with one or more attached properties. The trick in this class is to listen to the change event of one of the properties, and to act upon it.

Step 5
Mapping TextBoxBehavior class in window XAML,it is look like this
xmlns:Behavior="clr-namespace:SolAttachedBehavior"

Finally it is look like on window tag
<Window x:Class="SolAttachedBehavior.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Behavior="clr-namespace:SolAttachedBehavior"
        Title="MainWindow" Height="350" Width="525">

<Window>

Step 6
Apply Attached Property to textBox,it is look like this
<Grid>
<TextBox x:Name="txtUser" Height="23" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" Behavior:TextBoxBehavior.IsDigitInput="True"></TextBox>
</Grid>

Step 7
using attached property in style,it is look like this
<Window.Resources>
        <Style x:Key="TextBoxStyle" TargetType="{x:Type TextBox}">
            <Setter Property="Behavior:TextBoxBehavior.IsDigitInput" Value="True">   </Setter>
        </Style>
 </Window.Resources>
 

<TextBox x:Name="txtUserStyle" Height="23" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" Style="{StaticResource TextBoxStyle}" Margin="15,10"></TextBox>

Full XAML Code
<Window x:Class="SolAttachedBehavior.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Behavior="clr-namespace:SolAttachedBehavior"
        Title="MainWindow" Height="350" Width="525">
    
    <Window.Resources>
        <Style x:Key="TextBoxStyle" TargetType="{x:Type TextBox}">
            <Setter Property="Behavior:TextBoxBehavior.IsDigitInput" Value="True"></Setter>
        </Style>
    </Window.Resources>
    
    <Grid>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBox x:Name="txtUser" Height="23" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" Behavior:TextBoxBehavior.IsDigitInput="True"></TextBox>
            <TextBox x:Name="txtUserStyle" Height="23" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" Style="{StaticResource TextBoxStyle}" Margin="15,10"></TextBox>
        </StackPanel>
    </Grid>
</Window>



Output

Click on Image for Better View

Download
Download Source Code

1 comment: