Alessandro Del Sole's Blog

/* A programming space about Microsoft® .NET® */
posts - 159, comments - 0, trackbacks - 0

My Links

News

Your host

This is me! This space is about Microsoft® .NET® and Microsoft® Visual Basic development. Enjoy! :-)

These postings are provided 'AS IS' for entertainment purposes only with absolutely no warranty expressed or implied and confer no rights.

Microsoft MVP

My MVP Profile

I'm a VB!

Watch my interview in Seattle

My new book on VB 2015!

Pre-order VB 2015 Unleashed Pre-order my new book "Visual Basic 2015 Unleashed". Click for more info!

My new book on LightSwitch!

Visual Studio LightSwitch Unleashed My book "Visual Studio LightSwitch Unleashed" is available. Click the cover!

Your visits

Follow me on Twitter!

CodePlex download Download my open-source projects from CodePlex!

Article Categories

Archives

Post Categories

.NET Framework

Blogroll

Help Authoring

Microsoft & MSDN

Setup & Deployment

Visual Basic 2005/2008/2010

WPF: Introducing the Model-View-ViewModel pattern for Visual Basic 2010 developers - part 2

In my previous post I introduced the main features of the Model-View-ViewModel pattern in Windows Presentation Foundation, saying that this is an introductory blog post series dedicated to Visual Basic developers. I showed how to create a simple data model, a basic ViewModel for loading data and sending them to the View which was simply responsible of presenting loaded data.

 

In this blog post we want to make a further step, which is also quite complex: adding commands that will be bound to some buttons. If you had a chance of working with the so-called "commanding pattern", probably a lot of concepts will be familiar to you even if the logic has some differences. I will often refer to the previous post, so you might find useful downloading the previous source code so that you can work on it. You can also download the updated project for the current post.

 

Our goal

The application that we are going to build today is essentially an updated version of the previous one and will look like in the following figure: 

We'll do some complex work but it will remain unchanged in next posts.

 

The Model

The Model is unchanged from the previous post, thus the Customer and Customers classes and the data file named Customers.xml are exactly the same.

 

“Commands” in WPF

Since the first version introduced the concept of Command. A command is an object that implements the ICommand interface and is actually a command that executes the specified task. A command has the following important features:

 

1.    Execute method, which represents the action to be executed when the command is invoked

2.    CanExecute method, which gets or sets the command state. In other words, if the command can be executed depending on the value of the specified boolean condition (true or false).

3.    CanExecuteChanged event, which is raised when the value of the boolean condition mentioned above changes

 

Using ICommand objects offers an important advantage: they are fully separated from the user interface. In a non-MVVM scenario, you generally assign a Click event handler for a button which executes an action. When working with commands you instead take advantage of data-binding in order to bind the user control to the appropriate command. The command can be exposed from a layer different than the presentation one, such as a ViewModel. Moreover, one of the most important features in commands is that when the value of CanExecute is False, the data-bound user control gets automatically disabled while it gets enabled once the value becomes True. 

 

Relaying the command execution logic

In order to have a reusable infrastructure and to promote the abstraction between objects, in the M-V-VM pattern the approach in implementing commands is a little bit different from the classic commanding pattern. In fact, we need to define a class that switches requests about command execution and about checks on the command state. Such a class then sends back to the ViewModel the request of actually executing commands. At this point there is the first problem: there are at least two way of thinking and therefore at least two different kinds of implementations of the class that relays the command logic. I will personally used the simplified implementation, which is also the most appropriate for understanding some concepts.

This class is called, by convention, RelayCommand and can be defined in both a non-generic and a generic fashion. I will describe in another post the generic versione, for now let'see the non-generic one which looks like this:

 

Public Class RelayCommand

    Implements ICommand

 

    Private ReadOnly _execute As Action(Of Object)

    Private ReadOnly _canExecute As Predicate(Of Object)

 

    Public Sub New(ByVal execute As Action(Of Object))

        Me.New(execute, Nothing)

    End Sub

 

    Public Sub New(ByVal execute As Action(Of Object), ByVal canExecute As Predicate(Of Object))

        If execute Is Nothing Then

            Throw New ArgumentNullException("execute")

        End If

 

        _execute = execute

        _canExecute = canExecute

    End Sub

 

    <DebuggerStepThrough()> _

    Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute

        Return If(_canExecute Is Nothing, True, _canExecute(parameter))

    End Function

 

    Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged

        AddHandler(ByVal value As EventHandler)

            AddHandler CommandManager.RequerySuggested, value

        End AddHandler

        RemoveHandler(ByVal value As EventHandler)

            RemoveHandler CommandManager.RequerySuggested, value

        End RemoveHandler

        RaiseEvent(ByVal sender As System.Object, ByVal e As System.EventArgs)

        End RaiseEvent

    End Event

 

    Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute

        _execute(parameter)

    End Sub

 

End Class

 

The class implements ICommand and this is required in order to switch requests coming from objects of the same type. If you look at the second overload of the constructor, you will notice that it receives as an argument a delegate that represents the action to be executed and another delegate that represents the state of the command. Both Execute and CanExecute methods are simple to understand whereas about the CanExecuteChanged you need to pay deeper attention. Both AddHandler and RemoveHandler point to an event named CommandManager.RequerySuggested which is raised when the WPF runtime detects situations that can change the state (true or false) of the command. At this point we can define the real commands.

 

Defining commands

