Category Archives: WPF

HTML to FlowDocument Converter

I have an existing application that I wrote that stores “notes” in HTML format. I actually used the embedded HTML editor in IE to allow the user to create text – similar to what a RichText control would be used for but outputting to HTML rather than (the even more horrid) RTF.

I’m now in the process of upgrading that application to WPF, so its only natural that I want to display these notes using one of the WPF FlowDocument viewer controls. The problem I encountered was how to convert my HTML to something that could be nicely displayed in the FlowDocument?

Step 1 – Converting HTML to XAML

The solution was to download Microsoft’s sample HtmlToXaml Converter (which actually allows conversion in both directions). Its apparently not foolproof but its certainly more than enough to convert my very simple HTML to the corresponding FlowDocument.

Using the HtmlToXamlConverter classes ConvertHtmlToXaml we can take a HTML string and convert to a XAML string, e.g. from:

    <p>The <b>Markup</b> that is to be converted.</p>

to:

    <FlowDocument>
<Paragraph>The <Run FontWeight="bold">Markup</Run> that is to be converted.</Paragraph>
</FlowDocument>

Step 2 – Converting XAML markup into a FlowDocument instance

This was certainly a great start but it converts HTML text to XAML text – not actual objects. So the next hurdle was how to convert the XAML document markup at runtime and insert within a FlowDocument?

The solution was fairly easy to find thanks to Google and Ronald Clifford – although I can’t say I think its obvious.

    FlowDocument flowDocument = new FlowDocument();
string xaml = "<p>The <b>Markup</b> that is to be converted.</p>";
using (MemoryStream msDocument = new MemoryStream((new ASCIIEncoding()).GetBytes(xaml)))
{
TextRange textRange = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd);
textRange.Load(msDocument, DataFormats.Xaml);
}

Step 3 – Using DataBinding

So things were almost coming together. The last step was to perform this conversion as easily as possible – which meant using DataBinding. I had a list of items displayed, each with a field containing a string field that contained the HTML markup (as populated from a database using LINQ). So I wanted to be able to bind the FlowDocumentScrollViewer to the field containing the HTML and for things to work themselves out automatically. This required yet another IValueConverter class to use on the data binding.

    public class HtmlToFlowDocumentConverter : IValueConverter
    {
public object Convert(object value, Type targetType, object parameter, 
System.Globalization.CultureInfo culture) { if (value != null) { FlowDocument flowDocument = new FlowDocument(); string xaml = HtmlToXamlConverter.ConvertHtmlToXaml(value.ToString(), false); using (MemoryStream stream = new MemoryStream((new ASCIIEncoding()).GetBytes(xaml))) { TextRange text = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd); text.Load(stream, DataFormats.Xaml); } return flowDocument; } return value; } public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }

Note: The HtmlToXmlConverter class above is simply the Microsoft sample.

The Result

This means I can now use this converter anywhere in my XAML that I wish to display HTML content within a FlowDocument control.

    <Page.Resources>
        <conv:HtmlToFlowDocumentConverter x:Key=”htmlToXamlConverter”/>
    </Page.Resources>
    …
    <FlowDocumentScrollViewer Document=”{Binding Path=Events/Diaries/Comment,
                              Converter
={StaticResource htmlToXamlConverter}}”/>

Thumbnail Solutions

So based on last nights performance troubles loading thumbnail images I spent a couple of minutes “googling” and discovered the following useful links:

http://blogs.msdn.com/dditweb/archive/2007/08/22/speeding-up-image-loading-in-wpf-using-thumbnails.aspx

http://msdn2.microsoft.com/en-us/library/system.windows.media.imaging.bitmapframe_members.aspx

I tried the solution in the first link which involved creating a converter to use so that binding to a URI can result in a BitmapSource that uses the DecodePixelWidth/Height property.

    public class UriToBitmapConverter : IValueConverter
    {
public UriToBitmapConverter()
{
DecodeResolution = 100;
}
public int DecodeResolution { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.DecodePixelWidth = DecodeResolution;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri( value.ToString() );
bi.EndInit();
return bi;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
}

This converter is then hooked up in the XAML something like as follows.

<Window x:Class="ThumbnailLoading.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ThumbnailLoading"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <local:PhotoCollection x:Key="photos" x:Name="photos"/>
        <local:UriToBitmapConverter x:Key="uriToBitmapConverter" DecodeResolution="60"/>
    </Window.Resources>
    <StackPanel>
        <ListView ItemsSource="{StaticResource photos}">
             <ListView.ItemTemplate>
                <DataTemplate>
                    <Image Width="60" 
Source
="{Binding Path=Uri, Converter={StaticResource uriToBitmapConverter}}"/> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackPanel> </Window>

Whilst this is an improvement as I suggested in the previous post from the folks at DDITDev – it is still too slow.

So I spent some time reading the MSDN documentation. The BitmapFrame class seemed to have a lot of cool functionality – including the ability to access image metadata. They even have properties for the commonly used properties such as Camera Model and Rating – very nice! However, of most interest was the Thumbnail property which suggested that it would return the thumbnail stored with the image. I used the Binding converter idea and created my own converter as follows.

    public class UriToThumbnailConverter : IValueConverter
    {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
BitmapFrame bi = BitmapFrame.Create(new Uri(value.ToString()), 
BitmapCreateOptions.DelayCreation,
BitmapCacheOption.OnDemand); return bi.Thumbnail; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new Exception("The method or operation is not implemented."); } }

Then made the following changes to the XAML to declare and use the new binding converter.

<local:UriToThumbnailConverter x:Key=”uriToThumbnailConverter”/>

<Image Width=”60″ Source=”{Binding Path=Uri, Converter={StaticResource uriToThumbnailConverter}}”/>

This actually seemed to do the trick. I provided some statistics below which show the performance benefits. Of course in reality – as with PhotoPlay – any expensive image loading would normally happen on a background thread so maybe the binding converter isn’t really the best way to manage this.

  Bind direct to Uri Bind to Uri using DecodePixelWidth Bind to Uri using embedded Thumbnail
Loading 301 approx 280kb (1.1 megapixel) images 1 minutes
1.2 Gb
30 seconds
120 Mb
4 seconds
180 Mb
Loading 1102 approx 2Mb (5 megapixel) images Yeah right! 4 minutes 35 seconds
201 Mb
14 seconds
310 Mb

Note that the DecodePixel width method using the UriToBitmapConverter actually uses less memory because the embedded Thumbnails are actually bigger that the decode width of 60 pixels that I specified.

I also found this Microsoft sample application WPF Photo Viewer Demo which shows how Thumbnails and metadata can be extracted from digital photos.

Thumbnail Images

When I wrote my little PhotoPlay applet I quickly realised that loading large numbers of images into memory was slow and enormously expensive in terms of memory use. I remember the first time I pointed PhotoPlay at my year 2004 photo folder – I think I managed to kill the task at around 3Gb of virtual memory use.

So I did a quick search and found a great bit of code from Kourosh Derakshan that allows you to use the thumbnail images that are embedded into photos by almost all digital cameras. The images are actually stored as metadata against the real image along with all the other EXIF metadata tags such as camera model, flash type, focal length etc.

Using this code I was able to load 100s of photos in several seconds, as opposed to minutes, and the memory usage was amazingly small. Of course the gotcha is that if the image doesn’t have a thumbnail this method won’t help you. If the image didn’t come from a camera then chances are it won’t have a thumbnail – so really this is for digital photos only. [The main reason the thumbnail image is stored is because this is the image that the cameras use themselves when you browse photos on the camera.]

The key to this code is the System.Drawing.Image.FromStream() method. The overload used has a parameter “validateImageData” that if set to false means the image isn’t loaded into memory. Not obvious and certainly not apparent from reading the documentation! You end up with an Image instance that still allows you access the metadata properties without having any overhead of processing the image itself.

    // Written by Kourosh Derakshan - minor mods by Nigel Spencer.
        /// <summary>
        /// Gets the thumbnail from the image metadata. Returns null if no thumbnail
        /// is stored in the image metadata
        /// </summary>
        /// <param name="path">The full path to the image.</param>
        /// <returns>The thumbnail image contained within the metadata (EXIF) of the image, or 
