WPF / MVVM - Extending ViewModel Functionality Via ValueConverters

by Dean 28. September 2011 18:54

In a Model-View-ViewModel (MVVM) project, the ViewModel can easily become a large and unwieldy piece of code. You can break this down by creating a hierarchy of ViewModels that exist in a parent-child relationship, or you can extend the functionality of an existing ViewModel via the use of ValueConverters.

Here is a simple example of what I mean

First, a simple ViewModel that doesn’t contain any functionality, just properties and an implementation of INotifyPropertyChanged for change notifications

public class DemoViewModel1 : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private bool someBoolValue = true;
    public bool SomeBoolValue
    {
        get { return someBoolValue; }
        set
        {
            someBoolValue = value;
            if (PropertyChanged != null)
            {
                PropertyChanged(this, 
                    new PropertyChangedEventArgs("SomeBoolValue"));
                PropertyChanged(this,
                    new PropertyChangedEventArgs("SomeText"));
            }
        }
    }

    private string someText;
    public string SomeText
    {
        get { return someText; }
        set
        {
            someText = value;
            if (PropertyChanged != null)
            {
                PropertyChanged(this,
                    new PropertyChangedEventArgs("SomeText"));
            }
        }
    }
}

Now we’d like to add functionality to this ViewModel so that the string ‘SomeText’ has its characters reversed if the bool ‘SomeBoolValue’ is true. Rather than include this functionality in the ViewModel, lets include it via a ValueConverter.

Here's the ValueConverter code:

public class Demo1ValueConverter : IValueConverter
{
    public DemoViewModel1 ViewDataContext { get; set; }

    public object Convert(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        var bindingData = value as string;
        if (bindingData == null)
        {
            return null;
        }
        return ViewDataContext.SomeBoolValue ? new string(bindingData.Reverse()
            .ToArray()): bindingData;
    }

    public object ConvertBack(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
And now we just need to use our new ValueConverter in our XAML
<Window.Resources>
    <ViewModel:DemoViewModel1 x:Key="viewModel" />
    <Converters:Demo1ValueConverter x:Key="demoConverter" ViewDataContext="{StaticResource viewModel}" />
</Window.Resources>
<Grid DataContext="{StaticResource viewModel}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <TextBlock Text="Enter String" />
    <TextBox Width="70" Grid.Column="1" Text="{Binding SomeText}" />
    <TextBlock Grid.Column="2" Text="{Binding SomeText, 
        Converter={StaticResource demoConverter}}" />
    <TextBlock Text="Reverse?" Grid.Row="1" />
    <CheckBox IsChecked="{Binding SomeBoolValue}" Grid.Column="1" Grid.Row="1" />
    <Button Content="Process" Grid.Row="2" />
</Grid>

As you can see, the ValueConverter has supplemented the function of the ViewModel by extending its functionality.

Tags: , ,

C# | WPF | MVVM | DataBinding

WPF – Colour Picker Widget With Attached Properties

by Dean 21. November 2010 08:47

We can set the colour of any WPF control that supports it, but what about being able to specifically sett individual colour channels (RED, GREEN, BLUE, ALPHA)

I've seen many solutions that contain complex custom controls, but one of the key philosophies of WPF is to reuse what you already have, and change the look or extend the functionality via control templates and the dependency system.

Therefore, I have created a colour picker widget that uses only standard controls, and doesn't even change their control templates. Everything is accomplished via the use of attached properties, which are a very underrated tool in the WPF tool-box.

Here’s the attached property class

public static class BrushExtender
{
    public readonly static DependencyProperty BrushProperty =
        DependencyProperty.RegisterAttached("Brush", typeof(Brush), 
        typeof(BrushExtender), new PropertyMetadata(Brushes.Black, DoBrushChanged));

    public readonly static DependencyProperty RedChannelProperty = 
        DependencyProperty.RegisterAttached("RedChannel", typeof(int), 
        typeof(BrushExtender), new PropertyMetadata(DoColorChangedRed));

    public readonly static DependencyProperty GreenChannelProperty = 
        DependencyProperty.RegisterAttached("GreenChannel", typeof(int), 
        typeof(BrushExtender), new PropertyMetadata(DoColorChangedGreen));

    public readonly static DependencyProperty BlueChannelProperty =
        DependencyProperty.RegisterAttached("BlueChannel", typeof(int),
        typeof(BrushExtender), new PropertyMetadata(DoColorChangedBlue));

    public readonly static DependencyProperty AlphaChannelProperty = 
        DependencyProperty.RegisterAttached("AlphaChannel", typeof(int),
        typeof(BrushExtender), new PropertyMetadata(DoColorChangedAlpha));

    public readonly static DependencyProperty ColourValueProperty =
        DependencyProperty.RegisterAttached("ColourValue", typeof(string),
        typeof(BrushExtender), new PropertyMetadata(DoValueChanged));

    public static void SetRedChannel(DependencyObject o, int value)
    {
        o.SetValue(RedChannelProperty, value);
    }

    public static void SetGreenChannel(DependencyObject o, int value)
    {
        o.SetValue(GreenChannelProperty, value);
    }

    public static void SetBlueChannel(DependencyObject o, int value)
    {
        o.SetValue(BlueChannelProperty, value);
    }

    public static void SetAlphaChannel(DependencyObject o, int value)
    {
        o.SetValue(AlphaChannelProperty, value);
    }

    public static void SetBrush(DependencyObject o, SolidColorBrush brush)
    {
        o.SetValue(BrushProperty, brush);
    }

    public static void SetColourValue(DependencyObject o, string value)
    {
        o.SetValue(ColourValueProperty, value);
    }

    private static void DoColorChangedRed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var color = ((SolidColorBrush)d.GetValue(BrushProperty)).Color;
        DoColorChange(d, (int)e.NewValue, c => c.R, () => 
            Color.FromArgb(color.A, ((byte)(int)e.NewValue), color.G, color.B));
    }

    private static void DoColorChangedGreen(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var color = ((SolidColorBrush)d.GetValue(BrushProperty)).Color;
        DoColorChange(d, (int)e.NewValue, c => c.G, () => 
            Color.FromArgb(color.A, color.R, ((byte)(int)e.NewValue), color.B));
    }

    private static void DoColorChangedBlue(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var color = ((SolidColorBrush)d.GetValue(BrushProperty)).Color;
        DoColorChange(d, (int)e.NewValue, c => c.B, () => 
            Color.FromArgb(color.A, color.R, color.G, (byte)(int)e.NewValue));
    }

    private static void DoColorChangedAlpha(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var color = ((SolidColorBrush)d.GetValue(BrushProperty)).Color;
        DoColorChange(d, (int)e.NewValue, c => c.A, () => 
            Color.FromArgb((byte)(int)e.NewValue, color.R, color.G, color.B));
    }

    private static void DoColorChange(DependencyObject d, int newValue, Func<Color, int> colorCompare, 
        Func<Color> getColor)
    {
        var color = ((SolidColorBrush)d.GetValue(BrushProperty)).Color;
        if (colorCompare(color) == newValue)
            return;
        var newBrush = new SolidColorBrush(getColor());
        d.SetValue(BrushProperty, newBrush);
        d.SetValue(ColourValueProperty, newBrush.Color.ToString());
    }

    private static void DoValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var color = ((SolidColorBrush)d.GetValue(BrushProperty)).Color;
        if (color.ToString() == (string)e.NewValue)
            return;
        Color? newColour = null;
        try
        {
            newColour = (Color) ColorConverter.ConvertFromString((string)e.NewValue);
        }
        catch { }
        if (newColour == null)
            return;
        var newBrush = new SolidColorBrush(newColour.Value);
        d.SetValue(BrushProperty, newBrush);
    }


    private static void DoBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue == e.OldValue)
            return;
        var colour = ((SolidColorBrush)e.NewValue).Color;
        d.SetValue(RedChannelProperty, (int)colour.R);
        d.SetValue(GreenChannelProperty, (int)colour.G);
        d.SetValue(BlueChannelProperty, (int)colour.B);
        d.SetValue(AlphaChannelProperty, (int)colour.A);
        d.SetValue(ColourValueProperty, colour.ToString());
    }
}

And here is the simple WPF widget that makes use of this new functionality

<Window x:Class="ColourBlender.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:ColourBlender="clr-namespace:ColourBlender" 
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <TextBlock Text="Red" />
        <TextBlock Text="Green" Grid.Row="1" />
        <TextBlock Text="Blue" Grid.Row="2" />
        <TextBlock Text="Alpha" Grid.Row="3" />

        <Slider Name="redSlider" Grid.Column="1" Minimum="0" Maximum="255" Width="200" Height="20" 
                Grid.ColumnSpan="2" Value="{Binding ElementName=rect, 
                Path=(ColourBlender:BrushExtender.RedChannel), Mode=TwoWay}" />
        <Slider Name="greenSlider" Grid.Column="1" Grid.Row="1" Minimum="0" Maximum="255" Width="200" Height="20" 
                Grid.ColumnSpan="2" Value="{Binding ElementName=rect, 
                Path=(ColourBlender:BrushExtender.GreenChannel), Mode=TwoWay}"  />
        <Slider Name="blueSlider" Grid.Column="1" Grid.Row="2" Minimum="0" Maximum="255" Width="200" Height="20" 
                Grid.ColumnSpan="2" Value="{Binding ElementName=rect, 
                Path=(ColourBlender:BrushExtender.BlueChannel), Mode=TwoWay}"  />
        <Slider Name="alphaSlider" Grid.Column="1" Grid.Row="3" Minimum="0" Maximum="255" Width="200" Height="20" 
                Grid.ColumnSpan="2" Value="{Binding ElementName=rect, 
                Path=(ColourBlender:BrushExtender.AlphaChannel), Mode=TwoWay}"  />

        <Rectangle Fill="SandyBrown" Name="rect" Width="200" Height="50" Grid.Row="4" Grid.ColumnSpan="3" 
                Margin="0,20,0,10" ColourBlender:BrushExtender.Brush="{Binding RelativeSource={RelativeSource Self}, 
                Path=Fill, Mode=TwoWay}"/>
        
        <TextBlock Text="Colour Value" Margin="5,0,5,0" Grid.Row="5" HorizontalAlignment="Center"  />
        <TextBox Text="{Binding ElementName=rect, Path=(ColourBlender:BrushExtender.ColourValue), Mode=TwoWay}" 
                Margin="0,0,0,0" Grid.Row="5" Grid.Column="1" Width="100" HorizontalAlignment="Center" />
        
        <Button Content="Update" Grid.Row="5" Grid.Column="3" />
    </Grid>
