Jun 08 2010

Initialization State Manager

Often you need to run some initialization operations during application startup or control creation. Assume you can’t show or enable UI till all operations complete. In this case you need some code to watch for server requests and raise some ‘InitializationCompleted’ event when all requests are done.

This piece of code should store and manage current state (which operations are in Running state and which one are completed) somehow. And it is better if state management code will be reusable.

This is exactly what I gonna  to build for you.

I’d like the idea to use enum to define initialization phases:

[Flags]
public enum InitializationPhases: uint
{
	None = 0,
	MachineSeetingsLoad = 1,
	UserSettingsLoad = 2
}

Please note that I use Flags attribute on the enum type. This allow us to use bitwise operations on enum values.

The idea is to set particular bit to ‘1’ when operation starts and mark as ‘0’ when operation completes. If all bits become ‘0’ then all operations will be completed.

To make code reusable I’d like to create generic type. Unfortunately we can’t  define enum constraint for generic type parameter. But enum is also a structure so at least we could constraint generic parameter to structures.

Enum type support IConvertable interface. Which allow us to perform conversion to uint value and then we could do bitwise operations with that value. The only moment we should be sure about is that generic type is created with enum generic parameter. To check it we will use .NET  reflection.

public class InitializationStateManager<T> : INotifyPropertyChanged
    where T : struct, IConvertible
{
    private UInt32 _initializationPhasesInRun;

    static InitializationStateManager()
    {
        Type t = typeof(T);

        if (!t.IsEnum) throw new ArgumentException("T must be an enumerable type");

        bool flagsAttributeFound = false;
        foreach (object attribute in t.GetCustomAttributes(false))
        {
            flagsAttributeFound = attribute is FlagsAttribute;
            if (flagsAttributeFound) break;
        }
        if (!flagsAttributeFound) throw new ArgumentException("T must be marked with Flags attribute");
    }

    public bool IsInitialized
    {
        get { return _initializationPhasesInRun == 0; }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

    protected void OnPropertyChaneged(String propertyName)
    {
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public void MarkPhaseCompleted(T phase)
    {
        _initializationPhasesInRun &= ~phase.ToUInt32(null);
        OnPropertyChaneged("IsInitialized");
    }

    public void MarkPhaseStarted(T phase)
    {
        _initializationPhasesInRun |= phase.ToUInt32(null);
    }
}

In constructor we check what generic type parameter is enum and marked with Flags attribute.

Two methods MarkPhaseStarted and MarkPhaseCompleted are used to indicate that initialization phase started or completed. When all initialization phases will be completed the PropertyChanged event will be raised.

There are still some missed functionality:

  1. Code is not thread safe
  2. Probably it should be a Start() method to indicate that all initialization phases are started, to avoid the situation when some phase will complete before other one start
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

Jun 04 2009

Optimize PNG image or just replace it with silverlight content to minimize page size.

Today I’ve opened my first blog post and firebug showed me that page size is almost 150KB. No, no, no.. too much for a simple page with a couple of lines of text. 

So, I decided to optimize my blog load time. I found that the most traffic consuming part is a blog skin. It's a really huge.

But if you look at it you will fined that it is simple enough to be created as vector graphic. Great, I could use my favorite Silverlight to replace skin images and backgrounds with it!!!

Lets start!

Prerequisites

  1. I will use Visual Studio 2010 Beta for web development
  2. Expression Blend 3 to create a Silverlight control
  3. And of course Silverlight 3 Beta SDK

Picture

Lets take this cute 'Web 2.0' image as our first patient Smile

Really I'm not so good designer Smile so I will use a simplest technic to create 'clone' of this picture. So, lets open Expression Blend 3 and create a new project named Web20Image:

 

 Then add new UserControl named 'Web20.xaml'. Now we are ready to 'clone' our picture.

I put source 'Web 2.0' image to the canvas of the created UserControl, so that I could use it as a base:

 

Then with Pen tool I will add a polyhedron.

Remark: There is no build in polyhedron in Blend so I need to create it manually

 

Now we need to make sides of the polyhedron round. To do it just hold 'alt' key, press left button on middle of side and drug it out.

 

We could remove source 'Web 2.0' image now case we don't need it anymore.
The next step is to color our shape, set border thikness and etc. At the end we should have shape like this:

 

 In the source picture was a gradient area with bevelled side. To create it simply copy shape and past it again. Then remove unnessary vertexes and make a bevel. Set gradient for border and background of the shape. At the end you should get this:

 

The only thing left is a text 'web 2.0'. I don't have a font which was used to create initial picture and I don't want to enlarge size of xap file by adding external font in it. I will use standard font instead. I'd like 'Times New Roman' font.

Also I'd like to add drop shadow effect to the text to make it look same as on source picture. You coulf find it in Miscellaneous section (showed in red ellipse on picture below).

Great! Most difficult part is done and we have silverlight user control which looks like below:

 

If we will compile project now then we find that the size is pretty big. Ok, don't worry we will optimize it.

XAP Optimization

Lets look at xaml code which we got for our shape.

 

   1:  <UserControl
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:      mc:Ignorable="d"
   7:      x:Class="Web20Image.Web20"
   8:      d:DesignWidth="640" d:DesignHeight="480">
   9:      <Canvas>
  10:          <Path Stretch="Fill" 
  11:              Data="M167.83333...125.16666 z" 
  12:              StrokeThickness="2">
  13:              <Path.Stroke>
  14:                  <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  15:                      <GradientStop Color="#FFCBE248"/>
  16:                      <GradientStop Color="#FF84A718" Offset="1"/>
  17:                  </LinearGradientBrush>
  18:              </Path.Stroke>
  19:              <Path.Fill>
  20:                  <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  21:                      <GradientStop Color="#FFE1EF97" Offset="0"/>
  22:                      <GradientStop Color="#FFDBEA85" Offset="1"/>
  23:                  </LinearGradientBrush>
  24:              </Path.Fill>
  25:          </Path>
  26:          <Path Stretch="Fill" StrokeThickness="2" 
  27:              Data="M235.95343...119.59759 z" Canvas.Top="17.333" Canvas.Left="16" 
  28:              Visibility="Visible">
  29:              <Path.Fill>
  30:                  <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  31:                      <GradientStop Color="#FFC5DF33" Offset="0"/>
  32:                      <GradientStop Color="#FF84A718" Offset="1"/>
  33:                      <GradientStop Color="#FFB6D22D" Offset="0.691"/>
  34:                      <GradientStop Color="#FF94B51F" Offset="0.957"/>
  35:                  </LinearGradientBrush>
  36:              </Path.Fill>
  37:              <Path.Stroke>
  38:                  <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  39:                      <GradientStop Color="#FFC5DF33" Offset="0"/>
  40:                      <GradientStop Color="#FF84A718" Offset="1"/>
  41:                      <GradientStop Color="#FFB6D22D" Offset="0.691"/>
  42:                      <GradientStop Color="#FF94B51F" Offset="0.957"/>
  43:                  </LinearGradientBrush>
  44:              </Path.Stroke>
  45:          </Path>
  46:          <TextBlock Text="web" FontSize="24" Canvas.Top="22" Canvas.Left="25"
  47:              FontFamily="Times New Roman" FontWeight="Bold"
  48:              Foreground="#FFFFFFFF" TextAlignment="Center">
  49:              <TextBlock.Effect>
  50:                  <DropShadowEffect ShadowDepth="1" BlurRadius="1" Color="#FF8B8989"/>
  51:              </TextBlock.Effect>
  52:          </TextBlock>
  53:          <TextBlock Text="2.0" FontSize="32" Canvas.Top="39" Canvas.Left="26"
  54:              FontFamily="Times New Roman" FontWeight="Bold"
  55:              Foreground="#FFFFFFFF" TextAlignment="Center">
  56:              <TextBlock.Effect>
  57:                  <DropShadowEffect ShadowDepth="1" BlurRadius="1" Color="#FF8B8989"/>
  58:              </TextBlock.Effect>
  59:          </TextBlock>
  60:      </Canvas>
61: </UserControl>

 

We see a lot of code here. XAML will be compeleted to the byte representation called BAML but it will have the same structure as XAML so we need to clean-up code of our user control to make it as less as possible. Lets go throw the code now: 

  1. First of all we need to remove lines 6, 7 and 8 case they usefull only for Blend designer
  2. Next Path.Fill and Path.Stroke on lines 29-36 and 37-44 are the same. We could move them to resources
  3. Same as 2. could be done with TextBlock.Effect sections, lines 49-51 and 56-58
  4. And at the and both text boxes has pretty same parameters lets extract them to resources also

At the end we will have next xaml: 

   1:  <UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   2:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   3:      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   4:      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
   5:      <Canvas>
   6:          <Canvas.Resources>
   7:              <LinearGradientBrush x:Key="darkPartBrush" 
   8:                  EndPoint="0.5,1" StartPoint="0.5,0">
   9:                  <GradientStop Color="#FFC5DF33" Offset="0"/>
  10:                  <GradientStop Color="#FF84A718" Offset="1"/>
  11:                  <GradientStop Color="#FFB6D22D" Offset="0.691"/>
  12:                  <GradientStop Color="#FF94B51F" Offset="0.957"/>
  13:              </LinearGradientBrush>
  14:              <DropShadowEffect x:Key="textShadowEffect" ShadowDepth="1" BlurRadius="1" 
  15:                  Color="#FF8B8989"/>
  16:              <Style TargetType="TextBlock" x:Key="textStyle">
  17:                  <Setter Property="FontFamily" Value="Times New Roman"/>
  18:                  <Setter Property="FontWeight" Value="Bold"/>
  19:                  <Setter Property="Foreground" Value="#FFFFFFFF"/>
  20:                  <Setter Property="TextAlignment" Value="Center"/>
  21:                  <Setter Property="Effect" Value="{StaticResource textShadowEffect}"/>
  22:              </Style>
  23:          </Canvas.Resources>
  24:          <Path Stretch="Fill" Data="M167.83333...125.16666 z" StrokeThickness="2">
  25:              <Path.Stroke>
  26:                  <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  27:                      <GradientStop Color="#FFCBE248"/>
  28:                      <GradientStop Color="#FF84A718" Offset="1"/>
  29:                  </LinearGradientBrush>
  30:              </Path.Stroke>
  31:              <Path.Fill>
  32:                  <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  33:                      <GradientStop Color="#FFE1EF97" Offset="0"/>
  34:                      <GradientStop Color="#FFDBEA85" Offset="1"/>
  35:                  </LinearGradientBrush>
  36:              </Path.Fill>
  37:          </Path>
  38:          <Path Stretch="Fill" Fill="{StaticResource darkPartBrush}" 
  39:              Stroke="{StaticResource darkPartBrush}" StrokeThickness="2" 
  40:              Data="M235.95343...119.59759 z" Canvas.Top="17.333" Canvas.Left="16"/>
  41:          <TextBlock Text="web" Style="{StaticResource textStyle}" 
  42:              FontSize="24" Canvas.Top="22" Canvas.Left="25"/>
  43:          <TextBlock Text="2.0" Style="{StaticResource textStyle}" 
  44:              FontSize="32" Canvas.Top="39" Canvas.Left="26"/>
  45:      </Canvas>
  46:  </UserControl>
 

 

Looks better now

The next step will be to remove unessary files (which makes no sense for us) from a project. It's next files:

Next step will be to remove all unessary code and comments from app.xaml.cs and web20.xaml.cs. Don't forget to replace 'this.RootVisual = new MainControl();' with 'this.RootVisual = new Web20();' in app.xaml.cs in OnStartup method.

Now we are done. Choose Release and build the project.

Repack XAP

Really XAP file is a simple zip file and during compilation not the best method of compression is used. So unpack files from Web20Image.xap file and repack them againg using highest zip compression.

After repackprocedure I got ~3KB XAP file. PNG picture was ~5,5KB in size. Not bad for such small image Smile

Let's embede XAP file into website skin.

Embede Silverlight plugin into website

It's pretty simple step.

Create ClientBin folder at root of the web site. Copy Web20Image.xap file into that folder.

Add Silverlight.js file to web site. Add reference for it in the site.master headers section.

Then add javascript procedure for errors handling:

   1:  <script type="text/javascript">
   2:          function onSilverlightError(sender, args) {
   3:              if (args.ErrorCode == 8001) {
   4:                   var msg = "This sample application was built with Silverlight 3 Beta.\n";
   5:                   msg += "You currently have Silverlight installed, but not the version required to view this sample.\n\n";
   6:                   msg += "For more information about Silverlight 3, please visit: \n";
   7:                   msg += "<a href=\"http://go.microsoft.com/fwlink/?LinkID=141205\">Silverlight 3 Coming Soon</a>.";
   8:   
   9:                   var hostContainer = document.getElementById("silverlightControlHost");
  10:                   hostContainer.innerHTML = msg;
  11:               }
  12:              
  13:              var appSource = "";
  14:              if (sender != null && sender != 0) {
  15:                  appSource = sender.getHost().Source;
  16:              }
  17:              var errorType = args.ErrorType;
  18:              var iErrorCode = args.ErrorCode;
  19:   
  20:              var errMsg = "Unhandled Error in Silverlight 2 Application " + appSource + "\n";
  21:   
  22:              errMsg += "Code: " + iErrorCode + "    \n";
  23:              errMsg += "Category: " + errorType + "       \n";
  24:              errMsg += "Message: " + args.ErrorMessage + "     \n";
  25:   
  26:              if (errorType == "ParserError") {
  27:                  errMsg += "File: " + args.xamlFile + "     \n";
  28:                  errMsg += "Line: " + args.lineNumber + "     \n";
  29:                  errMsg += "Position: " + args.charPosition + "     \n";
  30:              }
  31:              else if (errorType == "RuntimeError") {
  32:                  if (args.lineNumber != 0) {
  33:                      errMsg += "Line: " + args.lineNumber + "     \n";
  34:                      errMsg += "Position: " + args.charPosition + "     \n";
  35:                  }
  36:                  errMsg += "MethodName: " + args.methodName + "     \n";
  37:              }
  38:   
  39:              throw new Error(errMsg);
  40:          }
  41:      </script>

 

And now place Silverlight Control Host definition:

 

   1:  <div id="web-20">
   2:      <div id="silverlightControlHost">
   3:          <object data="data:application/x-silverlight-2," 
                           type="application/x-silverlight-2" width="95" height="98">
   4:              <param name="source" value="../../ClientBin/Web20Image.xap"/>
   5:              <param name="onerror" value="onSilverlightError" />
   6:              <param name="minRuntimeVersion" value="3.0.40307.0" />
   7:              <param name="background" value="#00FFFFFF" />
   8:              <param name="windowless" value="true" /> 
   9:              <param name="autoUpgrade" value="false" />
  10:          </object>
  11:          <iframe style='visibility:hidden;height:0;width:0;border:0px'></iframe>
  12:      </div>
  13:  </div>

This code is almost usual, but there is some special moment which I need to highlite a little bit.

First thing is that I use Silverlight 3 beta. It's not release version and by default if user has Silverlight 2 installed then he will be forwarded to latest release version download page. Which is actualy Silverlight 2...

To avoid this redirection we set 'minRuntimeVersion' to "3.0.40307.0" (version of the Silverlight 3 beta), 'autoUpgrade' option to false and point 'onerror' to the our javascript function for errors handling. At the begin of the function you could find next block of code:

   1:  if (args.ErrorCode == 8001) {
   2:       var msg = "This sample application was built with Silverlight 3 Beta.\n";
   3:       msg += "You currently have Silverlight installed, but not the version required to view this sample.\n\n";
   4:       msg += "For more information about Silverlight 3, please visit: \n";
   5:       msg += "<a href=\"http://go.microsoft.com/fwlink/?LinkID=141205\">Silverlight 3 Coming Soon</a>.";
   6:   
   7:       var hostContainer = document.getElementById("silverlightControlHost");
   8:       hostContainer.innerHTML = msg;
   9:   }

The 8001 code mean that version of the Silverlight installed on a client computer is lower then 'minRuntimeVersion' value (in our case it is "3.0.40307.0"). In that case message box with description of the problem and with a link to Silverlight download page will be shown to the user.

Summary

Congratulations, now we have page element which looks same as source picture but written with help of Silverlight and has less size then initial image. Sure, 2KB is a not so big difference and added silverlight.js file adds some more kilobytes to our page. But we need to include js file only onec and many silverlight controls will be able to reuse it.

May 23 2009

Silverlight 2.0: usage of the converters from external assembly in app.xaml

Hi all,

it is my first post and I'd like to strat it from a small trick for defining value converters in app.xaml file in silverlight project.

Problem:

If you define Converters in app.xaml (see below) then you find that Visual Studio designer fails to render User Controls (Visual Studio designer become blank).

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:myconverters="clr-namespace:MyCustomConverters;assembly=
MyCustomConverters"
             x:Class="ConverterInAppXaml.App"> 
    <Application.Resources>
        <ResourceDictionary >
            <myconverters:MyCustomTextConverter x:Key="myTextConverter"/>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Upd: not the converter defenition in app.xaml file cause the visual studio designer to go blank but xmlns defenition.

Solution:

To avoid this designer problem move namespace declaration closer to usage:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             x:Class="ConverterInAppXaml.App"> 
    <Application.Resources>
        <ResourceDictionary xmlns:myconverters="clr-namespace:MyCustomConverters;
assembly=MyCustomConverters"
>
            <myconverters:MyCustomTextConverter x:Key="myTextConverter"/>
        </ResourceDictionary>
    </Application.Resources>
</Application>

 

Voila!!! now designer work perfect and able to render our content.