/// <see langword="null"/> if no thumbnail data is present.</returns> /// <remarks> /// The ExifTag metadata is copied from the original image to the thumbnail that is returned. /// </remarks> public static Image GetThumbnail (string path) { FileStream fileStream = null; try { fileStream = File.OpenRead(path); // Last parameter tells GDI+ not the load the actual image data Image originalImage = Image.FromStream(fileStream, false, false); // GDI+ throws an error if we try to read a property when the image // doesn't have that property. Check to make sure the thumbnail property // item exists. bool propertyFound = false; for (int i = 0; i < originalImage.PropertyIdList.Length; i++) if (originalImage.PropertyIdList[i] == (int)ExifTags.ThumbnailData) { propertyFound = true; break; } if (!propertyFound) return null; PropertyItem thumbnailPropertyItem = originalImage.GetPropertyItem((int)ExifTags.ThumbnailData); // The image data is in the form of a byte array. Write all // the bytes to a stream and create a new image from that stream byte[] imageBytes = thumbnailPropertyItem.Value; MemoryStream stream = new MemoryStream(imageBytes.Length); stream.Write(imageBytes, 0, imageBytes.Length); Image thumbnailImage = Image.FromStream(stream); // Copy all the original properties to the thumbnail. for (int i = 0; i < originalImage.PropertyIdList.Length; i++) { PropertyItem itemToCopy = originalImage.GetPropertyItem(originalImage.PropertyIdList[i]); thumbnailImage.SetPropertyItem(itemToCopy); } originalImage.Dispose(); return thumbnailImage; } finally { if (fileStream != null) { fileStream.Dispose(); } } }

So this method has been very useful to me, but in the last week I’ve been trying to do something similar in WPF. My first attempt was to just jump straight in and bind a ListBox to some images. This was incredibly slow to load – I mean horrendous! I wanted to use the GetThumbnail method but of course WPF doesn’t understand System.Drawing.Image – I need to use System.Windows.Media.ImageSource instead. I’ve messed briefly with DecodePixelWidth and Height but these only seem to control the size of the image that is held in memory (which is good) but it still takes forever to produce the thumbnail from the full image. I also noticed that the BitmapFrame class, not the BitmapSource seems to be more helpful because it allows Thumbnail and metadata access.

Well – its late – so it’ll have to be a job for tomorrow. Hopefully I’ll find an equivalent way of doing this in WPF that works and be able to blog the solution. Assuming I can get it working in WPF I’ll also include some performance comparisons against the method shown here.

XAML and WPF – or "I’m seeing stars"

Warning – This post just describes some fun I’ve been having learning about 2D graphics in WPF. There’s certainly nothing very clever going on here, but its been pointed out to me by a prominent Australian blogger that its not the quality but the quantity of blog posts that’s important :-p

But more seriously if you do want to read something of substance check out Paul’s post regarding why he doesn’t believe the Visual Studio 2008 Form Designer (Cider) will help developer productivity.

So anyway – about those stars…

I’m having a hard time at the moment really diving into a serious bit of WPF programming. It seems I’m constantly being tripped by the question of “should I do this in XAML, or should I do this in C#?”. Now traditionally (under WinForms) when writing a custom control I’d just start with a blank class file, determine the most appropriate Control/Component base class to derive from and start coding properties, events and any required rendering.

Now each time I’ve tried that approach with WPF – I end up realising pretty early on that its the wrong approach. Everything apart from the very specific properties and events are already there – or are provided by attached properties/events. The rendering is much easier to do in XAML too – in fact if done property the rendering is almost completely divorced from the control definition anyway and ends up in a theme based or Generic.xaml file.

Ok – so that’s good right? Well it sounds right – but I think I’m just having trouble coming to grips with it. The stumbling block I’m having at the moment with custom controls is realising when to step out of the XAML and into fleshing out the real logic. Trouble is with custom controls most of the logic is related to the UI!

As an example – I recently wanted to create some simple graphics by having some spinning stars. So my first reaction was to just jump straight into XAML and code up a filled polygon path using the System.Windows.Shapes.Polygon.

