Validation Scopes: Draft

In 2006 I published an article on Code Project explaining WPF Validation in depth. It was written for the .NET 3.0 version of WPF, and given that some of the validation capabilities have changed since then, the article has become somewhat outdated. One thing that I feel hasn’t changed is the general feeling that WPF’s validation system is limited in features, and very hard to extend. In the article I included code for an example ErrorProvider, which although it had problems, surprisingly became quite popular over time.

Over the past couple of weeks between client engagements and preparing to move to Brisbane, I’ve been working on a replacement to the ErrorProvider control that extends the possibilities of WPF validation and also fixes many of the issues of the previous attempt. As it is still a beta, I haven’t quite decided where to house the code yet, but I’m attaching it to this blog entry so that you can have a play.

I’d really appreciate if you could skim the PDF to get a sense of the design, and play with the code. In particular, check out the sample application.

What does this provide over WPF’s inbuilt validation?

The control is designed as a reusable component that exposes a heap of properties and interfaces that you can plug into around the validation process. Some of the scenarios that it enables are:

  • Control over when validation happens via Validation Triggers
    • As soon as the data loads
    • Only after the control has had focus
    • Only explicitly (hitting "Save")
  • Support for rich error information. Categories, help topics, localization, and so on.
  • Lists of errors
  • Errors around multiple controls
  • Automatically give focus to the first invalid control
  • Nesting validation scopes and error list rollups
  • Pluggable validation providers. Don’t like IDataErrorInfo? Want to use EntLib natively? Roll your own! (I’d love someone to contribute an EntLib provider)

The code includes a sample application to show it all in action. Have a go at playing with the various Validation Triggers.

Nesting Validation Scopes

This is one I’m keen to get feedback on. It’s explained in more depth in the document, but effectively it enables validation scopes to be nested and to aggregate the validation results at a higher level, by leveraging WPF’s hierarchical nature and the visual tree (the visual tree is not strictly required, but it’s a great case of convention over configuration). Here’s a picture from the document that might help to visualize it:

image

When the root Validation Scope is validated, it validates the controls it is associated with, and also the child Validation Scopes. It aggregates their results, so that if any child is invalid, the parent is invalid. This makes it great for implementing validation rollups:

image

image

A list of error messages could be placed on each tab with the messages just for that tab, or at the root of the window with all messages.

Field association

One of the departures I made from the existing validation system is that Bindings are not used as the target for showing the validation messages. This means you can use other controls to show validation messages instead. In the document, I describe how to implement something like this:

image

The Document

As I said, I’d really like your feedback on the attached document, as it will form the basis of my Code Project article on using the control and the documentation. Here’s the table of contents to whet your appetite:

  • Background
  • Introduction
  • Getting Started
    • Download
    • Usage
  • Concepts
    • Validation Triggers
    • Validation Providers
    • Validation Display Strategies
    • Validation Scope Nesting
  • How To
    • How To: Trigger validation when a user hits a button
    • How To: Show a validation result on a control (Style)
    • How To: Show a validation result on a control with an Adorner (Style + Template)
    • How To: Show a validation result around multiple controls
    • How To: Show extra information about an error
    • How To: Show a list of validation messages
    • How To: Show validation rollups on a tab control
  • Known Issues
    • Issue #1: Tab Controls with Validation Scopes don’t validate until selected
    • Issue #2: Virtualizing Panels
  • Conclusion
  • Thanks

Silverlight Soon

You’ll find that the attached code does not build unless you "Unload" the Bindable.Windows.Silverlight project. I did have the same validation system working in Silverlight, but I was making heavy changes today and got fed up with trying to keep the two projects in sync. I’m thinking of instead using the one project and using partial methods and classes to manage the SL/WPF differences. As soon as I’m happy with the WPF version I’ll finish the SL port.

I hope you see something valuable. Feedback much appreciated.

Presentation: QMSDNUG Smart Client Patterns

I gave a presentation last night at the Queensland MSDN User Group on Smart Client Patterns. We covered the following:

I used PowerPointPlex in the slide deck to add a bit of jazz to the slides and help communicate the structure of the talk.

You can get the sample code and slide deck from Subversion here:

http://svn.paulstovell.com/Presentations/Pattern%20Safari/

Rob Sanders did a surprise presentation at the end on SQL Data Services which was received very well. Check out his blog for more of his thoughts on SDS.

Thanks everyone who came along and made it a good night.

Overheard: WPF 4 and Fonts

This is an interesting comment from Soma on his .NET 4.0 post. Torsten Koehler asks:

We are currently working on a Silverlight app and the blurry fonts have become a major issue.  I am assuming this will be addressed by WPF4.  Do you have a rough ETA that we can pass on to our clients?

To which the reply is:

Yes, we are planning on addressing these in WPF4.  You will likely see pixel snapping in a beta release and improvements to the blurry font issue at RTM.  It is a little too early to talk about an ETA, but as we make more progress on .NET FX4, we will keep you posted.

I’ll be very happy.

Dino!

Watching the various announcements coming out of the PDC, you’ve probably come to the realization that the web is dead, and rich client technology is the future. Not convinced? Well, if you’re sure you still want to build on the web, then why not learn it from one of the best in the business: Dino Esposito!

image

Here’s a brief overview:

For the first time in Australia, Dino Esposito will deliver his Next Generation Web Applications Master Class as part of Readify’s Industrial Strength Series. Dino is an MVP based in Italy and is considered an authoritative and acknowledged world expert in Web applications built with .NET technologies.

