Wednesday 21 September 2011

Things You Won't Know Until You Know

During most of my free time over the past week or so I've been delving even deeper into WPF and building test applications here and there.  I've also run into many seemingly complicated issues only to find out that they boil down to really simple WPF design nuances.  To save anybody else from suffering the same trouble I've experienced I have compiled a list, as the title suggests, of things you won't know until you know (about WPF):

---------------------------------------

The Importance of the Background Property
For a FrameworkElement to be involved in hit-testing (i.e. respond to being clicked, mouseover, etc.) it requires a Background Brush value.  If you don't want your element to have a background colour then feel free to set it's Background property to "Transparent".  Also, don't be fooled if your element has a default colour.  By default the Background brush is set to null and this means no hit-testing.  As a quick aside; it doesn't matter where you set the Background property.  In your window xaml, a style, the code behind, wherever; it will work.

The Extreme Power of Binding
Suffice it to say; binding is - in my opinion - the single most powerful concept that underpins the whole of WPF.  By enabling you to make loose connections not only to properties and objects but also to list of objects you can harness your data with a UI that seems almost self-aware.  I won't be blogging about Binding unless asked to as Microsoft have already done an amazing job but I will be posting little snippets here and there about how Binding helped me get out of a jam.

Canvas Width and Height
Canvas objects don't have Width or Height values unless you have set them by explicitly stating "Width=".  If you need the values of a Canvas' on screen width and height then reference them with ActualWidth and ActualHeight instead to retrieve accurate values.

ContextMenu and the Visual Tree
I've mentioned it already in both of my two previous blog posts but I'll say it here again for emphasis; ContextMenus are on a separate Visual Tree to the rest of the application and this can cause issues.  For a more in depth explanation and a fix please see my previous post: SmartMenuItem - The Power of Binding.

FindResource and generic.xaml
You may not have a generic.xaml file but if you do; the method, FindResource, does not search your generic.xaml file for your resource.  For your resource to be able to be found using that method you will need to define it in a resource dictionary that you reference explicitly from the App, Window, UserControl, etc.  For more information on how to do this see the MSDN article on ResourceDictionary.MergedDictionary.

Microsoft Expression Blend
I've only recently discovered that while Microsoft Expression Blend does cost a fair bit, the SDK doesn't.  This means you can play with all the extremely powerful toys that come with Blend (Behaviors, to name one) directly in your xaml just without the fancy Blend front end.  Download the Microsoft Expression Blend SDK for free from Microsoft's Official Site

PropertyMetaData and Double
When defining DependencyProperties in your classes you may want a dependency property of type double.  This is absolutely fine.  However; make sure that when setting a default value you explicitly pass a double to the PropertyMetaData.  For example, if you want your Dependency Property to have a default of zero and create the PropertyMetaData like this:  new PropertyMetaData(0) then your code will throw an error.  Why? Because you just invoked the PropertyMetaData constructor for an int and this isn't compatible with your double DependencyProperty.  To avoid this problem, use new PropertyMetaData(0D) instead.

---------------------------------------

And that's it for the moment.  No doubt this list will grow as I continue my journey with the Windows Presentation Foundation.  If you've experienced similar plight at the hands of WPF then please feel free to let me know what it was in the comments below and I'll add it to the list.

Thursday 15 September 2011

SmartMenuItem - The Power of Binding

In my previous post I mentioned an issue that a lot of people come across with WPF regarding Commands raised by MenuItems.  The problem being that when a MenuItem is nested in a ContextMenu, the Commands the MenuItem raises are not always heard by the Window or its inhabitants.  This is because a ContextMenu is on a separate Visual Tree and the Commands don't get seen by the Window or its Visual Tree.

The way around this is to bind the object reference that the MenuItem sends its Commands (its CommandTarget) to the ContextMenu's PlacementTarget object reference (the object that you right-clicked to bring up the context menu).  You could do this in your xaml for every MenuItem by writing the following each time you add a MenuItem:

CommandTarget="{Binding Path="PlacementTarget", RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}">
Curly braces tell xaml that this is a MarkupExtension - a link to some background code.  Markup works like this:  {MarkupExtensionToRun Parameter1 Parameter2 etc.}. Note the use of curly braces { }.  These indicate to xaml that the the bit in between is a Markup Extension, a piece of code to be run in it's own right.

