Yet another blog about WPF, Surface, SL, MVVM, NUI....

2011

2010

2009

2008

Tag - pattern

Entries feed - Comments feed

 

Execute a command on a specified control when clicking on a button

16 June 2010

Today another example of the powerful ramora pattern : execute a RoutedCommand on an Control when you click on a button outside of the aimed control scope.

When is it useful ? : for example when you use the infragistics grid (xamdatagrid) and you want to execute the command which remove the selected row from a button outside of the grid. You then have to exectue the DeleteSelectedDataRecords command on the grid from a button which is outside of the scope of the grid ... then this behavior is useful !

Our implementation will also show how we can apply the "Weak Events pattern" with the dependency property trick (attached property / ramora pattern).

And of course this example can be adapted to subscribe on any event you wants and not only the click of a button.

The behavior itself

Our behavior will listen to the click button, and the handling of the click event will execute the routed command on the target element.

public class ExecuteCommandOnControl : DependencyObject
{
  #region Command
 
  /// <summary>
  /// Command Attached Dependency Property
  /// </summary>
  public static readonly DependencyProperty RoutedCommandProperty =
    DependencyProperty.RegisterAttached("RoutedCommand", typeof(RoutedCommand)
	     , typeof(ExecuteCommandOnControl));
 
  /// <summary>
  /// Gets the Command property.  This dependency property 
  /// indicates ....
  /// </summary>
  public static RoutedCommand GetRoutedCommand(DependencyObject d)
  {
    return (RoutedCommand)d.GetValue(RoutedCommandProperty);
  }
 
  /// <summary>
  /// Sets the Command property.  This dependency property 
  /// indicates ....
  /// </summary>
  public static void SetRoutedCommand(DependencyObject d, RoutedCommand value)
  {
    d.SetValue(RoutedCommandProperty, value);
  }
 
  #endregion
 
  #region IInputElement
  /// <summary>
  /// IInputElement Attached Dependency Property
  /// </summary>
  public static readonly DependencyProperty TargetProperty =
    DependencyProperty.RegisterAttached("Target", typeof(IInputElement), 
	   typeof(ExecuteCommandOnControl),
      new FrameworkPropertyMetadata(null,
        new PropertyChangedCallback(OnTargetChanged)));
 
  /// <summary>
  /// Gets the IInputElement property.  This dependency property 
  /// indicates ....
  /// </summary>
  public static IInputElement GetTarget(DependencyObject d)
  {
    return (IInputElement)d.GetValue(TargetProperty);
  }
 
  /// <summary>
  /// Sets the IInputElement property.  This dependency property 
  /// indicates ....
  /// </summary>
  public static void SetTarget(DependencyObject d, IInputElement value)
  {
    d.SetValue(TargetProperty, value);
  }
 
  /// <summary>
  /// Handles changes to the IInputElement property.
  /// </summary>
  private static void OnTargetChanged(DependencyObject d, 
     DependencyPropertyChangedEventArgs e)
  {
    Button button = d as Button;
 
    if (button != null)
    {
      ButtonClickEventManager.AddListener(button, _customEventListener);
 
    } else
    {
      throw 
	  new ArgumentException("This behavior can only be installed on non-null Button !");
    }
  }
 
  #endregion
 
  private static CustomEventListener _customEventListener = new CustomEventListener();
 
}



WeakEvents

So what is the class CustomEventListener ? This is the object which will handle the click on the button. This class implements the interface IWeakEventListener which is a necessary things to use the Weak event pattern.

We will then not subscribe the click event with the usual way ( using the operator +=) but by calling a WeakEventManager of our own : ButtonClickEventManager.

This manager will then keep a weak reference of our listener and the button on which we operate will then be free to be garbage collected. Otherwise, every button on which we subscribe would be keeped in the memory because each one would be connected to our behavior...

Here is the CustomEventListener class :

public class CustomEventListener : IWeakEventListener
{
  #region IWeakEventListener Members
  public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
  {
    if (managerType == typeof(ButtonClickEventManager))
    {
      var target = 
	  ExecuteCommandOnControl.GetTarget(sender as DependencyObject);
      var command = 
	  ExecuteCommandOnControl.GetRoutedCommand(sender as DependencyObject);
 
      if (target == null || command == null) return true;
 
      command.Execute(null, target);
      return true;
    } else
    {
      return false;
    }
  }
  #endregion
}

And here is our WeakEventMAnager :

public class ButtonClickEventManager : WeakEventManager
{
 
  public static void AddListener(Button source, 
       IWeakEventListener listener)
  {
    ButtonClickEventManager.
	   CurrentManager.ProtectedAddListener(source, listener);
  }
 
  public static void RemoveListener(Button source, 
       IWeakEventListener listener)
  {
    ButtonClickEventManager.
	   CurrentManager.ProtectedRemoveListener(source, listener);
  }
 
