Posts Tagged ‘WPF’

Validation example with some more tricky databinding

Wednesday, September 24th, 2008

In our WPF application we have come up with a control to give feedback to the user when validation errors occur. The best way to describe it is to show it in action. When an invalid value is entered the relevant control is highlighted in red and a button with a red info icon appears to the right:

clip_image001

Clicking on the button gives the user detailed information about the problem:

image

We’re pretty happy with how this validation control works in practice and it is quite easy to implement across the entire application.

We implement a ControlTemplate as an application level resource named StandardValidationTemplate as follows:

<ControlTemplate x:Key=StandardValidationTemplate>

    <Grid
        MaxWidth={Binding Path=AdornedElement.ActualWidth,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Adorner}}}>

        <Grid.ColumnDefinitions >
            <ColumnDefinition Width=*/>
            <ColumnDefinition Width=Auto/>
        </Grid.ColumnDefinitions>

        <Border
            Grid.Column=0BorderBrush=RedBorderThickness=1>
            <AdornedElementPlaceholder />
        </Border>

        <controls:ErrorButtonPopup
            Grid.Column=1Width=24Height=24ErrorMessage={Binding Path=AdornedElement.(Validation.Errors)[0].ErrorContent,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Adorner}}}/>

    </Grid>

</ControlTemplate>

This template is then applied to any controls that require validation by setting the Validation.ErrorTemplate attached property as follows:

<TextBox
    Validation.ErrorTemplate={StaticResource StandardValidationTemplate}>
    <TextBox.Text>
        <Binding Path=ZoomLevel>
            <Binding.ValidationRules>
                <validate:IntegerRangeValidationRule
                    MinValue=13MaxValue=17ErrorMessage=Zoom level must be an integer between 13 and 17./>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Here the IntegerRangeValidationRule is a simple ValidationRule written ourselves that checks that value is an integer within a certain range and ZoomLevel is the name of the property in the DataContext that we are binding to. When the validation fails the control is marked as invalid and the error message added to the collection of errors for that control. At this point the ControlTemplate for Validation.ErrorTemplate kicks in.

Our StandardValidationTemplate will wrap the problem control with a red Border and place it in a grid. The <AdornedElementPlaceholder /> element shows where the problem control (the one being adorned) is placed.

In the second column of the grid we will place an instance of our ErrorButtonPopup control. We need to set the ErrorMessage property of the ErrorButtonPopup. This is the message that is displayed to the user when the button is clicked. Setting this involves some typically cryptic WPF databinding syntax, but demonstrating the power of WPF databinding it can be done in a single statement.

ErrorMessage={Binding Path=AdornedElement.(Validation.Errors)[0].ErrorContent, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Adorner}}}

Breaking this down into its various parts, first we need to get a reference to the control being validated. To do that we get a reference to the Adorner control that our ErrorButtonPopup is contained within. That is got using the RelativeSource syntax:

RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Adorner}}

This is not too difficult, the RelativeSource markup extension finds the first Ancestor of type Adorner. The Adorner then has an AdornedElement property which in this case is the TextBox we were looking for.

Now we just need to get at the error message. Controls have a collection of validation errors for cases where more than one validation is attached to it. Our control only displays the first failing validation, it could be modified to handle more if necessary. The Validation.Errors attached property gives us a collection of ValidationError objects. We are just interested in the ErrorContent property of the first one.

Path=AdornedElement.(Validation.Errors)[0].ErrorContent

It’s a little tricky to figure out all this syntax but once in place everything works perfectly. The best thing about this is that now adding validation to any control is one line:

Validation.ErrorTemplate={StaticResource StandardValidationTemplate}

And modifying the look of the validation can be all done in one place.

Technorati Tags:

WPF Designer in 3.5 SP1

Friday, September 19th, 2008

I’ve just finished watching this Channel 9 video about the new features of the WPF designer in 3.5 SP1

WPF 3.5 SP1 Tools with Erick Ellis and Mark Wilson-Thomas

They talk a lot about the effort put into bug fixing and anyone who has used the Cider designer will appreciate that was needed. I was surprised though as installing SP1 was the final straw that caused me to set the XML editor as the default tool to edit XAML files. Before SP1 the Design tab of the WPF designer never worked for my current project, but I found the text editor of the XAML tab to have enough useful features to keep using it despite the slow loading and occasional crashes. Installing SP1 made it completely unusable and I reverted back to just using the XML Editor to write XAML.

I think the difficulties I experience are due to the way I implement the application. I create a Model object that is dynamically added as a resource at run-time. This is then set as the DataContext for the Main Window of the application and everything in the Model is then referenced in the UI using databinding. This architecture works really well in practice. The problem is that at design time there is no Model resource and even if there was it would not be in a valid state. I really need to be able to mock up an instance of the Model that the designer can use at design time to render the preview. Instead I just get errors and rotating discs anytime I try to use the designer.

