# Friday, 20 February 2009

On Wednesday I posted about coming up with a way of decorating any UIElement with a red border in response to a validation error.  The only drawback (that I could see just then) was that I had to add borders to all the elements that I might want to exhibit this behavior.  Today Shaun suggested that I try it as an attached property instead, which might save that hassle.  Indeed it does.  Because of the way attached properties work in Silverlight, it’s possible to use them to attach behavior as well as data be hooking the value changed event.  Here’s the same (basically) logic as an attached property

public class ValidationService
{
    public static readonly DependencyProperty ValidationBehaviorProperty =
        DependencyProperty.RegisterAttached("ValidationBehavior", typeof(bool), typeof(ValidationService),
                                    new PropertyMetadata(OnValidationBehaviorChanged));

    public static bool GetValidationBehavior(DependencyObject d)
    {
        return (bool)d.GetValue(ValidationBehaviorProperty);
    }

    public static void SetValidationBehavior(DependencyObject d, bool value)
    {
        d.SetValue(ValidationBehaviorProperty, value);
    }

    private static void OnValidationBehaviorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is FrameworkElement)
        {
            FrameworkElement fe = d as FrameworkElement;

            if ((bool)e.OldValue)
            {

            }
            if ((bool)e.NewValue)
            {
                fe.BindingValidationError += new EventHandler<ValidationErrorEventArgs>(fe_BindingValidationError);
            }
        }
    }

    static void fe_BindingValidationError(object sender, ValidationErrorEventArgs e)
    {
        FrameworkElement fe = e.OriginalSource as FrameworkElement;
        DependencyObject parent = fe.Parent;
        Border b = null;

        if (parent is Grid)
        {
            b = new Border() { BorderBrush = new SolidColorBrush(Colors.Red), BorderThickness = new Thickness(0) };

            Grid g = parent as Grid;
            g.Children.Add(b);
            int column = (int)fe.GetValue(Grid.ColumnProperty);
            b.SetValue(Grid.ColumnProperty, column);
            int row = (int)fe.GetValue(Grid.RowProperty);
            b.SetValue(Grid.RowProperty, row);
            g.Children.Remove((UIElement)fe);
            b.Margin = fe.Margin;
            fe.Margin = new Thickness(0);
            b.Child = (UIElement)fe;
        }
        else if (parent is Border)
        {
            b = parent as Border;
        }

        if (e.Action == ValidationErrorEventAction.Added)
        {
            if (b != null)
            {
                b.BorderThickness = new Thickness(1);
            }
            ToolTipService.SetToolTip(fe, new TextBlock() { Text = e.Error.Exception.Message });
        }
        if (e.Action == ValidationErrorEventAction.Removed)
        {
            if (b != null)
            {
                b.BorderThickness = new Thickness(0);
            }
            ToolTipService.SetToolTip(fe, null);
        }
    }
}

Note that when registering the attached property, we register a value changed handler (in this case OnValidationBehaviorChanged).  This will get called the first time the property is set, which gives us an opportunity to mess with the element that the property is attached to.  If the value of the property is being set to true, we hook up the BindingValidationError handler.

In the validation error handler, we have to find out if the element is currently surrounded by a Border, and add one if it isn’t.  Note: in this example it will only work if the element is originally the child of a Grid.  This could easily be modified to support the other Panel types. 

Once the parent is a border, then the old logic from the previous example can be reused to set the border and tooltip on or off as appropriate. 

Notice that we don’t mark the BindingValidationError event as Handled so that it will bubble.  That allows us to write additional forms specific error handling higher up in the visual hierarchy.  At the form level we might want to invalidate the submit button if validation failed, or something similar, while letting the logic in the attached property handle the UI effects.

In the XAML, this new attached property can be used to attached the desired visual effect to any FrameworkElement like so

<TextBox Width="Auto" x:Name="txtNewHours"  Text="{Binding Hours, Mode=TwoWay, Converter={StaticResource hoursConverter}, 
    NotifyOnValidationError=true, ValidatesOnExceptions=true}" 
    my:ValidationService.ValidationBehavior="true"
    Grid.Row="1" Grid.Column="1" Margin="5,5,5,5"/>
Friday, 20 February 2009 15:32:07 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, 19 February 2009

Stuart posted a great solution he came up with for writing dynamic orderby queries in LINQ when you don’t know the “column” name that you want to sort by ahead of time.

Thursday, 19 February 2009 13:38:59 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 

