Getting Started With MVVM

Getting-Started-With-MVVMI had a fantastic time delivering the webinar on Getting Started with MVVM in WPF & Silverlight.

Here’s the detailed post on my Infragistics blog. You will find the links to download the samples and slides.

-Nish-

Advertisements

WPF Datagrid – Update Source on fly with BindingList<T>

 

I was under an assumption that ObservableCollection<T> works perfect with WPF DataGrid until my friend wannabeegeek came up with a strange problem. The problem was making ObservableCollection<T> update the ItemSource on fly while editing them. What’s the big deal right? Wrong.

If you are using MVVM you do not want to wire up the event on DataGrid for an updated source. You will very much think that as long as you bind the collection to the ViewModel’s property and the mode is set to TwoWay – the source is updated automatically when items are edited in the DataGrid.

Your ViewModel will fire the property changed when the collection itself changes – that is if you replace the property itself.  So if you want to know the change in items of the collection you need to subscribe to the CollectionChanged event of the ObservableCollection<T> property in your ViewModel.

MSDN says:

Occurs when an item is added, removed, changed, moved, or the entire list is refreshed.

But there is a problem with ObservableCollection.CollectionChanged event –  it does not fire when your data item’s property changes. It fires only if the entire item is replaced. That is if you have a collection of an Entity “Expense” that has properties “Name” and “Amount” and if you simply change the Amount value in the grid it does not work! You need to replace the entire “Expense” object for you to see the collection change firing up. 

After googling and reading msdn forums I gave up on ObservableCollection<T> and picked up BindingList<T> to do the job.  Here is a demo that explains all the stuff:

.. and the screen shot:

ExpenseView

I have a DataGrid that is simply bound to an “BindingList<Expense>”. Right below that I have a Total that is bound to the “TotalExpense” property. Now here is what I wanted to do – Whenever user edits the amount in the Grid the Total should be calculated as he/she tabs out.

.. and the ViewModel with the solution:

  1. public class DataGridDisplayViewModel : ViewModelBase
  2.     {
  3.         private BindingList<Expense> _expenses;
  4.         private double _totalExpense;
  5.  
  6.         public double TotalExpense
  7.         {
  8.             get { return _totalExpense = _expenses.Sum(x => x.Amount); }
  9.             set { _totalExpense = value; }
  10.         }
  11.         
  12.         public BindingList<Expense> Expenses
  13.         {
  14.             get { return _expenses; }
  15.             set
  16.             {
  17.                 _expenses = value;
  18.                 if (_expenses != null)
  19.                 {
  20.                     _expenses.ListChanged += (o, e) => RaisePropertyChanged(() => this.TotalExpense);
  21.                 }
  22.                 RaisePropertyChanged(() => this.Expenses);
  23.             }
  24.         }
  25.  
  26.       
  27.         public DataGridDisplayViewModel()
  28.         {
  29.             Expenses = new BindingList<Expense>
  30.                             {
  31.                                 new Expense() {Name = "Gas", Amount = 1000},
  32.                                 new Expense() {Name = "Electricity", Amount = 1000},
  33.                                 new Expense() {Name = "Internet", Amount = 100},
  34.                                 new Expense() {Name = "Water", Amount = 100},
  35.                                 new Expense() {Name = "Misc", Amount = 1000}
  36.                             };
  37.         }
  38.     }

The TotalExpense getter simply calculates the sum. And the ListChanged event of Expenses property simply notifies the change in TotalExpense.

.. and in the View:

  1. <Grid>
  2.     <Grid.RowDefinitions>
  3.         <RowDefinition/>
  4.         <RowDefinition/>
  5.     </Grid.RowDefinitions>
  6.     <DataGrid ItemsSource="{Binding ExpensesOb, Mode=TwoWay}" AutoGenerateColumns="True" Grid.Row="0"/>
  7.     <TextBlock Grid.Row="1">
  8.         <Run Text="Total: "/>
  9.         <Run Text="{Binding TotalExpenseOb}"/>
  10.     </TextBlock>
  11. </Grid>

 

That’s it!

Download the full code here. The code contains a tiny MVVM framework(inside core folder). – Read the disclaimer of the blog before you play with my code.

If you know to achieve this exact behavior with ObservableCollection do make a comment to this post and let me know!

/*Nish*/

Attached Behaviors & MVVM

