Monday, 12 October 2009

WPF Enum List Converter

Introduction

This article demonstrates how to bind an C# enum definition to a WPF combobox, which is not as simple as it first seems.
The solution was extracted from a real-world commercial application.

Background

enum definitions are great for defining small fixed lists of options for a property. They are easy to define in code and are strongly typed, so they help produce robust code. Unfortunately these advantages become disadvantages when working with WPF as enums are not suitable for XAML.
My data models tend to use a lot of enums to define class specific properties. I needed a way of putting the properties onto a form and combobox, or listbox controls are the perfect controls for this. The problem is that the control's list of items needs to be defined and the value of the enum data property needs to be bound  to the control's selected item.
I did not want to reference the combobox in the code behind class, I wanted the bindings be entirely defined on the XAML side.


Using the Code


The solution to the enum problem is implemented in two small C# classes described below.
The source code contains everything needed to build the example, including the test data, which is taken from the SQL Server Adventure Works sample database.  The example code also makes use of some collapse converters, which are described in my previous article.
The example application allows the user to choose a product from a treeview and then edit its name and choose its color on the form on the right.
The color enum is bound to three controls; combobox, listbox and textblock in order to demonstrate how the same technique across different controls.
public class Product : TWGenericDataObject
{
public enum eColor
{
None,
Black,
Light_Blue,
Blue,
DarkBlue,
Grey,
Multi,
Red,
Silver,
White,
Yellow
}
public eColor Color 


This is the relevant part of the data model definition. We need to put those list of colors into the combobox and bind the Color property to combobox's selected item.

Note the camel case and underscores that need to be removed.


The conversion is done by a custom converter object and the first thing we need to do is to define it in the Windows Resources section in XAML.


<Window.Resources>
<local:ProductColorListConverter x:Key="productColorListConverter"/>
</Window.Resources> 


Now we can use it inside our combobox definition


<ComboBox Grid.Column="1" Grid.Row="1"  Margin="2" Name="comboBoxColor" VerticalAlignment="Top"
ItemsSource="{Binding Source={StaticResource productColorListConverter}}"
SelectedIndex="{Binding ElementName=treeViewCategoriesAndProducts, Path=SelectedItem.Color,
Mode=TwoWay,
Converter={StaticResource productColorListConverter}}" /> 


Our converter is used in two different ways by the combox.

When bound to ItemsSource it returns a collection of strings for each value in the enum definition. This fills the combobox's items collection with human readable enum names.


When bound to SelectedIndex it acts as a value converter to convert the enum property both to and from the combobox selection.


The Listbox implementation works in exactly the same way as they both share the same parent class: Selector.


That is all there is to do on the XAML side. We are not quite done yet, as there is one line of code that does need to be defined on the C# side.


public class ProductColorListConverter : TWWPFUtilityLib.TWEnumListConverter<TWSampleWPFDatabase.Product.eColor> { } 



ProductColorListConverter is just a simple subclass of the generic class that does all the work. This has to be done in C#  as XAML  does not understand generic C# definitions.


How it works



public class TWEnumListConverter<TEnumType> : ObservableCollection<string>, IValueConverter
{ 
public TWEnumListConverter()
{
HumanizeConverter hc = new HumanizeConverter();
string[] names = Enum.GetNames(typeof(TEnumType));
foreach (string s in names)
Add(hc.Humanize(s));
} 



TWEnumListConverter is a generic class that takes the enum type as its template variable. The constructors converts the enum into a string array using standard GetNames method. The enum names might be in camel case or may contain underscores, so each name is passed through the HumanizeConverter (described below).  The humanized names are added to itself, as the class inherits from ObservableCollection. It is this that allows the class to be used directly as a source of a combobox's list items.


public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int v = (int)value;
return v;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int v = (int)value;
Array values = Enum.GetValues(typeof(TEnumType));
if ((v < 0) || (v >= values.Length))
v = 0;
TEnumType et = (TEnumType)values.GetValue(v);
return et;
}
} 


The other job of the class is to convert between the int type of the combobox's  SelectedIndex and the property holding the  enum value. Converting an int into an enum in a generic way takes more code that one might think. One of the benefits of this class is that it hides these details from the user.


As enum definitions cannot contain spaces, CamelCase and the use of underscores are the alternatives that most people use. The job of the HumanizeConverter class is to put the spaces back into those names.


