February 18, 2014

Routing Events to Commands in WPF using MVVM Light

First reference some assemblies:
  • System.Windows.Interactivity
  • GalaSoft.MvvmLight.Extras.WPF4
In the XAML file add references to the namespaces in the window definition:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
Within you window component, add the event to command handler's
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <cmd:EventToCommand Command="{Binding Mode=OneWay, Path=LoadedCommand}"
                        PassEventArgsToCommand="True" />
    </i:EventTrigger>
    <i:EventTrigger EventName="Closing">
        <cmd:EventToCommand Command="{Binding Mode=OneWay, Path=ClosingCommand}"
                        PassEventArgsToCommand="True" />
    </i:EventTrigger>
</i:Interaction.Triggers>
Then some code to process the command:
private RelayCommand<RoutedEventArgs> loadedCmd;

public RelayCommand<RoutedEventArgs> LoadedCommand
{
  get
  {
    return this.loadedCmd ?? (this.loadedCmd = new RelayCommand<RoutedEventArgs>
    ((rea) => 
    {
      // Use event argument 'rea' if you need it 
      // (but PassEventArgsToCommand="True" is needed in the Xaml, see above)
      this.ActivityText = "Copy files from \'" +
         m_AsyncPhotoBackup.TargetDirectory +
         "\' to \'" +
         m_AsyncPhotoBackup.DestinationDirectory +
         "\'.";
      m_AsyncPhotoBackup.RunWorkerAsync();
    }));
  }
}

TaskbarItemInfo in MVVM and Sample WPF Converter Usage

In this case we are using a WPF TaskbarItemInfo to show progress on an icon in the Taskbar. The taskbar progress value takes a double value between 0.0d and 1.0d. However, in this cae the progresss value is generated as an integer percentage between 0 and 100. So we create a converter class to convert our interger value between 0 and 100 to the double value. An alternative would be to create another property that creates the progress value in the appropriate form.
// Convert an integer percentage (0 to 100) to a double 
// between (0.0d and 1.0d)
public class IntPercentageToDoubleConverter : IValueConverter
{
    public object Convert(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        double res = 0.0d;
        if (value is int)
        {
            int intVal = (int)value;
            res = intVal / 100.0d;
            if (res < 0.0d)
                res = 0.0d;
            else if (res > 100.0d)
                res = 100.0d;
        }
        return res;            
    }

    public object ConvertBack(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        return null;
    }
}
In the Xaml we first need to create an instance of the converter. A few namespace declarations are required:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PhotoBackupApp.ViewModel"
Create the converter instance:
<Window.Resources>
    <local:IntPercentageToDoubleConverter x:Key="intPercentageToDoubleConverter" />
</Window.Resources>
Use the converter with the bound proprety
<Window.TaskbarItemInfo>
    <TaskbarItemInfo ProgressValue="{Binding Mode=OneWay, 
       Path=ProgressPercentage, 
       Converter={StaticResource intPercentageToDoubleConverter},
       UpdateSourceTrigger=PropertyChanged}" 
       ProgressState="Normal" />
</Window.TaskbarItemInfo>
The 2 important declarartions here are 'Path=ProgressPercentage, Converter={StaticResource intPercentageToDoubleConverter},'. This is the property to bind to and the converter instance to use.

Closing a Dialog Window in WPF using MVVM

Alot of ideas are mentioned on this website:
http://stackoverflow.com/questions/4376475/wpf-mvvm-how-to-close-a-window

Simplest way is to simply define the close button as normal using the click event
<Button Content="OK" IsDefault="True" Click="okButton_Click" />
Here is the code behind:
private void okButton_Click(object sender, RoutedEventArgs e)
{
 this.Close();
}
This does not affect the ViewModel in any way, the ViewModel knows nothing about the close functionality. and there is nothing wrong with code behind if it does not affect the ViewModel.
However I wanted to be able to initiate the Close from the ViewModel. To do this I defined a property that takes a simple Action delegate
public Action CloseAction { get; set; }
In the constructor I give it a default value
this.CloseAction = () => 
    { Debug.WriteLine("ActivePhotoBackupViewModel - Close Action Undefined"); )};
Now I can use a command to perform the CloseAction
#region Done Command
private RelayCommand doneCommand;

public RelayCommand DoneCommand
{
 get
 {
  return this.doneCommand ?? 
    (this.doneCommand = new RelayCommand(() => 
     {
      DoSomeOtherStuffHere();
      this.CloseAction();
     }
    ));
 }
}
#endregion Done Command
When creating the view model the close action is defined:
...
ActivePhotoBackupViewModel vm = new ActivePhotoBackupViewModel(srcDir, destDir, photoBackupOptions);
PhotoBackupDlg photoBackupDlg = new PhotoBackupDlg();
vm.CloseAction = new Action(() => photoBackupDlg.Close());
...
Could have a used an interface here on a class to perform the same work but then the interface would have had one method so it is simpler just to use a delegate instead ('Action' is a system defined delegate). Also there were no future extra requirements envisaged on this interface (otherwise the interface route would have been worth it). This simple solution does not impede any unit testing of the ViewModel either.