When you think of MVVM and other patterns involved, it is often about separation of concerns as much as possible, unit testability and code maintainability. These type of patterns help in high quality of code and efficient development of business functionality. If you are new to MVVM then you may want to check out some of the articles by various intellects like Josh Smith, Laurent Bugnion and few others from XAML Disciples group(or just Google Smile).

There are lot many frameworks on MVVM out there and to be frank there isn’t any right or the wrong one. You need to evaluate and decide on the one that fits your need! Most often people land up writing one on their own and in a way it is good!

I personally like a framework written by Brette Esterbrooks because it is a skeleton of code that you need and helps you get started. I tweaked it a bit(basically removed the ones that I don’t need) and thus the sample attached on this blog will have a tiny little framework of MVVM.

All it has is just a few helper classes:

  1. ViewBase,
  2. ViewModelBase,
  3. RelayCommand,
  4. ObservableObject.

Attached Behaviors

 

Attached behavior is achieved by simply attaching a behavior to a control that otherwise wouldn’t have anything of its own.

And as per Josh Smith-

The idea is that you set an attached property on an element so that you can gain access to the element from the class that exposes the attached property. Once that class has access to the element, it can hook events on it and, in response to those events firing, make the element do things that it normally would not do. It is a very convenient alternative to creating and using subclasses, and is very XAML-friendly.

Ever since I am hooked to MVVM pattern, I try and avoid all the code that usually goes to the code behind. For e.g. a click event handler usually written in the code behind can be omitted and a RelayCommand in the ViewModel be used instead.

But what would happen if you want to handle a double click or a focus event of a control? Just retaining those kind of events to the code behind defeats the pattern itself. And thus Attached Behavior comes to rescue! Demo included in this post explains the DragDropBehavior.

Before you go further with this approach, I just want you to know that you can achieve this by Behavior<T> class included in the system.windows.interactivity.dll. However this dll is available only through Expression Blend installation.

In my demo I have explained both of the implementation and it’s usage. Here we go!

Screenshot of the Demo:

 

AttachedBImage

 

IBehavior

 

The view models can hold the reference of this contract and change at runtime if needed:

  1. public interface IBehavior
  2.     {
  3.         void OnEnabled(DependencyObject dependencyObject);
  4.         void OnDisabling(DependencyObject dependencyObject);
  5.     }

 

 

AttachedBehavior – A static class!

 

A static class AttachedBehavior is introduced which holds the attached properties “Behavior” and “IsEnabled”.

When toggled from Enabled state to Disabled State – OnDisabling() method of the IBehavior is called. Use this method to unwire your event handlers.

 

  1. /// <summary>
  2.         /// Attach a Behavior of type IBehavior
  3.         /// </summary>
  4.         public static readonly DependencyProperty BehaviorProperty = DependencyProperty.RegisterAttached("Behavior", typeof(IBehavior), typeof(AttachedBehavior), new UIPropertyMetadata(null));
  5.  
  6.         /// <summary>
  7.         /// Is Enabled, when set to true, fires Behaviors OnEnabled Method
  8.         /// </summary>
  9.         public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(AttachedBehavior), new UIPropertyMetadata(false, OnIsEnabledChanged));
  10.  
  11.         /// <summary>
  12.         /// Handles IsEnabledChanged
  13.         /// </summary>
  14.         /// <param name="d"></param>
  15.         /// <param name="e"></param>
  16.         private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  17.         {
  18.             if (d == null)
  19.                 return;
  20.             var behavior = GetBehavior(d);
  21.             if (behavior == null)
  22.                 return;
  23.             if ((bool)e.NewValue)
  24.                 behavior.OnEnabled(d);
  25.             else
  26.                 behavior.OnDisabling(d);
  27.         }

 

DragDropBehavior Class

 

