Sunday, July 28, 2013

WPF: Using Toolbar under MVVM pattern



Toolbar controls are containers for a group of commands or controls which are typically related in their function. ToolBar usually contains buttons which invoke commands.
Toolbar Control
The ToolBar control takes its name from the bar-like arrangement of buttons or other controls into a single row or column. WPF ToolBar controls provide an overflow mechanism which places any items that do not fit naturally within a size-constrained ToolBar into a special overflow area. Also, WPF ToolBar controls are usually used with the related ToolBarTray control, which provides special layout behavior as well as support for user-initiated sizing and arranging of toolbars.

Specifying the Position of ToolBars in a ToolBarTray
Use the Band and BandIndex properties to position the ToolBar in the ToolBarTray. Band indicates the position in which the ToolBar is placed within its parent ToolBarTray. BandIndex indicates the order in which the ToolBar is placed within its band. The following example shows how use this property to place ToolBar controls inside a ToolBarTray.

Often ToolBar controls contain more items than can fit into the toolbar's size. When this happens, the ToolBar displays an overflow button. To see the overflow items, a user clicks the overflow button and the items are shown in a pop-up window below the ToolBar. The following graphic shows a ToolBar with overflow items.
Toolbar with Overflow Items

Code Sample
This code sample given below will demonstrate how to use Toolbox under MVVM pattern. It will also show demonstrate to register Commands through ICommand interface.
Below is the application which has toolbar which give following functionality related to an email message new, open, save, cut, copy, paste and other operations.
Below are is XAML code of above UI. It contains ToolBarTray and ToolBars with Band and BandIndex values. Also we have shown how to show Overflow items.

<Window x:Class="WPFToolBarExample.MainWindow"
        Title="MainWindow" Height="350" Width="525">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <ToolBarTray Background="White"
                 FlowDirection="LeftToRight"                
                 Grid.Row="0">
      <ToolBar  Band="1"
                BandIndex="1" >
        <Button Margin="2" Command="{Binding NewFileCommand}">
          <Image Source="Images/Document.png" Stretch="None" ToolTipService.ToolTip="New Document"/>
        </Button>
        <Button Margin="2" Command="{Binding OpenNewFileCommand}">
          <Image Source="Images/Folder.png" Stretch="None" ToolTipService.ToolTip="New Folder"/>
        </Button>
        <Button Margin="2"
                Command="{Binding SaveCommand}"
                CommandParameter="MyDocument.Txt"
                ToolTipService.ShowOnDisabled="True"
                ToolTipService.ToolTip="Save">
          <Button.Content>
            <Viewbox Height="16" Width="16">
              <Image Stretch="None">
                <Image.Style>
                  <Style TargetType="{x:Type Image}">
                    <Setter Property="Source" Value="Images/Save.png"/>
                    <Style.Triggers>
                      <Trigger Property="IsEnabled" Value="False">
                        <Setter Property="Source" Value="Images/SaveDisabled.png"/>
                      </Trigger>
                    </Style.Triggers>
                  </Style>
                </Image.Style>
              </Image>
            </Viewbox>
          </Button.Content>
        </Button>
        <Button Margin="2" Command="{Binding ClearCommand}">
          <Image Source="Images/Delete.png" Stretch="None" ToolTipService.ToolTip="Delete"/>
        </Button>
      </ToolBar>
      <ToolBar  Band="2"
                BandIndex="1"               
                VerticalContentAlignment="Stretch"
                HorizontalAlignment="Left" Width="76">

        <Button Margin="2" Command="{Binding PrintCommand}">
          <Image Source="Images/Printer.png" Stretch="None" ToolTipService.ToolTip="Print"/>
        </Button>
        <Button  Margin="2">
          <Image Source="Images/Status Flag Red.png" Stretch="None" ToolTipService.ToolTip="Status"/>
        </Button>
      </ToolBar>

      <ToolBar Band="3"
               BandIndex="1">
        <Button  Margin="2">
          <Image Source="Images/Copy.png" Stretch="None" ToolTipService.ToolTip="Copy"/>
        </Button>
        <Button  Margin="2">
          <Image Source="Images/Paste.png" Stretch="None" ToolTipService.ToolTip="Paste"/>
        </Button>
        <Button  Margin="2">
          <Image Source="Images/Cut.png" Stretch="None" ToolTipService.ToolTip="Cut"/>
        </Button>

        <Separator/>
        <ToggleButton  Margin="2">
          <Image Source="Images/Format Font Larger.png" Stretch="None" />
        </ToggleButton>
        <ToggleButton  Margin="2">
          <Image Source="Images/Format Stylized Text.png" Stretch="None" ToolTipService.ToolTip="Bold"/>
        </ToggleButton>
        <ToggleButton  Margin="2">
          <Image Source="Images/Format Underline.png" Stretch="None" ToolTipService.ToolTip="Underline"/>
        </ToggleButton>

        <Separator/>

        <RadioButton Margin="2" ToolBar.OverflowMode="Always">
          <Image Source="Images/Format Align Left.png" Stretch="None" ToolTipService.ToolTip="Align Left"/>
        </RadioButton>
        <RadioButton Margin="2" ToolBar.OverflowMode="Always">
          <Image Source="Images/Format Align Center.png" Stretch="None" ToolTipService.ToolTip="Align Center"/>
        </RadioButton>
        <RadioButton Margin="2" ToolBar.OverflowMode="Always">
          <Image Source="Images/Format Align Right.png" Stretch="None" ToolTipService.ToolTip="Align Right"/>
        </RadioButton>
      </ToolBar>
    </ToolBarTray>
    <TextBox AcceptsReturn="True"
             Grid.Row="1"
             x:Name="textInput"
             BorderThickness="0"
             Foreground="#FF767676"
             MinHeight="140"
             Text="{Binding DisplayText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />

  </Grid>
