DCSIMG
December 2010 - Posts - Arik Poznanski's Blog

Arik Poznanski's Blog

It CAN be done with .NET

News

MVP

MCC

CodeProject MVP

MCPD

MCTS

Subscribe to my blog by email

Arik Poznanski LinkedIn Profile

Email: arik.com at gmail dot com
or, use this form

Locations of visitors to this page


Sela Group

Sela Canada

DZone MVB

Links

Official Blogs

WPF / SL Blogs

Developers Blogs

December 2010 - Posts

Order in Chaos: Dependency Property Value Resolution

One of the strength points for WPF is the ability to set a property value using a wide range of methods.

Unfortunately, this variety makes it hard to understand how a WPF dependency property gets its final value. Hopefully this post will help organize this issue.

This post is based on the comprehensive MSDN article “Dependency Property Value Precedence”.

Long Story Short

Diagram of dependency property value resolution

Can you show an example please?

In order to see this in action let’s create a WPF application that has all these scenarios used.

For this to be possible we will create a custom control named MyCustomControl and add it a dependency property named MyValue which we’ll use for setting values from different places.

To see the actual value at runtime we will present this property in a TextBlock. In fact, our custom control will have just that in his control template.

Step 1 – Default Value

First we add our dependency property and set its default value to “1”.

public string MyValue
{
    get { return (string)GetValue(MyValueProperty); }
    set { SetValue(MyValueProperty, value); }
}

public static readonly DependencyProperty MyValueProperty =
    DependencyProperty.Register("MyValue", typeof(string), typeof(MyCustomControl), new UIPropertyMetadata("1"));

After setting the default control template (Themes\Generic.xaml) as follows:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DependencyPropertyValueResolutionDemo">
    <Style TargetType="{x:Type local:MyCustomControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:MyCustomControl}">
                    <TextBlock
                        HorizontalAlignment="Center"
                        VerticalAlignment="Center"
                        FontSize="32"
                        Text="{TemplateBinding MyValue}" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

And the main window to:

<Window x:Class="DependencyPropertyValueResolutionDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DependencyPropertyValueResolutionDemo"
        Title="MainWindow" Height="150" Width="150">
    <Grid>
        <local:MyCustomControl />
    </Grid>
</Window>

We get:

Image with the number 1

Step 2 – Inheritance

To support inheritance we will change our dependency property into an attached property that can also be set using CLR property syntax.

In addition we will add FrameworkPropertyMetadataOptions.Inherits option to the property registration to allow the value to be inherited.

So the property declaration now goes:

public static string GetMyValue(DependencyObject obj)
{
    return (string)obj.GetValue(MyValueProperty);
}

public static void SetMyValue(DependencyObject obj, string value)
{
    obj.SetValue(MyValueProperty, value);
}

public static readonly DependencyProperty MyValueProperty =
    DependencyProperty.RegisterAttached("MyValue", typeof(string), typeof(MyCustomControl),
    new FrameworkPropertyMetadata("1", FrameworkPropertyMetadataOptions.Inherits));

public string MyValue
{
    get { return GetMyValue(this); }
    set { SetMyValue(this, value); }
}

And its use in the main window:

<Window x:Class="DependencyPropertyValueResolutionDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DependencyPropertyValueResolutionDemo"
        Title="MainWindow" Height="150" Width="150">
    <Grid local:MyCustomControl.MyValue="2">
        <local:MyCustomControl />
    </Grid>
</Window>

Which results in:

Image with the number 2

Step 3 - Default (theme) style

Adding a setter to the default style overrides the previous definition:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DependencyPropertyValueResolutionDemo">
    <Style TargetType="{x:Type local:MyCustomControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:MyCustomControl}">
                    <TextBlock
                        HorizontalAlignment="Center"
                        VerticalAlignment="Center"
                        FontSize="32"
                        Text="{TemplateBinding MyValue}" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="MyValue" Value="3" />
    </Style>
</ResourceDictionary>

So now we get:

Image with the number 3

Step 4 - Style setters

We can override the previous value by setting a style setter in the main window:

<Window x:Class="DependencyPropertyValueResolutionDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DependencyPropertyValueResolutionDemo"
        Title="MainWindow" Height="150" Width="150">
    <Grid local:MyCustomControl.MyValue="2">
        <local:MyCustomControl>
            <local:MyCustomControl.Style>
                <Style TargetType="local:MyCustomControl">
                    <Setter Property="MyValue" Value="4" />
                </Style>
            </local:MyCustomControl.Style>
        </local:MyCustomControl>
    </Grid>
</Window>

So now we get:

Image with the number 4

Step 5 - Template triggers

Now we will override our default template and add a template trigger, which will act when IsEnabled = True (I’ve just selected the property arbitrarily).

<Window x:Class="DependencyPropertyValueResolutionDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DependencyPropertyValueResolutionDemo"
        Title="MainWindow" Height="150" Width="150">
    <Grid local:MyCustomControl.MyValue="2">
        <local:MyCustomControl>
            <local:MyCustomControl.Style>
                <Style TargetType="local:MyCustomControl">
                    <Setter Property="MyValue" Value="4" />
                </Style>
            </local:MyCustomControl.Style>
            <local:MyCustomControl.Template>
                <ControlTemplate TargetType="local:MyCustomControl">
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="True">
                            <Setter Property="MyValue" Value="5" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                    <TextBlock
                        HorizontalAlignment="Center"
                        VerticalAlignment="Center"
                        FontSize="32"
                        Text="{TemplateBinding MyValue}" />
                </ControlTemplate>
            </local:MyCustomControl.Template>
        </local:MyCustomControl>
    </Grid>
</Window>

The result:

Image with the number 5

Step 6 - Style triggers

Now we add a trigger to the explicit style we’ve added before:

<Window x:Class="DependencyPropertyValueResolutionDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DependencyPropertyValueResolutionDemo"
        Title="MainWindow" Height="150" Width="150">
    <Grid local:MyCustomControl.MyValue="2">
        <local:MyCustomControl>
            <local:MyCustomControl.Style>
                <Style TargetType="local:MyCustomControl">
                    <Setter Property="MyValue" Value="4" />
                    <Style.Triggers>
                        <Trigger Property="IsEnabled" Value="True">
                            <Setter Property="MyValue" Value="6" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </local:MyCustomControl.Style>
            <local:MyCustomControl.Template>
                <ControlTemplate TargetType="local:MyCustomControl">
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="True">
                            <Setter Property="MyValue" Value="5" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                    <TextBlock
                        HorizontalAlignment="Center"
                        VerticalAlignment="Center"
                        FontSize="32"
                        Text="{TemplateBinding MyValue}" />
                </ControlTemplate>
            </local:MyCustomControl.Template>
        </local:MyCustomControl>
    </Grid>
</Window>

The result:

Image with the number 6

Step 7 - Implicit style

This step is brought here for completeness although we can’t use it to override the value of MyValue dependency property.

It is relevant ONLY if the dependency property you are trying to set is the Style property.

Step 8 - TemplatedParent template properties

OK, this is a tough one.

Here we set the value “8” (I skipped “7” to remain consistent with the step number) by setting the MyValue property on a MyCustomControl instance, BUT, only when it is generated as a template (by setting either a ControlTemplate or DataTemplate).

In the following code I’m creating a ContentControl, only to set its control template (via the Template property) to an instance of MyCustomControl. On this instance of MyCustomControl I set MyValue property to 8.

This appears to be setting a local value but it really isn’t!

The reason is that this template can be shared across several controls. So the value is saved behind the scenes as part of the template properties. We will see in the next step that setting a true local value will override this. In the mean time here is the code:

<Window x:Class="DependencyPropertyValueResolutionDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DependencyPropertyValueResolutionDemo"
        Title="MainWindow" Height="150" Width="150">
    <Grid local:MyCustomControl.MyValue="2">
        <ContentControl x:Name="contentControl">
            <ContentControl.Template>
                <ControlTemplate TargetType="ContentControl">
                    <local:MyCustomControl MyValue="8" x:Name="myCustomControl">
                        <local:MyCustomControl.Style>
                            <Style TargetType="local:MyCustomControl">
                                <Setter Property="MyValue" Value="4" />
                                <Style.Triggers>
                                    <Trigger Property="IsEnabled" Value="True">
                                        <Setter Property="MyValue" Value="6" />
                                    </Trigger>
                                </Style.Triggers>
                            </Style>
                        </local:MyCustomControl.Style>
                        <local:MyCustomControl.Template>
                            <ControlTemplate TargetType="local:MyCustomControl">
                                <ControlTemplate.Triggers>
                                    <Trigger Property="IsEnabled" Value="True">
                                        <Setter Property="MyValue" Value="5" />
                                    </Trigger>
                                </ControlTemplate.Triggers>
                                <TextBlock
                                    HorizontalAlignment="Center"
                                    VerticalAlignment="Center"
                                    FontSize="32"
                                    Text="{TemplateBinding MyValue}" />
                            </ControlTemplate>
                        </local:MyCustomControl.Template>
                    </local:MyCustomControl>
                </ControlTemplate>
            </ContentControl.Template>
        </ContentControl>
    </Grid>
</Window>

And the result:

Image with the number 8

Step 9 - Local value

In this step I’ll show you how I can override the previous template value by setting a local value from a button click handler. To prove that I’m not setting the same thing as in step 8 I’ll add another button that will clear the value, in which case the older value (“8”) will be restored.

So following is the code for main window, I’ve only added two buttons and replaced the main Grid with a StackPanel:

<Window x:Class="DependencyPropertyValueResolutionDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DependencyPropertyValueResolutionDemo"
        Title="MainWindow" Height="150" Width="150">
    <StackPanel local:MyCustomControl.MyValue="2">
        <ContentControl x:Name="contentControl">
            <ContentControl.Template>
                <ControlTemplate TargetType="ContentControl">
                    <local:MyCustomControl MyValue="8" x:Name="myCustomControl">
                        <local:MyCustomControl.Style>
                            <Style TargetType="local:MyCustomControl">
                                <Setter Property="MyValue" Value="4" />
                                <Style.Triggers>
                                    <Trigger Property="IsEnabled" Value="True">
                                        <Setter Property="MyValue" Value="6" />
                                    </Trigger>
                                </Style.Triggers>
                            </Style>
                        </local:MyCustomControl.Style>
                        <local:MyCustomControl.Template>
                            <ControlTemplate TargetType="local:MyCustomControl">
                                <ControlTemplate.Triggers>
                                    <Trigger Property="IsEnabled" Value="True">
                                        <Setter Property="MyValue" Value="5" />
                                    </Trigger>
                                </ControlTemplate.Triggers>
                                <TextBlock
                                    HorizontalAlignment="Center"
                                    VerticalAlignment="Center"
                                    FontSize="32"
                                    Text="{TemplateBinding MyValue}" />
                            </ControlTemplate>
                        </local:MyCustomControl.Template>
                    </local:MyCustomControl>
                </ControlTemplate>
            </ContentControl.Template>
        </ContentControl>

        <Button Content="set local value" Click="Set_Local_Click" />
        <Button Content="clear local value" Click="Clear_Local_Click" />
    </StackPanel>
</Window>

Following is the implementation of the buttons. In order to find the generated MyCustomControl I use the method FrameworkTemplate.FindName.

Note that I don’t set the value “8” in the code, instead I use ClearValue and it’s gets restored since the value is saved inside the template definition.

private void Set_Local_Click(object sender, RoutedEventArgs e)
{
    MyCustomControl myCustomControl = (MyCustomControl)contentControl.Template.FindName("myCustomControl", contentControl);
    myCustomControl.MyValue = "9";
}

private void Clear_Local_Click(object sender, RoutedEventArgs e)
{
    MyCustomControl myCustomControl = (MyCustomControl)contentControl.Template.FindName("myCustomControl", contentControl);
    myCustomControl.ClearValue(MyCustomControl.MyValueProperty);
}

The result now, after clicking on “set local value” looks like this:

Image with the number 9

Step 10- Animation

OK, this one is easy.

To set a value using animation we’ll add a storyboard to the window resources:

<Window.Resources>
    <Storyboard x:Key="animateValue">
        <StringAnimationUsingKeyFrames Storyboard.TargetProperty="MyValue">
            <DiscreteStringKeyFrame KeyTime="0:0:1" Value="10" />
        </StringAnimationUsingKeyFrames>
    </Storyboard>
</Window.Resources>

And when the “animate value” button is clicked we will start the animation:

private void Animate_Click(object sender, RoutedEventArgs e)
{
    MyCustomControl myCustomControl = (MyCustomControl)contentControl.Template.FindName("myCustomControl", contentControl);

    Storyboard animateValue = (Storyboard)FindResource("animateValue");
    animateValue.Begin(myCustomControl);
}

The result:

Image with the number 10

Step 11 - Property coercion

The last option for changing a dependency property value, which has the highest precedence is to use value coercion.

To use value coercion we just add a value coercion callback when registering the dependency property:

public static readonly DependencyProperty MyValueProperty =
    DependencyProperty.RegisterAttached("MyValue", typeof(string), typeof(MyCustomControl),
    new FrameworkPropertyMetadata("1", FrameworkPropertyMetadataOptions.Inherits, MyValueChanged, MyValueCoerceValue));

private static void MyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}

private static object MyValueCoerceValue(DependencyObject d, object baseValue)
{
    return "11";
}

And the last result:

Image with the number 11

Now nothing can change our dependency property value!

Summary

We have seen 11 ways to affect a dependency property value, each time overriding the previous value. Hopefully you have learned something new about dependency property value resolution.

You can download the full source code for this demo here.

That’s it for now,
Arik Poznanski.

My Session on Sela College Channel

A few months ago I delivered a session on Windows 7 features in WPF and WinForms applications.

image

In this session I show, among other things, how to work with the Windows 7 Taskbar, how to work with sensors information (light, temperature, etc.), how to use the Windows 7 Ribbon and more.

Fortunately for all those who missed it, the session was recorded and is now available via Sela’s latest invention: Sela College Channel, a broadcasting channel for software related videos.

You can see the session on this link. Note that the session is in Hebrew.
Enjoy :)

That’s it for now,
Arik Poznanski.

Ribbon for WinForms: More Details On RibbonGenerator Tool

My associate, Bernhard Elbl, as published another two posts on his blog further explaining the latest improvements in the Ribbon for WinForms project.

In the first post Bernhard explains what is the RibbonGenerator and how it is generating the .ribbon files.

In the second post he explains what does it takes to use the Ribbon for WinForms project using Visual Studio 2010 Express edition.

I suggest everyone who use this project to check out these posts.
Finally, I would like to thank Bernhard for his good work.

That’s it for now,
Arik Poznanski.

kick it on DotNetKicks.com Shout it
Posted: Dec 10 2010, 11:40 PM by arik | with 2 comment(s) |
תגים:, , ,

How To Implement a Windows Live Writer Plug-in That Checks For Missing Tags

This post provides the technical details on how I’ve implemented the Windows Live Writer Plug-in Check For Missing Tags, which was presented here.

What Is This Plug-in Anyway?

This plug-in will remind you to add the proper tags if they are missing.

Trying to post without proper tagging when the plug-in is installed will result with the following dialog:

image_thumb[2]

Clicking on the Cancel button (which is the default) will abort the publish operation and will let you update the categories.

The list of required categories is saved in a simple text file (required_categories.txt) next to the plug-in dll.

How It Was Built?

