RecentComments

Comment RSS

Going from Model-View-Presenter to MV-VM with WPF Commands (Part #3: MVVM and Commands)

by Ioannis 12. October 2009 07:44

In previous posts (Part 1 and Part2) I have presented the implementation of a toy application using the Model-View-Presenter pattern and Routed Commands. In this final post, I will present the migration to MVVM. In Part2, we have reached a point where we have managed to partly centralize the logic that is needed to be implemented for each Command but we were not yet fully satisfied with the wiring that needed to be done in the code-behind file. So, we will try to tackle this problem:

Approach #4 Our first ViewModel for MVVM

Our first ViewModel is an implementation that looks a lot like the presenter. The code is as follows:

    public class ViewModel
    {
        private Client _selectedClient = null;
        public Client SelectedClient
        {
            get { return _selectedClient; }
            set { _selectedClient = value; }
        }
        public ObservableCollection<Client> Clients
        {
            get { return ClientRepository.GetClients(); }
        }
        public void AddClientCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            ClientRepository.AddClient(new Client());
        }
        public void AddClientCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
        public void DeleteClientCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (_selectedClient != null)
            {
                ClientRepository.DeleteClient(_selectedClient);
            }
        }
        public void DeleteClientCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (_selectedClient != null);
        }
        public void ResetClientCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (_selectedClient != null)
            {
                _selectedClient.FirstName = "RESET";
                _selectedClient.LastName = "RESET";
                _selectedClient.Phone = "9999999999";
            }
        }
        public void ResetClientCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (_selectedClient != null);
        }
    }

The whole idea behind the MVVM model is the use of a ViewModel class that will provide everything needed to the View through the use of Bindings. Therefore, we want to have a code-behind file that contains the following:

public partial class Window4 : Window
{
    ViewModel model = new ViewModel();

    public Window4()
    {
        InitializeComponent();

        this.DataContext = model;

        this.CommandBindings.Add(new CommandBinding(ClientCommands.AddClient, 
                    model.AddClientCommand_Executed, model.AddClientCommand_CanExecute));
        this.CommandBindings.Add(new CommandBinding(ClientCommands.DeleteClient, 
                    model.DeleteClientCommand_Executed, model.DeleteClientCommand_CanExecute));
        this.CommandBindings.Add(new CommandBinding(ClientCommands.ResetClient, 
                    model.ResetClientCommand_Executed, model.ResetClientCommand_CanExecute));
       
    }
}

That is we initialize the ViewModel, we set the Window's DataContext to the model and associate the commands with methods of the ViewModel. The xaml file is similar to the previous approach but it does not define the Command bindings (since they are defined in code)  and the handler for the ListBox SelectionChanged event is substituted with a binding of the SelectedItem property to the SelectedClient property of the ViewModel.

Are we finished? Not quite so. Although we have managed to take most of the code out of the code-behind file, achieving a separation between GUI and logic, there are still the binding definitions that need to be specified in every window that uses Commands. We would really like to avoid writing those as well. That leads us to:

Approach #5 The final MVVM model

Up to this point we have been working with Routed Commands. As previously stated, Routed Commands are fired from controls and bubble up the visual tree until they find a control that holds the binding that implements their logic. What we want to do to the view is the following. We want to avoid defining the Command Bindings and assocate the MenuItems with something like this:

 

<MenuItem x:Name="MenuItemAddClient"  Header="{Binding Path=AddClientText}" 
          Command="{Binding Path=AddClient}" />
<MenuItem x:Name="MenuItemDeleteClient" Header="{Binding Path=DeleteClientText}" 
          Command="{Binding Path=DeleteClient}" />
<MenuItem x:Name="MenuItemEditClient" Header="{Binding Path=ResetClientText}" 
          Command="{Binding Path=ResetClient}" />

That is, we bind the command's implemementaion to the MenuItems directly in the xaml file. There is no routing of the commands to other visual elements.

For this approach to work we need to make the AddClient, DeleteClient and ResetClient commands implement the ICommand interface. Or, because all commands will share some functionaliy in common, we can create the following class that implements the ICommand interface and delegates the behavior according to the command:

public class SimpleCommand:ICommand
    {
        public Predicate<object> CanExecuteDelegate { get; set; }
        public Action<object> ExecuteDelegate { get; set; }

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            if (CanExecuteDelegate!=null) return CanExecuteDelegate(parameter);
            return true;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            if (ExecuteDelegate!=null) ExecuteDelegate(parameter);
        }

        #endregion
    }