</Window>

This widget allows you to alter the Fill colour of a rectangle via red, green, blue and alpha sliders. There is also a textbox that shows the colour code (which you can also edit and the change is reflected in the slider positions)

Here are some screen shots

 

colour1

colour2

Tags: ,

Attached Properties | DataBinding | WPF | XAML

MVVM – Simple Generic<T> Event Commands With Attached Properties

by Dean 10. November 2010 07:08

One of the challenges of MVVM is to favour command bindings over handling routed events, as currently all event handling must happen in the ‘code behind’ file rather than the ViewModel – which breaks the pattern.

With WPF4 we get the new InputBindings feature, that allows us to hook-up mouse and keyboard gestures to commands, but this still doesn't solve all scenarios where event handling seems to be our only option.

There are many elaborate and highly engineered strategies that I have seen to solve this, but maybe we could use a simple attached property implementation to achieve the desired effect.

Also, WPF bindings (including command bindings) don't support generics, but maybe we could use type inference to create a generic implementation.

In the example below, I have created an attached property that hooks up the ‘Selector’ (base control for ListBox and ListView) ‘SelectionChanged’ routed event to a custom ICommand that can be utilised in our ViewModel

Here's the attached property and command:

 

public class SelectionChangedCommand<T> : ICommand
{
    private readonly Action<List<T>, List<T>> action;

    public SelectionChangedCommand(Action<List<T>, List<T>> action)
    {
        this.action = action;
    }

    public void Execute(object parameter)
    {
        var args = parameter as SelectionChangedEventArgs;
        if (args == null)
            return;
        List<T> added = (
                from object item
                in args.AddedItems
                select (T)item
                ).ToList();
        List<T> removed = (
                from object item
                in args.RemovedItems
                select (T)item
                ).ToList();
        action(added, removed);
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;
}

public static class SelectionChangedCommand
{
    public static DependencyProperty BindProperty = DependencyProperty.RegisterAttached("Bind",
                    typeof(ICommand),
                    typeof(SelectionChangedCommand),
                    new PropertyMetadata(AttachCommand));

    public static void SetBind(DependencyObject d, ICommand command)
    {
        d.SetValue(BindProperty, command);
    }

    private static void AttachCommand(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Selector selector = d as Selector;
        if (selector == null)
            return;
        ICommand oldCommand = e.OldValue as ICommand;
        ICommand newCommand = e.NewValue as ICommand;
        if (oldCommand != null || newCommand == null)
            return;
        selector.SelectionChanged += (o, a) => newCommand.Execute(a);
    }
}

Here’s a test ViewModel where we supply data to the View and well as our command binding:

public class MainViewModel
{
    public List<string> MainList 
    { get; set; }
    public SelectionChangedCommand<string> MainListChanged
    { get; set; }

    public MainViewModel()
    {
        MainList = new List<string> { 
            "testitem1", "testitem2", "testitem3", "testitem4", "testitem5" };
        MainListChanged = new SelectionChangedCommand<string>(DoChange);
    }

    private void DoChange(List<string> addedItems, List<string> removedItems)
    {
        // do viewmodel update here
    }
}

Heres our View

<Window x:Class="MVVMEvents.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:e="clr-namespace:MVVMEvents" 
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox ItemsSource="{Binding MainList}" SelectionMode="Multiple"
               e:SelectionChangedCommand.Bind="{Binding MainListChanged}"/>
    </Grid>
</Window>

And here’s where we hook up our ViewModel to the View (usual code):

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainViewModel();
    }
}

And there it is, we’ve hooked up a non-generic Routed Event (SelectionChanged) to a generic command command that fits nicely into our MVVM pattern implementation.

NOTE: This simple implementation doesn't support Weak Events, which would be a worthwhile improvement.

Dean

Tags: , ,

WPF | Silverlight | MVVM | DataBinding | XAML | Events

WPF – Using Attached Properties For Animated Data Change Notifications

by Dean 29. October 2010 09:49

Working in the investment banking industry, we often need to feed live (changing) market data into our WPF controls, and often there is a requirement that certain movements (in value) are highlighted briefly so as to give a strong visual representation of data changes.

The best way to do this (in my opinion) is to have the TextBlock representing the value ‘blink’ a different colour when the value changes. You would normally include rules that for example stipulate that a movement of more than 3% is required before a notification.
You would also like movements ‘up’ to be shown in a different colour to movements ‘down’.

One key characteristic is that these background changes are very short lived – giving a quick ‘blink’ as a notification.

In fact, to add an extra feature, I have effectively removed the acceleration and deceleration phases of the animation to create a ‘toggle’ style blink (rather than pulse), so it suits our scenario better.

There are several ways that you could achieve this, but my favourite is with attached properties and animations.

Here’s the full code

public class ToggleEase : EasingFunctionBase
{
    protected override Freezable CreateInstanceCore()
    {
        return new ToggleEase();
    }

    protected override double EaseInCore(double normalizedTime)
    {
        if (normalizedTime == 1d)
            return 1d;
        return 0d;
    }
}

public class CellHighlight : DependencyObject
{
    public static readonly DependencyProperty ChangeSourceProperty =
        DependencyProperty.RegisterAttached("ChangeSource", typeof(double),
                                            typeof (CellHighlight),
                                            new PropertyMetadata(double.MinValue, DoPropertyChanged));

    public static readonly DependencyProperty MinimumPercentageMovementProperty =
        DependencyProperty.RegisterAttached("MinimumPercentageMovement", typeof(double),
                                            typeof(CellHighlight),
                                            new PropertyMetadata(0d));

    public static readonly DependencyProperty MovementUpColourProperty =
        DependencyProperty.RegisterAttached("MovementUpColour", typeof(Color),
                                            typeof(CellHighlight),
                                            new PropertyMetadata(Colors.LightGreen));

    public static readonly DependencyProperty MovementDownColourProperty =
        DependencyProperty.RegisterAttached("MovementDownColour", typeof(Color),
                                            typeof(CellHighlight),
                                            new PropertyMetadata(Colors.LightSalmon));

    public static void SetChangeSource(DependencyObject d, double useVal)
    {
        d.SetValue(ChangeSourceProperty, useVal);
    }

    public static void SetMinimumPercentageMovement(DependencyObject d, double perc)
    {
        d.SetValue(MinimumPercentageMovementProperty, perc);
    }

    public static void SetMovementUpColour(DependencyObject d, Color color)
    {
        d.SetValue(MovementUpColourProperty, color);
    }

    public static void SetMovementDownColour(DependencyObject d, Color color)
    {
        d.SetValue(MovementDownColourProperty, color);
    }

    private static void DoPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var oldv = (double) e.OldValue;
        var newv = (double) e.NewValue;

        if (oldv == double.MinValue)
            return;

        var percChange = (double) d.GetValue(MinimumPercentageMovementProperty);
        var upColour = (Color) d.GetValue(MovementUpColourProperty);
        var downColour = (Color) d.GetValue(MovementDownColourProperty);

        var diffPerc = Math.Abs((newv - oldv)/oldv*100);

        if (diffPerc <= percChange)
            return;

        var textblock = (TextBlock)d;

        SolidColorBrush brush = new SolidColorBrush();
        textblock.Background = brush;
        ColorAnimation anim = new ColorAnimation
                    {
                        To = oldv > newv ? downColour : upColour,
                        Duration = TimeSpan.FromMilliseconds(125),
                        AutoReverse = true,
                        EasingFunction = new ToggleEase()
                    };
        brush.BeginAnimation(SolidColorBrush.ColorProperty, anim);

    }

}
<Window x:Class="AnimatedCell.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:AnimatedCell="clr-namespace:AnimatedCell" 
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListView ItemsSource="{Binding}">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}" />
                        <GridViewColumn Header="Product Name" DisplayMemberBinding="{Binding ProductName}" />
                        <GridViewColumn Header="Price">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding MktPrice}" 
                                        AnimatedCell:CellHighlight.ChangeSource="{Binding MktPrice}" 
                                        AnimatedCell:CellHighlight.MinimumPercentageMovement="3"
                                        AnimatedCell:CellHighlight.MovementDownColour="Red" 
                                        AnimatedCell:CellHighlight.MovementUpColour="Green"   />
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>
internal class Trade : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private double mktPrice;
    public double MktPrice
    {
        get { return mktPrice; }
        set
        {
            if (mktPrice == value)
                return;
            mktPrice = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("MktPrice"));
        }
    }

    public string ID { get; set; }
    public string ProductName { get; set; }

    internal static List<Trade> GetTestDataCollection()
    {
        var rand = new Random(Guid.NewGuid().GetHashCode());
        var data = new List<Trade>();
        Enumerable.Range(0, 10).ToList().ForEach((i) =>
            data.Add(new Trade { ID = rand.Next(1000, 9999).ToString(), 
                mktPrice = rand.NextDouble(), ProductName = "Prod" + i.ToString() }));
        return data;
    }

}
public partial class MainWindow : Window
{
    private readonly DispatcherTimer timer = new DispatcherTimer();
    public MainWindow()
    {
        InitializeComponent();

        var rand = new Random(Guid.NewGuid().GetHashCode());
        var data = Trade.GetTestDataCollection();
        timer.Interval = TimeSpan.FromSeconds(1);
        timer.Tick += (o, e) => data.ForEach((t) => t.MktPrice = rand.NextDouble());

        DataContext = data;

        timer.Start();
    }

}

And heres a quick screen show (obviously you cant see the animation as such)

celanim

 

Dean

Tags: , ,

C# | DataBinding | Events | WPF | Animation

WPF – Easy INotifyPropertyChanged Via DynamicObject Proxy

by Dean 15. October 2010 07:53

There has been a big debate at the bank about how best to implement INotifyPropertyChanged on Model classes that need to support change notifications.

