Nov 21 2009

Silverlight 3 Validation Workaround

Silverlight 3 brings cool validation futures. But still it has some pitfalls. The main problem is the way how errors appear. Right now the only way we have to indicate error state of the control is to throw exception during data binding. Assume we have next data class:

public class DataClass
{
    private String _value;
    public String Value
    {
        get { return _value; }
        set { _value = value; }
    }
}

Lets add simple validation logic to it. We will checking for blank characters in a passed value. If blank characters are present then we will throw Exception with error description message.

public class DataClass
{
    private String _value;
    public String Value
    {
        get { return _value; }
        set
        {
            if (value.Length > 0 && value.IndexOf(' ') > 0)
            {
                throw new Exception("Value should not contain blank characters");
            }
            _value = value;
        }
    }
}

And if we have data binding defined:

<TextBox x:Name="exceptionInSetterBox" Text="{Binding Value, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>

then, if user enter a string with space characters the text box will take invalid state with error message in tooltip.

But what if you want manually set error state for particular control or perform async validation?

Well, every control has an Errors attached property. This property holds ReadOnlyObservableCollection<ValidationError> collection. Cool! We could create it by ourself and just set it throw SetValue method.

But... no :( we can't

The problem is in the ValidationError class. Simply it does not have public constructor available.

But, don't worry we will find workaround ;) (yes, ValidationError class also is marked as sealed, so we can't inherit from it)

If we can't instantiate ValidationError class then we could allow Silverlight to do it for us :)

The idea is to raise exception in some databound property of some Control (let's say ValidationErrors fabric) and grab ValidationErrors from there.

public class ValidationErrorBuilder: Control
{
    public static ReadOnlyObservableCollection<ValidationError> GetValidationErrors(String errorMessage)
    {
        ValidationErrorBuilder eb = new ValidationErrorBuilder();
        return GetErrors(eb, errorMessage);
    }

    private static readonly DependencyProperty ErrorProviderProperty =
        DependencyProperty.Register("ErrorProvider", typeof(ErrorProvider), typeof(ValidationErrorBuilder), new PropertyMetadata(null));

    private static ReadOnlyObservableCollection<ValidationError> GetErrors(Control control, string errorMessage)
    {
        var validationHelper = new ErrorProvider(errorMessage);

        control.SetBinding(ErrorProviderProperty, new Binding("ValidationError")
        {
            Mode = BindingMode.TwoWay,
            NotifyOnValidationError = false,
            ValidatesOnExceptions = true,
            UpdateSourceTrigger = UpdateSourceTrigger.Explicit,
            Source = validationHelper
        });

        control.GetBindingExpression(ErrorProviderProperty).UpdateSource();

        return (ReadOnlyObservableCollection<ValidationError>)control.GetValue(Validation.ErrorsProperty);
    }

    #region Nested type: ErrorProvider

    public class ErrorProvider
    {
        private readonly string _message;

        public ErrorProvider(string message)
        {
            _message = message;
            ThrowValidationError = true;
        }

        public bool ThrowValidationError { get; set; }

        [DebuggerNonUserCode]
        public object ValidationError
        {
            get { return null; }
            set
            {
                if (ThrowValidationError)
                {
                    throw new Exception(_message);
                }
            }
        }
    }

    #endregion
}

Now we could use ValidationErrorBuilder class for errors creation. To make picture complete let’s add extensions methods SetErrorState and ClearErrorState to all controls.

public static class ValidationExtender
{
    public static void SetErrorState(this Control control, string message)
    {
        control.SetValue(Validation.ErrorsProperty, ValidationErrorBuilder.GetValidationErrors(message));
        control.SetValue(Validation.HasErrorProperty, true);
        VisualStateManager.GoToState(control, "InvalidUnfocused", true);
        control.GotFocus += ControlGotFocus;
        control.LostFocus += ControlLostFocus;
    }

    static void ControlLostFocus(object sender, RoutedEventArgs e)
    {
        var control = sender as Control;
        if (control != null && (Boolean)control.GetValue(Validation.HasErrorProperty))
        {
            VisualStateManager.GoToState(control, "InvalidUnfocused", true);
        }
    }

    static void ControlGotFocus(object sender, RoutedEventArgs e)
    {
        var control = sender as Control;
        if (control != null && (Boolean)control.GetValue(Validation.HasErrorProperty))
        {
            VisualStateManager.GoToState(control, "InvalidFocused", true);
        }
    }

    public static void ClearErrorState(this Control control)
    {
        control.GotFocus -= ControlGotFocus;
        control.LostFocus -= ControlLostFocus;
        control.SetValue(Validation.ErrorsProperty, null);
        control.SetValue(Validation.HasErrorProperty, false);
        VisualStateManager.GoToState(control, "Valid", true);
    }
}

As result we will have:

Download validation library solution (VS 2010): AndrewVeresov.ValidationWorkaround v1.1

Oct 20 2009

Resharper 5.0 First Public Build is now available

Uhh, it's a hot day: first the visual studio 2010 beta 2 has came out and now JetBrains released first public available build of the ReSharper 5.0 :)

Here is download page: http://www.jetbrains.net/confluence/display/ReSharper/ReSharper+5.0+Nightly+Builds

What's New in ReSharper 5.0

  • External Sources
  • Structured Patterns
  • Project Refactorings
  • Call and Value Tracking
  • Internationalization
  • Visual Studio 2010 support (coming soon)
  • ASP.NET markup support
  • ASP.NET MVC support
  • Intellisense improvements
  • Bookmarks
  • Inspect project/folder
  • Upgrade-to-LINQ analysis
  • Native NUnit support
  • Xml Formatting

Here you could find more info about R# 5.0 futures: ReSharper 5.0 Overview

Oct 20 2009

Visual Studio 2010 beta 2 - Web Installer

Hi, now it is just 20th of October... but VS 2010 beta 2 will be available only at 21th :(

Can't wait? Ok, I have time machine for you:

Microsoft Visual Studio 2010 Ultimate Beta 2 — ENU (Web Installer)
http://go.microsoft.com/fwlink/?LinkId=151233

You are welcome! :)

Important: Remove ASP.NET MVC 1.1 beforeinstallation. If it does not want to uninstall saying "You first need to uninstall Microsoft Visual Studio 2010 Tools for MVC 1.1, then uninstall ASP.NET MVC 1.1 (this is the runtime component)" then delete next registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\MVCTools 1.1 (if you run 64bit OS then that key will be under HKLM\SOFTWARE\Wow6432Node node)

P.S.

If you have problems with TFSObjectModel during uninstalling VS 2010 beta 1 then just uninstall it from Programs and Futures manually ;)

 

It's a great day! :)

Oct 19 2009

Visual Studio 2010 beta 2 is out!!!

Hi all!

Greate news: visual studio 2010 beta 2 is ready for download!

Currently it is available only for MSDN subscribers. At October 21st Microsoft will push it to public.

Great news!