DragDropBehavior class implements the IBehavior. Have a look at the OnEnabled and OnDisabling methods.

  1. public class DragDropBehavior : IBehavior
  2.     {
  3.         #region Private Fields
  4.         private Point _startPosition;
  5.         private Point _mouseStartPosition;
  6.         private TranslateTransform _translatetransform;
  7.         private UIElement _associatedObject = null;
  8.         private Window _parent = null;
  9.         #endregion
  10.  
  11.         #region IBehavior Members
  12.         public void OnEnabled(DependencyObject dependencyObject)
  13.         {
  14.             var uiElement = dependencyObject as UIElement;
  15.             if (uiElement == null)
  16.                 return;
  17.             
  18.             _associatedObject = uiElement;
  19.             //TODO: set the parent accordingly
  20.              _parent = Application.Current.MainWindow;
  21.             _translatetransform = new TranslateTransform();
  22.             _associatedObject.RenderTransform = _translatetransform;
  23.             _associatedObject.MouseLeftButtonDown += AssociatedObjectMouseLeftButtonDown;
  24.             _associatedObject.MouseLeftButtonUp += AssociatedObjectMouseLeftButtonUp;
  25.             _associatedObject.MouseMove += AssociatedObjectMouseMove;
  26.         }
  27.         
  28.         public void OnDisabling(DependencyObject dependencyObject)
  29.         {
  30.             _associatedObject.MouseLeftButtonDown -= AssociatedObjectMouseLeftButtonDown;
  31.             _associatedObject.MouseLeftButtonUp -= AssociatedObjectMouseLeftButtonUp;
  32.             _associatedObject.MouseMove -= AssociatedObjectMouseMove;
  33.             _translatetransform = null;
  34.         }
  35.         #endregion
  36.  
  37.         #region Event Handlers
  38.         void AssociatedObjectMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
  39.         {
  40.             _startPosition = _associatedObject.TranslatePoint(new Point(), _parent);
  41.             _mouseStartPosition = e.GetPosition(_parent);
  42.             _associatedObject.CaptureMouse();
  43.         }
  44.         
  45.         void AssociatedObjectMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
  46.         {
  47.             var positionDifference = e.GetPosition(_parent) – _mouseStartPosition;
  48.             if(_associatedObject.IsMouseCaptured)
  49.             {
  50.                 _translatetransform.X = positionDifference.X;
  51.                 _translatetransform.Y = positionDifference.Y;
  52.             }
  53.         }
  54.  
  55.         void AssociatedObjectMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
  56.         {
  57.             _associatedObject.ReleaseMouseCapture();
  58.             
  59.         }
  60.         
  61.         #endregion
  62.  
  63.  
  64.     }

 

HelloView

 

Attach is the behavior to the control that you want to drag:

  1. <ToggleButton Name="dragDropToggleButton" Command="{Binding ToggleDragDropBehavior}" Width="200">
  2.                 <ToggleButton.Style>
  3.                     <Style TargetType="{x:Type ToggleButton}">
  4.                         <Setter Property="Content" Value="Start Dragging"/>
  5.                         <Style.Triggers>
  6.                             <Trigger Property="IsChecked" Value="True">
  7.                                 <Setter Property="Content" Value="Stop Dragging"/>
  8.                             </Trigger>
  9.                         </Style.Triggers>
  10.                     </Style>
  11.                 </ToggleButton.Style>
  12.             </ToggleButton>
  13.  
  14.  
  15.    
  16.               <TextBlock Background="Gold"
  17.                        Text="dragMe using attached behavior"
  18.                        Behaviors:AttachedBehavior.IsEnabled="{Binding IsDragBehaviorEnabled}"
  19.                        Behaviors:AttachedBehavior.Behavior="{Binding DragDropBehavior}"
  20.                        Canvas.Left="32"
  21.                        Canvas.Top="31" />

 

HelloViewViewModel

 

And finally the ViewModel that set’s the DragDropBehavior:

  1. public class HelloViewViewModel : ViewModelBase
  2.     {
  3.         private IBehavior _dragDropBehavior;
  4.         private bool _isDragBehaviorEnabled;
  5.  
  6.         public bool IsDragBehaviorEnabled
  7.         {
  8.             get { return _isDragBehaviorEnabled; }
  9.             set { _isDragBehaviorEnabled = value; RaisePropertyChanged(()=> this.IsDragBehaviorEnabled); }
  10.         }
  11.  
  12.         public IBehavior DragDropBehavior
  13.         {
  14.             get { return _dragDropBehavior; }
  15.             set { _dragDropBehavior = value; RaisePropertyChanged(() => this.DragDropBehavior); }
  16.         }
  17.  
  18.         public RelayCommand ToggleDragDropBehavior { get; set; }
  19.  
  20.         public HelloViewViewModel()
  21.         {
  22.             DragDropBehavior = new DragDropBehavior();
  23.             ToggleDragDropBehavior = new RelayCommand(() =>
  24.             {
  25.                 IsDragBehaviorEnabled = !IsDragBehaviorEnabled;
  26.             });
  27.  
  28.         }
  29.     }

 

That’s It – you are done!! Refer DragDropBehavior2 class for implementation using Behavior<T>.

Download the Code – Read the Disclaimer of this blog before you work with this code!

/*Nish*/