Alternatively you can write a tiny helper class that means you'll never have to worry about it again.  The below does exactly the same as the above but it does it for us.  All you have to do is start using SmartMenuItem instead of MenuItem.

The Code
public class SmartMenuItem : MenuItem
{
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        if (!BindingOperations.IsDataBound(this, MenuItem.CommandTargetProperty))
        {
            Binding b = new Binding("PlacementTarget");
            b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ContextMenu), 1);
            b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            this.SetBinding(MenuItem.CommandTargetProperty, b);
        }
    }
}

Step by Step Breakdown
Run this code when the template for a SmartMenuItem is applied
public override void OnApplyTemplate()
    {
        Do all the normal template application things first
        base.OnApplyTemplate();

        Check whether the CommandTarget was bound by the Template
        If it was then we don't apply our binding as we assume the user was trying to override our behaviour
        if (!BindingOperations.IsDataBound(thisMenuItem.CommandTargetProperty))
        {
            The Path is the name of the source property on our RelativeSource
            Binding b = new Binding("PlacementTarget");

            When looking for the Path of "PlacementTarget", look for it in our first ancestor of type ContextMenu
            b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ContextMenu), 1);

            Update our source whenever the target property changes (i.e. re-find our relative source)
            b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

            Apply the binding
            this.SetBinding(MenuItem.CommandTargetProperty, b);
        }
    }

Why Use Binding?
If we were to have written code that dug up the ancestor of type ContextMenu and then said something like mySmartMenuItem.CommandTarget = myParentContextMenu.PlacementTarget then this link would have been Static.  What does this mean?  It means that even if we moved our SmartMenuItem into a different menu then it would still reference the original ContextMenu's placement target and, fundamentally, would present behaviour that is "broken".

Binding, on the other hand, is Dynamic.  The above code doesn't find our SmartMenuItem's parent ContextMenu and use its PlacementTarget as the CommandTarget at compile time.  It does it when the code is executed during runtime.  As you can imagine; this is much more flexible and intuitive.

It's worth mentioning in closing that as a typical rule of thumb, all things dynamic are slower than their static equivalent.  You're not going to see a huge performance hit the second you start using Binding but don't go using it just for the sake of it.

Wednesday 14 September 2011

Where to Begin?

WPF isn't easy to learn.  The separation of View and Data is a tough concept to get used to if you've been working in any environment where they've been tightly knit for an extended period of time.  However, once you get over the initial learning "hump", it all makes a crazy kinda sense.

It's now been a week or so since I started working with the Windows Presentation Foundation to create modern, professional looking apps rather than have to strong-arm normal Windows WinForm Controls into doing what I want.

It has been an enjoyable - albeit equally frustrating - journey of learning and understanding so far.  The two key points I'm going to dump here that nowhere on the internet really told me until I really dug in deep and that held me up for more than eight hours each are:
  1. Context Menus do not belong to the visual tree of the window.  Why is this important?  When you bind a Menu Item in a Context Menu to a Command, the on-screen Element you're right clicking (or any of its Ancestors for that matter) will not see it.  This is easily rectified by binding the Menu Item's CommandTarget to the parent Context Menu's PlacementTarget.  I wrote a very small SmartMenuItem class that figures out if it is part of a Context Menu and does this binding automatically if it is so that I never have to deal with this problem again.
  2. Using Data Templates for a Style's Content Template is good for presentation only.  When working with Data Templates it's easy to think that you're creating a Template of the Visual Tree that will be copied per instance of the object that uses this Data Template.  This is not the case.  The Data Template represents a single instance of the visual tree that will be shared across all objects implementing that Data Template.  This is where Binding comes in.  By binding sections of the Data Template to the values in the parent you require to be unique; you can make the one Data Template represent different information depending on the context of the instance referencing it.
So at the moment these are my two main revelations regarding WPF.  There is absolutely no doubt in my mind that WPF is an incredibly powerful tool that, once understood, will greatly reduce the development time of any visually impressive Windows application.