The new ViewModel is as follows. Note the implementation of the commands compared to the previous version, where each command defines its own delegate methods that implements its logic.

 

    public class ViewModel2
    {
        private Client _selectedClient = null;
        public Client SelectedClient
        {
            get { return _selectedClient; }
            set { _selectedClient = value; }
        }

        public ObservableCollection<Client> Clients
        {
            get { return ClientRepository.GetClients(); }
        }

        private string _addClientText="Add Client";
        public string AddClientText
        {
            get { return _addClientText; }
            set { _addClientText = value; }
        }

        private string _deleteClientText="Delete Client";
        public string DeleteClientText
        {
            get { return _deleteClientText; }
            set { _deleteClientText = value; }
        }

        private string _resetClientText="Reset Client";
        public string ResetClientText
        {
            get { return _resetClientText; }
            set { _resetClientText = value; }
        }

        private SimpleCommand _addClient = new SimpleCommand();

        public SimpleCommand AddClient
        {
            get { return _addClient; }
            set { _addClient = value; }
        }

        private SimpleCommand _resetClient = new SimpleCommand();

        public SimpleCommand ResetClient
        {
            get { return _resetClient; }
            set { _resetClient = value; }
        }
        private SimpleCommand _deleteClient = new SimpleCommand();

        public SimpleCommand DeleteClient
        {
            get { return _deleteClient; }
            set { _deleteClient = value; }
        }

        public ViewModel2()
        {
            AddClient.CanExecuteDelegate += new Predicate<object>(AddClientCommand_CanExecute);
            AddClient.ExecuteDelegate += new Action<object>(AddClientCommand_Executed);

            ResetClient.CanExecuteDelegate += new Predicate<object>(ResetClientCommand_CanExecute);
            ResetClient.ExecuteDelegate += new Action<object>(ResetClientCommand_Executed);

            DeleteClient.CanExecuteDelegate += new Predicate<object>(DeleteClientCommand_CanExecute);
            DeleteClient.ExecuteDelegate += new Action<object>(DeleteClientCommand_Executed);
        }
        public void AddClientCommand_Executed(object sender)
        {
            ClientRepository.AddClient(new Client());
        }
        public bool AddClientCommand_CanExecute(object sender)
        {
            return true;
        }
        public void DeleteClientCommand_Executed(object sender)
        {
            if (_selectedClient != null)
            {
                ClientRepository.DeleteClient(_selectedClient);
            }
        }
        public bool DeleteClientCommand_CanExecute(object sender)
        {
            return (_selectedClient != null);
        }
        public void ResetClientCommand_Executed(object sender)
        {
            if (_selectedClient != null)
            {
                _selectedClient.FirstName = "RESET";
                _selectedClient.LastName = "RESET";
                _selectedClient.Phone = "9999999999";
            }
        }
        public bool ResetClientCommand_CanExecute(object sender)
        {
            return (_selectedClient != null);
        }
    }

 And that is all. The code-behind file now is as simple as this:

    public partial class Window5 : Window
    {
        ViewModel2 model = new ViewModel2();

        public Window5()
        {
            InitializeComponent();
            this.DataContext = model;          
        }
    }

and all the wiring is performed in the ViewModel. This is the maximum separation achieved with the MVVM pattern.

The project with all the approaches can be downloaded here.

 kick it on DotNetKicks.comShout it

Tags:

WPF

Going from Model-View-Presenter to MV-VM with WPF Commands (Part #2: Commands with MVP)

by Ioannis 12. October 2009 07:25

In my previous post, I have presented the first two approaches for implementing the GUI and the logic of a toy application:

 

  • Approach #1: All the logic in the code behind file.
  • Approach #2: The Model-View-Presenter pattern.

 

In this post, we will pick up from where we stopped, in our route to migrate to the benefits of the MVVM model and the use of Commands. It is recommended to take a look at the previous post in order to get an idea of the application and the two previous approaches)

 

Approach #3: A hybrid MVP that uses Routed Commands (Seeing the first benefits)

In this approach we will keep the MVP pattern but we will try to substitute the MenuItems' event handlers with Routed Commands. For a Routed Command to work, we need a place to define it, a control that fires the command and a control that will implement the logic that needs to be performed. They are called Routed because in the visual tree, the command will bubble up from the control that fired it to the control that carries the binding that implement its logic.

In our small scale application, we will need 3 Commands which are defined in a separate Command.cs file as follows:

public class ClientCommands
{
    private static RoutedUICommand addClient;
    private static RoutedUICommand resetClient;
    private static RoutedUICommand deleteClient;

