Click here to Skip to main content
15,868,016 members
Articles / Desktop Programming / WPF
Alternative
Article

Simplest WPF Dependency Property For Beginners On 'ValidState'

Rate me:
Please Sign up or sign in to vote.
5.00/5 (8 votes)
22 Nov 2017CPOL3 min read 31.5K   819   7   1
This is an alternative for "Simplest WPF Dependency Property For Beginners On Background Color"

Introduction

This is an introductory article on WPF Dependency Properties to show that they are quick and easy to use.

Background

There was an article posted that left me feeling that readers who didn't understand the topic were left more confused than before they read it. So I will try to address this and hopefully, by the end of this article, explain the virtues of Dependency Properties.

So what is a Dependency Property?

According to the Official Documentation:

Quote:

Represents a property that can be set through methods such as, styling, data binding, animation, and inheritance.

But I like the explanation by Matt Hamilton better:

Quote:

Dependency Properties are properties of classes that derive from DependencyObject, and they're special in that rather than simply using a backing field to store their value, they use some helper methods on DependencyObject. The nicest thing about them is that they have all the plumbing for data binding built in.

And what is a DependencyObject? Again, according to the Official Documentation:

Quote:

Represents an object that participates in the dependency property system.

I like this explanation better:

Quote:

Dependency object is the base object for all WPF objects. All the UI Elements like Buttons TextBox etc and the Content Elements like Paragraph, Italic, Span etc all are derived from Dependency Object. Dependency objects are used for WPF property system.

So DependencyObject implements all the plumbing for the sytyling, databinding, animation systems and more that is transparent to the developer, and Dependency Properties are the access points to store (Set) and retrieve (Get) the associated data.

How to Implement

Implementation is a little bit more involved than standard properties however, as mentioned above, can do far more. To implement, it is a two-part process:

  1. We need to register the Dependency Property
  2. We need to implement the getters and setters

Luckily, Visual Studio makes this easy to do via code snippets. For C# we have the propdb. And for VB, CTRL-K, CTRL-X > WPF > "Add a Dependency Property Registration". Here is an example of the code auto-generated:

C#
public int MyProperty
{
    get { return (int)GetValue(MyPropertyProperty); }
    set { SetValue(MyPropertyProperty, value); }
}

// Using a DependencyProperty as the backing store for MyProperty.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
    DependencyProperty.Register("MyProperty",
                                typeof(int),
                                typeof(ownerclass),
                                new PropertyMetadata(0));
VB
Public Property Prop1 As String
    Get
        Return GetValue(Prop1Property)
    End Get

    Set(ByVal value As String)
        SetValue(Prop1Property, value)
    End Set
End Property

Public Shared ReadOnly Prop1Property As DependencyProperty =
                       DependencyProperty.Register("Prop1",
                       GetType(String), GetType(),
                       New PropertyMetadata(Nothing))

Dependency Properties can be used to extend functionality for existing controls (lookless inheritence), adding properties to Usercontrols (templates), Behaviors, and Markup Extensions to add or modify existing controls without inheritance.

For the purpose of this article, I am going to inherit the TextBox control and add a Valid input state. See image below:

Image 1

There are two parts to the control:

  1. Text input area
  2. Visual indicator of validation state

We need to be able to:

  1. Track the Validation state: Pending, Valid, & Invalid
  2. Give each Validation state an Image & style

So we need 1 Dependency Properties: ValidState - to track the validation state: Pending, Valid, & Invalid.

Now we can create our custom control. Here I am creating a lookless control and extending the TextBox control:

C#
public class ValidateDataEntry : TextBox
{
    static ValidateDataEntry()
    {
        DefaultStyleKeyProperty.OverrideMetadata(ctrlType, new FrameworkPropertyMetadata(ctrlType));
    }

    private static readonly Type ctrlType = typeof(ValidateDataEntry);
    private const string ctrlName = nameof(ValidateDataEntry);

    public static readonly DependencyProperty ValidationStateProperty =
        DependencyProperty.Register(nameof(ValidationState),
        typeof(ValidationStateType), ctrlType, new PropertyMetadata(ValidationStateType.Pending));

    public ValidationStateType ValidationState
    {
        get { return (ValidationStateType)GetValue(ValidationStateProperty); }
        set { SetValue(ValidationStateProperty, value); }
    }
}