  protected override void StartListening(object source)
  {
    Button button = (Button)source;
    button.Click += this.OnButtonClick;
  }
 
  protected override void StopListening(object source)
  {
    Button button = (Button)source;
    button.Click -= this.OnButtonClick;
  }
 
  private void OnButtonClick(Object sender, RoutedEventArgs args)
  {
    base.DeliverEvent(sender, args);
  }
 
  private static ButtonClickEventManager CurrentManager
  {
    get
    {
      Type managerType = typeof(ButtonClickEventManager);
      ButtonClickEventManager manager = 
	  (ButtonClickEventManager)WeakEventManager.GetCurrentManager(managerType);
      if (manager == null)
      {
        manager = new ButtonClickEventManager();
        WeakEventManager.SetCurrentManager(managerType, manager);
      }
      return manager;
    }
  }
}



Example of use

Here a little example of how you can use it to launch the deleteSelected command of the infragistics datagrid from a button outside of the grid :

<Button Content="Delete selected"
  tools:ExecuteCommandOnControl.RoutedCommand="{x:Static igDP:DataPresenterCommands.DeleteSelectedDataRecords}"
  tools:ExecuteCommandOnControl.Target="{Binding ElementName=dataGrid}" />
<igDP:XamDataGrid Name="dataGrid" DataSource="{Binding Data}" />



Interesting readings



Shout it kick it on DotNetKicks.com Awaaz Up


 

Binding to the selected items of a ListBox (or an another items controls)

13 June 2010

A problem you often meet when using MVVM is to get the selected items of an items control, especially Listbox.

You can easily bind the selected item or the current items but when multi selection comes in the way, it becomes harder because the SelectedItems (with an 's' property is not available to binding).

In this article we will discover an easy way to bind yourself with an attached property to the SelectedItems property of the ListBox control.



We will use the Ramora pattern discussed before to bind ourself to the ListBox's selectionChanged events and then update the target list when the selection change.

If the target list implements INotifyCollectionChanged we will update the listbox selection when this event is raised.

Here is the resulting code :

public class ListBoxSelectedItemsSyncher : DependencyObject
{
  private static List<Syncher> _synchers = new List<Syncher>();
 
  #region ListToSync
 
  /// <summary>
  /// ListToSync Attached Dependency Property
  /// </summary>
  public static readonly DependencyProperty ListToSyncProperty =
    DependencyProperty.RegisterAttached("ListToSync", 
	             typeof(IList), typeof(ListBoxSelectedItemsSyncher),
      new FrameworkPropertyMetadata((IList)new List<Object>(),
        FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 
		       new PropertyChangedCallback(OnListToSyncChanged)));
 
 
  private static void OnListToSyncChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
    ListBox listBox = d as ListBox;
 
    if(!(d is ListBox)
     throw new ArgumentException("ListBoxSelectedItemsSyncher is only applyable to Listbox");
 
    Syncher synch = (from Syncher syncher 
	              in _synchers where syncher.ListBox == listBox select syncher)
                             .FirstOrDefault();
    if (synch != null)
    {
      synch.ListToSync = e.NewValue as IList;
    } else
    {
      synch = new Syncher(listBox, e.NewValue as IList);
      _synchers.Add(synch);
      listBox.Unloaded += new RoutedEventHandler(listBox_Unloaded);
    }
  }
 
  static void listBox_Unloaded(object sender, RoutedEventArgs e)
  {
    ListBox listBox = sender as ListBox;
    Syncher synch = (from Syncher syncher 
	              in _synchers where syncher.ListBox == listBox select syncher)
                      .FirstOrDefault();
    if (synch != null)
    {
      _synchers.Remove(synch);
      synch.Dispose();
      synch = null;
    }
  }
 
  /// <summary>
  /// Gets the ListToSync property.  This dependency property 
  /// indicates ....
  /// </summary>
  public static IList GetListToSync(DependencyObject d)
  {
    return (IList)d.GetValue(ListToSyncProperty);
  }
 
  /// <summary>
  /// Sets the ListToSync property.  This dependency property 
  /// indicates ....
  /// </summary>
  public static void SetListToSync(DependencyObject d, IList value)
  {
    d.SetValue(ListToSyncProperty, value);
  }
 
  #endregion
 
 
  internal class Syncher : IDisposable
  {
 
    private ListBox _listbox;
    public ListBox ListBox { get { return _listbox; } }
 
    private IList _listToSync;
    public IList ListToSync
    {
      get { return _listToSync; }
 
      set
      {
        detachTheListToSynch();
        _listToSync = value;
        attachTheListToSynch();
      }
    }
 
    public Syncher(ListBox listbox, IList listToSync)
    {
      _listbox = listbox;
      _listToSync = listToSync;
      attachTheListToSynch();
    }
 
    void collectionChangedList_CollectionChanged(object sender,
                        	NotifyCollectionChangedEventArgs e)
    {
      //Add new items   
      if (e.NewItems != null)
      {
        foreach (Object item in e.NewItems)
        {
          _listbox.SelectedItems.Add(item);
        }
      }
      if (e.OldItems != null)
      {
        foreach (Object item in e.OldItems)
        {
          _listbox.SelectedItems.Remove(item);
        }
      }
 
      if (e.Action == NotifyCollectionChangedAction.Reset)
        _listbox.SelectedItems.Clear();
 
      CommandManager.InvalidateRequerySuggested();
    }
 
    void _list_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      if (_listToSync != null)
      {
        //Add new items                   
        foreach (Object item in e.AddedItems)
        {
          _listToSync.Add(item);
        }
        foreach (Object item in e.RemovedItems)
        {
          _listToSync.Remove(item);
        }
        CommandManager.InvalidateRequerySuggested();
      }
    }
 
    #region IDisposable Members
    public void Dispose()
    {
      if (_listbox == null) return;
      _listbox.SelectionChanged -= _list_SelectionChanged;
      detachTheListToSynch();
      _listbox = null;
    }
    #endregion
 
    #region private methods
    private void attachTheListToSynch()
    {
      _listbox.SelectionChanged -= _list_SelectionChanged;
      if (_listToSync == null) return;
 
      INotifyCollectionChanged collectionChangedList = null;
      if ((collectionChangedList = _listToSync as INotifyCollectionChanged) != null)
        collectionChangedList.CollectionChanged 
	  += new NotifyCollectionChangedEventHandler(collectionChangedList_CollectionChanged);
 
      //Update the selection with the new list
      _listbox.SelectedItems.Clear();
 
      foreach (var item in _listToSync)
        _listbox.SelectedItems.Add(item);
 
      _listbox.SelectionChanged 
	       += new SelectionChangedEventHandler(_list_SelectionChanged);
    }
 
    private void detachTheListToSynch()
    {
      INotifyCollectionChanged collectionChangedList = null;
      if ((collectionChangedList = _listToSync as INotifyCollectionChanged) != null)
        collectionChangedList.CollectionChanged -= collectionChangedList_CollectionChanged;
    }
    #endregion
 
  }
 }


Here we are.

Edit : I found out later an another version of the same feature made by Samuel Jack on his blog : http://blog.functionalfun.net/2009/02/how-to-databind-to-selecteditems.html !



Shout it kick it on DotNetKicks.com


 

Binding on a Property which is not a DependencyProperty

5 April 2010

A lot of controls expose properties which are not DependencyProperties and then you can’t put a binding on it. On some other cases, you only have a getter as accessor and you can’t put a binding on it too…

This is for example the case for the ribbon’s group of the office ribbon or the converter’s parameter.

If you ever tried to do so, you surely had an exception throwned :

A 'Binding' cannot be set on the 'SetCEDEJDED' property of type 'Tralala'.
A 'Binding' can only be set on a DependencyProperty of a DependencyObject.


In this post we will discover a work-around…

The main idea is to use a kind of proxy/observer (a definition can be found in this post) which will reflect every change on the source object to the target object and vice versa.

Here are the main parts of the solution ..

Specification: the XAML code we'll use

Here is the code snippet which describe how we will use our proxy in the XAML. There will be no code-behind.

<Window x:Class="BindOnNonDependencyProperty.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:us="clr-namespace:BindOnNonDependencyProperty"
    Title="BindOnNonDependencyProperty" >
  <DockPanel >
    <TextBox x:Name="myTextBox" DockPanel.Dock="Top"  />
    <TextBox x:Name="monTextBlockCible"  DockPanel.Dock="Top"  />
    <us:ExtendedBinding Source="{Binding ElementName=myTextBox,Path=Text,Mode=TwoWay}"
              Target="{Binding ElementName=monTextBlockCible,Path=Text,Mode=TwoWay}"
              />
  </DockPanel>
</Window>



The correct base class for our proxy/observer

We will call it ExtendedBinding and it must be inherithing from DependencyObject at last to be abble to own DependencyProperty. But the only way to add a DependencyObject into our XAML is to add it into a resourceDictonary.

This is a drawnback because, by doing it, it will no more be into the control's tree and then it will be impossible to make a binding on one of it's property. Note that it's still possible to use it as a Target from another place in our XAML but you can't do a Binding on one of it's properties. This code will not work :

<Windows.Resources>
   <MyDependencyObject x:Key="myKey" MyProperty="{Binding Tralala, ElementName=myTarget}" />
</Windows.Resources>



Then to put it insode the control's tree, we only had to make it an UIElement will you say... No because in the actual version of the Framework you won't have inheritance of the DataContext and the use of the 'ElementName' binding will be prohibited. Hopefully, there is a solution, our proxy have to inherit from FrameworkElement and everything will work fine !

The DependencyProperties

We will add two dependencyProperties, one will be the target and the second will be the source.

These DP will be customize by using the FrameworkPropertyMetadata two enables these features :

  • Binding will be done using the TwoWay mode,
  • The UpdateSourceTrigger used will be the PropertyChanged event.


How it works

The core of our proxy is to override the DependencyObject's OnPropertyChanged method. Each change on the source or the target will update it's counterparty.

We have to take care not to fall into a loop : when we will update the target or the source we'll also raise a PropertyChanged event and we must ignore this one....


Final code

public class ExtendedBinding : FrameworkElement
  {
    #region Source DP
    //We don't know what will be the Source/target type so we keep 'object'.
    public static readonly DependencyProperty SourceProperty =
      DependencyProperty.Register("Source", typeof(object), typeof(ExtendedBinding),
      new FrameworkPropertyMetadata()
      {
        BindsTwoWayByDefault = true,
        DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
      });
    public Object Source
    {
      get { return GetValue(ExtendedBinding.SourceProperty); }
      set { SetValue(ExtendedBinding.SourceProperty, value); }
    }
    #endregion
 
    #region Target DP
      //We don't know what will be the Source/target type so we keep 'object'.
    public static readonly DependencyProperty TargetProperty =
      DependencyProperty.Register("Target", typeof(object), typeof(ExtendedBinding),
      new FrameworkPropertyMetadata()
      {
        BindsTwoWayByDefault = true,
        DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
      });
    public Object Target
    {
      get { return GetValue(ExtendedBinding.TargetProperty); }
      set { SetValue(ExtendedBinding.TargetProperty, value); }
    }
    #endregion
 
    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
      base.OnPropertyChanged(e);
      if (e.Property.Name == ExtendedBinding.SourceProperty.Name)
      {
	//no loop wanted
        if (!object.ReferenceEquals(Source, Target))
          Target = Source;
      }
      else if (e.Property.Name == ExtendedBinding.TargetProperty.Name)
      {
	//no loop wanted
        if (!object.ReferenceEquals(Source, Target))
          Source = Target;
      }
    }
 
  }



Shout it kick it on DotNetKicks.com



 

MVVM - Creating ViewModel : create dynamic proxies with Castle (solution 3 of n)

11 March 2010

Here is the next episode of our serie MVVM - Creating ViewModel. A list of all the article about creating a ViewModel is here..

Today we are going to see how to create dynamic proxies for our business objects.

What are Dynamic proxies ?

Readers in a hurry can directly jump to the third part "An implementation".

Proxies are a Desing Pattern which is used a lot in our computer programming world. Here is the Wikipedia definition :

A proxy, in its most general form, is a class functioning as an interface to something else. The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate.


The easiest example to understand I have found is sunglasses. The sunglasses are a proxy between the real world (with all the funky colors) and the world you see trough them (where everything is grey/brown)... You also noticed that the proxy is not neutral, it adds a behaviors(here the gray filter on your vision), but this is not mandatory.
Note : Laurent pointed out that he prefers the TV analogy to explain proxies but I prefer the sunglasses which there is no transformations 3D to 2D.

Proxies

With this in mind let met introduce some vocabulary :

  • The subject: this is the proxied object. In our case : the real world,
  • The client: this is the object which wants to use the subject. In our case : the one who wear the sunglasses,
  • The proxy: this is the object used by the client and which uses the subject. In our case : the sunglasses,
  • The behaviors/interceptors: this is a behavior that the subject does not have and that the proxy creates. In a program, a common interceptor is a logger which tells the start and the end of a method.


Behavior algo example :

public void loggerExampleBehavior(Method theMethod){
  Log("Before method execution.");
  //Execution of the method
  Log("After method execution.");
}


So how to use them for our ViewModel ?(theory)

Readers in a hurry can directly jump to the third part "An implementation".

To be brief : we will add the INotifyPropertyChanged behavior to the business objects by creating a dynamicProxy. We will then no more use directly the business object but the proxies of/to them.
We will so launch the PropertyChangedEvent for each call made to a setter of the business object. Pretty simple ? proxies_INotify_logic.jpg

An implementation with Castle dynamicProxy (code)


To implement the proxy pattern dynamicly we will use Castle Dynamic Proxy. This framework is pretty simple to use as you'll see.

There is only one limitation : properties of your business object must be marked as virtual.

Creation of the proxy

To create our proxy I declare a static method. This method is generic and make any object send NotifyPropertyChanged events on setter calls. The differents class used will be described later.

public class ProxyCreator
{
  public static T MakeINotifyPropertyChanged<T>() where T : class, new()
  {
    //Creates a proxy generator
    ProxyGenerator proxyGen = new ProxyGenerator();
 
    //Generates a proxy using our Interceptor and implementing INotifyPropertyChanged
    var proxy = proxyGen.CreateClassProxy(
      typeof(T),
      new Type[] { typeof(INotifyPropertyChanged) },
      ProxyGenerationOptions.Default,
      new NotifierInterceptor()
      );
 
    return proxy as T;
  }
}

The interceptor


The interceptor does two main things :

  1. It exposes a PropertyChangedEventHandler,
  2. It raises the PropertyChangedEventHandler event when a setter is called with the good name.



Also, I have cached the PropertyChangedEventArgs for better performance.

public class NotifierInterceptor : IInterceptor
{
  private PropertyChangedEventHandler handler;
  public static Dictionary<String, PropertyChangedEventArgs> _cache =
    new Dictionary<string, PropertyChangedEventArgs>();
 
  public void Intercept(IInvocation invocation)
  {
    //Each subscription to the PropertyChangedEventHandler is intercepted (add)
    if (invocation.Method.Name == "add_PropertyChanged")
    {
      handler = (PropertyChangedEventHandler)
            Delegate.Combine(handler, (Delegate)invocation.Arguments[0]);
      invocation.ReturnValue = handler;
    }
    //Each de-subscription to the PropertyChangedEventHandler is intercepted (remove)
    else if (invocation.Method.Name == "remove_PropertyChanged")
    {
      handler = (PropertyChangedEventHandler)
         Delegate.Remove(handler, (Delegate)invocation.Arguments[0]);
      invocation.ReturnValue = handler;
    }
    //Each setter raise a PropertyChanged event
    else if (invocation.Method.Name.StartsWith("set_"))
    {
      //Do the setter execution
      invocation.Proceed();
      //Launch the event after the execution
      if (handler != null)
      {
        PropertyChangedEventArgs arg =
          retrievePropertyChangedArg(invocation.Method.Name);
        handler(invocation.Proxy, arg);
      }
    }
    else invocation.Proceed();
  }
 
  // Caches the PropertyChangedEventArgs
  private PropertyChangedEventArgs retrievePropertyChangedArg(String methodName)
  {
    PropertyChangedEventArgs arg = null;
    NotifierInterceptor._cache.TryGetValue(methodName, out arg);
    if (arg == null)
    {
      arg = new PropertyChangedEventArgs(methodName.Substring(4));
      NotifierInterceptor._cache.Add(methodName, arg);
    }
    return arg;
  }
}

How we use it in the application


We'll only have to expose the proxies to our views with a little snippet of code :

MyBusinessObject myBusinessObject;
DataContext = myBusinessObject = ProxyCreator.MakeINotifyPropertyChanged<MyBusinessObject>();

Interesting links




Shout it kick it on DotNetKicks.com



 

MVVM - Creating ViewModel tutorial

MVVM - Creating ViewModel is a serie of blogposts that talk in details about various ways to create a ViewModel to use it with the MVVM pattern.

You often do not create the business object used by the application which are created by another team and that these objects are not ready for binding.

So you must find a solution to create an object which will in fact be very similar of your business object BUT ready for binding. These posts describe the differents solutions you have in hand.


So far the following parts have been published:

  1. Wrap your business object
  2. Use XAML Power Toys
  3. Create dynamic proxies
  4. Use the object builder of CAB
  5. Generate IL code (TO DO)



If you have other solution, don't forget to tell about in the comments !





 

MVVM - How to integrate the Office Ribbon respecting the pattern (especially the commands)

4 March 2010

Synopsis

The ribbon controls - introduced by office 2007 -are available for free on the Microsoft Office web site (French users should set the language to "english" to access the ressources). They can leverage the user's experience of your application and are pretty simple to use.

When I wanted to add them into one of my application I realized that it was broking the M-V-VM pattern.

In this post, we will see how to use the Ribbon, then what exactly is the issue and finally examine the solution I use as a work-around.

How to use the ribbon

This is very easy, here are the steps :

  • Download the library on the web site (http://msdn.microsoft.com/fr-fr/office/aa973809.aspx),
  • Add a reference to the dll in your project and dclare in the XAML this XML namespace : clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary
  • Then you are free to use the ribbon's controls.


A central part of the Ribbon library is the RibbonCommand. A RibbonCommand is a WPF command plus a lot of things related to how it's presented : a label, a description, an large image, a small image, etc.... Then every button, combobox, checkbox, ... used in the Ribbon use these infos to change the way they are presented. Here is a little example : MVVMRibbonExample

<Window x:Class="MVVMRibbon.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
    Title="MainWindow" Height="350" Width="525">
 
  <Window.Resources>
    <r:RibbonCommand x:Key="MyFirstCommand" LabelTitle="A command"
        LabelDescription="A command description"
        LargeImageSource="/MVVMRibbon;component/antoine64x64.jpg"
        SmallImageSource="/MVVMRibbon;component/antoine64x64.jpg"
        Executed="RibbonCommand_Executed" CanExecute="RibbonCommand_CanExecute" />
 
    <r:RibbonCommand x:Key="ApplicationMenuCommand"
        LargeImageSource="/MVVMRibbon;component/antoine64x64.jpg"
        SmallImageSource="/MVVMRibbon;component/antoine64x64.jpg" />
  </Window.Resources>
 
  <DockPanel LastChildFill="True">
    <r:Ribbon DockPanel.Dock="Top">
      <!--I hide the QuickAccessToolBar because I have no use of it-->
      <r:Ribbon.QuickAccessToolBar>
        <r:RibbonQuickAccessToolBar Visibility="Collapsed" />
      </r:Ribbon.QuickAccessToolBar>
 
      <!--Here is the ApplicationMenu : the bubble acting as a main menu in Office-->
      <r:Ribbon.ApplicationMenu>
        <r:RibbonApplicationMenu  Command="{StaticResource ApplicationMenuCommand}" />
      </r:Ribbon.ApplicationMenu>
 
      <!-- And finally the well-know "tabs"-->
      <r:RibbonTab Label="A first tab">
        <!--The controls are grouped in the tabs-->
        <r:RibbonGroup>
          <r:RibbonButton Command="{StaticResource MyFirstCommand}" />
        </r:RibbonGroup>
      </r:RibbonTab>
 
      <r:RibbonTab Label="A second tab"></r:RibbonTab>
    </r:Ribbon>
 
    <FlowDocumentReader />
  </DockPanel>
</Window>


Why using the RibbonCommand is broking the pattern

As you can see in the code above, when you declare the RibbonCommands in the XAML, you have to set a Execute and CanExecute event's handler. These handlers are set in the code behind and this is what breaks the pattern.

Sow why not only declare RibbonCommand inside the viewModels ? Because this will put presentation informations (those inside the RibbonCommand like images, description) inside the ViewModel which one must be decoupled from the way the data is presented.

Actually, only declaring RibbonCommands inside the ViewModel breaks the pattern because it exist a very strong link between the data and how it's presented in these objects.

An another thing is that you can't bind anything to the Ribbon commands because : A 'Binding' cannot be set on the 'XXX' property of type 'RibbonCommand'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.... Yes, a RibbonCommand is not a DependencyObject.

So I can't use the ribbon ?

Nooooo ! A solutions exists : first you can create some kind of proxies to the command which will make the commands available as a ressource in the views(CommandReference) through binding. Then the view will be responsible to create the RibbonCommands as they which from these commands in the ressources. To this purpose we'll have to extend the standard RibbonCommand to make them accepts a Command as a property.

Ok, ok, I heard your question : why not directy make the extended RibbonCommands acts as a proxy ? The answere is that RibbonCommand does not inherits from DependencyObject and so we can't bind anything on it :-( ! (Which means, by the way, that we can't bind the commands of the viewModels directly to them).

I did not invent this technique, it's from :



The proxy for the commands

As pointed out in this article I call them CommandReference.

We declare a DependencyProperty on which we will bind the command in the ViewModel. As you can see, this class is also a ICommand : all the calls will be translated to the binded command.

public class CommandReference : Freezable, ICommand
{
public static readonly DependencyProperty CommandProperty = 
  DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference), 
     new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
public ICommand Command
{
  get { return (ICommand)GetValue(CommandProperty); }
  set { SetValue(CommandProperty, value); }
}
 
#region ICommand Members
public bool CanExecute(object parameter){ 
  return (Command != null) ?Command.CanExecute(parameter):false;
}
 
public void Execute(object parameter){ Command.Execute(parameter);}
 
public event EventHandler CanExecuteChanged;
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  CommandReference commandRef = d as CommandReference;
  if (commandReference != null)
  {
	ICommand lastCommand = e.OldValue as ICommand;
	if (oldCommand != null) lastCommand .CanExecuteChanged -= commandRef .CanExecuteChanged;
 
	ICommand nextCommand = e.NewValue as ICommand;
	if (newCommand != null) nextCommand .CanExecuteChanged += commandRef .CanExecuteChanged;
  }
}
#endregion
 
#region Freezable
protected override Freezable CreateInstanceCore()
{
  return new CommandReference();
}
#endregion

The extended RibbonCommands

We simply add a ICommand property to the RibbonCommand wich one we will be abble to fill in with a StaticRessource.

public class RibbonCommandExtended : RibbonCommand
{
  private ICommand _command;
  public ICommand Command
  {
    get { return _command; }
    set
    {
      _command = value;
      if (_command != null)
      {
        this.CanExecute += us_CanExecute;
        this.Executed += us_Executed;
      }
      else
      {
        this.CanExecute -= us_CanExecute;
        this.Executed -= us_Executed;
      }
    }
  }
 
  private void us_Executed(object sender, ExecutedRoutedEventArgs e)
  {
    Command.Execute(e.Parameter);
  }
 
  private void us_CanExecute(object sender, CanExecuteRoutedEventArgs e)
  {
    e.CanExecute = Command.CanExecute(e.Parameter);
  }
}


Then, what will my XAML looks like .

Here it is, especially for you, very simple :

<Window.Resources>
  <fwk:CommandReference x:Key="MyCommandReference"
      Command="{Binding MyViewModelCommand}" />
  <fwk:RibbonCommandExtended x:Key="cmd_MyCommand" LabelTitle="A labek"
      LabelDescription="A description"
      Command="{StaticResource MyCommandReference}"
      LargeImageSource="/MVVMRibbon;component/antoine64x64.jpg"
      SmallImageSource="/MVVMRibbon;component/antoine64x64.jpg" />
</Window.Resources>

Then you use the RibbonCommandExtended as you will have used the standard RibbonCommand.

Isn't it a little long to make something pretty simple ? The answer is definitively yes but the Microsoft seems to be working on new version of the ribbon which will respects the M-V-VM pattern...

Why not using our RamoraPattern ?

This is not possible because as I pointed out before, the RibbonCommands are not DependencyObject and so we can't attach properties to them :-( !



Links

Here are some links you may find interesting on the same subjects :


Shout it kick it on DotNetKicks.com



 

MVVM : How to keep collections of ViewModel and Model in sync

2 March 2010

As pointed out in this post collections of the ViewModels and the models are not sync. This is because we do not access directly to the model but to an ObservableCollection(in the viewModel) which contains the object of the original collection(in the model) and that these two collections are not the same...

As pointed out in the comments on CodeProject, there is work-around. I try here to present two of them !

A first solution : register to the wrapping collection changes

The first solution is to register to the events of the ObservableCollection in your ViewModel and to translate the changes to the wrapped collection.
It is very straighforward but it becomes very fastidious if you have a lot of collections to deal with.

Here is the code :

//Wrap the business object collection
_friendsName = new ObservableCollection<string>(myBusinessObject.FriendsName);
//Register to the wrapping CollectionChanged event
_friendsName.CollectionChanged += new NotifyCollectionChangedEventHandler(_friendsName_CollectionChanged);
 
...
 
//Translate the changes to the underlying collection
void _friendsName_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
 switch (e.Action)
 {
  case NotifyCollectionChangedAction.Add:
   _myBusinessObject.FriendsName.AddRange(
    e.NewItems.OfType<String>()
    );
   break;
  case NotifyCollectionChangedAction.Remove:
   _myBusinessObject.FriendsName.RemoveAll(
    friendName => e.OldItems.Contains(friendName)
    );
   break;
  //Reset = Clear
  case NotifyCollectionChangedAction.Reset:
   _myBusinessObject.FriendsName.Clear();
   break;
 }
}



Another solution : create a proxy

You also can create a class which will act as a Proxy to the businessObject. Its only function will be to leverage the INotifyCollectionChanged events when necessary. I called it MVMCollectionSyncher for ModelViewModelCollectionSyncher and here is the code (which is very straightforward) :

public class MVMCollectionSyncher<T> : ICollection<T>, IDisposable, INotifyCollectionChanged
{
 #region fields
 private ICollection<T> _wrappedCollection;
 #endregion
 
 public MVMCollectionSyncher(ICollection<T> wrappedCollection)
 {
  if (wrappedCollection == null)
   throw new ArgumentNullException(
    "wrappedCollection",
    "wrappedCollection must not be null.");
  _wrappedCollection = wrappedCollection;
 }
 
 #region ICollection<T> Members
 public void Add(T item)
 {
  _wrappedCollection.Add(item);
  FireCollectionChanged(
   new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
 }
 
 public void Clear()
 {
  FireCollectionChanged(
   new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
  _wrappedCollection.Clear();
 }
 
 public bool Contains(T item)
 {
  return _wrappedCollection.Contains(item);
 }
 
 public void CopyTo(T[] array, int arrayIndex)
 {
  _wrappedCollection.CopyTo(array, arrayIndex);
 }
 
 public int Count
 {
  get { return _wrappedCollection.Count; }
 }
 
 public bool IsReadOnly
 {
  get { return _wrappedCollection.IsReadOnly; }
 }
 
 public bool Remove(T item)
 {
  if (_wrappedCollection.Remove(item)) {
   FireCollectionChanged(
     new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
   return true;
  }
  return false;
 }
 
 #endregion
 
 #region IEnumerable<T> Members
 public IEnumerator<T> GetEnumerator()
 {
  return _wrappedCollection.GetEnumerator();
 }
 #endregion
 
 #region IEnumerable Members
 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
 {
  return _wrappedCollection.GetEnumerator();
 }
 #endregion
 
 #region INotifyCollectionChanged Members
 public event NotifyCollectionChangedEventHandler CollectionChanged;
 private void FireCollectionChanged(NotifyCollectionChangedEventArgs eventArg)
 {
  NotifyCollectionChangedEventHandler handler = CollectionChanged;
  if (handler != null) handler.Invoke(this, eventArg);
 }
 #endregion
 
 #region IDisposable Members
 public void Dispose() { _wrappedCollection = null; }
 #endregion
}



Then in your ViewModel instead of presenting an ObservableCollection<> you offer an MVMCollectionSyncher with this code.

//Creation
MVMCollectionSyncher _friendsName = new MVMCollectionSyncher<string>(myBusinessObject.FriendsName);
...
//Property
public MVMCollectionSyncher<String> FriendsName
{
 get { return _friendsName; }
 set
 {
  if (value != null){
   _friendsName.Dispose();
   _friendsName = value;
   FirePropertyChanged("FriendsName");
  }
 }
}



Here are some links dealing on the same subject :



Shout it kick it on DotNetKicks.com
 

MVVM - Creating ViewModel : wrap your business object (solution 1 of n).

24 February 2010

Intro

When you create WPF applications, you may (or you should !) use the M-V-VM pattern and so have to use/create ViewModel. The viewModel job is mainly to expose properties of your businessObjects to your views, ready for binding.

To be ready for the binding the most used solution is to implement INotifyPropertyChanged and to fire events fo every change made. An issue is that you often do not create the business object used by the application which are created by another team and that these object are not ready for binding. So you must find a solution to create an object which will in fact be very similar of your business object BUT ready for binding.

In this serie of post I will try to give some of the solution we can use to do so. A list of all the article is here..

Wrap your business object(solution 1 of n)

The first solution which appears in every developper's brain is to wrap theBusinessObject(BO) into the viewmodel. Every properties of your ViewModel will actually be some kind of proxy to/from the underlying BO.

For example let's take for granted that you have a businessObject like this :

/// <summary>
/// I'am the business object created by another team. I'am not binding-aware : shame on me !
/// </summary>
public class MyBusinessObject
{
  public String LastName { get; set; }
  public String FirstName { get; set; }
  public int Age { get; set; }
  public List<String> FriendsName { get; set; }
}


You will then gives the Business object to your viewModel which will act as a proxy. the result will be something like this :

public class ViewModelWrapped : ViewModelBase
{
private MyBusinessObject _myBusinessObject;
private ObservableCollection<String> _friendsName;
 
public ViewModelWrapped(MyBusinessObject myBusinessObject)
{
  _myBusinessObject = myBusinessObject;
  _friendsName = new ObservableCollection<string>(myBusinessObject.FriendsName);
}
 
public String FirstName
{
  get { return _myBusinessObject.FirstName; }
  set
  {
    FirePropertyChanged("FirstName");
    _myBusinessObject.FirstName = value;
  }
}
 
public String LastName
{
  get { return _myBusinessObject.LastName; }
  set
  {
    FirePropertyChanged("LastName");
    _myBusinessObject.LastName = value;
  }
}
 
public int Age
{
  get { return _myBusinessObject.Age; }
  set
  {
    FirePropertyChanged("Age");
    _myBusinessObject.Age = value;
  }
}
 
public ObservableCollection<String> FriendsName
{
  get { return _friendsName; }
  set
  {
    if (value != null)
    {
      FirePropertyChanged("FriendsName");
      _myBusinessObject.FriendsName = value.ToList<String>();
    }
  }
}
}



Notes : Something interesting to look at is how we wrap our collections to make them bindable : quite a job ! More over the model and the viewModel list are no more synched... The list object itself is synched but the operation on the collection will be made on the viewModel collection and no more on the model collection. In this case adding or removing a friend's name will affect only the ViewModel and not the model.


Pros and cons

Pro :

  • The name of the properties exposed to your views can be differents from those in the business object
  • It's very easy to understand the code when you read it again, even a few months later



Cons:

  • It's a very boring job to re-create the properties of the viewModel to map those from the BO
  • Collection and Set of the model are no more synched with the ViewModel
  • Copy-cut code can leads to error, especially when raising INotifyPropertyChanged events where case matters



NB: I've found an article of Josh Smit on this subject that you may find useful too.
and this link too : MVVM: To Wrap or Not to Wrap? How much should the ViewModel wrap the Model? (Part 1)

Shout it kick it on DotNetKicks.com