Open the CustomerViewModel.vb defined in the previous post. What we already did remains unchanged. This time we need to declare three commands that we'll then bind to the View. We basically need a command for saving data, one for adding new data and one for removing data. With that said, let's first add three fields that serve as the commands infrastructure:

 

 

    Private _cmdAddCommand As ICommand

    Private _cmdRemoveCommand As ICommand

    Private _cmdSaveCommand As ICommand

 

Adding such fields should give you the feeling that we're going to expose them in the form of properties. This is because, as you probably know, properties in WPF offer specific data-binding support. So the ViewModel exposes the three commands like this:

 

    Public ReadOnly Property AddCommand() As ICommand

        Get

            If _cmdAddCommand Is Nothing Then

                _cmdAddCommand = New RelayCommand(AddressOf AddExecute, AddressOf CanAddExecute)

            End If

            Return _cmdAddCommand

        End Get

    End Property

 

    Public ReadOnly Property RemoveCommand() As ICommand

        Get

            If _cmdRemoveCommand Is Nothing Then

                _cmdRemoveCommand = New RelayCommand(AddressOf Remove, AddressOf CanRemove)

            End If

            Return _cmdRemoveCommand

        End Get

    End Property

 

    Public ReadOnly Property SaveCommand() As ICommand

        Get

            If _cmdSaveCommand Is Nothing Then

                _cmdSaveCommand = New RelayCommand(AddressOf Save, AddressOf CanSave)

            End If

            Return _cmdSaveCommand

        End Get

    End Property

 

They are read-only properties and are of type ICommand. In case the related field does not hold an instance, a new instance of the RelayCommand class is created, passing references to the two methods, one that executes the command and one that checks if it can be executed. Among other things, RelayCommand is also responsible for invoking the WPF's command manager in order to check if the command can be executed or not. Let's begin with the fisrt command which is handled by the following methods: 

 

    'Always returns True because you can

    'always add a new item

    Private Function CanAddExecute(ByVal param As Object) As Boolean

        Return True

    End Function

 

    Private Sub AddExecute(ByVal param As Object)

        Dim cust As New Customer

        Me.Customers.Add(cust)

    End Sub

 

Notice how the first method returns the boolean condition that determines if the command can be executed or not. In this particular case it is always True because we want that our user is always able of adding new items. Next, the method that executes the command creates a new instance of the Customer class adding such an instance to the collection. Next command removes an item; notice how the condition check is performed against the non-null value of the Selection property which will be bound to the View:

 

    Private Function CanRemove(ByVal param As Object) As Boolean

        Return Me.Selection IsNot Nothing

    End Function

 

    Private Sub Remove(ByVal param As Object)

        Me.Customers.Remove(Me.Selection)

    End Sub

 

Finally the last command is about saving data. Since we need to save data as XML, we can use XML Literals and embedded expressions:

 

    Private Function CanSave(ByVal param As Object) As Boolean

        Return True

    End Function

 

    Private Sub Save(ByVal param As Object)

        Dim doc = <?xml version="1.0"?>

                  <Customers>

                      <%= From cust In Me.Customers

                          Select <Customer CompanyName=<%= cust.CompanyName %>

                                     CustomerID=<%= cust.CustomerID %>

                                     Address=<%= cust.Address %>

                                     Representative=<%= cust.Representative %>/>

                      %>

                  </Customers>

 

        doc.Save(AppDomain.CurrentDomain.BaseDirectory + "Data\Customers.xml")

    End Sub

 

Also notice that the param argument in methods is never used but it is required by the interface's signatures. At this point I want to ask you a question: after all this code, what is the conclusion? Ok, I will also give you the answer: we can execute commands against data without a single interaction with the user interface, providing full separation between the two layers. This the great benefit offered by commands in WPF.

 

 

UI, buttons and data-binding

Once we have our commands, we need to bind them to the user interface. Let's open the View, which is our main window, and right after the DataGrid definition let's add the following code:

 

        <Button Content="Add"  Command="{Binding AddCommand}"

                Height="30" HorizontalAlignment="Left" Margin="373,62,0,0" Name="Button1" VerticalAlignment="Top" Width="116" />

        <Button Content="Save"  Command="{Binding SaveCommand}"

                Height="30" HorizontalAlignment="Left" Margin="373,102,0,0" Name="SaveButton" VerticalAlignment="Top" Width="116" />

        <Button Content="Remove"  Command="{Binding RemoveCommand}"

                Height="30" HorizontalAlignment="Left" Margin="373,142,0,0" Name="RemoveButton" VerticalAlignment="Top" Width="116" />

 

 

Notice how the Command property of each button is bound to the appropriate property of type ICommand, exposed by the ViewModel. We need no other code, just binding. If you now run the application you can notice that:

 

1.    The Remove is disabled until you select an item inside the DataGrid and this is possible because of the WPF runtime which detects if a command can be executed

2.    If you click the buttons, commands are executed correctly but via the ViewModel and not as we used to do in the past via Click event handlers.

 

Oops, I was forgetting one thing: I did not change the main Window's code behind... :-) This means that assigning the ViewModel instance to the DataContext property of the Window is exactly the same in order to populate everything including commands.

 

Source code download

The source code for this blog post is available at this address. Should you have any feedback or suggestions, please share.

 

Alessandro

Print | posted on venerdì 25 giugno 2010 15:10 | Filed Under [ Visual Studio 2010 Visual Basic Windows Presentation Foundation ]

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 5 and 2 and type the answer here:

Powered by:
Powered By Subtext Powered By ASP.NET