public class HumanizeConverter : IValueConverter
{
Regex reUnderscore = new Regex(@"_", RegexOptions.Multiline | RegexOptions.CultureInvariant);
Regex reCamel = new Regex(@"[a-z][A-Z]", RegexOptions.Multiline | RegexOptions.CultureInvariant);
public static string SplitCamel(Match m)
{
string x = m.ToString();
return x[0] + " " + x.Substring(1, x.Length - 1);
}
public string Humanize(object value)
{
string s = null;
if (value != null)
{
s = value.ToString();
s = reUnderscore.Replace(s, " ");
s = reCamel.Replace(s, new MatchEvaluator(HumanizeConverter.SplitCamel));
}
return s;
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Humanize(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("unexpected Convertback");
}
} 


It does the work using two regular expressions.

As well as being used in the TWEnumListConverter, this class can also be used directly for example to bind an enum as the source of a TextBlock.


<TextBlock Grid.Column="1" Grid.Row="4" Margin="2" Name="textBlockColor" VerticalAlignment="Top"
Text="{Binding ElementName=treeViewCategoriesAndProducts, Path=SelectedItem.Color, Converter={StaticResource humanizeConverter}}" />



Conclusion



Sometimes C# strong type checking can get in the way of a simple solution, which maybe is why dynamic languages, such as Ruby, are gaining in popularity.

However it is often the case, as hopefully this article shows, that a small utility class can make things easy to do, without loosing the advantages of a strongly typed language.


License



Copyright (c) 2009 Tom F Wright. All rights reserved.

This code is distributed under the Microsoft Public License (Ms-PL).


Download



Exe file


Full Source code - VS2008

Thursday, 8 October 2009

WPF Collapse Converters for easier data layouts

Introduction

This article demonstrates how WPF Converters can be used in an innovate way to replace the use of  WPF Triggers in a common user interface task. The code contains a small set of self contained classes that can be easily added to any WPF project.

Background

A common design issue when presenting complex data is how much detail to display.  Too much detail and the user may get confused or find it more difficult to find the item that they are looking for. Too little and the user may not have all the information that they need. In the past many of these decisions were fixed at design time. Modern user interfaces dynamically hide and display additional data through the users actions.
This example uses a product list from a fictional bike company.
Products have a code, name, price, description, optional photo and a sale ended date.
The data is taken from the SQL Server Adventure Works sample database. All the data is contained in the code so there is no need to use an external database to run the example code.
The example application consists of a single window containing an ordinary list box filled with a list of objects from the Product class. A WPF DataTemplate is used to format each product in the list. This sort of layout can be found in many WPF samples and tutorials. What makes it more interesting is the checkboxes on the window that dynamically control how much information is displayed.
In the second screenshot the images and prices have been hidden and only the currently selected product has its description shown. There are additional automatic features as well. If an item has a blank Sale Ended date then the red line at the bottom is hidden and if an image is not available the image and its border are hidden.
The usual way of doing this
The data template contains contains controls to display each part of the product. Each UIElement  has a Visibility Property and setting that to the value Collapsed hides the element and forces its container to layout the other controls as if it was not there.  So all that is needed is a means for setting the Visibility property under the right circumstances.
The usual way to do this is with a DataTrigger, as show in this code example.
<TextBlock Margin="0,3,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="Auto" Height="Auto" TextWrapping="Wrap" FontStyle="Italic" TextDecorations="None"
Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" TextAlignment="Left" Text="{Binding Path=Description}" TextTrimming="WordEllipsis" FontSize="10" >
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem} }, Path=IsSelected}"
Value="False">
<Setter Property="TextBlock.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>



This is essentially a giant if statement that says if the parent item is not selected then hide the description. I do not like triggers because they add programming logic to the user interface, which is not a good thing. They are also over complicated, the trigger code above takes no account of the 'Always Display Descriptions' checkbox, to do so would need an even more complicated definition.


The Collapse Converters


The great thing about WPF is that there are usually several ways to achieve the same thing so I came up with an alternative way that uses converters instead of triggers.


Converters are used to convert one value type into another type during data binding. A classic case contained in the example code implements a converter that formats a C# DateTime into a string for display as the Sell End Data. Converters are C# classes that implement the IvalueConverter interface.


Before describing the collapse converters converters in detail I will show you how they are used.


public partial class CollapseConverterWindow : Window
{
public CollapseConverterWindow()
{
InitializeComponent();
listBoxProducts.ItemsSource = TWSampleWPFDatabase.SampleData.Instance.Products.Items;
}
} 


The code behind the example window is very minimal the only thing it does is initialize the Window and set the listbox's contents to an ObservableCollection of Product objects.  The product class and the same data is all contained in a separate sub-project that may be worth an article in itself, but all we need to know here is that the Product class has public properties for each item that we want to display.