None of the approaches seem entirely satisfactory, and here is a quick rundown of the main contenders:

  1. Default approach – hard-code string representations of the property name that’s changing –> this is the easiest approach, and the one that’s included in most documentation, but it can quickly introduce subtle binding errors in WPF if you forget to re-write the strings when you refactor your model classes
  2. Derive Model classes from DependencyObject and use the WPF dependency system for change notifications –> This is an effective solution, but breaks down if you want to do good unit testing. Also architecturally its not good, as your model classes now depend on systems designed to support WPF controls.
  3. Wrap your model classes in their own ViewModel that implements the interface –> this is a sound solution, but requires the coding of additional decorator classes, and can lead to a lot of code-bloat.
  4. Use Lambda Expressions –> this is refactor-proof, but is a poor performer as deciphering the lambda expressions in order to get at the property name uses reflection. Even though lambda expressions are lazy-compiled, there’s still a significant performance hit and the coding style seems counter-intuitive, which may confuse junior developers.
  5. Get the name of the current method inside the property setter, and then strip out the ‘set_’ part of the method name –> this is a really ugly solution, and may not work if you obfuscate the code.
  6. Look at the method call stack inside a helper class –> really ugly code and fragile if refactored.
  7. Derive your model classes from ContextBoundObject so they can be automatically remoted, and then inject AOP code into the message sink-chain –> are you serious !!!

None of the approaches are great, and every one requires the original Model class to be modified in order to support the approach.

So I thought – how about a generic class that derives from DynamicObject, and implements INotifyPropertyChanged. With such a class you’d achieve the following:

  1. Automatic property changed notifications without changing a line of code on your model classes
  2. The solution is completely refactor-proof
  3. It performs reasonably well
  4. The additional memory consumption and strain on the GC is negligible.
  5. It a '’one size fits all’ solution

So, here’s my implementation:

public class DynamicBindingProxy<T> : DynamicObject, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private static readonly Dictionary<string, Dictionary<string, PropertyInfo>> properties =
        new Dictionary<string, Dictionary<string, PropertyInfo>>();
    private readonly T instance;
    private readonly string typeName;

    public DynamicBindingProxy(T instance)
    {
        this.instance = instance;
        var type = typeof(T);
        typeName = type.FullName;
        if (!properties.ContainsKey(typeName))
            SetProperties(type, typeName);
    }

    private static void SetProperties(Type type, string typeName)
    {
        var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        var dict = props.ToDictionary(prop => prop.Name);
        properties.Add(typeName, dict);
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (properties[typeName].ContainsKey(binder.Name))
        {
            result = properties[typeName][binder.Name].GetValue(instance, null);
            return true;
        }
        result = null;
        return false;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        if (properties[typeName].ContainsKey(binder.Name))
        {
            properties[typeName][binder.Name].SetValue(instance, value,null);
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(binder.Name));
            return true;
        }
        return false;
    }
}

 

As you can see, the only minor initial performance hit is when reflecting the classes type for the first time, which is then cached in a static dictionary.

And here is it being used:

public class TestObj
{
    public string Name { get; set; }
    public int ID { get; set;  }
    public DateTime SomeDate { get; set; }
    public double Amount { get; set; }
}

 

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        mc:Ignorable="d" x:Class="WpfApplication1.MainWindow"
        Title="MainWindow" Height="350" Width="525">
    <Border Padding="10">
        <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <UniformGrid Rows="4" Columns="2">
                <TextBlock TextWrapping="Wrap"><Run Language="en-gb" Text="Name"/></TextBlock>
                <TextBox TextWrapping="Wrap" Text="{Binding Name}"/>
                <TextBlock TextWrapping="Wrap"><Run Language="en-gb" Text="ID"/></TextBlock>
                <TextBox TextWrapping="Wrap" Text="{Binding ID}"/>
                <TextBlock TextWrapping="Wrap"><Run Language="en-gb" Text="Amount"/></TextBlock>
                <TextBox TextWrapping="Wrap" Text="{Binding Amount}"/>
                <TextBlock TextWrapping="Wrap"><Run Language="en-gb" Text="Some Date"/></TextBlock>
                <TextBox TextWrapping="Wrap" Text="{Binding SomeDate}"/>
            </UniformGrid>
            <StackPanel HorizontalAlignment="Left" Orientation="Horizontal" Grid.Row="1" 
                        d:LayoutOverrides="Height" Margin="0,10,0,0">
                <TextBox TextWrapping="Wrap" Margin="0,0,10,0" Width="150" Name="newText"/>
                <Button Content="Change Name" Click="UpdateName" d:LayoutOverrides="Width"/>
            </StackPanel>
        </Grid>
    </Border>
</Window>
public partial class MainWindow : Window
{
    private readonly TestObj tObj;
    private DynamicBindingProxy<TestObj> dynObj;
    public MainWindow()
    {
        InitializeComponent();
        tObj = new TestObj() { Name = "test", Amount = 123.45, ID = 44, SomeDate = DateTime.Now };
        dynObj = new DynamicBindingProxy<TestObj>(tObj);
        DataContext = dynObj;
    }

    private void UpdateName(object sender, RoutedEventArgs e)
    {
        ((dynamic)dynObj).Name = newText.Text;
    }
}

As you can see if you run this code, clicking on the update button changes the value of the name property via the dynamic proxy, which publishes the change notification.

 

Dean

Tags: , , ,

C# | DataBinding | MVVM | WPF | XAML

WPF - Simple Error Notifications For Data Binding Expressions

by Dean 7. October 2010 14:19

One of the frustrating things about a large and complex XAML view in WPF, is that you are not often aware of errors in your data binding expressions.

You may not even know there's an error, and if you do then you have to trawl through debugger outputs to try and find it. You could always use snoop, but surely theres a simpler way

Here it is, in just a few lines of code:

 

public partial class MainWindow : Window
{
    public MainWindow()
    {
        BindingErrorListener.Listen(m => MessageBox.Show(m));
        InitializeComponent();
        DataContext = new string[] { "hello" };
    }
}

public class BindingErrorListener : TraceListener
{
    private Action<string> logAction;
    public static void Listen(Action<string> logAction)
    {
        PresentationTraceSources.DataBindingSource.Listeners
            .Add(new BindingErrorListener() { logAction = logAction });
    }
    public override void Write(string message) { }
    public override void WriteLine(string message)
    {
        logAction(message);
    }
}

so I tried it with an incorrect binding:

    <Grid>
        <TextBlock Text="{Binding BadBinding}" />
    </Grid>

and I got this

 

 

Its simple, easy to implement and can be easily removed for production builds

Dean

Tags: , ,

DataBinding | WPF

WPF – DataContext Virtualization With Paged Services

by Dean 27. April 2010 21:51

Many WPF applications need to handle a very large data collections – maybe the users really need a million rows in their GridView control. The way we cope with this is to ‘virtualize’ the data, and have it available to your control on an ‘as needed’ basis.

Most list controls in WPF (including the standard ListView/GridView) include the concept of a ‘viewport’ under the hood. A viewport is a virtual ‘window’ on the underlying data collection, which only requires the data currently being displayed – so if your collection is a million rows, and your ‘viewport’ is only 100 rows high, then you only need 100 rows (although they must be the ‘right’ rows).

In addition, if the data collection that is your DataContext implements the non-generic ‘IList’ interface, the viewport will optimize it access to the collection calling the ‘IList.this[index]’ for row enumeration rather than GetEnumerator().

This means that if we create a custom collection that implements the non-generic ‘IList’ interface, we can ‘virtualize’ access to the data – giving us an opportunity to put a million rows in our grid in the blink of an eye.

Also, this ‘million row’ scenario may need to retrieve data from a WCF service (for example), and the service method to retrieve data will most likely be paged, requiring page number and page size parameters to retrieve the correct page.

OK, that’s the intro over, so lets discuss a REALLY easy solution to this requirement

Firstly, I am assuming that you have a service reference to a WCF service that has 2 service methods

  1. A method that gets the total collection count
  2. A method that takes page number and page size parameters and returns a strongly-typed collection representing a single ‘page’ of data

As an example of what I mean, here is a sample service contract for a service I have created for this post

namespace WCFDataPaging.WCF
{
    [ServiceContract]
    public interface IService
    {
 
        [OperationContract]
        List<TestDataObject> GetPageData(int page, int pageSize);
 
        [OperationContract]
        int GetDataCount();
    }
}

and here is my service implementation

public class MainService : IService
{
    public List<TestDataObject> GetPageData(int page, int pageSize)
    {
        return Utility.TestData.Skip(page*pageSize).Take(pageSize).ToList();
    }
 
    public int GetDataCount()
    {
        return Utility.TestData.Count;
    }
}

Now the utility class simple generates a test collection of about a million rows. In real life, these services will be retrieving real data from some kind of data store.

Now I need a collection class on the WPF side, that can perform all of the necessary virtualization:

public sealed class VirtualServiceCollection<T> : IList<T>, IList
{
    private readonly Func<int, int, T[]> dataFunction;
    private readonly Func<int> countFunction;
    private readonly int pageSize;
    private readonly List<T> data;
    private int currentPage;
 
    public VirtualServiceCollection(Func<int,int,T[]> dataFunction, Func<int> countFunction, int pageSize)
    {
        this.dataFunction = dataFunction;
        this.countFunction = countFunction;
        this.pageSize = pageSize;
        data = new List<T>(dataFunction(0, pageSize));
    }
 
    public IEnumerator<T> GetEnumerator()
    {
        var count = countFunction();
        for (var i = 0; i < count; i++)
            yield return this[i];
    }
 
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
 
    public void Add(T item)
    {
        throw new NotImplementedException();
    }
 
    public int Add(object value)
    {
        throw new NotImplementedException();
    }
 
    public bool Contains(object value)
    {
        throw new NotImplementedException();
    }
 
    void IList.Clear()
    {
        DoClear();
    }
 
    public int IndexOf(object value)
    {
        return data.IndexOf((T)value);
    }
 
    public void Insert(int index, object value)
    {
        throw new NotImplementedException();
    }
 
    public void Remove(object value)
    {
        throw new NotImplementedException();
    }
 