I’m going to be teaching two new one-day courses on setting up a Continuous Integration process, one using freely available tools, and one using Microsoft Team Foundation Server.  For more details and to register, see our registration page.

Both classes will be held at the Westside campus of OIT

Course Description:

This one day course will provide a hands on look at setting up a Continuous Integration process using freely available tools from beginning to end. Students will learn the theory and practice behind establishing a Continuous Integration process, including setting up a build script, running unit tests, setting up an automated build/test server, and capturing reporting information for the whole process.

Course Technology

This course uses Microsoft Windows XP, the Microsoft .NET 3.5 Framework, NAnt, NUnit, and CruiseControl.NET.

At the end of the course you will be able to:

  • Create a build script using NAnt or MSBuild
  • Create and run unit tests using NUnit
  • Set up and run an automated build using CruiseControl.NET
  • Capture reporting data from the automated CI process

Course Outline
    Continuous Integration in Theory
    • Why CI is important
    • How CI fits into the Software Development process
    Creating a build script
    • What goes in a build script?
    • How does NAnt work?
    • How does MSBuild work?
    • Creating build scripts for NAnt or MSBuild
    • Running an automated build
    Adding unit tests to your build
    • Writing NUnit tests
    • Running tests as part of a build
    • Capturing test results
    Continuous Integration with CruiseControl.NET
    • Installing and configuring CC.NET
    • Adding projects to a build server
    • Reporting with CC.NET
    • Running multiple builds on the same server
        Dependencies between builds

Prerequisites

This class is intended for experienced .NET 2.0 software developers.

Thursday, 19 February 2009 12:37:26 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, 18 February 2009

I was having a bit of trouble getting validation to work during data binding in SL, but that seems to be resolved now.  I’ve got a little time tracking app, and want to be able to log new time entries…

CropperCapture[8]

I want the user to be able to enter hours only in 1/4 hour increments.  The way that gets handled in Silverlight is different from WPF.  To make it work in Silverlight the data binding source has the throw an exception if the new value is unacceptable.  In this case, the source is a “LogEntry” class that has a property “Hours” that implements INotifyPropertyChanged to support full two-way data binding.

public class LogEntry : INotifyPropertyChanged
{

    private decimal hours;
    public decimal Hours 
    {
        get
        {
            return hours;
        }
        set
        {
            if ((value % .25M) != 0)
            {
                throw new Exception("please enter time in quarter-hour increments");
            }
            if (hours != value)
            {
                hours = value;

                NotifyPropertyChanged("Hours");
            }
        }
    }

    private void NotifyPropertyChanged(string p)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(p));
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

In the property setting for the Hours property, if the value isn’t evenly divisible by .25 an Exception gets thrown.  In the XAML, the binding is set up to notify (as a binding error) when an exception gets thrown

 

<TextBox Width="Auto" x:Name="txtNewHours"  Text="{Binding Hours, 
    Mode=TwoWay, 
    Converter={StaticResource hoursConverter}, 
    NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>

The grid that holds all the controls handles the BindingValidationError event, which is where all the fun stuff happens…

private void newEntryGrid_BindingValidationError(object sender, ValidationErrorEventArgs e)
{
    if (e.Action == ValidationErrorEventAction.Added)
    {
        //not valid
    }
    if (e.Action == ValidationErrorEventAction.Removed)
    {
        //now it's valid
    }
}

OK, so now I know when the value isn’t valid, but what to do about it.  In WPF, the easiest thing to do would be to use a “decorator” to add a border around the UI element that failed validation, but in Silverlight we don’t get decorators.  Instead, I wrapped each item that needed to support validation in a red border with a thickness of 0 in XAML.

<Border BorderBrush="Red" BorderThickness="0">
    <TextBox x:Name="txtNewHours" Text="{Binding ...}"/>
</Border>

That’s a little extra work that I’d rather not do, but there it is.  It would be easy to wrap up this behavior in a custom control, but this was much quicker.

In the validation handler then, all we have to do is get the original source of the binding error and use the VisualTreeHelper to get it’s parent.  If that parent happens to be a border, set the border’s thickness to 1 to make it visible.  I’m also setting the tooltip for that offending control to be the message of the validation error.  If the ValidationErrorEventAction == Removed, then get rid of the tooltip and set the border’s thickness back to 0.