<Window x:Class="TWCollapseConverters.CollapseConverterWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TWCollapseConverters"
Title="Collapse Converter Example" Height="419" Width="396" Loaded="Window_Loaded">
<Window.Resources>
<local:CollapseOnBlankConverter x:Key="collapseOnBlankConverter"/>
<local:CollapseOnBlanksConverter x:Key="collapseOnBlanksConverter"/>
<local:CollapseOnBlanksFirstPairOrConverter x:Key="collapseOnBlanksFirstPairOrConverter"/>
<local:DateToShortDateStringConverter x:Key="dateToShortDateStringConverter"/>
<DataTemplate x:Key="ProductTemplate">
.....
</DataTemplate>
</Window.Resources>
<Grid Background="LightGray">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="3" HorizontalAlignment="Center">
<CheckBox Height="16" Name="checkBox_AlwaysDisplayDescriptions" IsChecked="True">Always Display Descriptions</CheckBox>
<CheckBox Height="16" Margin="10,0,0,0" Name="checkBox_DisplayImages" IsChecked="True">Display Images</CheckBox>
<CheckBox Height="16" Margin="10,0,0,0" Name="checkBox_DisplayPrices" IsChecked="True">Display Prices</CheckBox>
</StackPanel>
<ListBox Grid.Row="1" Name="listBoxProducts"  ItemTemplate="{StaticResource ProductTemplate}" HorizontalContentAlignment="Stretch" />
</Grid>
</Window> 


The XAML window definition follows a simple layout, with the complexity contained in the DataTemplate. Instances of the converters are defined in the Windows.Resources section and then referenced in the DataTemplate.


Instead of listing the entire data template the parts that demonstrate each of the converts is shown below.

CollapseOnBlankConverter


The simplest of the three collapse converters takes a single value and converts it to either Visible or Collapse.


<StackPanel  Orientation="Horizontal" Visibility="{Binding ElementName=checkBox_DisplayPrices, Path=IsChecked, Converter={StaticResource collapseOnBlankConverter}}" Grid.ColumnSpan="2" HorizontalAlignment="Left">
<TextBlock Text="Price: $" Margin="4,0,0,0" Foreground="DarkGreen" FontWeight="Bold" />
<TextBlock Text="{Binding Path=ListPrice}" Margin="0,0,2,0" Foreground="DarkGreen" FontWeight="Bold" />
</StackPanel> 


In this example visibility of the container for the Price text is bound to the value of the 'Display Prices' checkbox. The IsChecked property returns a boolean value which cannot be directly bound to a visibility field, but by specifying collapseOnBlankConverter  as the Converter the check value will be converted into Visible or Collapse.


<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Visibility="{Binding Path=SellEndDate, Converter={StaticResource collapseOnBlankConverter}}">
<TextBlock Text="Sale Ended On: " Margin="0,3,3,0" Foreground="DarkRed" FontWeight="Bold" />
<TextBlock Text="{Binding Path=SellEndDate, Converter={StaticResource dateToShortDateStringConverter}}" Margin="0,3,0,0" Foreground="DarkRed" FontWeight="Bold" />
</StackPanel> 


In the second example the visibility is bound to the value of the date and this too is converted into Visible or Collapse.

How the Converter interprets its input gives rise to its name. If a value can be considered blank then it will return Collapse otherwise it returns Visible. So a boolean false, a null reference, an empty string, a blank date, or the number zero are all considered blank and so cause the element to be hidden.

CollapseOnBlanksConverter


The second collapse converter takes a list of inputs instead on a single input. Defining a multiple binding cannot be done via the inline notation in the previous example. Instead it has to be defined the verbose way.


<Border Margin="5" BorderThickness="1" BorderBrush="DarkGray" HorizontalAlignment="Center" VerticalAlignment="Top">
<Border.Visibility>
<MultiBinding  Converter="{StaticResource collapseOnBlanksConverter}">
<MultiBinding.Bindings>
<Binding ElementName="checkBox_DisplayImages" Path="IsChecked"  />
<Binding Path="ThumbNailPhotoAvailable"  />
</MultiBinding.Bindings>
</MultiBinding>
</Border.Visibility>
<Image  Source="{Binding Path=ThumbNailPhoto}" />
</Border> 


This example the visibility of the image border element is bound to both the 'Display Images' check box and the boolean property of the Product that indicates if an image is available.