    static ClientCommands()
    {
        InputGestureCollection inputs = new InputGestureCollection();
        inputs.Add(new KeyGesture(Key.N, ModifierKeys.Control, "Ctrl+N"));
        addClient = new RoutedUICommand("Add Client", "AddClient", typeof(ClientCommands), inputs);

        inputs = new InputGestureCollection();
        inputs.Add(new KeyGesture(Key.R, ModifierKeys.Control, "Ctrl+R"));
        resetClient = new RoutedUICommand("Reset Client", "ResetClient", typeof(ClientCommands), inputs);

        inputs = new InputGestureCollection();
        inputs.Add(new KeyGesture(Key.D, ModifierKeys.Control, "Ctrl+D"));
        deleteClient = new RoutedUICommand("Delete Client", "DeleteClient", typeof(ClientCommands), inputs);
    }

    public static RoutedUICommand AddClient
    {
        get { return addClient; }
    }
    public static RoutedUICommand DeleteClient
    {
        get { return deleteClient; }
    }
    public static RoutedUICommand ResetClient
    {
        get { return resetClient; }
    }
}

Note that three commands are defined for the Add Client, Delete Client and Reset Client actions of the MenuItems. The Commands are bound to specific key shortcuts (this is global - a nice benefit out of the box with Commands) and in their constructors provide the text that will appear in the Buttons or MenuItems that will fire them. For example, the MenuItem that will fire the Command AddClient and all other future Buttons that will fire the same Command, will display with no extra effort the text "Add Client" (a second benefit with Commands where you have a centralized way of defining the language of the text of the controls- great for localization).

Having defined the commands we need to alter the xaml file for using them in the View. This is done as follows. First we define that the Commands are fired from the MenuItems (in the place where the event handler definitions used to be):

 

<MenuItem Header="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}" 
     Command="local:ClientCommands.AddClient" />
<MenuItem Header="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}" 
     Command="local:ClientCommands.DeleteClient"/>
<MenuItem Header="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}" 
     Command="local:ClientCommands.ResetClient"/>
 

Do not forget to reference the namespace the ClientCommands class resides at the beginning of the xaml file as "local". Then, we need to specify the place Routed Commands are caught to be handled. We choose that place to be the Window itself. Therefore the command bindings are defined in the Window as follows:

 

<Window.CommandBindings>
    <CommandBinding Command="local:ClientCommands.AddClient" 
	Executed="AddClientCommand_Executed" CanExecute="AddClientCommand_CanExecute"/>
    <CommandBinding Command="local:ClientCommands.DeleteClient" 
	Executed="DeleteClientCommand_Executed" CanExecute="DeleteClientCommand_CanExecute"/>
    <CommandBinding Command="local:ClientCommands.ResetClient" 
	Executed="ResetClientCommand_Executed"  CanExecute="ResetClientCommand_CanExecute"/>
</Window.CommandBindings>

 

For the Commands, we define two handlers. The one is the Executed method which holds the implementation of the logic of each command. The other is the CanExecute method which is called by the GUI in order to determine whether the control which fires the command should be enabled or not. Note that implementing this method will provide a disable/enable mechansim to all the Controls the fire the command with no additional effort.

The logic in the code-behind file, is similar to the previous case with the difference that now instead of event handles we have Routed Event handlers:

    public partial class Window3 : Window,IView
    {
        IPresenter myPresenter = new Presenter();
        public Window3()
        {
            myPresenter.SetView(this);
            InitializeComponent();
            myPresenter.InitGUI();
        }
        private void ListBoxClients_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            myPresenter.SetSelectedClient(ListBoxClients.SelectedItem as Client);
        }

        #region IView Members

        public void ToggleMenuItems(bool Enable)
        {
            this.MenuItemEditClient.IsEnabled =Enable;
            this.MenuItemDeleteClient.IsEnabled = Enable;
        }
        public void InitializeList(ObservableCollection<Client> Clients)
        {
            this.ListBoxClients.ItemsSource = Clients;
        }

        #endregion

        private void AddClientCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            myPresenter.AddClient();
        }
        private void AddClientCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
        private void DeleteClientCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            myPresenter.DeleteClient();
        }
        private void DeleteClientCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = myPresenter.EditAddCanExecute();
        }
        private void ResetClientCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            myPresenter.ResetClient();
        }
        private void ResetClientCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = myPresenter.EditAddCanExecute();
        }
    }

 We need to add in the Presenter class the method EdtAddCanExecute which determines when the commands need to be fired. Let's summarize what we have achieved s far:

 

  • We have enabled easy testing for our application with the MVP pattern (previous post).
  • We have enabled easy change of the View or the logic of the Presenter through the use of interfaces.
  • We have centralized the key shortcuts and the dispayed text of the commands in our application.
  • We have provided a specific method that defines for all controls that fire the command, whether they should be enabled or not

 

But we still have some problems. First there is still a lot of wiring up that needs to be done in the code behind file. Second, commands are implemented in the code-behind file (although they divert the execution to the presenter). Consider that it is problematic to implement the CanExecute wiring in every Window that fires the command.

In the next and final post on this series we will fully implement the MVVM model which also solves the aforementioned issues.

kick it on DotNetKicks.com Shout it

Tags:

WPF

Going from Model-View-Presenter to MV-VM with WPF Commands (Part #1: MVP)

by Ioannis 6. October 2009 08:01

In this post I will try to show the application of the MVP and MV-VM programming approaches and the use of WPF commands in them. I will start with a simple Window in WPF with all the code intertwined in the code-behind file and transform it to a Window with no code at all at the code-behind file, showing the separation between the view and the logic that is achieved with MV-VM and Commands.

The Window is the following:

Window Screenshot

The "Add Client" adds a new client, the "Delete Client" removes the selected client and the "Reset Client" resets the values of the selected client. Note that the two menu items that require a client to be selected are currently disabled. This is the functionality we want to provide.

We create a support class named "ClientRepository" that will play the role of our database-datalayer in this trivial example:

public static class ClientRepository
{
    private static ObservableCollection<Client> _clients = new ObservableCollection<Client>()
    {
        new Client(){FirstName="Test1",LastName="Test1",Phone="9999999999"},
        new Client(){FirstName="Test2",LastName="Test2",Phone="9999999999"},
        new Client(){FirstName="Test3",LastName="Test3",Phone="9999999999"},
        new Client(){FirstName="Test4",LastName="Test4",Phone="9999999999"},
        new Client(){FirstName="Test5",LastName="Test5",Phone="9999999999"},
    };

    public static void AddClient(Client me)
    {
        _clients.Add(me);
    }

    public static ObservableCollection<Client> GetClients()
    {
        return _clients;
    }

    public static void DeleteClient(Client me)
    {
        _clients.Remove(me);
    }
}

Approach #1 - View and Model fully intertwined

In this approach everything will reside in the code-behind file. In the XAML file Menu Items are related to event handlers as follows:


<MenuItem Header="Add Client" Click="MenuItemAddClient_Click" />
<MenuItem Header="Delete Client" Click="MenuItemDeleteClient_Click" />
<MenuItem Header="Reset Client" Click="MenuItemEditClient_Click" />
 

The clients in the ListBox, are displayed using a DataTemplate as follows:


<DataTemplate x:Key="DataTemplate1">
  <Canvas Width="502" Height="37">
    <TextBox ... Text="{Binding Path=FirstName,Mode=TwoWay}" TextWrapping="Wrap"/>
    <TextBox ... Text="{Binding Path=LastName,Mode=TwoWay}" TextWrapping="Wrap"/>
    <TextBox ... Text="{Binding Path=Phone,Mode=TwoWay}" TextWrapping="Wrap"/>
  </Canvas>
</DataTemplate>

 The code-behind file handles all jobs, from binding the ListBox's ItemsSource to the clients' list to handling the MenuItems' events:

public partial class Window1 : Window
    {
        private Client _selectedClient = null;
        public Client SelectedClient
        {
            get { return _selectedClient; }
            set
            {
                _selectedClient = value;
                this.MenuItemEditClient.IsEnabled = (_selectedClient!=null);
                this.MenuItemDeleteClient.IsEnabled = (_selectedClient != null);
            }
        }

        public Window1()
        {
            InitializeComponent();
            ListBoxClients.ItemsSource = ClientRepository.GetClients();
        }

        private void MenuItemEditClient_Click(object sender, RoutedEventArgs e)
        {
            if (_selectedClient != null)
            {
                _selectedClient.FirstName = "RESET";
                _selectedClient.LastName = "RESET";
                _selectedClient.Phone = "9999999999";
            }
        }

        private void ListBoxClients_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            SelectedClient = ListBoxClients.SelectedItem as Client;
        }

        private void MenuItemDeleteClient_Click(object sender, RoutedEventArgs e)
        {
            if (_selectedClient != null)
            {
                ClientRepository.DeleteClient(_selectedClient);
            }
        }

        private void MenuItemAddClient_Click(object sender, RoutedEventArgs e)
        {
            ClientRepository.AddClient(new Client());
        }
    }

