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 MVVM – Simple Property Rules Engine

by Dean 9. September 2011 19:47

I'm currently working on a large legacy WPF project where my ViewModels often have a large number of properties, and those properties have some complex inter-relationships that need to be reflected in the behaviour of the app,  via the ViewModels and XAML bindings.

Specifically, some properties may need to support some of the following behaviours:

1) A property may need to invoke a change notification in the UI if a related property changes

2) A property may be a calculated value, and the calculation may rely on other property values which may change.

3) A property may need to support validation

With these requirements in mind, I wrote a simple POC (proof of concept) of a ‘rules engine’ that may provide those features and can be utilised in the ViewModel in a straightforward way.

Below is the code for the rules engine class:

using System;
using System.Collections.Generic;
using System.ComponentModel;

public class PropertyRuleEngine
{
    private readonly Dictionary<string, List<string>> dependencies = 
        new Dictionary<string, List<string>>();
    private readonly Dictionary<string, Action> evaluations = 
        new Dictionary<string, Action>();
    private readonly Dictionary<string, List<Func<string>>> validations = 
        new Dictionary<string, List<Func<string>>>();
    private readonly object source;

    public PropertyRuleEngine(object source)
    {
        this.source = source;
    }

    public PropertyRuleEngine AddDependency(string dependentPropertyName, 
        string propertyName)
    {
        if (!this.dependencies.ContainsKey(propertyName))
        {
            this.dependencies.Add(propertyName, new List<string>());
        }

        if (!this.dependencies[propertyName].Contains(dependentPropertyName))
        {
            this.dependencies[propertyName].Add(dependentPropertyName);
        }

        return this;
    }

    public PropertyRuleEngine AddEvaluatedProperty(string propertyName, 
        Action calculateAction)
    {
        this.evaluations.Add(propertyName, calculateAction);
        return this;
    }

    public PropertyRuleEngine AddValidationProperty(string propertyName, 
        Func<string> validationFunction)
    {
        if (!this.validations.ContainsKey(propertyName))
        {
            this.validations.Add(propertyName, new List<Func<string>>());
        }

        this.validations[propertyName].Add(validationFunction);
        return this;
    }

    public void Notify(string property, PropertyChangedEventHandler handler)
    {
        this.InvokePropertyChangedHandler(property, handler);
        if (!this.dependencies.ContainsKey(property))
        {
            return;
        }

        foreach (string tmp in this.dependencies[property])
        {
            if (this.evaluations.ContainsKey(tmp))
            {
                this.evaluations[tmp]();
            }
            else
            {
                this.InvokePropertyChangedHandler(tmp, handler);
            }
        }
    }

    public IEnumerable<string> GetErrors(string property)
    {
        List<string> result = null;
        if (!this.validations.ContainsKey(property))
        {
            return result;
        }

        foreach (var validation in validations[property])
        {
            var tmp = validation();
            if (!string.IsNullOrEmpty(tmp))
            {
                if (result == null)
                {
                    result = new List<string>();
                }
                result.Add(tmp);
            }
        }
        return result;
    }

    private void InvokePropertyChangedHandler(string propertyName, 
        PropertyChangedEventHandler originalHandler)
    {
        PropertyChangedEventHandler handler = originalHandler;
        if (handler == null)
        {
            return;
        }

        Delegate[] delegates = handler.GetInvocationList();
        foreach (Delegate d in delegates)
        {
            d.DynamicInvoke(new[] { this.source, 
                new PropertyChangedEventArgs(propertyName) });
        }
    }
}

 

And here is the (rather simplistic) test ViewModel that utilises the rules engine. It demonstrates how it handles inter-property dependencies, calculated properties and validation

using System;
using System.ComponentModel;

public class TestDomainObjectViewModel : INotifyPropertyChanged, IDataErrorInfo
{
    private readonly PropertyRuleEngine rulesEngine;
    public event PropertyChangedEventHandler PropertyChanged;

    public TestDomainObjectViewModel()
    {
        this.rulesEngine = new PropertyRuleEngine(this);
        this.rulesEngine
            .AddDependency("TestName", "TestId")
            .AddEvaluatedProperty("TestName", () => 
                this.TestName = string.Format("Name : {0}", this.TestId.ToString()))
            .AddValidationProperty("TestId", () => 
                    this.TestId > 50 ? "Number too big" : string.Empty);
    }

    private string testName;
    public string TestName
    {
        get
        {
            return testName;
        }
        set
        {
            testName = value;
            this.rulesEngine.Notify("TestName", this.PropertyChanged);
        }
    }

    private int testId;
    public int TestId
    {
        get
        {
            return this.testId;
        }
        set
        {
            this.testId = value;
            this.rulesEngine.Notify("TestId", this.PropertyChanged);
        }
    }
        