The CollapseOnBlanksConverter checks that all its inputs and if any one is blank it will return Collapse. That way the image will be hidden if is not available or the user has turned images off with the checkbox.


There is no limit to the number of conditions that you can set in this way.


The Image element will not display anything is its source is not defined so this converter may look unnecessary. However the border will still be displayed around a blank image and anyway the Product class returns an image valid image.


CollapseOnBlanksFirstPairOrConverter


The third converter is a variation on the second in that it takes the combined value of the first two elements and compares those with the rest.

e.g. CollapseOnBlanksConverter => (A && B && C && D) and CollapseOnBlanksFirstPairOrConverter => ((A || B) && C && D)


<TextBlock Margin="0,3,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="Auto" Height="Auto" TextWrapping="Wrap" FontStyle="Italic" TextDecorations="None"
Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" TextAlignment="Left" Text="{Binding Path=Description}" TextTrimming="WordEllipsis" FontSize="10" >
<TextBlock.Visibility>
<MultiBinding  Converter="{StaticResource collapseOnBlanksFirstPairOrConverter}">
<MultiBinding.Bindings>
<Binding ElementName="checkBox_AlwaysDisplayDescriptions" Path="IsChecked"  />
<Binding Path="IsSelected" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem} }"   />
<Binding Path="Description"  />
</MultiBinding.Bindings>
</MultiBinding>
</TextBlock.Visibility>
</TextBlock> 


So in this example the logic is to always hide the description if it is blank and to show it if the item is selected or if the 'Always Display Descriptions' checkbox is ticked.

This does the same job and a lot more than the Trigger example given at the top of the article and is I think a lot easier to define.


The C# code


Now you have seen how they are used all that is left is to show you the Converters code implementation.

The code is in a single C# file TWCollapseConvertersLib.cs, which is part of the main project. It could be put in its own library but its too small to bother.


CollapseOnBlanksConverter


public class CollapseOnBlankConverter : IValueConverter
{
static public bool IsBlank(object value)
{
if ((value == null) ||
((value is bool) && ((bool)value == false)) ||
((value is string) && (((string)value).Length == 0)) ||
((value is int) && (((int)value) == 0)) ||
((value is double) && (((double)value) == 0)) ||
((value is decimal) && (((decimal)value) == (decimal)0)) ||
((value is System.DateTime) && ((System.DateTime)value == System.DateTime.MinValue))
)
return true;
return false;
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (CollapseOnBlankConverter.IsBlank(value))
return System.Windows.Visibility.Collapsed;
return System.Windows.Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("unexpected Convertback");
}
} 


The static IsBlank method interprets whether the input is blank or not. The value is passed in a an object so it has to determine its type and then  perform the relevant comparison. If you are using a type not listed here then its a simple matter to add a new clause to the list.


The IvalueConverter requires we define conversion going both ways. We never need to convert back so an exceptions has been added as a sanity check.


CollapseOnBlanksConverter

This converter implements the multi value interface and so loops through the given list.


public class CollapseOnBlanksConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
foreach (object v in values)
if (CollapseOnBlankConverter.IsBlank(v))
return System.Windows.Visibility.Collapsed;
return System.Windows.Visibility.Visible;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("unexpected Convertback");
}
} 


CollapseOnBlanksFirstPairOrConverter

This converter is the most complex of the three as it has to treat the first two values differently, so the code is a little more complex.

However it is so much easier to Implement complex logic in C# than it is in XAML using Triggers.


public class CollapseOnBlanksFirstPairOrConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
for (int i = 0; i < values.Length; i++)
{
if ((i==0) && (values.Length > 1))
{
if ((CollapseOnBlankConverter.IsBlank(values[i])) && (CollapseOnBlankConverter.IsBlank(values[i+1])))
return System.Windows.Visibility.Collapsed;
i++;
}
else if (CollapseOnBlankConverter.IsBlank(values[i]))
return System.Windows.Visibility.Collapsed;
}
return System.Windows.Visibility.Visible;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("unexpected Convertback");
}
} 


Conclusion

Hopefully this article will have shown you how WPF can be used in different ways from the standard tutorials and how a little extra C# coding can go a long way to make your XAML layout a lot easier to develop.


Download

Copyright (c) 2009 Tom Wright. All rights reserved.

This code is distributed under the Microsoft Public License (Ms-PL).


Exe file


Full Source code - VS2008

Thursday, 1 October 2009

SQL Server Native Client 10.0 - Datetime field overflow

