Click here to Skip to main content
15,867,686 members
Articles / Web Development / IIS

WCF by Example - Chapter VIII - WPF Client - Relay Command

Rate me:
Please Sign up or sign in to vote.
4.96/5 (15 votes)
11 Oct 2011CPOL6 min read 65.1K   41   2
WPF ICommand implementation based on Josh Smith's pattern.

Previous

Next

Chapter VIIChapter IX

The series

WCF by example is a series of articles that describes how to design and develop a WPF client using WCF for communication and NHibernate for persistence purposes. The series introduction describes the scope of the articles and discusses the architect solution at a high level.

Chapter overview

In the previous chapter, we kept working on the client side, and we articulated a mechanism that permits invoking server services from the ViewModel class in a decoupled fashion. In this chapter, we will define the pattern for the View (XAML) to invoke the mentioned services from the ViewModel. We will introduce the RelayCommand pattern which leverages the use of XAML binding capabilities, easing the execution of methods declared in the ViewModel.

This pattern is based on an article by Josh Smith about the MVVM pattern; he covers a lot of MVVM topics in his blog. The source code for this chapter can be found at CodePlex change set 93453. The latest code for the eDirectory solution can be found at CodePlex.

ICommand

A comprehensive explanation of the pattern can be found in Josh's articles. As a result, we are going to focus on the implementation in our solution rather than explaining the pattern in detail. You may find it beneficial reading the article before continuing with this chapter.

In our client, we created two command buttons in Chapter VI when the XAML was defined; the Save and the Refresh buttons. It might not have been obvious at the time, but we declared bindings to actions in the ViewModel. These methods were not defined in the CustomerViewModel on Chapter VI, but did not stop us from executing the application. It is worth noting this aspect of the XAML Views, faulty bindings don't throw exceptions; however, well documented warnings can be found in VS when debugging the application. As an example, the Save button XAML declaration in Chapter VI was:

XML
<Button Grid.Column="1" Grid.Row="2" 
  Padding="5" Margin="5" 
  Command="{Binding SaveCommand}">Save</Button>

The command action will invoke a read-only property called SaveCommand in the ViewModel; the property in the ViewModel must implement the ICommand interface to work.

Relay Command

The RelayCommand class provides a simple mechanism for declaring properties in the ViewModel so buttons can invoke actions using binding definitions. In the example of the Save button, the binding indicates that the button will invoke a property called SaveCommand; the CustomerViewModel class declares the property as follows:

C#
01      private RelayCommand SaveCommandInstance;

        public RelayCommand SaveCommand
        {
            get
            {
                if (SaveCommandInstance != null) return SaveCommandInstance;
02              SaveCommandInstance = new RelayCommand(a => Save());
                return SaveCommandInstance;
            }
        }

        private void Save()
        {
03          var result = CustomerServiceAdapter.Execute(
                s => s.CreateNewCustomer(Model.NewCustomerOperation));
            Refresh();
        }

We are using a lazy approach (line 02) to instantiate the RelayCommand private instance (line 01); this approach permits to use a lambda expression that indicates the internal method that will be called when the View invokes the Execute method of the RelayCommand. It is worth noting that two constructors are available for the RelayCommand; in this example, we are using the one that only requires to pass the action to invoke. There is an overloaded constructor that provides a mechanism for validation, which in the case of command buttons ensures that the button is only enabled if the validation is successful. We may see examples in later chapters of how to use this feature.

Let's see what other changes are required to get our front-end invoking service methods.

Service Adapter - Usage in the ViewModel

In line 03, we can see that the Save method is invoking the CreateNewCustomer service method. Couple aspects to explain at this point are how the CustomerServiceInstance is declared and also the mechanism used to pass customer details from the UI to the service using the NewCustomerOperation DTO.

CustomerServiceAdapter is instantiated on the ViewModel constructor:

C#
01  private readonly ServiceAdapter<ICustomerService> CustomerServiceAdapter;

    public CustomerViewModel()
    {            
02      CustomerServiceAdapter = new ServiceAdapter<ICustomerService>();
        Refresh();
        View = new CustomerView { DataContext = this };
        View.ShowDialog();
    }

In line 01, we instantiate the ServiceAdapter indicating that we want to deal with the Customer services for this ViewModel. This is a very nice way to leverage WCF services in the client side; the ViewModel is not coupled to the WCF service implementation, which reduces complexity when developing and testing. It also provides an easy way to avoid using MS WCF proxies if we are developing both the server and client side. We discuss in Chapter XII - WCF Implementation how easy it is to implement the WCF services in the client side using this design.

The Refresh method is called before we display the view; we do so to initialise the Model:

C#
private void Refresh()
{
01    var result = CustomerServiceAdapter.Execute(s => s.FindAll());
02    Model = new CustomerModel { NewCustomerOperation = new CustomerDto(), 
                                  CustomerList = result.Customers};
}

The purpose of the Refresh method is to retrieve all the Customer instances invoking the FindAll service method and then create an instance of the CustomerModel class which is the model for the CustomerView. In line 02, a CustomerDto instance is assigned to the NewCustomerOperation property. This is bound to the customer details section in the front-end using a TwoWay mode. This means that the XAML is capable of updating the CustomerDto instance without any other additional code; not too bad. This is a nice pattern for populating service method parameters as we saw in the Save method.

BootStrapper Changes

Image 3

At this stage in the project, we just need the WPF client to invoke the service methods within a single domain process, without the overhead of dealing with WCF services. In a later chapter, we will cover DI and how we can deploy the server assemblies without having to create references in our client to the server components. For the time being, for the sake of keeping things simple, we will just add those references to the client project.

Then, we just need to enhance the eDirectoryBootStrapper, adding the following code to the InitialiseDependencies method:

C#
private void InitialiseDependencies()
{
01      GlobalContext.Instance().TransFactory = new TransManagerEntityStoreFactory();
02      Container.RequestContext = new RequestContextNaive();      
03      ClientServiceLocator.Instance().CommandDispatcher = new DirectCommandDispatcher();
}

So we indicate to the client that we want to use the in-memory implementations for the TransFactory in the GlobalContext service (line 01), and the RequestContext is set to the naive implementation (line 02): the RequestContextNaive class. We also set the CommandDispatcher so we use the direct service implementation (line 03) that we discussed in the previous article.

Almost There

Image 4

If we run the application and we enter some customer details and then press the Save button, we notice that nothing happens. But if we debug our code, we can confirm that our Save method is executed:

Image 5

It seems that everything is working properly; the Refresh method seems to work fine after the CreateNewCustomer service method is executed. We can even inspect the results and see that the returned collection contains a customer and all the properties are correctly populated. So then why is the front-end not being refreshed?

Image 6

This is another characteristic of WPF client applications; we need to indicate to the View that the model has been refreshed. In order to do so, we need to introduce the INotifyPropertyChanged interface, we cover this aspect in the next article.

Chapter Summary

In this chapter, we introduced the RelayCommand class, an implementation of the ICommand by Josh Smith. We discussed how the View and ViewModel are designed so the front-end can invoke service methods using XAML bindings. At this point, we almost have the application working, using the in-memory mode and invoking the server methods within the client domain process; we are currently avoiding the WCF and NHibernate components for convenience purposes. At this point, we just need the ViewModel to notify the View when the Model has been updated.

The next chapter resolves the notification issue, and provides a comprehensive infrastructure to start demonstrating our solution features to the client.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Ireland Ireland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Özgür21-May-11 0:05
Özgür21-May-11 0:05 
GeneralRe: My vote of 5 Pin
Enrique Albert21-May-11 1:44
Enrique Albert21-May-11 1:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.