Now that we have our control, we need to style it. I have taken the standard TextBox template and added our elements that use our new Dependency Properties. To do this, I added a normal TextBox to the MainWindow, then right-clicked on the TextBox in the visual XAML editor and selected: "Edit Style" > "Edit a Copy" and the default style was generated. Here is our modified Xaml from our Generic.Xaml ResourceDistionary in the Themes folder (trimmed for briefity):

XML
<ControlTemplate x:Key="ValidateDataEntryTemplate" TargetType="{x:Type cc:ValidateDataEntry}">
    <Grid x:Name="root" UseLayoutRounding="True" SnapsToDevicePixels="True">
        <Border x:Name="border" Margin="2"
                Background="{TemplateBinding Background}"
				BorderBrush="{TemplateBinding BorderBrush}"
				BorderThickness="{TemplateBinding BorderThickness}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <ScrollViewer x:Name="PART_ContentHost"
                                Focusable="false"
                                HorizontalScrollBarVisibility="Hidden"
                                VerticalScrollBarVisibility="Hidden"/>
                <Grid Grid.Column="1">
                    <Path x:Name="Valid"
                            Style="{StaticResource ValidStyle}"
                            Data="{StaticResource ValidGeometry}"/>
                    <Path x:Name="InValid"
                            Style="{StaticResource InvalidStyle}"
                            Data="{StaticResource InvalidGeometry}"/>
                    <Path x:Name="Pending" Opacity="1"
                            Style="{StaticResource PendingStyle}"
                            Data="{StaticResource PendingGeometry}"/>
                </Grid>
            </Grid>
        </Border>
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsEnabled" Value="false">
            <Setter Property="Opacity" TargetName="border" Value="0.56"/>
        </Trigger>
        <Trigger Property="IsMouseOver" Value="true">
            <Setter Property="BorderBrush" TargetName="border"
                    Value="{StaticResource ValidateDataEntry.MouseOver.Border}"/>
        </Trigger>
        <Trigger Property="IsKeyboardFocused" Value="true">
            <Setter Property="BorderBrush" TargetName="border"
                    Value="{StaticResource ValidateDataEntry.Focus.Border}"/>
        </Trigger>
        <Trigger Property="ValidationState" Value="Valid">
            <Setter Property="Opacity" TargetName="Valid" Value="1"/>
            <Setter Property="Opacity" TargetName="Pending" Value="0"/>
        </Trigger>
        <Trigger Property="ValidationState" Value="Invalid">
            <Setter Property="Opacity" TargetName="InValid" Value="1"/>
            <Setter Property="Opacity" TargetName="Pending" Value="0"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

<Style TargetType="{x:Type cc:ValidateDataEntry}">
    <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="BorderBrush" Value="{StaticResource ValidateDataEntry.Static.Border}"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="SnapsToDevicePixels" Value="False"/>
    <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
    <Setter Property="HorizontalContentAlignment" Value="Left"/>
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    <Setter Property="AllowDrop" Value="true"/>
    <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
    <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
    <Setter Property="Template" Value="{StaticResource ValidateDataEntryTemplate}"/>
    <Style.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsInactiveSelectionHighlightEnabled" Value="true"/>
                <Condition Property="IsSelectionActive" Value="false"/>
            </MultiTrigger.Conditions>
            <Setter Property="SelectionBrush"
                    Value="{DynamicResource 
                               {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
        </MultiTrigger>
    </Style.Triggers>
</Style>

To Use

Now to use, we need to reference our new user control location, then we can added to our Xaml code (trimmed for briefity):

XML
<cc:ValidateDataEntry Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"
                      ValidationState="{Binding ValidationState, Mode=TwoWay}"/>

How it Works

When the ValidationState changes, the Data Binding of our new Dependency Property will see the PropertyChanged event, the ControlTemplate Trigger binding will be notified and will change the visual validation image based on the value of the Dependency Property for that control.

To see the Control with our new Dependency Property, download the code, compile, and run.

Summary

Whilst the Dependency Property is a little bit more verbose than a standard Property, they are easy to implement and the Dependency Object handles all the internal plumbing for you using the Binding system behind the scene.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
PraiseNice job Pin
srilekhamenon30-Sep-18 17:34
professionalsrilekhamenon30-Sep-18 17:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.