I got a nasty surprise when one of my apps failed when configured to use the latest SQL Server ODBC driver. The code is 6 years old and has been running without a problem in all that time  and has been used with all kinds of databases and different versions of Windows.
The app tries to insert a value into a datetime field and the following error is reported....
22008 [Microsoft][SQL Server Native Client 10.0]
Datetime field overflow. Fractional second precision exceeds the scale specified in the parameter binding.

As I said the code runs fine against all databases including SQL Server 2008, when using the SQL Server 2005 Client. It is just the  SQL Server Native Client 10.0 driver that fails.
After quite a bit of searching I came across a forum posting with this explanation....
http://social.msdn.microsoft.com/Forums/en-US/sqldataaccess/thread/ac1b5a6d-5e64-4603-9c92-b75ba4e51bf2
For ODBC the rule for all types is that truncation on input is an error and truncation on output is a warning see http://msdn.microsoft.com/en-gb/library/ms716298(VS.85).aspx for details.
Earlier ODBC drivers for SQL Server could infer the server type (datetime or smalldatetime) from the scale (which had to be 0 or 3) and so could be more relaxed than SQL Server 2008 Native Client. The default scale for OdbcParameter is 0, and so earlier drivers could assume the server type must be smalldatetime and ignore any fractional seconds. With the introduction of datetime2 and a user defined scale of between 0 and 7 the driver can no longer infer the type from the scale and has to default to the richest type, datetime2. When the actual server type is not datetime2 there will be a server side conversion from datetime2 to the actual server type. I apologise for the invonvenience this has caused you, but we had little choice and the new behavior is documented.
With the addition of variable scale for datetime2 in SQL Server 2008 we had to tighten up on parameter validation for SQL_TYPE_TIMESTAMP to avoid the possibility that an application could unwittingly suffer data corruption. The is documented in Books Online at http://msdn.microsoft.com/en-us/library/bb964722(SQL.100).aspx
which says
Stricter SQL_C_TYPE _TIMESTAMP and DBTYPE_DBTIMESTAMP parameter validation.
Prior to SQL Server 2008 Native Client, datetime values were rounded to fit the scale of datetime and smalldatetime columns by SQL Server. SQL Server 2008 Native Client now applies the stricter validation rules that are defined in the ODBC core specification for fractional seconds. If a parameter value cannot be converted to the SQL type by using the scale specified or implied by the client binding without truncation of trailing digits, an error is returned.
The default scale of OdbcParameter is 0. You need to ensure that the scale of the parameter and its value match. If the scale of the datetime2 column is 7 (which is the default for datetime2), then the following line of code is required.
cmnd.Parameters[1].Scale = 7; 
In other words they have changed the way things work and broken backward compatibility.
In my case I was using C++ to directly access the C ODBC API.
This is the relevant bits of code.
SQLSMALLINT ValueType,ParameterType,DecimalDigits = 0;
SQLUINTEGER ColumnSize = 0;
SQLINTEGER     BufferLength = 0;
SQL_TIMESTAMP_STRUCT d;
....
ValueType = SQL_C_TYPE_TIMESTAMP;
ParameterType = SQL_TYPE_TIMESTAMP;
ColumnSize = 23;
....
pT = (const sXPTimeStructured *)(paramValues[i]);
....
d.year = pT->Year();
d.month = pT->Month();
d.day = pT->Day();
d.hour = pT->Hour();
d.minute = pT->Minute();
d.second = pT->Second();
d.fraction = pT->Milliseconds();
d.fraction *= 1000000;
StrLen[i] = 0;
BufferLength = 0;
....
r = SQLBindParameter(hstmt,i+1,SQL_PARAM_INPUT,ValueType,ParameterType,ColumnSize,
DecimalDigits, &(ParameterValue[i]),BufferLength,&(StrLen[i]));
....
r = SQLExecute(hstmt);



The SQLBindParameter would always work, the error was generated when running the execute....


If  d.fraction is 0 then there is no problem, otherwise the 22008 Datetime field overflow message is produced.


The solution is to add the following line before the call to  SQLBindParameter.


DecimalDigits = 3;



This seems to tell the ODBC driver that the scale of the second fraction is set 3 decimal places and everything works as it did before using the new MS SQL 10.0 driver.


Despite the MS claims I cannot find this documented anywhere, so I do not feel bad about not putting it in six years ago. Now I think about it the most remarkable thing about all this is that this is the first time this code has been broken by a backward compatibility issue.

Window Live Writer Source code formatters

This is a review of three source code formatters plug-ins for Windows Live Writer. All three plug-in do a similar job and produce very similar output.

 