Following are the full instruction on how to build such a plug-in.
You can find the full source here.

  1. Create new class library project named CheckMissingTagsPlugin. Make sure you use .NET framework 3.5 since .NET 4 plugins are not supported.
  2. Add reference to WindowsLive.Writer.Api, located in C:\Program Files\Windows Live\Writer.
  3. Add reference to System.Windows.Forms, located in the GAC.
  4. Create a class named CheckMissingTags that inherits from PublishNotificationHook.
  5. Apply attribute WriterPluginAttribute to the class.

    [WriterPlugin("EF0CAF97-12C2-4446-BDC4-19EE53781351", "Check For Missing Tags")]
    public class CheckMissingTags : PublishNotificationHook
    {

    }

  6. Override the method OnPrePublish. Here we can check for missing categories and notify the user.


    public override bool OnPrePublish(IWin32Window dialogOwner, IProperties properties, IPublishingContext publishingContext, bool publish)
    {
        // get list of required categories
        string[] requiredCategories;
        if (File.Exists(FileName))
        {
            requiredCategories = File.ReadAllLines(FileName);
        }
        else
        {
            // warn about missing required categories
            string message =
                "Missing categories file: " + FileName + "\n" +
                "Post anyway?";

            DialogResult dialogResult = MessageBox.Show(
                dialogOwner,
                message,
                "Warning",
                MessageBoxButtons.OKCancel,
                MessageBoxIcon.Warning,
                MessageBoxDefaultButton.Button2);

            // allow post only if user selected OK
            return dialogResult == DialogResult.OK;
        }

        // get list of available categories
        IEnumerable<string> availableCategories =
            from category in publishingContext.PostInfo.Categories
            select category.Name;

        // is one of the required categories available?
        bool hasAnyRequestedCategory = availableCategories.Any(
            s => requiredCategories.Contains(s, StringComparer.OrdinalIgnoreCase));

        if (!hasAnyRequestedCategory)
        {
            string allRequiredCategories = string.Join(", ", requiredCategories);
            string message =
                "None of the required categories are available.\n" +
                "(" + allRequiredCategories + ")\n" +
                "Post without required categories?";

            // warn about missing required categories
            DialogResult dialogResult = MessageBox.Show(
                dialogOwner,
                message,
                "Warning",
                MessageBoxButtons.OKCancel,
                MessageBoxIcon.Warning,
                MessageBoxDefaultButton.Button2);

            // allow post only if user selected OK
            return dialogResult == DialogResult.OK;
        }

        return true;
    }

  7. Add the following line to the post build event:

    XCOPY /D /Y /R "$(TargetPath)" "%ProgramFiles%\Windows Live\Writer\Plugins\"
  8. Build project.

Note: In order for the post build event to work you need to run Visual Studio as Administrator.

That’s it for now,
Arik Poznanski.

Never Forget To Tag Your Post Again!

Background

A few days ago, there was a bloggers meeting at Microsoft Raanana Office.

While I was there I’ve heard some people complain that they sometimes forget to add the proper tags to their blog posts (e.g. DEV, ITPRO, TECH, OFFTOPIC, VIDEO).

Presenting: Check For Missing Tags Plug-in

I’ve decided to pick up the glove and implement a Windows Live Writer plug-in that will remind you to add the proper tags.

Trying to post without proper tagging when the plug-in is installed will result with the following dialog:

image

Clicking on the Cancel button (which is the default) will abort the publish operation and will let you update the categories.

The list of required categories is saved in a simple text file (required_categories.txt) next to the plug-in dll.

How Do I Install This Cool Plug-in?

  1. Download the following zip file.
  2. Extract it into C:\Program Files\Windows Live\Writer\Plugins\ or the equivalent folder on your system (on 64 bit systems it’s Program Files (x86) ).
  3. Enjoy :)

If you’re interested in the code, check out the following post.

That’s it for now,
Arik Poznanski.

Adding Missing WPF Controls To The Toolbox

The other day I was teaching a WPF course when one of the students asked me where is the “Line” shape in the Visual Studio Toolbox.
I approach to show him, only to find out that this control is indeed missing from the Visual Studio 2010 Toolbox. Even if you search the alleged All WPF Controls” tab..

image

After a quick check I’ve found that this is not the only shape missing. The missing shapes are: Line, Path, Polygon, Polyline. And who knows which other controls are missing.

Anyway, you can add this controls manually to the toolbox by following these steps:

  • Right-click the toolbox tab where you want to add a control (e.g. “All WPF Controls” and select “Choose Items”.
image
  • From the WPF Components tab choose the requested control (e.g. Line). You can use the Filter to help you find the control.

image

  • And there you go. Now you have the missing control where you want it.

image

Note that it has a proper icon meaning the WPF team prepared this element to be on the toolbox, but somehow it slipped away.

That’s it for now,
Arik Poznanski.