    void IList.RemoveAt(int index)
    {
        throw new NotImplementedException();
    }
 
    private T GetItem(int index)
    {
        var bot = currentPage * pageSize;
        var top = Math.Min(bot + pageSize, countFunction());
        if (index >= bot && index < top)
            return data[index - bot];
        currentPage = (int)Math.Floor(index / (double)pageSize);
        data.Clear();
        data.AddRange(dataFunction(currentPage, pageSize));
        return data[index - (currentPage * pageSize)];
    }
 
    object IList.this[int index]
    {
        get { return GetItem(index); }
        set { throw new NotImplementedException(); }
    }
 
    bool IList.IsReadOnly
    {
        get { return false; }
    }
 
    public bool IsFixedSize
    {
        get { return false; }
    }
 
    private void DoClear()
    {
        currentPage = 0;
        data.Clear();
    }
 
    void ICollection<T>.Clear()
    {
        DoClear();
    }
 
    public bool Contains(T item)
    {
        throw new NotImplementedException();
    }
 
    public void CopyTo(T[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }
 
    public bool Remove(T item)
    {
        throw new NotImplementedException();
    }
 
    public void CopyTo(Array array, int index)
    {
        throw new NotImplementedException();
    }
 
    int ICollection.Count
    {
        get { return countFunction(); }
    }
 
    public object SyncRoot
    {
        get { return this; }
    }
 
    public bool IsSynchronized
    {
        get { return false; }
    }
 
    int ICollection<T>.Count
    {
        get { return countFunction(); }
    }
 
    bool ICollection<T>.IsReadOnly
    {
        get { return false; }
    }
 
    public int IndexOf(T item)
    {
        return data.IndexOf(item);
    }
 
    public void Insert(int index, T item)
    {
        throw new NotImplementedException();
    }
 
    void IList<T>.RemoveAt(int index)
    {
        throw new NotImplementedException();
    }
 
    public T this[int index]
    {
        get { return GetItem(index); }
        set { throw new NotImplementedException(); }
    }
}

One of the things that’s interesting about this is that it takes two Func delegates in its constructor – one to get the collection count, and one to retrieve a page

now, we need to wire it all up

First here’s my WPF code-behind:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Loaded += DoLoaded;
    }
 
    private void DoLoaded(object sender, RoutedEventArgs e)
    {
        var s = new ServiceClient();
        var coll = new VirtualServiceCollection<TestDataObject>(s.GetPageData, s.GetDataCount, 100);
        DataContext = coll;
    }
}

As you can see, we pass into our collection the to service methods that get the data we need

And finally – here’s the XAML

<Window x:Class="WCFDataPaging.WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Count : " />
            <TextBlock Text="{Binding Path=Count}" />
        </StackPanel>
        <ListView ItemsSource="{Binding}" Grid.Row="1">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
                        <GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}" />
                        <GridViewColumn Header="Start Date" DisplayMemberBinding="{Binding StartDate}" />
                        <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Price}" />
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

We’ve got a textbox at the top for the count, and the columns for our strongly-typed object

And here it is in action:

virtualdata

This took about half a second to load, and scrolls through the million rows pretty smoothly. FYI the first part of the name column data in my test collection is the row numer, so I can check it’s working :)

 

Dean

Tags: , ,

DataBinding | WCF | XAML | WPF

Lightweight ‘O/R Mapping’ in F# Interactive

by Dean 26. April 2010 21:15

I’ve been playing a lot with F# lately, particularly in the area of financial option modelling, which requires quite a lot of number crunching – a perfect scenario for tinkering around in F# interactive.

However, I need to get data out of my data store, and use it to create collections of records, that represent the data that I need.

This was becoming a little cumbersome, so I thought I’d create a little ORM function to do the trick

open System.Data.SqlClient
open Microsoft.FSharp.Reflection
 
let BuildData<'T> (connection:string, command:string) = 
    let conn = new SqlConnection(connection)
    let comm = new SqlCommand(command,conn)
    let recordType = typeof<'T>
    let fieldCount = FSharpType.GetRecordFields(recordType).Length
    conn.Open()
    let db = comm.ExecuteReader()
    let rec populate (reader:SqlDataReader) (l:'T list) = 
        match reader.Read() with
        | false -> l
        | _ -> 
            let vals = Array.create<obj> fieldCount null
            ignore(reader.GetValues(vals))
            let dataObj = FSharpValue.MakeRecord(recordType,vals) :?> 'T 
            let x = dataObj::l
            populate reader x
    let  data = populate db []
    conn.Close()
    data

run the above in interactive, and you’ll get

val BuildData : string * string -> 'T list

now lets give it a spin

 

type pricedata = { Price : Decimal; Symbol : String; PriceDate : DateTime }
let conn = "Data Source=DEAN-PC\SQLEXPRESS;Initial Catalog=StockData;Integrated Security=SSPI"
let comm = "select price, symbol, pricedate from symboldata where symbol = 'AA'"
let data = BuildData<pricedata>(conn,comm)

so we now have a strongly-typed collection, ready for pumping into our financial modelling functions

or alternatively, you could display the data in a grid :-

open System.Windows.Forms
 
let grid data =
    let form = new System.Windows.Forms.Form(Visible=true,TopMost=true)
    let g = new System.Windows.Forms.DataGrid(Dock = DockStyle.Fill, Visible=true)
    g.DataSource <- List.toArray data
    form.Controls.Add(g) 
 
grid data

and the result is like this

stockdata

Tags: , , ,

F# | DataBinding

F# And MVVM – A Simple ViewModel

by Dean 12. April 2010 20:51

For many years, OOP abstractions and design patterns have been the cornerstones of my development methodology as a senior C# developer in investment banking.

However, over the last year or so I have taken quite a shine to Microsoft’s new FP language (F#), not just because purely functional program code is concise powerful and elegant, but because the eclectic mix of functional and OOP paradigms in F# enable me to develop better, faster, stronger and more maintainable applications.

In investment banking, the business has been changing fast, and being able to get production quality WPF apps onto the trader desks has been a big priority, and with F# I find myself more able to meet that challenge.

The WPF design pattern ‘du jour’ is MVVM. Anyone who’s serious about enterprise-strength development in WPF would have come across this pattern and probably have used it at some point in the recent past.

Detailed below is a very basic implementation where :-

  1. There is a WPF project that contains only Views
  2. There is a C# ‘model’ project that contains only the generated classes from SqlMetal (for Linq to SQL)
  3. The is an F# code library project that contains my ViewModel

The F# ViewModel is a very simple one, but it covers all the main bases:-

  1. It has a generic ICommand implementation for command binding
  2. It implements INotifyPropertyChanged for change notifications
  3. It has Data for binding

So here it is

#light
module FSharpMVVM
 
open System
open System.Windows.Input
open System.Data.SqlClient
open System.ComponentModel
open FsMVVM.DAL
 
// below is our Linq-SQL data context, created via SqlMetal and in a separate
// C# project - as SqlMetal doesnt generate F# yet *)
 
let ctx = new DataClassesDataContext()
 
// an implemnentation of a generic ICommand, that takes two functions
// one to see if the command can be executed, and one to execute the command 
 
type FuncCommand (canExec:(obj -> bool),doExec:(obj -> unit)) =
    let cecEvent = new DelegateEvent<EventHandler>()
    interface ICommand with
        [<CLIEvent>]
        member x.CanExecuteChanged = cecEvent.Publish
        member x.CanExecute arg = canExec(arg)
        member x.Execute arg = doExec(arg)
 
// Our ViewModel is below
 
type MainViewModel() =  
    let mutable prods = Seq.toList ctx.Products 
    let a =  new PropertyChangedEventArgs("ProductData")
    let propChangedEvent = new DelegateEvent<PropertyChangedEventHandler>()
    interface INotifyPropertyChanged with 
        [<CLIEvent>]
        member x.PropertyChanged = propChangedEvent.Publish
    member x.ProductData with get() = prods
    member x.FilterCommand = 
        new FuncCommand(
            (fun d -> not(Seq.isEmpty ctx.Products)),
            (fun e -> prods <- Seq.toList ctx.Products 
                    |> List.filter (
                        fun p -> p.ProductName.Contains(e.ToString()));
                        propChangedEvent.Trigger([| box x; box a|])))

 

and below is the simple View (XAML)

 

<Window x:Class="FsMVVM.WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Margin="5">
            <TextBox Name="searchText" MinWidth="100" />
            <Button Content="Search" Command="{Binding FilterCommand}" 
                    CommandParameter="{Binding ElementName=searchText, Path=Text}" />
        </StackPanel>
        <ListView ItemsSource="{Binding ProductData}" Grid.Row="1">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="Name" DisplayMemberBinding="{Binding ProductName}" />
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

 

and finally, its wired up in the code-behind

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new FSharpMVVM.MainViewModel();
    }
}

and that's all it takes

Now, I know that there isnt a great deal of functional programming in this example, but now I have the building blocks for a more complex ViewModel, that will no doubt include a great deal more FP before it is finished

 

Dean

Tags: ,

DataBinding | F# | MVVM

Using CLR 4 Dynamics To Mock Bindable Objects in XAML

by Dean 3. February 2010 07:45