WLW Source Code Formatter (2432 downloads)

This formatter comes with support for a large number of languages and has lots of nice additional features like code highlighting and being able to choose the box size. What is more the author makes the Source code available as well.

The big negative of this plug-in is the HTML code it produces. It embeds the full inline style into every line of code which produces a very verbose code which would be very difficult to edit by hand.

  1: SQLSMALLINT ValueType,ParameterType,DecimalDigits = 0;
  2: SQLUINTEGER ColumnSize = 0;
  3: SQLINTEGER     BufferLength = 0;
  4: SQL_TIMESTAMP_STRUCT d;
  5: 
  6: r = SQLBindParameter(hstmt,i+1,SQL_PARAM_INPUT,ValueType,ParameterType,ColumnSize,
  7:   DecimalDigits, &(ParameterValue[i]),BufferLength,&(StrLen[i]));
  8: 


Code Snippet (12102 downloads)



Code Snippet has a much simpler interface with a few basic options. It does have an options dialog box where the css styles can be altered which is a useful feature. Similar to the first formatter the HTML is very verbose.





   1: SQLSMALLINT ValueType,ParameterType,DecimalDigits = 0;



   2: SQLUINTEGER ColumnSize = 0;



   3: SQLINTEGER     BufferLength = 0;



   4: SQL_TIMESTAMP_STRUCT d;



   5:  



   6: r = SQLBindParameter(hstmt,i+1,SQL_PARAM_INPUT,ValueType,ParameterType,ColumnSize,



   7:   DecimalDigits, &(ParameterValue[i]),BufferLength,&(StrLen[i]));




Insert Code (19250 downloads)



Insert Code is the simplest of the formatters with a very limited set of languages it supports. It supports C# which will probably do for C++ support in most case, but its not great. Its options are limited to line numbers and alternate line colors which the other formatters support as well. The one positive that this formatter has is that it produces nicer HTML. It defines the styles it needs and then refers to them in the code lines.



 




   1:  SQLSMALLINT ValueType,ParameterType,DecimalDigits = 0;


   2:  SQLUINTEGER ColumnSize = 0;


   3:  SQLINTEGER     BufferLength = 0;


   4:  SQL_TIMESTAMP_STRUCT d;


   5:   


   6:  r = SQLBindParameter(hstmt,i+1,SQL_PARAM_INPUT,ValueType,ParameterType,ColumnSize,


   7:    DecimalDigits, &(ParameterValue[i]),BufferLength,&(StrLen[i]));




Conclusion



The clear winner for me is the WLW Source Code Formatter for its impressive features the others do not have and its ease of use. The runner up is Code Snippet



Interestingly I rated these in reverse order of the number of downloads they got. I doubt this is a coincidence as the better snap-ins were released later on. No doubt the authors saw the older ones and implemented the same features and then added some of their own. As Insert Code is the oldest it had a lot of downloads already at the time the others were released. This will have placed it at the front of the queue  in plug-in list, a position it has since kept as the others will find it impossible to overtake despite being better quality. Its a common problem with list such as these that are ranked on simple popularity.

Getting to know Windows Live Writer

I am writing this post in Windows Live Writer as a way of getting to know it and I have already discovered that the way to make it work like I want is through plug-ins. I love the idea of Plug-ins and there seems to be a few on offer, most seem quite old though.

So here is a review of the ones I have downloaded as I test them out

Blog This in Windows Live Writer

Installs into Firefox to let you blog what you are browsing. Does not work in the latest Firefox, not a good start....

Flickr Image Reference

DSCF0529

I have a Flickr account so this plug-in looks interesting. It lets you search your photos and then paste the selected photo into the blog with a variety of options such as size and alignment. Very nice. I will definitely be making use of this one.

Geo Microformat

Integrates with Virtual Earth to allow an easy method of inserting a geo microformat (latitude and longitude) snippet into a blog post.

Well I selected a point on the earth and got this bit of html posted into the blog.

<div class="geo"><abbr class="latitude" value="52.7495937267412"><abbr class="longitude" value="-1.49414062499999"></abbr></abbr></div>

I think I may be missing something here, but as it stands that not much use.

Windows Live Writer

I have just installed Windows Live Writer in the hope it will make me keep this Blog up to date.

It all looks good and even uses the style from the Blog itself.

Only thing that I cannot find how to do so far is to edit the styles. It just has

Heading

and paragraphs with quotations.

I could do with some more styles, like code examples.

So now to see if it will publish onto my blog …. It works!