<Polygon Points="30,0 35,20 55,25 35,30 30,50 25,30 5,25 25,20" Fill="Gold" Stroke="Black"
StrokeThickness="2"/> <Polygon Points="30,0 35,10 45,15 35,20 30,30 25,20 15,15 25,10" Fill="Gold" Stroke="Black"
StrokeThickness="2"> <Polygon.LayoutTransform> <RotateTransform Angle="45"/> </Polygon.LayoutTransform> </Polygon>

SimpleXAMLStar 

Ok – but that’s a pretty lame star… and I want lots of them right – so I should create a custom control inheriting from Shape? Whilst I’m at it add a property that lets me configure the number of points too.

public class Star : Shape
{
// Using a DependencyProperty as the backing store for NumberOfPoints.  
public static readonly DependencyProperty NumberOfPointsProperty = DependencyProperty.Register("NumberOfPoints", typeof(int), typeof(Shape), new UIPropertyMetadata(5)); public int NumberOfPoints { get { return (int)GetValue(NumberOfPointsProperty); } set { SetValue(NumberOfPointsProperty, value); } } protected override Geometry DefiningGeometry { get { return VisualContainer.CreateStarGeometry(NumberOfPoints); } } }

Creating the geometry for an n-pointed star could be done a heap of ways. Mine was the easiest to visualize but certainly not very elegant. Just create a triangle for each prong and keep rotating for as many as required. Then use the GetOutlinedPathGeometry to get the enclosing path.

public static Geometry CreateStarGeometry(int numberOfPoints)
{
GeometryGroup group = new GeometryGroup();
group.FillRule = FillRule.Nonzero;
Geometry triangle = PathGeometry.Parse("M 0,-30 L 10,10 -10,10 0,-30");
group.Children.Add(triangle);
double deltaAngle = 360 / numberOfPoints;
double currentAngle = 0;
for (int index = 1; index < numberOfPoints; index++)
{
currentAngle += deltaAngle;
triangle = triangle.CloneCurrentValue();
triangle.Transform = new RotateTransform(currentAngle, 0, 0);
group.Children.Add(triangle);
}
Geometry outlinePath = group.GetOutlinedPathGeometry();
return outlinePath;
}

Now I’ve got a Shape they’re much easier to re-use – so much so we may as well even add some animation.

<control:Star NumberOfPoints="5" Width="60" Height="60" Stroke="Black" Fill="Gold" StrokeThickness="2"
Opacity="0.5"> <control:Star.Triggers> <EventTrigger RoutedEvent="control:Star.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard TargetProperty="Angle"> <DoubleAnimation Storyboard.TargetName="starRotation" From="0" To="72"
Duration="0:0:1" AccelerationRatio="0.3" DecelerationRatio="0.3"/> <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.5" To="1.0"
Duration="0:0:0.5" AutoReverse="True"/> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </control:Star.Triggers> <control:Star.RenderTransform> <RotateTransform x:Name="starRotation" Angle ="0"/> </control:Star.RenderTransform> </control:Star> <control:Star NumberOfPoints="6" Width="60" Height="60" Stroke="Black" Fill="Orange"
StrokeThickness="2"/> <control:Star NumberOfPoints="7" Width="60" Height="60" Stroke="Black" Fill="Red"
StrokeThickness="2"/>

ShapeStars

So I think I’m getting the hang of it. But hold on, according to the book if I’m going to have heaps of these things then I shouldn’t be using Shapes – I should be using DrawingVisuals within a container.

