Project Description
The Human Interface Project (HIP) is a tiny library for the .NET platform and Windows Presentation Foundation (WPF) aimed at making complex and/or mundane operations simple and elegant, for both end-users and developers.

hip:Link

hip:Link is the first component to be released as part of the Human Interface Project. It allows developers to construct complex dynamic bindings in a simple and familiar manner similar to that of events: you define a method or property in your code-behind and hip:Link will apply the result to a property you target in XAML, automatically re-evaluating it when its dependencies change. Dependencies are specified by the use of code-side attributes, meaning they are always right beside the definition, making it easy to see and verify what dependencies exist, unlike traditional WPF bindings in which the converter and converter parameters are defined in separate locations.

Here's an example of what you can do with hip:Link:

XAML:
<TextBlock
    x:Name="Title"
    Text="{hip:Link}" />

C#:
[DependsOn( "this.Title" )]
[DependsOn( "this.Editable" )]
private string Title_Text {
    get {
        if ( !String.IsNullOrEmpty( this.Title ) )
            return this.Title.Trim();
        else if ( this.Editable )
            return "Click to add a title.";
        else
            return "Untitled";
    }
}

One of the goals of hip:Link is to make the assignment of arbitrarily-complex definitions to properties in XAML as simple and concise as possible. The above example uses hip:Link's default naming convention, which is based on the assigned name of the target element ("Title") and the name of the property being set ("Text"). You can also explicitly specify the name of an existing property or parameterless method as the default (unnamed) parameter of the hip:Link directive. These identifiers point to members in the codebehind file corresponding to the xaml file the directive is used in:

MyControl.xaml:
<TextBlock
    Text="{hip:Link GetTitle}" />

MyControl.xaml.cs:
private string GetTitle() {
    return "I am a title with no dependencies!";
}

Note also that hip:Link supports private members, so your class can remain clean and tidy to the outside even if it has lots of hip:Link definitions.

Two-way bindings

Two-way binding (i.e. propagating changes back to dependencies) is supported automatically when you link to a property (the associated property setter is used if it exists). For methods and properties without an associated setter, an explicit "setback" method or property can be specified, which represents either a method that accepts a single 'object' parameter, or another property that has a setter. This is provided by the SetBack attribute:

<TextBox Text="{hip:Link GetTitle, SetBack=SetTitle}" />

private void SetTitle( object title ) {
    this.Title = (string)title;
}

Setback methods do not require DependsOn attributes because they do not directly affect the evaluation of the definition, and any changes they make to other dependencies should initiate the proper updates themselves (see Property change notifications below).

Dependencies