During this five-day intensive course, the problem of AJAX architectures and the Web in general will be discussed to identify an easy and a not-so-easy way to AJAX with their positives and negatives, their features and drawbacks. Students should devise an AJAX presentation layer as a two-tier model with a JavaScript-powered front-end and a multi-layer service-based back-end communicating over HTTP and using JSON feeds. It looks like a plan, but the devil is in the details… Which services should you use? REST, WS-*, WCF, or just a remote API? Should you create the user interface programmatically in JavaScript or have it generated as mark-up on the server? Is it worth considering client technologies more powerful than JavaScript? And a delivery format for the application that is more descriptive than HTML.

Course Highlights:

Day 1- ASP.NET AJAX—The Easy Way

  • AJAX: Nutrition Facts and the Starvation of the Classic Web Architecture
  • Orchestrating AJAX in ASP.NET
  • Adding AJAX Capabilities to ASP.NET
  • Real-world Partial Rendering

Day 2- ASP.NET AJAX—The Not-So-Easy Way

  • Power to the Client
  • Aspect-oriented AJAX
  • Common Problems and AJAX Patterns
  • The Service Layer

Day 3 - ASP.NET State-of-the-Art

  • LINQ at a Glance
  • LINQ for (ASP.NET) Architects
  • ASP.NET 3.5—What’s New
  • ASP.NET 3.5 Service Pack 1

Day 4- The (Deep) Impact of Silverlight

  • Silverlight and AJAX
  • Essential WPF
  • Scripting the Silverlight Engine
  • Silverlight 2.0

Day 5- Building RIA with Silverlight

  • Fundamentals of RIA with Silverlight
  • Networking and I/O
  • Architecture and Programming Model
  • One XAML Fits All

So what are you waiting for?

WPF: Dependency Injection in XAML

Sometimes you will build custom controls that you wish to create in XAML, but those controls may have a dependency on a service which you want to be provided by your dependency injection container.

Assuming your control is declared like this:

class CurrencyControl : Control
{
    public ICurrencyConverter CurrencyConverter { get; set; }
    public ICurrencyDescriptor FromCurrency { get; set; }
    public ICurrencyDescriptor ToCurrency { get; set; }
}

(Of course, each of those properties would be dependency properties.)

You could declare it in XAML:

<z:CurrencyControl
    x:Name="_currency"
    FromCurrency="{Binding Path=SelectedProduct.Currency}"
    ToCurrency="{Binding Path=UserProfile.Currency}"
    />

And then set the property using codebehind:

public Window1()
{
    InitializeComponent();
    _currency.CurrencyConverter = ServiceLocator.Current.GetInstance<ICurrencyConverter>();
}

A nicer way to do this is to use a MarkupExtension to tell the container to resolve the service. It might look like this:

<z:CurrencyControl
    CurrencyConverter="{z:ResolveDependency Type={x:Type z:ICurrencyConverter}}"
    FromCurrency="{Binding Path=SelectedProduct.Currency}"
    ToCurrency="{Binding Path=UserProfile.Currency}"
    />

Or, since the markup extension has access to the property that the value is being set on, you could infer the correct service type to resolve based on the property type, leaving:

<z:CurrencyControl
    CurrencyConverter="{z:ResolveDependency}"
    FromCurrency="{Binding Path=SelectedProduct.Currency}"
    ToCurrency="{Binding Path=UserProfile.Currency}"
    />

Such a markup extension would look like this:

[MarkupExtensionReturnType(typeof(object))]
public class ResolveDependency : MarkupExtension
{
    public ResolveDependency()
    { }

    public ResolveDependency(Type type)
    {
        Type = type;
    }

    [ConstructorArgument("type")]
    public Type Type { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (serviceProvider != null)
        {
            var provideValueTarget = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
            if (provideValueTarget != null)
            {
                var targetObject = provideValueTarget.TargetObject as DependencyObject;
                if (targetObject != null)
                {
                    // If the type isn't specified, infer it from the property being assigned
                    var resolve = Type;
                    if (resolve == null)
                    {
                        if (provideValueTarget.TargetProperty is DependencyProperty)
                        {
                            resolve = ((DependencyProperty) provideValueTarget.TargetProperty).PropertyType;
                        }
                    }

                    if (resolve == null)
                    {
                        throw new Exception("...");
                    }

                    return ServiceLocator.Current.GetInstance(resolve);
                }
            }
        }
        return null;
    }
}

In fact, you could go so far as to resolve a whole control this way:

<ContentPresenter Content="{z:ResolveDependency Type={x:Type z:IUserView}}" />

You can go further by adding a design-time value, for when your IOC container is not available:

public Type DesignTime { get; set; }

// In your MarkupExtension:
// At design time, return an instance of the DesignTime type
var isDesignMode = DesignerProperties.GetIsInDesignMode(targetObject);
if (isDesignMode && DesignTime != null)
{
    try
    {
        return Activator.CreateInstance(DesignTime, true);
    }
    catch (Exception ex)
    {
        throw new Exception(string.Format("An exception occured while evaluating {0}. The DesignTime type '{1}' could not be instantiated. Please see the InnerException for more details. Message: {2}", this.ToString(), DesignTime.ToString(), ex.Message), ex);
    }
}

Making the XAML:

<z:CurrencyControl
    CurrencyConverter="{z:ResolveDependency Type={x:Type z:ICurrencyConverter}, DesignTime={x:Type z:DesignerCurrencyConverter}"
    FromCurrency="{Binding Path=SelectedProduct.Currency}"
    ToCurrency="{Binding Path=UserProfile.Currency}"
    />

If your IOC container is not available as a static object, one approach would be to use an attached inherited dependency property, and to set that at the root of your Window.