    public string this[string columnName]
    {
        get
        {
            var result = rulesEngine.GetErrors(columnName);
            if (result == null)
            {
                return null;
            }
            return string.Join(Environment.NewLine, result);
        }
    }

    public string Error
    {
        get
        {
            return null;
        }
    }
}

 

And finally, here is the XAML and (if your new to MVVM) the code-behind file for completeness

 

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <TextBlock Text="ID" />
    <TextBox Text="{Binding TestId, Mode=TwoWay, ValidatesOnDataErrors=True,
        UpdateSourceTrigger=LostFocus}" Grid.Column="1" Width="100">
        <TextBox.Style>
            <Style TargetType="TextBox">
                <Style.Triggers>
                    <Trigger Property="Validation.HasError" Value="True">
                        <Setter Property="ToolTip" 
                                Value="{Binding RelativeSource={RelativeSource Mode=Self}, 
                            Path=(Validation.Errors)[0].ErrorContent}" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>
    <TextBlock Text="Name" Grid.Row="1" />
    <TextBox Text="{Binding TestName, Mode=TwoWay}" Grid.Row="1" Grid.Column="1" 
                Width="100" IsReadOnly="True" />
    <Button Content="Update" Grid.Row="2" />
</Grid>
public MainWindow()
{
    InitializeComponent();
    DataContext = new TestDomainObjectViewModel();
}

Tags: ,

C# | MVVM | WPF | XAML

WPF TreeView – SelectedItem Two Way Binding

by Dean 1. September 2011 07:30

Because the standard WPF TreeView implementation supports Virtualization it is unable to support two way bindings on its SelectedItem property as standard. This makes sense, because with Virtualization you may not have a container (TreeViewItem) available for any particular bound data item at the time you need it (to set its IsSelected property of the TreeViewItem  to ‘True’).

The solution is to use attached properties to force the ItemContainerGenerator to create the necessary containers for each data item. This will break the virtualization support, but that is a price you need to pay for a solution to this issue, so you should only use it on TreeView controls where the lack of Virtualization wont be a significant drawback.

Here is the attached property implementation:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
 
public class DemoAttachedProps
{
    public static DependencyProperty SelectedItemProperty = DependencyProperty.RegisterAttached(
        "SelectedItem", typeof(object), typeof(DemoAttachedProps), 
        new PropertyMetadata(new object(), OnSelectedItemChanged));
 
    public static object GetSelectedItem(TreeView treeView)
    {
        return treeView.GetValue(SelectedItemProperty);
    }
 
    public static void SetSelectedItem(TreeView treeView, object value)
    {
        treeView.SetValue(SelectedItemProperty, value);
    }
 
    private static void OnSelectedItemChanged(DependencyObject d, 
        DependencyPropertyChangedEventArgs args)
    {
        var treeView = d as TreeView;
        if (treeView == null)
        {
            return;
        }
        treeView.SelectedItemChanged -= TreeViewItemChanged;
        var treeViewItem = SelectTreeViewItemForBinding(args.NewValue, treeView);
        if (treeViewItem != null)
        {
            treeViewItem.IsSelected = true;
        }
        treeView.SelectedItemChanged += TreeViewItemChanged;
    }
 
    private static void TreeViewItemChanged(object sender, 
        RoutedPropertyChangedEventArgs<object> e)
    {
        ((TreeView)sender).SetValue(SelectedItemProperty, e.NewValue);
    }
 
    private static TreeViewItem SelectTreeViewItemForBinding(
        object dataItem, ItemsControl ic)
    {
        if (ic == null || dataItem == null)
        {
            return null;
        }
        IItemContainerGenerator generator = ic.ItemContainerGenerator;
        using (generator.StartAt(generator.GeneratorPositionFromIndex(-1), 
            GeneratorDirection.Forward))
        {
            foreach (var t in ic.Items)
            {
                bool isNewlyRealized;
                var tvi = generator.GenerateNext(out isNewlyRealized);
                if (isNewlyRealized)
                {
                    generator.PrepareItemContainer(tvi);
                }
                if (t == dataItem)
                {
                    return tvi as TreeViewItem;
                }
 
                var tmp = SelectTreeViewItemForBinding(dataItem, 
                    tvi as ItemsControl);
                if (tmp != null)
                {
                    return tmp;
                }
            }
        }
        return null;
    }
}

And here is the attached property in action:

public class TestDataObjViewModel
{
    public List<TestDataObj> TestList { get; private set; }
    public List<TestDataObj> TestHierarchy { get; private set; }
 
    public TestDataObjViewModel()
    {
        TestList = new List<TestDataObj>();
        var top = new TestDataObj { TestText = "top" };
        TestList.Add(top);
        var second1 = new TestDataObj { TestText = "second 1" };
        TestList.Add(second1);
        top.Children.Add(second1);
        var second2 = new TestDataObj { TestText = "second 2" };
        TestList.Add(second2);
        top.Children.Add(second2);
        var third = new TestDataObj { TestText = "third" };
        TestList.Add(third);
        second1.Children.Add(third);
        TestHierarchy = new List<TestDataObj> { top };
    }
}
 