From the demos in this video the designer looks like it is improving, but they only show simple scenarios like dragging buttons on forms and resizing grids. For production applications that make extensive use of databinding and advanced WPF features I’m not convinced it is useful yet. I would be happy with a XAML editor that did not have any design surface, but just gave you Intellisense, refactoring, document outline, code navigation and features like this. This would be enough to keep most developers happy and then Expression Blend could be used for the more graphical stuff. When I have some time I will investigate if such an editor exists and maybe see if I can modify my application to make it friendlier for the current designer.

I know it will take time for the tools to reach the level we were used to with Windows Forms and Webforms, we were spoilt after years of development of these. At least editing XAML by hand is not too difficult once you get used to the syntax, but it is frustrating to be back in the dark ages writing XAML without even Intellisense to help.

Technorati Tags:

EvalBinding Examples

Thursday, August 28th, 2008

Databinding in WPF is very powerful for binding directly to your business objects or UI Model. One thing that is missing here and would be useful for other areas of WPF is a way of writing expressions. Without this you can end up creating properties that duplicate each other just so that you can create suitable Bindings. A company called IdentityMine sell a set of controls called Blendables. These include the EvalBinding control that allows you write JScript expressions in your XAML. We’ve used EvalBinding extensively in our code and it’s been great. It saves us a lot of time and makes the code more readable and maintainable.

But like the rest of XAML sometimes the syntax of the markup can be awkward and difficult to figure out the first time. The fact that EvalBindings are JScript expressions written inside Markup extensions in turn written in XML doubly complicates everything So here I will list some examples of using EvalBinding that I’ve come across.

In all examples the uiModel namespace is the namespace of the properties that I am binding to. In these cases it is in a different class library so is referenced at the top of the XAML as:

xmlns:uiModel=clr-namespace:SlowTrain.UIModel;assembly=UIModel

Here I bind the visibility of a control to the value of an enumeration in my Model. If the value of SelectedViewMode is ViewModeFull, then the control will be visible. Otherwise it will be hidden. The syntax is difficult as you need to include x:Static as well as references to your own namespace, but once you have the syntax figured out it works perfectly.

Visibility={blendables:EvalBinding [.SelectedViewMode] \=\= [{x:Static uiModel:TrackViewMode.Full}] ? [{x:Static Visibility.Visible}] : [{x:Static Visibility.Collapsed}]}

Here I bind the ToolTip property of a ToggleButton to an expression based on one of its own properties, very useful in this scenario.

<ToggleButton IsChecked={Binding Path=IsRepeatEnabled, Mode=TwoWay}ToolTip={blendables:EvalBinding [{Self}.IsChecked] ? \’Turn repeat off\’ : \’Turn repeat on\’ }/>

Technorati Tags:

WPF Validation - Validation for Modal Dialog Boxes

Friday, August 22nd, 2008

Validation is one of those areas in WPF that still feels like a bit of a work in progress. The pretty much undocumented BindingGroup that has appeared in .NET 3.5 SP1 shows that some work is still going on on this (I suspect that BindingGroup has mainly appeared to support the upcoming DataGrid). But with a few tweaks validation in WPF is very useful and can do most of what is required.

The first tweak I use is for enforcing validation in modal dialogs. This static class has a method HandleOkButtonClick to be called when the dialog is being closed. This will validate each child in the logical tree, using the recursive CheckIsValid method. If validation passes then DialogResult is set to true, otherwise focus is put on the offending control.

public static class DialogBoxValidationHelper
{
    public static void HandleOkButtonClick(Window dialogWindow)
    {
        DependencyObject nodeFailingValidation;
        if (Validate.DialogBoxValidationHelper.CheckIsValid(dialogWindow, out nodeFailingValidation))
        {
            dialogWindow.DialogResult = true;
        }
        else
        {
            if (nodeFailingValidation is IInputElement)
            {
                Keyboard.Focus((IInputElement)nodeFailingValidation);
            }
        }
    }

    public static bool CheckIsValid(DependencyObject nodeToValidate, out DependencyObject nodeFailingValidation)
    {
        bool result = true;
        nodeFailingValidation = null;

        if (nodeToValidate == null)
            throw new ArgumentNullException(“nodeToValidate”);

        if( Validation.GetHasError(nodeToValidate) )
        {
            result = false;
            nodeFailingValidation = nodeToValidate;
        }
        else
        {
            foreach (object child in LogicalTreeHelper.GetChildren(nodeToValidate))
            {
                if (child is DependencyObject)
                {
                    result = CheckIsValid((DependencyObject)child, out nodeFailingValidation);
                    if (result == false) break;
                }
            }
        }

        return result;
    }
}

Binding to a static using XAML

Friday, August 22nd, 2008

It’s very useful to be able to bind to static variable, either one defined in your own classes or in the class libraries. But the XAML syntax is difficult to remember so here’s an example of binding to Int32.MaxValue:

<Binding Source=”{x:Static sys:Int32.MaxValue}” Mode=”OneWay”>

The System namespace is defined as follows:

xmlns:sys=”clr-namespace:System;assembly=mscorlib”

Logo

Monday, June 9th, 2008

This is our first blog post written using Windows Live Writer

To start with here’s a picture of our logo.

slow_train_card1