When I’m building prototypes in WPF or working on a GUI spike in an agile development team I often find it really unproductive to continuously switch between working in XAML (with my designer hat on), and working on the plumbing code (with my C# hat on).

Wouldn't it be nice to be able to model my data in XAML, and seamlessly use it with XAML Binding expressions that’ll be valid once the ‘real’ data gets plumbed in.

Well, thanks to the dynamic features in CLR v4 this is now a trivial task.

Firstly lets look at the XAML – you’ll quickly see the flexibility in using this approach -

<Window x:Class="DynamicWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <collections:Hashtable x:Key="data">
            <sys:Double x:Key="Prop1">50</sys:Double>
            <sys:String x:Key="Prop2">Hello</sys:String>
        </collections:Hashtable>
    </Window.Resources>
    <StackPanel Orientation="Vertical">
        <TextBox Text="{Binding Path=Prop2, Mode=TwoWay}" />
        <TextBlock Text="{Binding Path=Prop2}" />
        <Button Content="{Binding Path=Prop1}" Height="{Binding Path=Prop1}" />
    </StackPanel>
</Window>

The critical thing to look at is the resources section (this could be in a separate resource dictionary if you wanted to keep things neat). Basically, I'm creating a hashtable that is going to model the data I anticipate will be available when I hook it all up after the design phase. Essentially, the hashtable keys are the property names of my object, and the hashtable values are the initial values of the properties.

In the example above, I’m expecting my data object to have 2 properties – one called “Prop1”which is a double, and has an initial value of 50, the other is called “Prop2” which is a string, and has an initial value of “Hello”.

Using the above approach, I could model (or mock) just about any simple object I can imagine.

Now we need to turn this hashtable into a real object, and this is where the new DynamicObject class in CLR 4 comes in.

public class DynamicDataObject : DynamicObject, INotifyPropertyChanged
{
    private Hashtable data;
    public event PropertyChangedEventHandler PropertyChanged;
 
    public DynamicDataObject(Hashtable data)
    {
        this.data = data;
    }
 
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = data.ContainsKey(binder.Name) ? data[binder.Name] : null;
        return true;
    }
 
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        data[binder.Name] = value;
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(binder.Name));
        return true;
    }
}

When you inherit from DynamicObject, your object is essentially ‘dynamic’ which means that it’s members are discovered at runtime. In the class above, I'm using the hashtable passed in as the backing data to enable the correct member resolution at runtime. Effectively, this DynamicDataObject class will correctly mock a compiled CLR object with the same member signature.

Now, all we have to do is hook it up with a single line of code in our code-behind file:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new DynamicDataObject((Hashtable)this.Resources["data"]);
    }
}

And there you have it. The binding expression syntax in your XAML will work seamlessly with this solution. You can specify any type of binding, including the use of valueconverters etc.

Of course, any behaviour you wanted in your data objects cannot be implemented this way, but you shouldn’t mix data and logic anyway :)

 

Dean

Tags: , ,

DataBinding | XAML

Thread-Safe & Dispatcher-Safe Observable Collection for WPF

by Dean 1. February 2010 12:22

A common problem in WPF (& Silverlight) development is when you are working with multiple threads that need to change a collection that is a binding source and implements INotifyCollectionChanged.

Basically, the standard ObservableCollection<T> will only allow updates from the dispatcher thread, which means you need to write a lot of code for the worker threads to marshal changes onto the main message pump via the dispatcher. This can be a bit tedious, so I recently wrote a collection that performs all of the necessary marshalling internally, so users of this type do not have to be concerned about thread affinity issues.

Also, I decided to use a ReaderWriterLock to provide thread-safety during updates to the collection.

Here is my collection class:

 
public class SafeObservable<T> : IList<T>, INotifyCollectionChanged
{
    private IList<T> collection = new List<T>();
    private Dispatcher dispatcher;
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    private ReaderWriterLock sync = new ReaderWriterLock();
 
    public SafeObservable()
    {
        dispatcher = Dispatcher.CurrentDispatcher;
    }
 
    public void Add(T item)
    {
        if (Thread.CurrentThread == dispatcher.Thread)
            DoAdd(item);
        else
            dispatcher.BeginInvoke((Action)(() => { DoAdd(item); }));
    }
 
    private void DoAdd(T item)
    {
        sync.AcquireWriterLock(Timeout.Infinite);
        collection.Add(item);
        if (CollectionChanged != null)
            CollectionChanged(this,
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
        sync.ReleaseWriterLock();
    }
 
    public void Clear()
    {
        if (Thread.CurrentThread == dispatcher.Thread)
            DoClear();
        else
            dispatcher.BeginInvoke((Action)(() => { DoClear(); }));
    }
 
    private void DoClear()
    {
        sync.AcquireWriterLock(Timeout.Infinite);
        collection.Clear();
        if (CollectionChanged != null)
            CollectionChanged(this,
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        sync.ReleaseWriterLock();
    }
 
    public bool Contains(T item)
    {
        sync.AcquireReaderLock(Timeout.Infinite);
        var result = collection.Contains(item);
        sync.ReleaseReaderLock();
        return result;
    }
 
    public void CopyTo(T[] array, int arrayIndex)
    {
        sync.AcquireWriterLock(Timeout.Infinite);
        collection.CopyTo(array, arrayIndex);
        sync.ReleaseWriterLock();
    }
 
    public int Count
    {
        get
        {
            sync.AcquireReaderLock(Timeout.Infinite);
            var result = collection.Count;
            sync.ReleaseReaderLock();
            return result;
        }
    }
 
    public bool IsReadOnly
    {
        get { return collection.IsReadOnly; }
    }
 
    public bool Remove(T item)
    {
        if (Thread.CurrentThread == dispatcher.Thread)
            return DoRemove(item);
        else
        {
            var op = dispatcher.BeginInvoke(new Func<T,bool>(DoRemove), item);
            if (op == null || op.Result == null)
                return false;
            return (bool)op.Result;
        }
    }
 
    private bool DoRemove(T item)
    {
        sync.AcquireWriterLock(Timeout.Infinite);
        var index = collection.IndexOf(item);
        if (index == -1)
        {
            sync.ReleaseWriterLock();
            return false;
        }
        var result = collection.Remove(item);
        if (result && CollectionChanged != null)
            CollectionChanged(this, new
                NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        sync.ReleaseWriterLock();
        return result;
    }
 
    public IEnumerator<T> GetEnumerator()
    {
        return collection.GetEnumerator();
    }
 
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return collection.GetEnumerator();
    }
 
    public int IndexOf(T item)
    {
        sync.AcquireReaderLock(Timeout.Infinite);
        var result = collection.IndexOf(item);
        sync.ReleaseReaderLock();
        return result;
    }
 
    public void Insert(int index, T item)
    {
        if (Thread.CurrentThread == dispatcher.Thread)
            DoInsert(index, item);
        else
            dispatcher.BeginInvoke((Action)(() => { DoInsert(index, item); }));
    }
 
    private void DoInsert(int index, T item)
    {
        sync.AcquireWriterLock(Timeout.Infinite);
        collection.Insert(index, item);
        if (CollectionChanged != null)
            CollectionChanged(this,
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
        sync.ReleaseWriterLock();
    }
 
    public void RemoveAt(int index)
    {
        if (Thread.CurrentThread == dispatcher.Thread)
            DoRemoveAt(index);
        else
            dispatcher.BeginInvoke((Action)(() => { DoRemoveAt(index); }));
    }
 
    private void DoRemoveAt(int index)
    {
        sync.AcquireWriterLock(Timeout.Infinite);
        if (collection.Count == 0 || collection.Count <= index)
        {
            sync.ReleaseWriterLock();
            return;
        }
        collection.RemoveAt(index);
        if (CollectionChanged != null)
            CollectionChanged(this,
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        sync.ReleaseWriterLock();
 
    }
 
    public T this[int index]
    {
        get
        {
            sync.AcquireReaderLock(Timeout.Infinite);
            var result = collection[index];
            sync.ReleaseReaderLock();
            return result;
        }
        set
        {
            sync.AcquireWriterLock(Timeout.Infinite);
            if (collection.Count == 0 || collection.Count <= index)
            {
                sync.ReleaseWriterLock();
                return;
            }
            collection[index] = value;
            sync.ReleaseWriterLock();
        }
 
    }
}
 

To test the effectiveness of this collection class, I wrote a simple WPF app, that bound to the new collection class and updated it via multiple threads:

 

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel Orientation="Vertical" VerticalAlignment="Top">
        <Button Content="Start" Click="Button_Click" />
        <ListView Name="list" ItemsSource="{Binding}" DisplayMemberPath="Text" />        
    </StackPanel>
</Window>

And the code behind is below:

 

public partial class Window1 : Window
{
    class TestData
    {
        public string Text { get; set; }
    }
 
    private Random rand = new Random(DateTime.Now.Millisecond);
    private SafeObservable<TestData> data = new SafeObservable<TestData>();
    public Window1()
    {
        InitializeComponent();
    }
 
    void Button_Click(object sender, RoutedEventArgs e)
    {
        list.DataContext = data;
        List<Action> work = new List<Action>();
        for (int i = 0; i < 100; i++)
        {
            work.Add(new Action(DoWorkAdd));
            work.Add(new Action(DoWorkClear));
            work.Add(new Action(DoWorkRemove));
            work.Add(new Action(DoWorkRemoveAt));
            work.Add(new Action(DoWorkInsert));
            work.Add(new Action(DoWorkReplace));
        }
        for (int i = 0; i < 1000; i++)
            work[rand.Next(0, work.Count)].BeginInvoke(null, null);
 
    }
 
    void DoWorkAdd()
    {
        Thread.Sleep(rand.Next(500, 30000));
        data.Add(new TestData() { Text = string.Format("Thread {0} Added", Thread.CurrentThread.ManagedThreadId) });
    }
 
    void DoWorkClear()
    {
        Thread.Sleep(rand.Next(500, 10000));
        data.Clear();
        Debug.WriteLine((string.Format("Thread {0} Clear", Thread.CurrentThread.ManagedThreadId)));
    }
 
    void DoWorkRemove()
    {
        Thread.Sleep(rand.Next(500, 10000));
        if (data.Count == 0)
            return;
        var item = data[0];
        data.Remove(item);
        Debug.WriteLine((string.Format("Thread {0} Remove", Thread.CurrentThread.ManagedThreadId)));
    }
 
    void DoWorkRemoveAt()
    {
        Thread.Sleep(rand.Next(500, 10000));
        if (data.Count == 0)
            return;
        data.RemoveAt(0);
        Debug.WriteLine((string.Format("Thread {0} RemoveAt", Thread.CurrentThread.ManagedThreadId)));
    }
 
    void DoWorkInsert()
    {
        Thread.Sleep(rand.Next(500, 10000));
        data.Insert(rand.Next(0, data.Count), new TestData() 
            { Text = string.Format("Thread {0} Insert", Thread.CurrentThread.ManagedThreadId) });
    }
 
    void DoWorkReplace()
    {
        Thread.Sleep(rand.Next(500, 10000));
        data[rand.Next(0, data.Count)] = new TestData() 
            { Text = string.Format("Thread {0} Replace", Thread.CurrentThread.ManagedThreadId) };
    }
 
}

All my WPF app does is run a number of random actions against the collection from a variety of threads.

NOTE:  When removing items from the collection I used the Refresh action of NotifyCollectionChangedAction instead of Remove. This is because the remove action doesnt work correctly in a multi-threaded scenario when used as a binding source for a list control in WPF.

If anyone has any siggestions or enhancements, please let me know

Dean

Tags:

DataBinding | C# | Threading

Silverlight DataGrid – A Simple Pager Control

by Dean 11. February 2009 10:39

I love the fact that in Silverlight you can get all of your data onto the datagrid at the same time, rather than having to used a paged control in ASP.NET. However I’ve found that some users really want the data paging to remain, which means that I’ve got to roll may own DataGrid paging control.

I know there are a few example out there, but I wanted to create a control that was a simple as possible but covered all of the major bases – so as little ‘code-behind’ as possible, and all the styling done in Blend.

So this is what I came up with:

Screenshot

 

pagergrid

 

Here’s the XAML

 

<Grid x:Name="LayoutRoot" Background="White" Width="Auto" 
      Height="Auto" HorizontalAlignment="Left" VerticalAlignment="Top" 
      Margin="10,10,10,10">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" 
                    VerticalAlignment="Top">
        <TextBlock Text="Page 1 of 10 (Total 100 items)" x:Name="Total" 
                    HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <TextBlock Text="Page Size" Margin="10,0,0,0" 
                    HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <ComboBox Name="PageSize" SelectionChanged="PageSizeChanged" 
                    Margin="3,3,3,3" HorizontalAlignment="Center" 
                    VerticalAlignment="Center" />
    </StackPanel>
    <data:DataGrid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
                    DataContext="{Binding Mode=OneWay, 
                        Source={StaticResource PersonCollectionDS}}" 
                    ItemsSource="{Binding}" x:Name="MainGrid" 
                    CanUserSortColumns="False" Grid.Row="1" />
    <Border Margin="0,3,0,3" Padding="5,5,5,5" BorderBrush="Gray" 
                    BorderThickness="1,1,1,1" Grid.Row="2">
        <StackPanel Height="Auto" HorizontalAlignment="Center" 
                    VerticalAlignment="Top" Width="Auto" x:Name="Pager" 
                    Orientation="Horizontal">
            <Button Content="&lt;&lt;" Margin="0,0,3,0" x:Name="FirstButton" 
                    Click="GoFirst"/>
            <Button Content="&lt;" Margin="0,0,3,0" x:Name="BackButton" 
                    Click="GoBack"/>
            <TextBlock Text="Page" Margin="3,0,0,0" HorizontalAlignment="Center" 
                    VerticalAlignment="Center"/>
            <ComboBox Name="PageSelector" Margin="3,0,0,0" 
                      SelectionChanged="PageChanged"/>
            <Button Content="&gt;" Margin="3,0,0,0" 
                    x:Name="ForwardButton" Click="GoForward"/>
            <Button Content="&gt;&gt;" Margin="3,0,0,0"
                    x:Name="LastButton" Click="GoLast"/>
        </StackPanel>
    </Border>
</Grid>

 

And Here’s the code-behind

 

public partial class Page : UserControl
{
    private readonly PersonCollection masterCollection = 
        new PersonCollection();
    private int pageSize = 10;
    private int currentPage;
    private int maxPage;
 
    public Page()
    {
        InitializeComponent();
        Loaded += PageLoaded;
    }
 
    private void PageLoaded(object sender, RoutedEventArgs e)
    {
        // this line just creates test data - ignore;
        masterCollection.BuildTestData(1000,1000);
 
        masterCollection.CollectionChanged += (s, a) => SetPageData();
        SetPageSize();
        PageSize.ItemsSource = new List<int>(new[] {10, 25, 50});
        PageSize.SelectedIndex = 0;
        SetPageData();
    }
 
    private void SetPageSize()
    {
        maxPage = (int)Math.Ceiling((double)masterCollection.Count 
            / (double)pageSize);
        for (var i = 1; i <= maxPage; i++)
            PageSelector.Items.Add(i);
    }
 
    private void SetPageData()
    {
        MainGrid.DataContext = masterCollection
            .OrderBy(p => p.Name)
            .Skip(pageSize * currentPage)
            .Take(pageSize);
        BackButton.IsEnabled = FirstButton.IsEnabled = currentPage > 0;
        ForwardButton.IsEnabled = LastButton.IsEnabled = 
            currentPage+1 < maxPage;
        Total.Text = string.Format("Page {0} of {1} (Total : {2})", 
            currentPage+1, maxPage, masterCollection.Count);
        PageSelector.SelectedIndex = currentPage;
    }
 
    private void GoBack(object sender, RoutedEventArgs e)
    {
        currentPage--;
        SetPageData();
    }
 
    private void GoForward(object sender, RoutedEventArgs e)
    {
        currentPage++;
        SetPageData();
    }
 
    private void GoLast(object sender, RoutedEventArgs e)
    {
        currentPage = maxPage-1;
        SetPageData();
    }
 
    private void GoFirst(object sender, RoutedEventArgs e)
    {
        currentPage = 0;
        SetPageData();
    }
 
    private void PageChanged(object sender, 
        SelectionChangedEventArgs e)
    {
        currentPage = (int)PageSelector.SelectedItem-1;
        SetPageData();
    }
 
    private void PageSizeChanged(object sender, 
        SelectionChangedEventArgs e)
    {
        pageSize = (int) PageSize.SelectedItem;
        currentPage = 0;
        SetPageSize();
        SetPageData();
    }
}

 

As you can see, it’s pretty simple stuff.

Because the Silverlight DataGrid doesn't expose the built-in column sorting events, implementing sorting on the columns is a little more tricky – you’d have to re-template the headers and include a button who’s click event can invoke a custom sorting algorithm, which would be easy enough to implement should you want.

Any comments ? let me know.

(Source Files Below)

DemoPager.zip

Dean

Tags: ,

DataBinding | Silverlight

Generic Filter Control for Silverlight DataGrid

by Dean 6. February 2009 14:13

During the creation of a recent demo Silverlight project, I tasked myself with creating a generic (re-useable) filter control that I could use to filter rows in a Silverlight DataGrid.

The control had to be lightweight, and work automatically with any object collection that the DataGrid was bound to – thus making it plug-and-play for any future uses.

I’ve created a great starting point with this, by designing a set of inter-operating classes that have the following key features.

  1. The Filter control of writtem completely in Xaml, using Xaml binding syntax (good designer supprt)
  2. By virtue of point 1, it is easily modified in Blend
  3. The filter control works by creating Lambda/Linq Expression trees to perform filtering.
  4. The filter control can ustilise the DescriptionAttribute for plug-and-play.

Here are a couple of screenshots:

1) No filtering

filter1

2) Adding a couple of filters, by clicking on the ‘Add New Filter’ button, setting the filter and clicking on ‘Apply All Filters’

filter2

 

As you can see, the compiled Expression tree effectively filtered the data in the main grid.

The advantages of this filter control are