The magic of hip:Link comes from its ability to automatically re-evaluate a property definition when any of its declared dependencies change. In general, a dependency is any object or property mentioned within the body of a definition that a) is capable of changing, and b) affects the result of the definition. Pretty much every definition will have at least one dependency (if it doesn't, you can probably just declare the value of the property statically). Consider the following example:

<TextBlock
    x:Name="Title"
    Text="{hip:Link}"
    Foreground="{hip:Link GetTitleColor}" />

public Brush GetTitleColor() {
    if ( this.IsTitleInvalid )
        return Brushes.Red;
    else
        return new BrushConverter().ConvertFromString( Settings.Default.TitleColor );
}

In this small method we are determining which color the Title TextBlock should have. We base this decision on a simple rule: if the title is "invalid" (whatever we may choose that to mean), color it red; otherwise, color it according to the user's established preference. This method has two direct dependencies: the local IsTitleInvalid property, and the TitleColor application setting. To tell hip:Link about these dependencies, we attach two DependsOn attributes to the method, like so:

[DependsOn( "this.IsTitleInvalid" )]
[DependsOn( "settings.TitleColor" )]
public Brush GetTitleColor() {
    if ( this.IsTitleInvalid )
        return Brushes.Red;
    else
        return new BrushConverter().ConvertFromString( Settings.Default.TitleColor );
}

Now when either of those dependencies change, GetTitleColor will be re-evaluated and the Foreground property of the title TextBlock will be updated to reflect the new value. (See below for information on the "settings" dependency target.)

The first part of the dependency identifier is the dependency target (see Dependency targets below) and represents the source of the underlying binding. Everything following the first dot identifies the path to the dependency, relative to the source. In the simplest scenarios this will just be the name of the property you are identifying, however, since this string actually represents the Path property of the underlying binding, you can take advantage of WPF's advanced path syntax to identify subproperties, attached properties, indexes, and so on. Read more about WPF Path syntax here.

Property change notifications

hip:Link piggybacks on WPF's existing databinding engine to handle dependency changes and re-evaluations. This means that out of the box, hip:Link will support two kinds of property change notifications: DependencyProperties and INotifyPropertyChanged. What this means is if the dependency you are identifying is already a DependencyProperty, it automatically supports change notifications and will work automatically. If the property you are identifying as a dependency is not a DependencyProperty, the class that contains it must implement INotifyPropertyChanged and signal when the property changes.

This is generally very simple to do, and can be automated with the wonderful MoXAML Power Toys by Pete O'Hanlon. You can read more implementing INotifyPropertyChanged here.

Dependency targets

hip:Link supports three built-in target types for specifying dependencies: this, target, and context. These are defined as follows:

this:
This represents the current instance of the root element of the file, and is analogous to the 'this' keyword in the codebehind file. For example, if you are defining a custom UserControl called ClockEditor, "this" refers to the instance of ClockEditor the link is defined within.

target:
The target represents the element that hosts the property being defined. In the above examples, this would be the 'TextBlock' or 'TextBox'. You can use this target to define dependencies analogous to the {RelativeSource Self} directive.

context:
The context represents the current inherited DataContext for the target element. DataContexts are used extensively in binding to provide an inherited context that applies to an entire scope of child elements in XAML. Read more about DataContext here. You can even set the DataContext on elements using a hip:Link declaration!

Adding custom targets

hip:Link also supports the ability to add or remove custom dependency targets that work on a global scale throughout your application:

static ClockEditor() {
    Hip.LinkExtension.AddTarget( "settings", Properties.Settings.Default );
}

This allows you to add dependencies for properties in that object:

[DependsOn( "settings.Use24HourClock" )]
private string Hours_Text {
    get {
        ...
    }
}

Requirements

hip:Link is made possible thanks to new constructs introduced in .NET 4.0 and therefore will not run on installations prior to version 4.0. It will, however, run on the client profile subset of .NET 4.

Known issues

There is potentially a bug in Visual Studio 2010 that will cause the debugger to halt on exceptions that occur inside a MethodInfo.Invoke call that should otherwise be caught in code. This presents a problem with hip:Link because of the way WPF evaluates bindings. In particular, WPF may call your definition property/method before its dependencies have been initialized, usually resulting in an exception. In general this is not a problem because as soon as the dependencies are initialized, the definition will be re-evaluated with the proper values, but it may trigger the exception helper when running in debug mode.

Do note that this is only a problem when debugging, and should not affect the final program at all. It also does not appear to affect everyone, so you may well be immune to it. I have submitted a bug report to Microsoft Connect describing the problem.

Workarounds

If you are experiencing this problem, one thing to do is to always wrap your hip:Link definitions in try/catch blocks and simply return a harmless value (like null or Binding.DoNothing) from your catch block. Because the exception is caught before it reaches the MethodInfo.Invoke layer, it should prevent the debugger from thinking the exception is unhandled.

Another (more convenient) workaround is to disable "Just My Code" in the Visual Studio debugger (Tools -> Options -> Debugging | Enable Just My Code). This seems to work just as well and doesn't require you to add extraneous try/catch blocks to your code.

Last edited Oct 27, 2011 at 5:06 PM by chaiguy, version 44