</Window>


Commands
To implement backend functionality for ToolBar items under MVVM design we need to implement commands for each functionality.
What is a Command? 

A Command is an object that is bound to. It provides a separation between a user interface and logic.
                                                                                                                                                      
This is the core concept. In a bit more detail we can describe a command as follows:
  1. A command is an object that implements the ICommand interface.
  2. Generally, it is associated with a function in some code.
  3. User interface elements bind to commands - when they are activated by the user the command is fired - which calls the associated function.
  4. Commands know if they are enabled or not.
  5. A function can disable the command object - automatically disabling any user interface elements associated with it. 
Actually, there's a whole lot more to Commands. We can use commands to deal with asynchronous functionality, to provide logic that can be tested with or without a user interface, and more.

To make things easy, I have created a RelayCommand class implemented ICommand interface.  We will use RelayCommands to register our commands with CommandManager.
RelayCommand Class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace WPFToolBarExample.Helper
{
  /// <summary>
  /// To register commands in MMVM pattern
  /// </summary>
  public class RelayCommand : ICommand
  {
    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    /// <summary>
    /// Constructer takes Execute events to register in CommandManager.
    /// </summary>
    /// <param name="execute">Execute method as action.</param>
    public RelayCommand(Action<object> execute)
      : this(execute, null)
    {
      try
      {
        if (null == execute)
        {
          throw new NotImplementedException("Not implemented");
        }

        _execute = execute;
      }
      catch (Exception)
      {

        throw;
      }
    }

    /// <summary>
    /// Constructer takes Execute and CanExcecute events to register in CommandManager.
    /// </summary>
    /// <param name="execute">Execute method as action.</param>
    /// <param name="canExecute">CanExecute method as return bool type.</param>
    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
      try
      {
        if (null == execute)
        {
          _execute = null;
          throw new NotImplementedException("Not implemented");
        }

        _execute = execute;
        _canExecute = canExecute;
      }
      catch (Exception)
      {

      }
    }

    /// <summary>
    /// Can Executed Changed Event
    /// </summary>
    public event EventHandler CanExecuteChanged
    {
      add
      {
        CommandManager.RequerySuggested += value;
      }
      remove
      {
        CommandManager.RequerySuggested -= value;
      }
    }

    /// <summary>
    /// Execute method.
    /// </summary>
    /// <param name="parameter">Method paramenter.</param>
    public void Execute(object parameter)
    {
      _execute(parameter);
    }

    /// <summary>
    /// CanExecute method.
    /// </summary>
    /// <param name="parameter">Method paramenter.</param>
    /// <returns>Return true if can execute.</returns>
    public bool CanExecute(object parameter)
    {
      return _canExecute == null ? true : _canExecute(parameter);
    }
  }
}