  1. You could package it up, and drop it into any scenario – the only thing the filter control needs to know is the Type details of the objects in the data collection.
  2. You could play around with the visuals, and create a ‘FilteringDataGrid’ as a single control
  3. It could be a great starting point for implementing a Microsoft Excel – like ‘Auto Filter’ grid.

The ideas are limitless, and the link below will allow you to download the project and try it out for yourself.

SimpleFilterControl.zip

 

Here are some Caveats

  1. This is not production quality code. A lot of production-necessary engineering is missing for the sake of illustrating the approach(es) taken in as simple a way as possible.
  2. This code can be freely used, but comes with no guarantees whatsoever.
  3. I'm happy to hear about suggested improvements or alternative ideas, but if you have any criticisms about the quality of the code, please refer to point 1 above.

I hope those that take a look find it interesting, and I look forward to your comments.

 

Dean

Tags: ,

Silverlight | DataBinding

Silverlight DataGrid – Prototyping Tip For Column Headers

by Dean 5. February 2009 14:58

Working in investment banking, I often get asked to create semi-functional prototypes of user interfaces, where the development methodology is all about speed and not about code-quality.

Often these projects will be centred around the Silverlight DataGrid, and I want to get it up and running fast with whatever data object that needs to be used (the DataGrid will need to be bound to a collection of such objects).

By default, a DataGrid for Silverlight will automatically generate columns, which is a great little feature in these scenarios, which is a great time saver – especially when the underlying object that your rows are binding to is in a state of flux.

However, when automatically generating columns for your data, the column header is just set to the name of the property – which isn't going to impress the audience when you show off your prototype.

A great solution I put together, is to use the ‘DescriptionAttribute’ on your data object’s properties, and have the DataGrid automatically pick that up.

To Illustrate this, please find below the ‘before’ and ‘after’ code and screenshots.

 

1) Before

 

    public class Person
    {
        public string Name { get; set; }
 
        public DateTime Dob { get; set; }
 
        public int EmployeeId { get; set; }
 
        public double Salary { get; set; }
 
    }

 

<UserControl.Resources>
    <AttributesConverter:PersonCollection x:Key="PersonCollectionDS" 
        d:IsDataSource="True"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
    <data:DataGrid Name="PersonGrid" HorizontalAlignment="Left" 
        ItemsSource="{Binding Mode=OneWay, Source={StaticResource PersonCollectionDS}}" />
</Grid>

proto_before

 

2) After

 

    public class Person
    {
        [Description("Employee Name")]
        public string Name { get; set; }
 
        [Description("Date Of Birth")]
        public DateTime Dob { get; set; }
 
        [Description("Employee Id")]
        public int EmployeeId { get; set; }
 
        [Description("Salary Amount")]
        public double Salary { get; set; }
 
    }

<UserControl.Resources>
    <AttributesConverter:PersonCollection x:Key="PersonCollectionDS" 
        d:IsDataSource="True"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
    <data:DataGrid Name="PersonGrid" HorizontalAlignment="Left" 
        ItemsSource="{Binding Mode=OneWay, Source={StaticResource PersonCollectionDS}}" 
        AutoGeneratingColumn="ColumnGenerate"   />
</Grid>