private void newEntryGrid_BindingValidationError(object sender, ValidationErrorEventArgs e)
{
    if (e.Action == ValidationErrorEventAction.Added)
    {
        DependencyObject ui = e.OriginalSource as DependencyObject 
        DependencyObject parent = VisualTreeHelper.GetParent(ui);
        if (parent is Border)
        {
            ((Border)parent).BorderThickness = new Thickness(1);
        }
        ToolTipService.SetToolTip(ui, new TextBlock() { Text = e.Error.Exception.Message });
    }
    if (e.Action == ValidationErrorEventAction.Removed)
    {
        DependencyObject ui = e.OriginalSource as DependencyObject 
        DependencyObject parent = VisualTreeHelper.GetParent(ui);
        if (parent is Border)
        {
            ((Border)parent).BorderThickness = new Thickness(0);
        }
        ToolTipService.SetToolTip(ui, null);
    }
}

CropperCapture[9]

That same code should work for any control that I want to do validation on, as long as I remember to add the borders in XAML. 

Wednesday, 18 February 2009 14:10:45 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Friday, 13 February 2009

In the wake of our Silverlight presentations this week, Shaun (CTO of SoftSource, and my boss) is going to be blogging on his demos, starting with Silverlight/JavaScript interactivity.  His demos were pretty cool, so I’m looking forward to reading his commentary.  I might just follow suit with a few examples too…

Friday, 13 February 2009 12:16:28 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 

Check out http://events.sftsrc.com/ for more new classes…

  • LINQ
    • 2 days of LINQ to Objects, LINQ to XML, LINQ to SQL, LINQ to Entities and more
  • ASP.NET
    • 5 days of ASP.NET including some AJAX and a hint of Silverlight
  • WPF
    • 3 days of WPF goodness
  • Silverlight
    • 2 days for those with WPF experience or
    • 3 days for web developers
  • C#  and the .NET Framework
    • 5 days of C# 3.0 and the 3.5 framework, including LINQ, extension methods and the rest of the new language features
Friday, 13 February 2009 10:17:36 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Friday, 06 February 2009

Google is great at providing 20/20 hindsight. 

The little problem I was having with insert triggers is (unsurprisingly) not uncommon.  I googled a bit after a suggestion in a comment (thanks El Guapo) and found a great explanation by Mike Taulty on how to deal with just such a concurrency problem.  He’s specifically dealing with a timestamp column, but the issue is essentially the same. 

Good to know…

Friday, 06 February 2009 09:39:15 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, 05 February 2009

I’ll be there, soaking up the Silverlight-y goodness. 

Thursday, 05 February 2009 14:56:38 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 

Lets say I have a nice simple database (guess which one…)

CropperCapture[5]

Now I want to insert a new Order into the Orders table

 

using (NorthwindDataContext context = new NorthwindDataContext())

{

    Order order = new Order();

    context.Orders.InsertOnSubmit(order);

    context.SubmitChanges();

}

No problem.  Totally works like you’d think.  What happens if I make more changes after that and call SubmitChanges() again?

using (NorthwindDataContext context = new NorthwindDataContext())

{

    Order order = new Order();

    context.Orders.InsertOnSubmit(order);

    context.SubmitChanges();

 

    order.Freight = 98.77M;

    order.ShipName = "Fred Jones";

 

    context.SubmitChanges();

}

Still cool.  The problem comes in if I add an insert trigger.  Let’s say I add a trigger to set the OrderDate column of the order to make sure it’s always the right date…

ALTER TRIGGER Trigger1

ON dbo.Orders

FOR INSERT

AS

UPDATE Orders

SET OrderDate = GETDATE()

FROM Orders INNER JOIN inserted ON Orders.OrderID = inserted.OrderID

Whether or not that’s a good idea is a whole ‘nother topic.  Seemed like the thing to do at the time…  However, now there’s a problem.  The first SubmitChanges() works fine, but the second one fails with an update conflict.  WTF?  After I stopped to think about it (and talked to Stuart) I realized that that makes perfect sense.  The copy of Order that I have in memory doesn’t know anything about the result of the insert trigger, and so the record is out of date, hence the conflict.  In this particular case it wasn’t a problem because it wasn’t too much work to just take out the trigger and set the date in the code.  I had been hoping that I wouldn’t have to redeploy the web app, hence the trigger.  Best laid plans…

Anyway, it turns out that something similar came up on another project, so it is something to be aware of.

Thursday, 05 February 2009 14:49:23 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [2]  | 

After reading Scott’s latest missive, I finally gave in and joined the Tweeple.

@pcauldwell

Thursday, 05 February 2009 13:04:23 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |