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

2011

2010

2009

 

Amazing WPF Controls / JetPack Theme updated !

13 June 2011

Hello,

I have updated my previously introduced control library on codeplex. I received a lot of comments because the jetPack theme was not working fine bacause of some animation.

It is now fixed and I want to thank everyone for the feedback !__

Here are the list of the available updates:

  • JetPack Theme is now building and working fine at design time and runtime,
  • The brushes are now freezed for better performance,
  • The showcase code is easier to read and refactored,



For those wondering how I solved the issues here is the explanation. Instead of providing directly the value to the DiscreteObjectKeyFrame which seems to mislead it in the conversion to the correct type, I provide the aimed object.

<!--This code-->
<DiscreteObjectKeyFrame KeyTime="0" Value="*" />
 
<!--Is replaced by this one :--> 
<DiscreteObjectKeyFrame KeyTime="0">
   <DiscreteObjectKeyFrame.Value>
      <GridLength>*</GridLength>
   </DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>



If you find another issue, mail me ! My email adress is on this page or you can contact me through twitter...

Shout it kick it on DotNetKicks.com



 

What ? Dynamic resources creates Memory leaks in WPF 3.5 (SP1) ???

27 March 2011

Everyone is told to not use DynamicResource during its childhood in WPF-(wonder)land. They are simply evil and they kill the app performance. Sometimes, we fall in the dark side of the force and use them effectively to be sure that the application will follow the trend and suits itself well. This is not as bad as it seems and it is in fact sometimes necessary and wanted.

 But today, while I was trying to improve the performance and the memory usage of a WPF application I work on, I discovered that they were also creating memory leaks! I felt betrayed. I was using them and they put a knife in my back while I trusted them to be useful. 


In this post we will see how it can happens, and how to solve this (little) issue.

Continue reading...

 

UIAutomation, Coded UI tests, AutomationPeer and WPF/silverlight custom controls

24 March 2011

The Coded UI Tests, available in Visual Studio Ultimate or Premium, enable the creation of automated tests for the User Interface. This is a really nice feature because you are no more forced to make "hand made" tests which takes hours to be performed. 

The WPF controls in the framework are ready to be used by the Microsoft UI Automation which is itself used by the coded UI tests. This means that when you use the screen recorder to record the tests on your UI, it will be able to find the several control used in your application.

When you create you own custom controls or extend standard one, the recorder would not be able to find them at first and so a whole part of the screen may not be available for tests. Actually, it is possible to record a test but every steps will be done using screen position: click at (120,30), drag from (120,30) to (10,40). This is really annoying because any changes in the UI may broke all your tests.


In this post, we will see how to make a custom control fully useable in Coded UI tests scenarii. We will so answer the question "Why cannot the code UI test recorder find anything inside my WPF or Silverlight custom control ?"


Note: the same technique is used by the accessibility clients and by enabling this feature you also ease the people using your application through UI automation client like the partially-sighted person.

Continue reading...

 

Introducing the amazing WPF controls library on Codeplex !

25 October 2010

Hello everyone,

 

I am pleased to announce you the creation of the Amazing WPF controls library on Codeplex !

It will contains the differents controls I describe and/or peel on this blog. I will try to make an article for each control added to the library.

 

Here is a list of the currently available controls :

 

 

I also added to it the JetPack theme ported to WPF.

 

 

I hope you enjoy it !

 

http://amazingwpfcontrols.codeplex.com/



Shout it kick it on DotNetKicks.com

 

Migrate the Jetpack theme from Silverlight to WPF

14 October 2010

There is a great theme named Jetpack available for Silverlight applications which can be found on the Tim Heuer web site. I was wondering if it can be used in WPF applications and the answer is yes ! Here are the modifications I had to do to make all this work :

 

  • SelectionBackground replace by SelectionBrush (except for the DatePicker)
  • I removed the HyperlinkButton which does not exist in WPF
    DataGridFrozenGrid is not existing in WPF : I replaced it by a GRID
  • No navigation system : Frame style removed,
  • No page : replaced by HeaderedContentControl
  • DescriptionViewer : not available in WPF and removed,
  • TabNavigation replaced by : KeyboardNavigation.ControlTabNavigation
  • ValidationSummary does not exist in WPF : I removed it,
  • No Watermark in WPF: I removed it
  • ContentPresenter does not support direct content :  Ireplaced it by ContentControl,
  • AutoCompleteBox : not available in WPF and removed,
  • DataPager : not available in WPF and removed,
  • ChildWindow : not available in WPF and removed,
  • I removed the style targeting the textblock because it was imposing itself anywhere especially in the listboxitem leadind to wong behaviours,
  • I replace the differents name by their WPF PART_XXX counterparts
  • PasswordBox :  Ireplaced the ContentElement border by a ScrollViews named PART_ContentHost
  • I Replaced the animations key <DiscreteObjectKeyFrame KeyTime="0" Value=”Visible" /> by <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}" />
  • Rewrite entirely the scrollbar style so it works nicely...
  • Rewrite entirely the Slider style so it works nicely...
  • Rewrite a fex the TabItem/Tab control style so it works nicely...

 

 

JetpackingWPF

 

So far, here is a list of the translated control’s theming :

  • TextBox,
  • ListBox,
  • Combobox,
  • PasswordBox,
  • ProgressBar,
  • Button,
  • ToggleButton,
  • RadioButton
  • Checkbox,
  • Scrollbar,
  • ScrollViewer,
  • Slider,
  • TreeView,
  • TabControl,
  • Label

 

So far, here is a list of the NOT TESTED and may not working element

  • Datagrid,
  • DatePicker,
  • Grid Splitter,
  • Expander,
  • ContextMenu.

 

 

PS: the demo application is in the post attachment…

 

I am currently working on it to add more working them components.

 

Edit (16/10/2010, 01:28) : I added new controls themes(Scrollbar, ScrollViewer, Slider, TreeView, TabControl, Label) and a new demo applications.


 Shout it kick it on DotNetKicks.com

 

How to create your own control library (how-to + tips)

3 October 2010

Reusability and factorizing are maybe the most commons things you want and use when you are developing applications. In WPF it often means creating controls library (i don’t mean UserControl) that will be easy to use in multiple applications.


In this post we'll see all the step to create a control library useable in differents projects.

The example to illustrate the theory will be to create an headered control.

 

PS: note that it already exists in the framework under the nice name of GroupBox.

Creating a control library

Foundation of the project

The first step is to use the VS2010 Wizard named "WPF Custom control library" to create the library which will contain the controls. We will name it, by excess of modesty : “AmazingsWPFControls” !

 

As you can see, VS has created some files and directory for us :

project tree

 

 

Let’s take them one by one ! The “Themes\generic.xaml” file is where the WPF engine, when it loads your control, looks for it’s theme aka. a style setting a template. Here is its content :

<Style TargetType="{x:Type local:CustomControl1}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomControl1}">
                <Border Background="{TemplateBinding Background}"
 BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

 

The “AssemblyInfo.cs” file which contains a specific attribute :" ThemeInfo”. It tells the framework where to look for the theme files. Usually it is used like this :

[assembly: ThemeInfo( ResourceDictionaryLocation.None,

//where theme specific resource dictionaries are located //(used if a resource is not found in the page, // or application resource dictionaries) ResourceDictionaryLocation.SourceAssembly

//where the generic resource dictionary is located //(used if a resource is not found in the page, // app, or any theme specific resource dictionaries) )]

 

The “CustomControl1.cs” which is an empty custom control the wizard created for you(how kind of him). Its content is interesting because it show you that you have to override the MetaData of the newly created control to force the WPF engine using the style defined in the generic.xaml file.

public class CustomControl1 : Control
{
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1),
new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
}

 

 

Each time you want to add a new custom control to the library, you will have to reproduce each of these steps. Of course, the wizard to add a new Custom Control can do it for you but its always a good thing to know how it works. In our case we’ve to rename CustomControl1 to HeaderedControl.

 

Here we are with the foundation of the library. Now we are going to add a template and behaviors to the control.

 

Choose the more appropriated base class

By default the wizard make the control a inherit from the Control class but there are differents options that you may consider for base class :

  • Control : This is a base class which lets you the most freedom but the most to do !
  • ContentControl : This is a good choice if the control will be used to represent a single piece of content.
  • HeaderedContentControl : This is a good choice to represent a single element with an header.
  • ItemsControl: This is a good choice if the control will be used to represent a list of items.

 

 

In our case the best choice should be to inherit from HeaderedContentControl  but we will prefer the ContentControl for demonstration purpose. Note that it meets the specifications: we will represent a single element with an header.

 

Adding a Template and some behaviors to the control

Setting the template of the control

Unlike UserControl there is no code-behind and you set the template of the control by creating a style in the generic.xaml file. This also means that it’s not possible to access the elements via their names because the control and its representation are linked together only at runtime. But don’t worry, you can still access the differents parts following the steps described in this post.

 

 

One of the best practice is to name the revelants parts of your control prefixed with “PART”. By revelants parts, I mean the elements which together build the behavior of your control : for example the track of the slider control is named “PART_Track”. This best practice make your control re-templatable by designer which will know that the controls prefixed by “PART_” are mandatory in the new template they are building. Finally, the parts are declared in the control class via an attribute “TemplatePart” defining the name and the aimed type of control. So in our example, we’ll add a part named “PART_Header which will be the header :

 

/// <summary>
/// A control displaying an header at the top of its content.
/// </summary>    
[TemplatePart(Name = "PART_Header", Type = typeof(Border))]
public class HeaderedControl : Control
{
  // ...
}

 

Adding a content to the control

To add and display a content two things are needed : a property containing the content and a place in the template to display it. The content can be of any type of your choice and is usually placed in the Content property for ContentControl and the Items property for ItemsControl. This is the default behavior but you can override it and tell which property is the content by using the ContentPropertyAttribute on the control’s class :

[ContentPropertyAttribute("Content")] public class HeaderedControl : ContentControl { // ... }

 

Then, in the template, you define where the content is displayed using a ContentPresenter that you can place where you wants depending of the need. We also use a TemplateBinding to bind the content of the class to the ContentPresenter. In our case we add the content under the header :

<Style TargetType="{x:Type local:HeaderedControl}">
   <Setter Property="Template">
       <Setter.Value>
           <ControlTemplate TargetType="{x:Type local:HeaderedControl}">
               <DockPanel>
                   <Border x:Name="PART_Header" DockPanel.Dock="Top" Background="{TemplateBinding Background}"
                           BorderBrush="{TemplateBinding BorderBrush}"
                           BorderThickness="{TemplateBinding BorderThickness}" />
                   <ContentPresenter Content="{TemplateBinding Content}" />
               </DockPanel>
           </ControlTemplate>
       </Setter.Value>
   </Setter>
</Style>

 

 

Adding a bindable property to the control

Adding a property to your control is quite useful if you want to permit some customization of its behavior. This is something very common that you use often without noticing it : every attribute you set in the XAML is simply a property of the controls you use.

 

In our case there is a lot which are already here because of our inheritance of FrameworkElement. But let’s said that we want to add another one all we have to do is do declare a new dependency property and its accessor as in the code behind. In our case we add two property : the header (of type Object) and the position of the header in the control. So we have this class :

public class HeaderedControl : ContentControl
{
  static HeaderedControl()
  {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(HeaderedControl),
          new FrameworkPropertyMetadata(typeof(HeaderedControl)));
  }
 
  #region Header
 
  /// <summary>
  /// Header Dependency Property
  /// </summary>
  public static readonly DependencyProperty HeaderProperty =
      DependencyProperty.Register("Header", typeof(object), typeof(HeaderedControl),
          new FrameworkPropertyMetadata((object)null));
 
  /// <summary>
  /// Gets or sets the Header property. This dependency property 
  /// indicates the header to display.
  /// </summary>
  public object Header
  {
      get { return (object)GetValue(HeaderProperty); }
      set { SetValue(HeaderProperty, value); }
  }
 
  #endregion
 
  #region HeaderPosition
 
  /// <summary>
  /// HeaderPosition Dependency Property
  /// </summary>
  public static readonly DependencyProperty HeaderPositionProperty =
      DependencyProperty.Register("HeaderPosition", typeof(HeaderPosition), typeof(HeaderedControl),
          new FrameworkPropertyMetadata(HeaderPosition.Top));
 
  /// <summary>
  /// Gets or sets the HeaderPosition property. This dependency property 
  /// indicates ....
  /// </summary>
  public HeaderPosition HeaderPosition
  {
      get { return (HeaderPosition)GetValue(HeaderPositionProperty); }
      set { SetValue(HeaderPositionProperty, value); }
  }
 
  #endregion
 
 
  /// <summary>
  /// Defines where to place the header
  /// </summary>
  public enum HeaderPosition : int
  {
      /// <summary>
      /// The header is positioned on the left.
      /// </summary>
      Left = 0,
 
      /// <summary>
      /// The header is positioned at the top.
      /// </summary>
      Top = 1,
 
      /// <summary>
      ///  The header is positioned on the right.
      /// </summary>
      Right = 2,
 
      /// <summary>
      ///  The header is positioned at the bottom.
      /// </summary>
      Bottom = 3,
 
  }
}
 

Now let’s use these properties in the template to show and place the header at the right position. We will add another content presenter to display the header and we’ll use a converter to convert the header position into the correct DockPanel.Dock value (only the revelant XAML is showed) :

<ControlTemplate TargetType="{x:Type local:HeaderedControl}">
  <Border Background="{TemplateBinding Background}" 
          BorderBrush="{TemplateBinding BorderBrush}"
          BorderThickness="{TemplateBinding BorderThickness}">
      <DockPanel>
          <Border x:Name="PART_Header" 
              DockPanel.Dock="{Binding PositionOfTheHeader,
              RelativeSource={RelativeSource Mode=TemplatedParent},
              Converter={conv:HeaderPositionToDockPositionConverter}}">
              <ContentPresenter Content="{TemplateBinding Header}" />
          </Border>
          <ContentPresenter Content="{TemplateBinding Content}" />
      </DockPanel>
  </Border>
</ControlTemplate>
Adding a custom command to the control

Adding a command to our control is not as straightforward as I thought before to try Smile but here is the guideline is used to make it work fine :

  1. Create the RoutedCommand of your choice in the control class,
  2. Create a private method which will be called when the command is executed,
  3. Register a Class Command binding of this command in the static constructor of the control (you can think of it as a static command binding which will be called each time the command is executed),
  4. In the handler of the class command binding gets the sender of the command, cast it to your class and call the private method you created just before.

 

In our case there no really need of a command but lets say that we will add a command which sets the position of the header property to the “Top” value we will results with this code (against only the revelant part):

static HeaderedControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(HeaderedControl),
   new FrameworkPropertyMetadata(typeof(HeaderedControl)));
 
//Instanciate the command
MoveHeaderToTopCommand = new RoutedUICommand("MoveHeaderToTop",
   "MoveHeaderToTop", typeof(HeaderedControl));
 
//Create the command binding
CommandBinding moveHeaderToTopCommandBinding =
   new CommandBinding(MoveHeaderToTopCommand, 
                  MoveHeaderToTopCommand_Executed,
                  MoveHeaderToTopCommand_CanExecute);
 
CommandManager.
   RegisterClassCommandBinding(typeof(HeaderedControl), 
                     moveHeaderToTopCommandBinding);
}
 
 
static void MoveHeaderToTopCommand_Executed(
        object sender, ExecutedRoutedEventArgs e)
{
    HeaderedControl headeredControl = 
                 sender as HeaderedControl;
    if (headeredControl != null)
       headeredControl.moveHeaderToTop();
}
 
static void MoveHeaderToTopCommand_CanExecute(object sender, 
       CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
 
private void moveHeaderToTop()
{
this.PositionOfTheHeader = HeaderPosition.Top;
}
 

The command is then useable as any other WPF command in your application and you can even use parameters if needed.

 

Adding a Routed event to the control

Adding an event can be useful to notify the others part of your application that an action occurred in the control or to deliver informations.

 

To add an event here are the guideline :

  1. (optionnal) Creates the events args inherithing from RoutedEventArgs that you will use
  2. Declare the event handler of the event,
  3. Declare the RoutedEvent with the name of your choice,
  4. Creates the accesor for a simplier use,
  5. (optionnal) creates a raiseYourEvent method to raise the event easily.

 

 

Now lets assume we wants to raise an event when the header is clicked. For this we’ll subscribe to the Header “MouseDown” event in the OnApplyTemplate() method and raise a newly created event in its handler :

public override void OnApplyTemplate()
{
   base.OnApplyTemplate();
 
   PART_Header = this.GetTemplateChild("PART_Header") as Border;
   if (PART_Header == null)
       throw new ArgumentNullException(
           "Can't find PART_Header in the HeaderedControl template.");
   PART_Header.MouseDown += (a, b) => { RaiseHeaderClickedEvent(); };
}
 
 
/// <summary>
/// the event handler delegate
/// </summary>
public delegate void HeaderClickedEventHandler(object sender, 
    HeaderClickedEventArgs e);
 
/// <summary>
/// Create a custom routed event by first 
/// registering a RoutedEventID
/// This event uses the bubbling routing strategy
/// </summary>
public static readonly RoutedEvent HeaderClickedEvent = 
    EventManager.RegisterRoutedEvent(
   "HeaderClicked", RoutingStrategy.Bubble, 
   typeof(HeaderClickedEventHandler), typeof(HeaderedControl));
 
/// <summary>
/// Occurs when the header is clicked.
/// </summary>
public event RoutedEventHandler HeaderClicked
{
   add { AddHandler(HeaderClickedEvent, value); }
   remove { RemoveHandler(HeaderClickedEvent, value); }
}
 
/// <summary>
/// Raises the header clicked event.
/// </summary>
void RaiseHeaderClickedEvent()
{
   HeaderClickedEventArgs newEventArgs = 
    new HeaderClickedEventArgs(
            HeaderedControl.HeaderClickedEvent);
   RaiseEvent(newEventArgs);
}
 
/// <summary>
/// The header has been clicked event args
/// </summary>
public class HeaderClickedEventArgs : RoutedEventArgs
{
   /// <summary>
   /// Initializes a new instance of the
   /// <see cref="HeaderClickedEventArgs"/> class.
   /// </summary>
   /// <param name="routedEvent">The routed event.</param>
   public HeaderClickedEventArgs(RoutedEvent routedEvent) 
     : base(routedEvent) { }
}

 

 

 

Ease the use of your library by declaring an URL as an XML namespace

 

What is it ? Simply that when you will use the controls in another page you will not declare the XML namespace via an assembly name but via an URL easy to remember. Example :

<Window x:Class="AmazingWPFControls.Showcase.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
 xmlns:controls="http://blog.lexique-du-net.com/wpf/AmazingsWPFControls"
/>


Why to do it ?

So now all you have to do is to create a bright new control Smile

Interesting links



Shout it kick it on DotNetKicks.com

 

WPF tip : How can I use static/dynamic resources in my tests

24 September 2010

You are surely, as I am, creating a lot of units tests in your WPF applications.

Some of the tests may use statics or dynamics resources declared in your application and when you launch the tests, all you get is an XamlParseException exception thrown.

System.Windows.Markup.XamlParseException: 'Provide value on 'System.Windows.StaticResourceExtension' threw an exception.' Line number '11' and line position '83'.
---> System.Exception: Cannot find resource named 'myStaticResource'. Resource names are case sensitive.



The tip to make them work is quite simple to use. What is actually happening ? : the created control is looking for the resource via the classic russian puppets system and does not find the application because the tests does not create it. And the application is exactly where you have declared the needed ressources. So all we've got to do is to create the application and initialize it before running the tests.

Also we called the InitializeComponent() methods to make sure the application is initialized. It seems that this last part is only necessary if the resources are from the same assembly than the application.

So just add this method to your tests classes :

/// <summary>
///Use ClassInitialize to run code before running the first test in the class
/// </summary>
/// <param name="testContext">The test context.</param>
[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext)
{
  //Create the application for resources.
  if (System.Windows.Application.Current == null)
  {
    App application = new App();
    application.InitializeComponent();
  }
}



The App.xaml content is below:

<Application x:Class="MyAssembly.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:myLib="clr-namespace:MyAssembly.ViewModels"
	StartupUri="Views\MainWindow.xaml">
 
  <Application.Resources>
    <ResourceDictionary>
      <myLib:ViewModelLocator x:Key="myStaticResource" />
    </ResourceDictionary>
 
  </Application.Resources>
</Application>

Here we are !


Shout it kick it on DotNetKicks.com
 

Quick tip : My WPF Fullscreen window still display a border on right and bottom side... how to remove them

23 September 2010

Creating a fullscreen window in WPF is quite simple, you just have to set WindowState to Maximized and WindowStyle to None.

But in my current project I was facing a little drawback : a border was still visible on the bottom and on the right side of my window :

Example of broken border

I have seen some solution on the web talking about Win32 interop and WM_GETMINMAXINFO messages, butit is quite simplier : just set the ResizeMode attribute of your Window to ''NoResize''. You will then end up with this windows :

<Window x:Class="lexiqueDuNet.com.Window1" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
       WindowState="Maximized" WindowStyle="None"  ResizeMode="NoResize" >
</Window>



Shout it kick it on DotNetKicks.com



 

How to create an animated expander

21 September 2010

The expander control can be used in a lot of situations but the one proposed by default is quite "rigid".

In this post we will discover how to animate it quite simply just via XAML !

The WPF engine lets us redefine the template of the controls and we'll just do that.

The goal aimed

What we aim is to get the same functionnality as the original expander.
This is not as simple as we tought and I've seen a lot of expander loosing some of their behaviors when they became "animated" : original value of IsExpanded ignored, ExpandDirection ignored, etc...

Getting the necessary files

The files needed are :

  1. the original control template of the Expander
  2. the expander's button style which are linked to it


To get them, I used Expression Blend folowing the MSDN steps on this page : http://msdn.microsoft.com/en-us/library/cc294908.aspx

Especially for you, they are also linked to the post :) !. Here is the expander original template :

<ControlTemplate TargetType="{x:Type Expander}">
  <Border SnapsToDevicePixels="true" 
      Background="{TemplateBinding Background}" 
      BorderBrush="{TemplateBinding BorderBrush}" 
      BorderThickness="{TemplateBinding BorderThickness}" 
      CornerRadius="3">
    <DockPanel>
      <ToggleButton FocusVisualStyle="{StaticResource ExpanderHeaderFocusVisual}" 
              Margin="1" 
              MinHeight="0" 
              MinWidth="0" 
              x:Name="HeaderSite" 
              Style="{StaticResource ExpanderDownHeaderStyle}" />
      <ContentPresenter Focusable="false" 
                Visibility="Collapsed" 
                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                Margin="{TemplateBinding Padding}" 
                x:Name="ExpandSite"   />
    </DockPanel>
  </Border>
  <ControlTemplate.Triggers>
    <Trigger Property="IsExpanded" Value="true">
      <Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
    </Trigger>
    <Trigger Property="ExpandDirection" Value="Right">
      <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Right"/>
      <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Left"/>
      <Setter Property="Style" TargetName="HeaderSite" 
                     Value="{StaticResource ExpanderRightHeaderStyle}"/>
    </Trigger>
    <Trigger Property="ExpandDirection" Value="Up">
      <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Top"/>
      <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Bottom"/>
      <Setter Property="Style" TargetName="HeaderSite" 
                    Value="{StaticResource ExpanderUpHeaderStyle}"/>
    </Trigger>
    <Trigger Property="ExpandDirection" Value="Left">
      <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Left"/>
      <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Right"/>
      <Setter Property="Style" TargetName="HeaderSite" 
                    Value="{StaticResource ExpanderLeftHeaderStyle}"/>
    </Trigger>
    <Trigger Property="IsEnabled" Value="false">
      <Setter Property="Foreground" 
               Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
    </Trigger>
  </ControlTemplate.Triggers>
</ControlTemplate>



Add the new behavior to the expander

As you can see, the original template plays on the Visibility property to expand or collapse the "expandable" part.

We will change that and add a scaling transformation on the "expendable part" of the control that we'll animate at the right moment (when the IsExpanded property value change).
Also we'll not use simple DataTrigger but MultiTrigger because we have to starts differents animation depending of the Expand direction.

The result is a quite simple but lenghty XAML file (the AnimatedExpanderStyles is linked to the post) :

<Style x:Key="ourAnimatedExpanderStyle" TargetType="{x:Type Expander}">
<Setter Property="Foreground"
             Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Template">
  <Setter.Value>
    <ControlTemplate TargetType="{x:Type Expander}">
 
      <ControlTemplate.Resources>
        <ResourceDictionary>
          <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary
     Source="/My.Assembly;component/AnimatedExpander/AnimatedExpanderStyles.xaml" />
        </ResourceDictionary.MergedDictionaries>
          <Storyboard x:Key="scaleYUp">
            <DoubleAnimation From="0" To="1" Duration="0:0:0.25" 
Storyboard.TargetName="ExpandSite"
Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleY)" />
          </Storyboard>
          <Storyboard x:Key="scaleYDown">
            <DoubleAnimation Fr  om="1" To="0" Duration="0:0:0.25" 
Storyboard.TargetName="ExpandSite"
Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleY)" />
          </Storyboard>
          <Storyboard x:Key="scaleXUp">
            <DoubleAnimation From="0" To="1" Duration="0:0:0.25"
Storyboard.TargetName="ExpandSite"
Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleX)" />
          </Storyboard>
          <Storyboard x:Key="scaleXDown">
            <DoubleAnimation From="1" To="0" Duration="0:0:0.25"  
Storyboard.TargetName="ExpandSite"
Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleX)" />
          </Storyboard>
        </ResourceDictionary>
      </ControlTemplate.Resources>
 
      <Border BorderBrush="{TemplateBinding BorderBrush}"
          BorderThickness="{TemplateBinding BorderThickness}"
          Background="{TemplateBinding Background}" CornerRadius="3"
                SnapsToDevicePixels="true">
        <DockPanel>
          <ToggleButton
              IsChecked="{Binding Path=IsExpanded, Mode=TwoWay, 
                                    RelativeSource={RelativeSource TemplatedParent}}"
              Margin="1" MinHeight="0" MinWidth="0" x:Name="HeaderSite"
              Style="{StaticResource ExpanderDownHeaderStyle}">
            <ContentPresenter Content="{TemplateBinding Header}"
                ContentTemplate="{TemplateBinding HeaderTemplate}"
                ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}" Margin="1"
                Focusable="false" />
          </ToggleButton>
 
          <ContentPresenter x:Name="ExpandSite"
              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
              Margin="{TemplateBinding Padding}" Focusable="false">
            <ContentPresenter.LayoutTransform>
              <ScaleTransform x:Name="scaleTransform" ScaleX="1" ScaleY="1" />
            </ContentPresenter.LayoutTransform>
          </ContentPresenter>
        </DockPanel>
      </Border>
      <ControlTemplate.Triggers>
        <MultiTrigger>
          <MultiTrigger.Conditions>
            <Condition Property="IsExpanded" Value="True" />
            <Condition Property="ExpandDirection" Value="Up" />
          </MultiTrigger.Conditions>
          <MultiTrigger.EnterActions>
            <BeginStoryboard Storyboard="{StaticResource scaleYUp}" />
          </MultiTrigger.EnterActions>
          <MultiTrigger.ExitActions>
            <BeginStoryboard Storyboard="{StaticResource scaleYDown}" />
          </MultiTrigger.ExitActions>
        </MultiTrigger>
 
        <MultiTrigger>
          <MultiTrigger.Conditions>
            <Condition Property="IsExpanded" Value="True" />
            <Condition Property="ExpandDirection" Value="Down" />
          </MultiTrigger.Conditions>
          <MultiTrigger.EnterActions>
            <BeginStoryboard Storyboard="{StaticResource scaleYUp}" />
          </MultiTrigger.EnterActions>
          <MultiTrigger.ExitActions>
            <BeginStoryboard Storyboard="{StaticResource scaleYDown}" />
          </MultiTrigger.ExitActions>
        </MultiTrigger>
 
        <MultiTrigger>
          <MultiTrigger.Conditions>
            <Condition Property="IsExpanded" Value="True" />
            <Condition Property="ExpandDirection" Value="Left" />
          </MultiTrigger.Conditions>
          <MultiTrigger.EnterActions>
            <BeginStoryboard Storyboard="{StaticResource scaleXUp}" />
          </MultiTrigger.EnterActions>
          <MultiTrigger.ExitActions>
            <BeginStoryboard Storyboard="{StaticResource scaleXDown}" />
          </MultiTrigger.ExitActions>
        </MultiTrigger>
        <MultiTrigger>
          <MultiTrigger.Conditions>
            <Condition Property="IsExpanded" Value="True" />
            <Condition Property="ExpandDirection" Value="Right" />
          </MultiTrigger.Conditions>
          <MultiTrigger.EnterActions>
            <BeginStoryboard Storyboard="{StaticResource scaleXUp}" />
          </MultiTrigger.EnterActions>
          <MultiTrigger.ExitActions>
            <BeginStoryboard Storyboard="{StaticResource scaleXDown}" />
          </MultiTrigger.ExitActions>
        </MultiTrigger>
 
        <Trigger Property="ExpandDirection" Value="Down">
          <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Bottom" />
          <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Top" />
 
        </Trigger>
        <Trigger Property="ExpandDirection" Value="Up">
          <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Top" />
          <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Bottom" />
          <Setter Property="Style" TargetName="HeaderSite"
              Value="{DynamicResource ExpanderUpHeaderStyle}" />
 
        </Trigger>
        <Trigger Property="ExpandDirection" Value="Right">
          <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Right" />
          <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Left" />
          <Setter Property="Style" TargetName="HeaderSite"
              Value="{DynamicResource ExpanderRightHeaderStyle}" />
 
        </Trigger>
 
        <Trigger Property="ExpandDirection" Value="Left">
          <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Left" />
          <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Right" />
          <Setter Property="Style" TargetName="HeaderSite"
              Value="{DynamicResource ExpanderLeftHeaderStyle}" />
        </Trigger>
 
      </ControlTemplate.Triggers>
    </ControlTemplate>
  </Setter.Value>
</Setter>
</Style>

Conclusion

To use this Expander we have to use this little snippet :

<Expander Header="Our text" ExpandDirection="Up" 
               Style="{StaticResource ourAnimatedExpanderStyle}"    >
    <!--- content here ! --->
</Expander>



Everythings seems to works fine and all the behaviors of the original expander are still here !

Shout it kick it on DotNetKicks.com




 

UserControl/Control : how to get a reference to an element of the template

14 September 2010

When you want to create your own custom control you have two choices : create an UserControl or inherit from one of the "Control's classes" (ContentControl, ItemsControls or Control itself). When doing so, you'll surely need to access to the visual parts of your template from the code to add to it a nice behavior.

In this post, we'll discover how to access the template children by using the FindName method even on UserControl.

You create a control

I won't explain you how to create a custom control, so here is its base code :

[TemplatePart(Name = "PART_MyGrid", Type = typeof(Grid))]
  public class MyCustomControl : ContentControl
  {
    private Grid myAimedGrid;
 
    static MyCustomControl()
    {
	  //Overrides the style by ours
      DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl),
          new FrameworkPropertyMetadata(typeof(MyCustomControl)));
    }
  }



And here is how we define its template for the generic visual theme in the "Themes\generic.xaml" file. Notice that we add a named Grid :"PART_MyGrid". We'll seek for it later from the code.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:FindNamesApplication.MyContentControl">
  <Style TargetType="{x:Type local:MyCustomControl}">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type local:MyCustomControl }">
          <Grid x:Name="PART_MyGrid" Background="Black" Width="{TemplateBinding Width}"
              Height="{TemplateBinding Height}">
            <ContentPresenter Content="{TemplateBinding Content}" />
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>



Now how can we find the grid from the code behind ? Simply by accessing the template at the right moment : when the template is applied.
To do so we will override the OnApplyTemplate() method and access directly to the grid by its name with the FindName method. We can then act on it as we wish.

public override void OnApplyTemplate()
{
  //Effectively apply the template
  base.OnApplyTemplate();
 
  //Find the grid in the template once it's applied
  myAimedGrid = base.Template.FindName("PART_MyGrid", this) as Grid;
 
  //We can subscribe to its events
  myAimedGrid.PreviewMouseDown += 
     new MouseButtonEventHandler(myAimedGrid_PreviewMouseDown);
}
 
void myAimedGrid_PreviewMouseDown(object sender,
 System.Windows.Input.MouseButtonEventArgs e)
{
  //Proof 
  MessageBox.Show("Mouse preview Down on the grid !");
}



By the way, when you create a custom control which is focused on reusability you should imperatively declare its differents parts by using the TemplatePart attribute :

[TemplatePart(Name="PART_MyGrid",Type=typeof(Grid))]
public class CustomControl : ContentControl
{
// ....
}



You create an user control

Now the hardest part of the post : you create an user control as a reusable part of your application. To do so you create the C# file and the XAML file and as you want it to be customized, you set it's ContentTemplate as below :

/// <summary>
/// Interaction logic for MyCustomUserControl.xaml
/// </summary>
public partial class MyCustomUserControl : UserControl
{
  private Grid myAimedGrid;
 
  public MyCustomUserControl()
  {
    InitializeComponent();
  }
}

The XAML file :

<UserControl x:Class="FindNamesApplication.MyUserControl.MyCustomUserControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300"
        d:DesignWidth="300">
    <UserControl.ContentTemplate>
        <DataTemplate>
            <Grid x:Name="PART_MyGrid" Background="Black" Width="{TemplateBinding Width}"
                    Height="{TemplateBinding Height}">
                <ContentPresenter Content="{TemplateBinding Content}" />
            </Grid>
        </DataTemplate>
    </UserControl.ContentTemplate>
</UserControl>



Then, as you have seen before you override the OnApplyTemplate and get the child with the FindName methods : this won't do the job ! Actually, all you will get is 'null' or an InvalidOperationException sometimes.

Why ? Because by setting the controlTemplate, you define a DataTemplate which is then used by our UserControl to be applied on it's internal ContentPresenter. So by using findName on the UserControl we search the element named "PART_MyGrid" in the template of the UserControl and not in the template created by us and actually used.

So the solution is to seek the element on the correct element which is the ContentPresenter of the template of the UserControl. To do so we'll find it using the VisualTreeHelper to get the ContentPresenter and then use the FindName method with it as a parameter. Here is the code :

public override void OnApplyTemplate()
{
  base.OnApplyTemplate();
 
  //The ContentPresenter is the second child of the UserControl...
  ContentPresenter presenter = (ContentPresenter)
    (VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(this, 0), 0));
 
  //Be sure that the template is applied on the presenter
  presenter.ApplyTemplate();
 
  //get the grid from the presenter
  myAimedGrid =
    presenter.ContentTemplate.FindName("PART_MyGrid", presenter) as Grid;
 
  //We can subscribe to its events
  myAimedGrid.PreviewMouseDown
    += new MouseButtonEventHandler(myAimedGrid_PreviewMouseDown);
}
 
void myAimedGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
  //Proof 
  MessageBox.Show("Mouse preview Down on the grid !");
}



Interesting links

Here are some links to go further on the subject :



Conclusion

As we can see, nothing is impossible and once seen, it's quite easy to implement these differents solutions... Have a great code ! The source solution is linked to the post.

Shout it kick it on DotNetKicks.com



 

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


 

[Performance tips] Use the system shadows instead of your own

27 April 2010

Today a fast and easy tip about shadows and performance.

In a project I have recently made, we've told the designer not to use BitmapEffects because they are performance killer. He so decided to create it's own shadows by duplicating each shape and make them looks like shadows(designer magic, voodoo things, etc...). I was then surprised to see that it kills performance too !


There is still the shaders effect which came with the 3.5 SP1 framework but they will be available only on vista or greater plateforms and their performance will depend of your graphic cards.

But we have an another ace in you deck : the system shadow which are quite fast even on software rendering!

Using it is quite easy :

  1. Add the PresentationFramework.Aero reference to your project,
  2. Add the following XML namespace : ”clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero”,
  3. Use the SystemDropShadowChrome element available with this namespace !



But there is a drawback : you can only produce squared shadows. But you can still play with the CornerRadius property to create simily round shadows.

Here is a little example of XAML code:

<UniformGrid
    xmlns:shadows="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
    Columns="2">
  <shadows:SystemDropShadowChrome Margin="25">
    <ListBox>
      <ListBoxItem Content="One item" />
      <ListBoxItem Content="Another item" />
      <ListBoxItem Content="Another third item" />
    </ListBox>
  </shadows:SystemDropShadowChrome >
 
  <shadows:SystemDropShadowChrome Margin="25" CornerRadius="800" Width="100" Height="100">
    <Ellipse Stroke="Black" Fill="White" />
  </shadows:SystemDropShadowChrome>
</UniformGrid>

Shadows screenshot

Shout it kick it on DotNetKicks.com




 

Freeze brushes directly in the XAML to improve your application's performances

12 April 2010

When you read the MSDN guidelines to improve WPF's performances you can find that it's a good idea to freeze Freezable objects.

It's a quite easy thing to do via the code but it's quite harder to do it directly in the XAML. In this post we will see how to do so.

What are freezable objects

One upon a time, the MSDN said :

A Freezable is a special type of object that has two states: unfrozen and frozen. When unfrozen, a Freezable appears to behave like any other object. When frozen, a Freezable can no longer be modified.

A Freezable provides a Changed event to notify observers of any modifications to the object. Freezing a Freezable can improve its performance, because it no longer needs to spend resources on change notifications. A frozen Freezable can also be shared across threads, while an unfrozen Freezable cannot.



With this definition in mind, you surely had guess that it's a good thing to freeze them because and you surely want to do it... But how to do ?

Freeze freezable objects via the code

It's quite easy to do it in the code. The only thing do check is the property CanFreeze which tells you if you can or not freeze the Freezable object. You will then use this code :

if (myBrush.CanFreeze)
{
    // Makes the brush unmodifiable.
    myBrush.Freeze();
}



Freeze them (with ice) in XAML

The tips is to know the good XML Namespace http://schemas.microsoft.com/winfx/2006/xaml/presentation/options.

The use in the XAML is then quite easy :

<LinearGradientBrush ice:Freeze="True" 
xmlns:ice="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options" 
/>



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



In fact you are looking for this page : MVVM - Creating ViewModel : create dynamic proxies with Castle (solution 3 of n)

 

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

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

- page 1 of 2