public class TestDataObj
{
    private readonly List<TestDataObj> children = new List<TestDataObj>();
 
    public string TestText { get; set; }
 
    public List<TestDataObj> Children
    {
        get
        {
            return children;
        }
    }
}

Here's the code-behind for the demo XAML

public MainWindow()
{
    InitializeComponent();
    DataContext = new TestDataObjViewModel();
}

And here’s the XAML

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <TreeView ItemsSource="{Binding TestHierarchy}" x:Name="treeView" 
              Margin="0,10" DemoApp1:DemoAttachedProps.SelectedItem="{x:Null}" >
        <TreeView.ItemContainerStyle>
            <Style TargetType="TreeViewItem">
                <Setter Property="IsExpanded" Value="True" />
            </Style>
        </TreeView.ItemContainerStyle>
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                <TextBlock Text="{Binding TestText}" />
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
    <ComboBox ItemsSource="{Binding TestList}" DisplayMemberPath="TestText" 
              Grid.Row="1" HorizontalAlignment="Left" x:Name="comboBox" 
              SelectedItem="{Binding ElementName=treeView, 
                Path=(DemoApp1:DemoAttachedProps.SelectedItem), Mode=TwoWay}" />
</Grid>

Tags: , , ,

Attached Properties | MVVM | 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 – 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 MVVM – Simple ‘MessageBox.Show’ With Action & Func

by Dean 6. May 2010 08:08

In the MVVM world, things like message boxes (MessageBox.Show) and Dialogs (open file, save file etc), don't naturally fit.

These popups are closely tied to the ‘View’ part of MVVM, but they can only really be invoked from the ‘ViewModel’ which will break the clean separation in MVVM.

If you google this issue, you will find a wide range of elaborate solutions, many of which are significant engineering projects in their own right.

I am a huge fan of implementing simple solutions wherever possible, as verbose code is the number one culprit in un-maintainable projects, so I was keen to find a solution that is simple, robust, elegant and doesnt break the MVVM pattern

The solution I came up with, is to use generic Action and Func Delegates.

OK, to illustrate my solution, I have created a new project using the ‘WPF Model-View-ViewModel Toolkit’, (http://wpf.codeplex.com/wikipage?title=WPF%20Model-View-ViewModel%20Toolkit), which installs a project template in VS2008

Here is my altered ‘MainViewModel'.cs’ class

public class MainViewModel : ViewModelBase
{
    private DelegateCommand exitCommand;
    private Action<string> popup;
    private Func<string, string, bool> confirm;
 
    public MainViewModel(Action<string> popup, Func<string, string, bool> confirm)
    {
        this.popup = popup;
        this.confirm = confirm;
    }
 
    public ICommand ExitCommand
    {
        get
        {
            if (exitCommand == null)
                exitCommand = new DelegateCommand(Exit);
            return exitCommand;
        }
    }
 
    private void Exit()
    {
        if (confirm("Are you sure you want to exit", "confirm exit"))
            Application.Current.Shutdown();
    }
}

As you can see, the MainViewModel’s constructor takes 2 delegates, 1 for popup and 1 for confirm

Now take a look at App.xaml.cs, where the View and the ViewModel get instantiated

private void OnStartup(object sender, StartupEventArgs args)
{
    // messagebox
    var popup = (Action<string>)(msg => MessageBox.Show(msg));
 
    // confirm box
    var confirm = (Func<string, string, bool>)((msg, capt) => 
        MessageBox.Show(msg, capt, MessageBoxButton.YesNo) == MessageBoxResult.Yes);
 
    Views.MainView view = new Views.MainView();
    view.DataContext = new ViewModels.MainViewModel(popup,confirm);
    view.Show();
}

If you look closely, you’ll see that my delegates actually map to methods in the static class ‘MessageBox’, which will give us the popups we need. The popup delegate will instantiate a simple message popup, and the confirm delegate will instantiate a message popup with confirm buttons.

And this is what happens when we click on the Exit menu item (note: this menu is created by default when you create a new project using the toolkit)

popup

Now, when we want to run unit tests on our ViewModel, we can just pass in dummy delegates

[TestMethod()]
public void MainViewModelConstructorTest()
{
    var dummyPopup = (Action<string>)((a) => {return;});
    var dummyConfirm = (Func<string,string,bool>)((a,b) => {return true;});
    ViewModels.MainViewModel target = new ViewModels.MainViewModel(dummyPopup, dummyConfirm);
    Assert.Inconclusive("TODO: Implement code to verify target");
}

Dean

Tags: ,

MVVM | C# | WPF | Unit Tests

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

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