Note that the logic for enabling/disabling the MenuItems resides in the setter of the SelectedClient property. Additionally, the logic for the MenuItems' actions is implemented within the event handlers. This means that if we need to change the view in the future (implement another Window for the same functionailty), we practically need to do everything from scratch. Not to mention, that it is nearly impossible to test the GUI.

Approach #2 - Converting the App to MVP (Model-View-Presenter)

We want to separate the logic from the view. So we apply the following rules:

  • The View (code-behind) will not have any properties related to the Business Logic (eg the property SelectedClient should go). Those properties will reside in the Presenter Class.
  • The View will do nothing more in its event handlers other than calling a method of the presenter.
  • If the Presenter needs to do something in the View (disable the MenuItems), it will do it by calling a method of the View.
  • To decouple the implementations both View and Presenter should implement an Interface (IView and IPresenter) respectively which will be used in the method calls.

This in action is as follows. The XAML remains intact, but the code-behind file:

    public partial class Window2 : Window,IView
    {
        IPresenter myPresenter = new Presenter();

        public Window2()
        {
            myPresenter.SetView(this);

            InitializeComponent();

            myPresenter.InitGUI();
        }

        private void MenuItemEditClient_Click(object sender, RoutedEventArgs e)
        {
            myPresenter.ResetClient();
        }

        private void ListBoxClients_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            myPresenter.SetSelectedClient(ListBoxClients.SelectedItem as Client);
        }

        private void MenuItemDeleteClient_Click(object sender, RoutedEventArgs e)
        {
            myPresenter.DeleteClient();
        }
        private void MenuItemAddClient_Click(object sender, RoutedEventArgs e)
        {
            myPresenter.AddClient();
        }

        #region IView Members

        public void ToggleMenuItems(bool Enable)
        {
            this.MenuItemEditClient.IsEnabled =Enable;
            this.MenuItemDeleteClient.IsEnabled = Enable;
        }
        public void InitializeList(ObservableCollection<Client> Clients)
        {
            this.ListBoxClients.ItemsSource = Clients;
        }

        #endregion
    }

First thing to notice is that the View implements the IView interface.The IView defines two methods which (as you will see) are used by the Presenter. Second, note that the View is bound to the IPresenter interface and whenever logic need to be executed it diverts the call to a method in the Presenter. The Presenter's implementation is as follows:

 public class Presenter:IPresenter
{
    public IView MyView = null;
    private Client _selectedClient = null;

    public void AddClient()
    {
        ClientRepository.AddClient(new Client());
    }
    public void SetSelectedClient(Client me)
    {
        _selectedClient = me;
        MyView.ToggleMenuItems(_selectedClient != null);
    }
    public void InitGUI()
    {
        MyView.InitializeList (ClientRepository.GetClients());
    }
    public void DeleteClient()
    {
        if (_selectedClient != null)
        {
            ClientRepository.DeleteClient(_selectedClient);
        }
    }
    public void ResetClient()
    {
        if (_selectedClient != null)
        {
            _selectedClient.FirstName = "RESET";
            _selectedClient.LastName = "RESET";
            _selectedClient.Phone = "9999999999";
        }
    }
    public void SetView(IView view)
    {
        MyView = view;
    }
}

By using the MVP approach, we have succesfully separated the GUI from the logic. Now, if we need to test the GUI we can just implement a dummy class that implements the IView interface and attach it to the Presenter. Moreover, we can provide more than one Presenter for the same View. Finally, if we need to change the GUI, we just implement a new Window and all we have to do is make sure that the new Window implements the IView interface.

But there is still some nusty wiring to be done in the code-behind file. Is there a better approach? As it turns out for WPF there is and it ows its existence to WPF's powerful Binding Engine (its ability to have any property of the GUI data bound to something) and to the Command Pattern implemented for the event handlers in WPF.

The next post will be following by the end of the week.

 

kick it on DotNetKicks.com Shout it

Tags:

WPF

Powered by BlogEngine.NET 1.5.0.7

Programming Blogs - BlogCatalog Blog Directory Add to Technorati Favorites

MVP Award

Ioannis Panagopoulos





This blog is using BlogEngine.Net and is hosted in the hoster below. I have not experienced any problems installing BlogEngine.Net in the host and I am satisfied with the host's response times. Therefore I recommend it.


DiscountASP Add