Creating Command
Here are the steps to create a command for UI control.
·         Create Execute command method.
·         Create CanExecute command method.
·         Create ICommand Property.
·         Register both command methods through RelayCommand class.
·         Return registered command through Property.
·         Bind property with Control’s Command property.
Below is the code how to create command property using steps mentioned above.

    ICommand _saveCommand;

    public ICommand SaveCommand
    {
      get
      {
        if (null == this._saveCommand)
          //Register command with RelayCommand class.
          this._saveCommand = new RelayCommand(param => this.SaveCommand_Execute(param),
            param => this.SaveCommand_CanExecute(param));
        return _saveCommand;
      }
    }
    bool SaveCommand_CanExecute(object param)
    {
      if (string.IsNullOrEmpty(this.DisplayText))
        return false;

      return true;
    }

    void SaveCommand_Execute(object param)
    {
      //Do some save coding.
      MessageBox.Show("You have sent '" + param.ToString() + "' as command parameter to save this document.");
    }

View Model
We have created View Model that will contain Commands. And properties bound with UI controls.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Input;
using WPFToolBarExample.Helper;

namespace WPFToolBarExample.ViewModel
{
  class ApplicationUIViewModel : BaseViewModel
  {
    string _displayText = string.Empty;

    public string DisplayText
    {
      get { return _displayText; }
      set
      {
        _displayText = value;
        OnPropertyChanged(() => this.DisplayText);
      }
    }

    public ApplicationUIViewModel()
    {
      this.DisplayText = "Hi Team, " + "\n" + "\nI will be out of office for a week." + "\n" + "\nRegards," + "\n" + "\n Siddharth Mishra";
    }

    #region Commands

    ICommand _newFileCommand;

    public ICommand NewFileCommand
    {
      get
      {
        if (_newFileCommand == null)
          this._newFileCommand = new RelayCommand(param => this.NewFileCommand_Execute(param),
            param => this.NewFileCommand_CanExecute(param));
        return _newFileCommand;
      }
    }

    bool NewFileCommand_CanExecute(object param)
    {
      return true;
    }

    void NewFileCommand_Execute(object param)
    {
      this.DisplayText = string.Empty;
    }

    ICommand _openNewFileCommand;

    public ICommand OpenNewFileCommand
    {
      get
      {
        if (this._openNewFileCommand == null)
          this._openNewFileCommand = new RelayCommand(param => this.OpenNewFileCommand_Execute(param),
            param => this.OpenNewFileCommand_CanExecute(param));
        return _openNewFileCommand;
      }
    }

    bool OpenNewFileCommand_CanExecute(object param)
    {
      return true;
    }

    void OpenNewFileCommand_Execute(object param)
    {
      string initialDir = string.Empty;
      if (null != param)
        initialDir = param.ToString();

      var dialog = new OpenFileDialog();
      dialog.Filter = "TXT files (*.TXT)|*.TXT";
      dialog.FilterIndex = 1;
      dialog.Title = "Please select a text file..";

      dialog.InitialDirectory = string.IsNullOrEmpty(initialDir) ? Environment.GetFolderPath(Environment.SpecialFolder.Desktop) : initialDir;

      DialogResult result = dialog.ShowDialog();
    }

    ICommand _saveCommand;

    public ICommand SaveCommand
    {
      get
      {
        if (null == this._saveCommand)
          //Register command with RelayCommand class.
          this._saveCommand = new RelayCommand(param => this.SaveCommand_Execute(param),
            param => this.SaveCommand_CanExecute(param));
        return _saveCommand;
      }
    }

    bool SaveCommand_CanExecute(object param)
    {
      if (string.IsNullOrEmpty(this.DisplayText))
        return false;

      return true;
    }

    void SaveCommand_Execute(object param)
    {
      //Do some save coding.
      MessageBox.Show("You have sent '" + param.ToString() + "' as command parameter to save this document.");
    }

    #endregion

  }
}


Registering ViewModel with UI data context.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WPFToolBarExample.ViewModel;

namespace WPFToolBarExample
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
      this.Loaded += MainWindow_Loaded;
    }

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
      ApplicationUIViewModel appVM = new ApplicationUIViewModel();
      this.DataContext = appVM;
    }
  }
}


Above application is a simple demonstration of MVVM pattern with Commands and Toolbar implementation.