private void ColumnGenerate(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    var attrib = gridDataType.GetProperty(e.PropertyName).GetCustomAttributes(
        typeof (DescriptionAttribute), true);
    e.Column.Header = attrib == null ? 
        e.PropertyName : 
        ((DescriptionAttribute)attrib[0]).Description;
}

proto_after

 

As you can see, the second screenshot has user-friendly headings.

The code differences between before and after are summarised below:

  1. In the ‘After’ code, our ‘Person’ data object has its properties decorated with the DescriptionAttribute attribute – this is where we set our user-friendly descriptions.
  2. In the ‘After’ code, our DataGrid sets a handler for the ‘AutoGeneratingColumn’ event.
  3. In the ‘After’ code, our code-behind handles the above event, and sets the column headers to match the property description custom attribute.

Pretty simple really, and a great productivity boon during prototype development. Simply setup your grid, and as your domain object changes so does your grid – automatically.

 

I hope this little tips helps anyone else out in this scenario.

 

If anyone has any further comments, please let me know.

 

Dean

Tags: ,

Silverlight | DataBinding

Enumeration Binding in Silverlight

by Dean 31. January 2009 10:57

A contractor colleague of mine (Phil Steel) had an interesting Silverlight problem yesterday. He wanted to populate a Silverlight ComboBox with an enumeration, and implement 2-way binding to a property on his Data class (i.e. binding his property to the ‘SelectedItem’ on the ComboBox.

His requirements were as follows:

  1. The solution must have full design-time support in Expression Blend
  2. The code must give the developer an opportunity to create metadata for the enumeration that can be used for ‘user friendly’ visual values in the ComboBox
  3. There must be full 2-way binding, so no need to tap into the ‘SelectionChanged’ event to update the data object.
  4. Minimal C# code, with as much as possible being re-useable ‘as is’ for any enumeration.

Ok, so I googled around and only found fragments of a possible solution, so I decided to engineer one myself.

The details are below

Task 1 : Create reusable code that converts a .NET enumeration into a collection

 

There are 2 classes we need as part of this task, and the code is below

public sealed class EnumContainer
{
    public int EnumValue { get; set; }
    public string EnumDescription { get; set; }
    public object EnumOriginalValue { get; set; }
    public override string ToString()
    {
        return EnumDescription;
    }
 
    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;
        return EnumValue.Equals((int)obj);
    }
 
    public override int GetHashCode()
    {
        return EnumValue.GetHashCode();
    }
 
}
 
public class EnumCollection<T> : List<EnumContainer> where T : struct 
{
    public EnumCollection()
    {
        var type = typeof (T);
        if (!type.IsEnum)
            throw new ArgumentException("This class only supports Enum types");
        var fields = typeof (T).GetFields(BindingFlags.Static | BindingFlags.Public);
        foreach(var field in fields)
        {
            var container = new EnumContainer();
            container.EnumOriginalValue = field.GetValue(null);
            container.EnumValue = (int) field.GetValue(null);
            container.EnumDescription = field.Name;
            var atts = field.GetCustomAttributes(false);
            foreach (var att in atts)
                if (att is DescriptionAttribute)
                {
                    container.EnumDescription = ((DescriptionAttribute) att).Description;
                    break;
                }
            Add(container);
        }
        
    }
}

 

These 2 classes represent a bindable version of our enumeration. One class is an object that represents the enumeration values (EnumContainer), and the other is the collection.

(The part of the code above describing ‘CustomAttributes’ relates to the task below.

 

Task 2 (optional) : Add ‘user friendly’ metadata to your enumerations

 

This is a well known solution using the ‘DescriptionAttribute’ class

 

    public enum CustomerStatus
    {
        [Description("Not Yet Approved")]
        UnApproved,
 
        [Description("Pending Approval")]
        PendingApproval,
 
        [Description("Fully Approved")]
        Approved
    }

These descriptions will appear in your combobox

 

Task 3 : Create an IValueConverter to support 2-way binding of the ComboBox to the data object.

 

public class EnumValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, 
                CultureInfo culture)
    {
        return (int) value;
    }
 
    public object ConvertBack(object value, Type targetType, object parameter, 
CultureInfo culture)
    {
        if (value == null)
            return null;
        if (value.GetType() == targetType)
            return value;
        return ((EnumContainer) value).EnumOriginalValue;
    }
}

 

Task 4 : Implement a solution to suit your needs

 

Ok, now that we have all the base code added to our project, it’s time to implement a solution to our problem. The only c# class that needs to be created is a simple collection class that inherits from EnumCollection and allows full designer support in Expression Blend

So here (for example) are our data classes that we will use to demonstrate our solution

1) Our example ‘Customer’ class

public class Customer
{
    public string CustomerName { get; set; }
    public CustomerStatus Status { get; set; }
}

2) Our example Enumeration (notice the use of the DescriptionAttribute, which gives the ComboBox user friendly test)

 

    public enum CustomerStatus
    {
        [Description("Not Yet Approced")]
        UnApproved,
 
        [Description("Pending Approval")]
        PendingApproval,
 
        [Description("Fully Approved")]
        Approved
    }

 

3) Our collection to provide Blend support (as you can see, its just a simple inheritor as XAML doesnt easily support generics).

public class CustomerStatusEnumeration : EnumCollection<CustomerStatus>
{
 
}

 

4) Here's an example of wiring up the data object in the code

 

private void PageLoaded(object sender, RoutedEventArgs e)
{
    customer = new Customer() { CustomerName = "Customer1", 
        Status = CustomerStatus.PendingApproval };
    DataContext = customer;
}

 

Task 5 : Wire it all up in the Xaml

<UserControl x:Class="TestEnum.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300" xmlns:TestEnum="clr-namespace:TestEnum" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
    <UserControl.Resources>
        <TestEnum:CustomerStatusEnumeration 
            x:Key="CustomerStatusEnumerationDS" 
            d:IsDataSource="True"/>
        <TestEnum:EnumValueConverter  
            x:Key="EnumConverter" />
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <TextBox Text="{Binding Mode=TwoWay, Path=CustomerName}" 
                 Margin="5" HorizontalAlignment="Left" VerticalAlignment="Top" />
        <ComboBox Name="Combo" HorizontalAlignment="Left" 
                  VerticalAlignment="Top" 
                  ItemsSource="{Binding Mode=OneWay, 
                        Source={StaticResource CustomerStatusEnumerationDS}}" 
                  SelectedItem="{Binding Status, Mode=TwoWay, 
                        Converter={StaticResource EnumConverter}}" 
                  Margin="5" Grid.Column="1"/>
        <Button Content="Update" Click="UpdateCustomer" Margin="5" 
                HorizontalAlignment="Left" 
                VerticalAlignment="Top" Grid.Column="2" />
    </Grid>
</UserControl>

An here’s a screenshot of the above solution

enumexample

As you can see, it’s a very simple and effective solution. And should we require to wire up a second enum to our Silverlight app then the only class we’d need to create (assuming the enum already exists) is the empty collection class as described above.

 

I hope you like the solution, any comments about how to improve it then please let me know.

 

Dean

Tags: , ,

WCF | Silverlight | DataBinding

Fixed - Issues With WCF and Mixed VS/Blend Development

by Dean 25. January 2009 12:37

Some of you may have come across an issue when developing ‘fast and dirty’ demo apps in Silverlight that have a WCF backend service on the web application.

When developing throwaway demo apps for clients, you need to take all of the shortcuts you can get, so I always use the ‘Add service reference’ feature of Visual Studio to add a service reference within my Silverlight app to the host ASP.NET service (not advisable for production-quality apps though). This is a great feature because as you change the service interface you can keep in sync on your Silverlight app using a single service ‘Update’ button.

However, the big drawback of this approach is that the Visual Studio tool that creates the service reference hard-codes the service Uri into the generated classes.

If you were just using Visual Studio then this wouldn't be a problem, but unfortunately VS and Blend use different development servers that cannot co-exist on the same TCP port, so for one of the two development environments the Uri of the service is going to be wrong.

for example, you create the service reference in Visual Studio, which is configured to run the website on url ‘http://localhost:43667/DemoApp’ , then the hard-coded Uri for the service will be something like ‘http://localhost:43667/DemoApp/Service1.svc’.

However, Expression Blend will always run the solution on a different port, so it will run the app on a base url of something like ‘http://localhost:52234/DemoApp/’ which means that the Silverlight app wont access the service and your app will not be able to access the service when run from Blend.

Your gut reaction might be to try the following:

  1. Fix the port in Visual Studio to be the same as the one in Blend. – This wont work because Blend will detect VS is on it’s preferred port and will automatically switch to an alternative port.
  2. Use your local version of IIS to avoid the clash of the ports. – This will work but doesn’t really fit in with the idea of creating a portable throwaway demo app.
  3. Work in both VS and Blend, but only press F5 while in VS. This works but is a pain in the butt.
  4. Play with crossdomain policy files etc. – Not really a solution, especially as VS always tears-down the dev server when in DEBUG mode.

However, there is a solution for this scenario that only requires changing a single line of code to your Silverlight project.

Below is the some ‘Before’ sample code

private void PageLoaded(object sender, RoutedEventArgs e)
{
    var service = new Service1Client();
    service.DoWorkCompleted += (source, args) => MessageBox.Show("DONE");
    service.DoWorkAsync();
}


And now here is the same code with an added line that fixes this issue.

 

private void PageLoaded(object sender, RoutedEventArgs e)
{
    var service = new Service1Client(new BasicHttpBinding(BasicHttpSecurityMode.None), 
        new EndpointAddress(Application.Current.Host.Source.AbsoluteUri.Replace(
            Application.Current.Host.Source.AbsolutePath,"/Service1.svc")));
    service.DoWorkCompleted += (source, args) => MessageBox.Show("DONE");
    service.DoWorkAsync();
}

 

Basically, we’ve just changed the way we’ve constructed the service object, and passed in the necessary parameters to make the service reference relative rather than absolute. (You may need to replace the ‘Service1.svc’ parameter with one that is relevant to your project).

This is a neat little code snippet to keep on hand when developing these kinds of apps

I hope this helps you fix this annoying issue in your Silverlight development.

If you have any further suggestions then let me know.

 

Dean

Tags: , ,