public class VisualContainer : Canvas
{
private List<Visual> visuals = new List<Visual>();
public VisualContainer()
{
DrawingVisual star = CreateStar(5);
visuals.Add(star);
foreach (Visual visual in visuals)
{
AddVisualChild(visual);
AddLogicalChild(visual);
}
}
public static DrawingVisual CreateStar(int numberOfPoints)
{
DrawingVisual star = new DrawingVisual();
using (DrawingContext drawingContext = star.RenderOpen())
{
Geometry outlinePath = CreateStarGeometry(numberOfPoints);
drawingContext.DrawGeometry(Brushes.Gold, new Pen(Brushes.Black, 2), outlinePath);
}
return star;
}
public static Geometry CreateStarGeometry(int numberOfPoints)
{         ...  as above ...
}
protected override int VisualChildrenCount
{
get { return visuals.Count; }
}
protected override Visual GetVisualChild(int index)
{
if (index < 0 || index >= visuals.Count)
throw new ArgumentOutOfRangeException("index");
return visuals[index];
}
public void AddStar()
{
Visual visual = CreateStar(5);
visuals.Add(visual);
PositionVisuals();
AddVisualChild(visual);
AddLogicalChild(visual);
}
private void PositionVisuals()
{
if (visuals.Count == 1)
((DrawingVisual)visuals[0]).Offset = new Vector(Width / 2, Height / 2);
else
{
double angle = 0;
double deltaAngle = Math.PI * 2 / visuals.Count;
double radius = Width / 2;
foreach (DrawingVisual visual in visuals)
{
visual.Offset = new Vector(Width / 2 + Math.Cos(angle) * radius, 
Height / 2 + Math.Sin(angle) * radius); angle += deltaAngle; } } } }

So that gives me a container that I can create heaps of stars in and only have one UIElement – the stars themselves are the apparently much lighter weight DrawingVisual instances. The cool thing with these is that Visual Hit Testing actually allows me to respond to click events on individual stars.

<control:VisualContainer Width="150" Height="150" MouseLeftButtonUp="VisualContainer_MouseLeftButtonUp" 
                                     RenderTransformOrigin="0.5,0.5"> <control:VisualContainer.RenderTransform> <RotateTransform x:Name="rotateTransform" Angle="0" /> </control:VisualContainer.RenderTransform> </control:VisualContainer> <Button HorizontalAlignment="Center">Rotate <Button.Triggers> <EventTrigger RoutedEvent="Button.Click"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard TargetProperty="Angle"> <DoubleAnimation Storyboard.TargetName="rotateTransform" From="0" To="360"
                                                    Duration="0:0:5"/> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Button.Triggers> </Button>

Of course I couldn’t resist rotating the whole thing too

RotatingDrawingVisuals

Validation Panel

The panel I’ve been working on to display rich validation messages is slowly coming along. I spent quite a bit of effort over the last two nights making it a well behaved “lookless” control.

The panel itself is simply a class derived from ItemsControl – has no XAML of its own. I then have a Themes/Generic.xaml file that contains the default rendering for the control 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:Spencen.Windows.Controls.Validation">
   <Style TargetType="{x:Type local:ValidationPanel}">
        <!-- Default property values -->
        <Setter Property="Background" 
Value="{DynamicResource {x:Static SystemColors.InfoBrushKey}}"/> <Setter Property="BorderBrush"
Value
="{DynamicResource {x:Static SystemColors.InfoTextBrushKey}}"/> <Setter Property="BorderThickness" Value="1.5"/> <Setter Property="Margin" Value="2,4"/> <Setter Property="Padding" Value="1"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:ValidationPanel}"> <Border Background="{Binding RelativeSource={RelativeSource TemplatedParent},
Path
=Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Margin="{TemplateBinding Margin}" CornerRadius="2"> <ItemsPresenter Margin="{TemplateBinding Padding}"/> </Border> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate DataType="validation:ValidationContent"> <Grid Margin="4"> <Grid.Resources> <local:SeverityToImageConverter x:Key="imageConverter"/> </Grid.Resources> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Image Source="{Binding Path=Severity,
Converter
={StaticResource imageConverter}}"
Stretch="None" Margin="0,0,2,0"/> <TextBlock Text="{Binding Path=MessageText}" Grid.Column="1"/> <TextBlock Text="{Binding Path=MessageDetailText}"
Foreground
="DarkGray"
FontStyle
="Italic"
Grid.Column
="1" Grid.Row="1" TextWrapping="Wrap"/> </Grid> </DataTemplate> </Setter.Value> </Setter> <Setter Property="ItemsPanel"> <Setter.Value> <ItemsPanelTemplate> <StackPanel/> </ItemsPanelTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>

This displays the panel as shown below which is fairly standard for a message display area.

ThemedStatusPanel

I then went ahead and created a customised style that changes not just colours but also changes the layout a little. I finally came up with this “vista-ish” look – although its probably a little overdone for real-world use. In this version the secondary text (which in the contrived example is a little redundant) is displayed via a popup invoked by the Help image button.

StyledStatusPanel

Now I’m starting on some custom validation rules. One of the things that I want to achieve is to have the rules specify a set of optional commands. These commands will then be used by the Validation Panel to build a context menu (configurable UI of course). Example commands might be focus to control in error, apply default etc.

Since the look of each item in the panel is determined by a DataTemplate which itself is bound to a type – I also want to try creating a derived ValidationContent class so that I can try multiple item UIs within the same panel. This might be useful for example in displaying an “acknowledgement” panel that places the form “in error” until the user has acknowledged the message via a CheckBox. Could be used in scenarios like the typical “I acknowledge that I’ve read your 20 page EULA” message that is often displayed in installers.

WPF Validation Rules

Wow – just reading the section on Advanced DataBinding in the WPF Unleashed book. This talks about how to do validation during data binding – specifically by creating custom classes that inherit from ValidationRule and return a ValidationResult. Apart from being very déjà-vu – because at work we have our own validation rules engine that uses exactly the same class names it also sounds a little odd. Having a ValidationRules collection hanging right off the Binding itself – is that really the best place to define the rules?

My experience with our work validation engine is that certainly many of the rules – the simple ones – are actually derived directly from the binding. Therefore it would make sense to have them directly associated. Rules such as “is the field mandatory”, “what is the fixed range”, “what is the maximum text length”, “what is the precision associated with a number”. But what about inter-field rules such as “field A must be greater than field B”, “field C is mandatory when field D is set to value x”. Those are really entity based rules – not field based. Do we/could we put them at the DataBinding level?

I guess I’ll have to keep reading to find out :-p

————————

Ok – so now I’ve given this a quick test. To start with I’ve just created a simple class to bind to (Person) and hooked up a TextBox.

    <TextBox SpellCheck.IsEnabled="True">
        <TextBox.Text>
            <Binding Source="{StaticResource nos}" Path="FirstName" 
UpdateSourceTrigger="PropertyChanged"> <Binding.ValidationRules> <valid:MandatoryRule/> </Binding.ValidationRules> <Binding.NotifyOnValidationError>true</Binding.NotifyOnValidationError> </Binding> </TextBox.Text> </TextBox>

Ok – the spell checking was just for fun.

Pretty longwinded isn’t it. Setting the NotifyOnValidationError means that I can hook my Status Panel up to the events so that entries are added and removed. The ValidationErrorEventArgs has a host of properties that allows me to determine exactly which Binding and UI element. Also, because the error content is an object I could provide my own Error class – so instead of just returning a ValidationResult with a string (as per example below) I could generate a rich error instance (with primary/secondary text, priority, help URL etc.)

My MandatoryRule is as simple as can be.

    public class MandatoryRule : ValidationRule
    {
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (value == null || object.Equals(value, string.Empty))
return new ValidationResult(false, "The field is mandatory.");
else
                return ValidationResult.ValidResult;
}
}

Hopefully I will put all this together and post soon with a working example of the Status Panel, together with some ideas on other features that could be included to make it more functional – such as clicking on a message to focus the associated UI element.

Off Track

Ok – I’ve managed to get a little side-tracked – there are just so many things rummaging around in my head at the moment.

This one popped into (virtual) reality the other day – a WPF based application notification area (status panel). The idea is that the panel interacts with a validation engine which governs all validation on a given page. It would also be related to a WPF equivalent of ErrorProvider. The WinForms ErrorProvider was kinda cool for doing simple demos but in real apps I always thought it was a bit of a joke. I’m aiming here to produce “notifications” that have associated metadata – primary text, secondary text (the long version), actions (e.g. intellisense options, corrective measures), locators (e.g. click to focus on the bad control) etc.

Based on previous experience most of this is really easy – its the rules validation engine that gets tricky – especially when table validation is required (e.g. grids).

 StatusPanel v0.1