Silverlight | DataBinding | WCF

Generic IValueConverter for Silverlight or WPF

by Dean 24. January 2009 10:37

When using the GridView in ASP.NET it is very handy to be able to include a ‘FormatString’ attribute to bound columns and the like – enabling you to display those dates, currency values, numbers etc in a more readable form.

I was surprised that Silverlight or WPF doesn't offer this out of the box, and I couldn't find any ‘obvious’ answers when I googled the subject,

Therefore, I created a simple IValueConverter to achieve the same result.

Here's the code for the converter:

namespace CBSSilverlight
{
    public class FormatStringValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, 
            CultureInfo culture)
        {
            if (value == null)
                return string.Empty;
            return !string.IsNullOrEmpty(parameter.ToString()) ? string.Format(culture, 
                parameter.ToString(), value) : value.ToString();
        }
 
        public object ConvertBack(object value, Type targetType, object parameter, 
            CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

As you can see, it doesn't support 2 way conversions, so it’s only really appropriate for read-only grids.

So in order to use this IValueConverter, the first step is to add the class with the code above into your VS2008 project.

Next, we need to include it as a static resource in our UserControl by adding the following to our XAML (don't forget to reference your namespace in the UserControl declaration)

 
<UserControl.Resources>
    <CBSSilverlight:FormatStringValueConverter x:Key="FormatConverter" />
</UserControl.Resources>
 

Now we can use the IValueConverter in our column declarations:

 

<data:DataGrid HorizontalAlignment="Left" 
               Grid.Row="1" Margin="0,0,0,1" 
               d:LayoutOverrides="Height" 
               ItemsSource="{Binding}" 
               AutoGenerateColumns="False" 
               MaxHeight="370" 
               SelectionChanged="SelectionChanged" >
    <data:DataGrid.Columns>
        <data:DataGridTextColumn 
            Header="Name" 
            Binding="{Binding Path=Name}" />
        <data:DataGridTextColumn 
            Header="Order Date" 
            Binding="{Binding Converter={StaticResource FormatConverter}, 
                ConverterParameter=\{0:dd-MMM-yy HH:mm\}, Path=OrderDate}"/>
        <data:DataGridTextColumn 
            Header="Total" 
            Binding="{Binding Converter={StaticResource FormatConverter}, 
                ConverterParameter=\{0:£0.00\}, Path=ItemsTotal}" />
        <data:DataGridTextColumn 
            Header="Discount" 
            Binding="{Binding 
            Path=DiscountCode}"/>
        <data:DataGridTextColumn 
            Header="Potage" 
            Binding="{Binding Converter={StaticResource FormatConverter}, 
            ConverterParameter=\{0:£0.00\}, Path=Postage}"/>
        <data:DataGridTextColumn 
            Header="Total" 
            Binding="{Binding Converter={StaticResource FormatConverter}, 
                ConverterParameter=\{0:£0.00\}, Path=GrandTotal}"/>
        <data:DataGridTextColumn 
            Header="Post" 
            Binding="{Binding Path=PostageType}"/>
        <data:DataGridTextColumn 
            Header="Num." 
            Binding="{Binding Path=TotalOrders}"/>
        <data:DataGridTextColumn 
            Header="First" 
            Binding="{Binding Converter={StaticResource FormatConverter}, 
            ConverterParameter=\{0:dd-MMM-yy\}, Path=FirstShippedOrder}" />
        <data:DataGridTextColumn 
            Header="Last" 
            Binding="{Binding Converter={StaticResource FormatConverter}, 
            ConverterParameter=\{0:dd-MMM-yy\}, Path=LastShippedOrder}" />
        <data:DataGridTextColumn 
            Header="Min. Val." 
            Binding="{Binding Converter={StaticResource FormatConverter}, 
            ConverterParameter=\{0:£0.00\}, Path=MinOrderValue}" />
        <data:DataGridTextColumn 
            Header="Max. Val." 
            Binding="{Binding Converter={StaticResource FormatConverter}, 
            ConverterParameter=\{0:£0.00\}, Path=MaxOrderValue}" />
    </data:DataGrid.Columns>
</data:DataGrid>

As you can see, the converter elegantly handles Dates, Money and anything else you can dream of, just like in the ASP.NET GridView.

And here’s a screen-shot below of the converter in action

formatgrid

As you can see – Dates, Date/Times and Money columns look great.

If anyone has any comments, or suggests any improvements – please feel free to add a comment below

Thanks

Dean Chalk

Tags: , ,

DataBinding | Silverlight

ObservableCollection Base Class With Expression Blend Designer Support – An Example Project

by Dean 19. January 2009 17:31

In my previous post showed how to create a lightweight collection class that can be used in expression blend enabling design-support for data bound control development.

In this post, I thought I’d expand on my previous writings and actually create a sample project from beginning to end – demonstrating how easy it is to use this solution.

Step 1 – Create Your Project (Programmers Job)

In VS2008, create a Silverlight application project – Im going to call mine EmployeeInfo for the purpose of this blog. I also need to create my data classes, So I’m going to create 3

  1. My generic base collection class that provides all of the test data (CollectionBase.cs)
  2. My Domain/Business object – in this example, it’s going to be a ‘person’ object that represents employee data (Person.cs)
  3. My person-specific collection class that provides a data-binding target for Expression Blend design (PersonCollection.cs)

EmployeeInfoSolution

The code for CollectionBase.cs and PersonCollection.cs is available in the previous post here

The code for the Person.cs code file is below

using System;
using System;
 
namespace EmployeeInfo
{
    public class Person
    {
        public string Name { get; set; }
        public DateTime Dob { get; set; }
        public int EmployeeId { get; set; }
        public double Salary { get; set; }
    }
}

As you can see, my example Person class is pretty simple, but you can make it as complicated as you like.

Now, I know in my project Im going to want to use a Silverlight DataGrid control, so I know that I need to add an additional project reference for this, and it’s easier to do this while in Visual Studio. Simple click on ‘Add Reference’ in the solution explorer and select the System.Windows.Controls and System.Windows.Controls.Data references to the solution (see below)

AddRef

Save all your files, lets move on to Blend.

Step 2 – Use Your Collection in Expression Blend

Open up the project in Blend and you’ll see the same code files in the blend project.

EmployeeInfoSolutionBlend

 

Now lets drop a DataGrid into our Page in order to demonstrate our example.

When ‘Design’ mode, click on the Asset Library button on the far left and select DataGrid from the available controls. Double click on the DataGrid button to add a DataGrid to your Page.

With the DataGrid selected in the Object and Timeline panel, go to the Properties tab on the right and set the Horizontal and Vertical Alignments to ‘Stretch (this will expand the DataGrid to fit it’s parent, giving us a surface for the DataGrid in the designer).

Next, lets start using our new Data classes.

On the project tab on the right hand side, click on the ‘+CLR Object’ button to popup the object data source creation panel

SelectCLR

Select ‘PersonCollection’ and hit enter, you will now see a new entry in the Data section of the right tab panel

DataSourceAdded

Now the final operation is to add this DataSource to our DataGrid, which simply requires us to drag and drop the new DataSource (as named ‘PersonCollection (Array)’ in above screenshot) onto the DataGrid. As soon as you do this you get a popup confirming where you want to attach the DataSource

AddToGrid

Select ‘DataGrid’ and the select ‘ItemsSource’ when it asks what you’d like to bind to in you DataGrid.

Instantly you should see a DataGrid full of test data

FinalDataGeid

As you can see – instant test data.

And now you can begin working on this grid in Blend – maybe hand-crank your column definitions, wire in your ValueConverters or re-template your headers etc.

(There's a neat trick with this solution, in that every time you re-build - Ctrl+Shift+B – you get new data, so you can keep rebuilding until you get the test data that suits you the most)

However, the test data is just random strings and numbers, which should be enough in most cases, but if not, then you can do the following

Step 3 (optional) – Refine Your Test Data

In the preceding code, the data was randomly generated, to get a more accurate version of the data available at design  time we can override the BuildTestData() method, and inject our own test objects into the collection.

Here's an example:

public override void BuildTestData()
{
    Add(new Person
            {
                Dob = new DateTime(1966, 12, 1),
                EmployeeId = 1445667,
                Name = "John Smith",
                Salary = 45025
            });
    Add(new Person
            {
                Dob = new DateTime(1981, 7, 31),
                EmployeeId = 3342556,
                Name = "Dave Hanley",
                Salary = 88343
            });
    Add(new Person
            {
                Dob = new DateTime(1977, 3, 5),
                EmployeeId = 5544667,
                Name = "Kunwar Singh",
                Salary = 66500
            });
    Add(new Person
            {
                Dob = new DateTime(1972, 3, 1),
                EmployeeId = 5688833,
                Name = "James Madden",
                Salary = 58332
            });
    Add(new Person
            {
                Dob = new DateTime(1980, 5, 9),
                EmployeeId = 988776,
                Name = "Harvey Sanders",
                Salary = 77221
            });
    Add(new Person
            {
                Dob = new DateTime(1969, 8, 12),
                EmployeeId = 665887,
                Name = "Joshua Jones",
                Salary = 34700
            });
}

And now in Blend when we re-build we get this:

DateGridData

So, there we have the test data in Blend

Step 4 – Add The Real Data Feed When Ready

This part is really simple, and is demonstrated in the code below

public partial class Page : UserControl
{
    public Page()
    {
    InitializeComponent();
    Loaded += ControlLoaded;
    }
 
    private void ControlLoaded(object sender, RoutedEventArgs e)
    {
        service.GetEmployeesCompleted += (source, args) => 
            {if (args.Error == null && !args.Cancelled) {
            PersonGrid.ItemsSource = args.Result; }};
        service.GetEmployeesAsync();
    }
}

And there you have it

Finally, another great tip, if you want to override the BuildTestData() method, and already have a service that can provide plenty of data – and that is create a quick and dirty console application that retrieves a suitable number of objects from the service, then use the XmlSerializer to serialize the collection to an xml string. This can then be added to your test collection class and de-serialized to re-create the same data without needing any backend services.

Dean

Tags: ,

Silverlight | DataBinding

RecentComments

Comment RSS
Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2012 Dean Chalk's Blog