Home Web Development Building a WPF .NET Client

Building a WPF .NET Client

0
1564
7 min read

In this article by Einar Ingebrigtsen, author of the book SignalR: Real-time Application Development – Second Edition we will bring the full feature set of what we’ve built so far for the web onto the desktop through a WPF .NET client. There are quite a few ways of developing Windows client solutions, and WPF was introduced back in 2005 and has become one of the most popular ways of developing software for Windows. In WPF, we have something called XAML, which is what Windows Phone development supports and is also the latest programming model in Windows 10. In this chapter, the following topics will be covered:

  • MVVM
  • Brief introduction to the SOLID principles
  • XAML
  • WPF

(For more resources related to this topic, see here.)

Decoupling it all

Learn Programming & Development with a Packt Subscription

So you might be asking yourself, what is MVVM? It stands for Model View ViewModel: a pattern for client development that became very popular in the XAML stack, enabled by Microsoft based on Martin Fowlers presentation model (http://martinfowler.com/eaaDev/PresentationModel.html). Its principle is that you have a ViewModel that holds the state and exposes behavior that can be utilized from a view. The view observes any changes of the state the ViewModel exposes, making the ViewModel totally unaware that there is a View. The ViewModel is decoupled and can be put in isolation and is perfect for automated testing. As part of the state that the ViewModel typically holds is the model part, which is something it usually gets from the server, and a SignalR hub is the perfect transport to get this. It boils down to recognizing the different concerns that make up the frontend and separating it all.

This gives us the following diagram:

SignalR: Real-Time Application Development - Second Edition

Decoupling – the next level

In this chapter, one of the things we will brush up is the usage of the Dependency Inversion Principle, the D of SOLID. Let’s start with the first principle: the S in SOLID of Single Responsibility Principle, which states that a method or a class should only have one reason to change and only have one responsibility. With this, we can’t have our units take on more than one responsibility and need help from collaborators to do the entire job. These collaborators are things we now depend on and we should represent these dependencies clearly to our units so that anyone or anything instantiating it knows what we are depending on. We have now flipped around the way in which we get dependencies. Instead of the unit trying to instantiate everything itself, we now clearly state what we need as collaborators, opening up for the calling code to decide what implementations of these dependencies you want to pass on. Also, this is an important aspect; typically, you’d want the dependencies expressed in the form of interfaces, yielding flexibility for the calling code. Basically, what this all means is that instead of a unit or system instantiating and managing its dependencies, we decouple and let something called as the Inversion of Control container deal with this. In the sample, we will use an IoC (Inversion of Control) container called Ninject that will deal with this for us. What it basically does is manage what implementations to give to the dependency specified on the constructor. Often, you’ll find that the dependencies are interfaces in C#. This means one is not coupled to a specific implementation and has the flexibility of changing things at runtime based on configuration. Another role of the IOC container is to govern the life cycle of the dependencies. It is responsible for knowing when to create new instances and when to reuse an instance. For instance, in a web application, there are some systems that you want to have a life cycle of per request, meaning that we will get the same instance for the lifetime of a web request. The life cycle is configurable in what is known as a binding. When you explicitly set up the relationship between a contract (interface) and its implementation, you can choose to set up the life cycle behavior as well.

Building for the desktop

The first thing we will need is a separate project in our solution:

  1. Let’s add it by right-clicking on the solution in Solution Explorer and navigating to Add | New Project:

    SignalR: Real-Time Application Development - Second Edition

  2. In the Add New Project dialog box, we want to make sure the .NET Framework 4.5.1 is selected.

    We could have gone with 4.5, but some of the dependencies that we’re going to use have switched to 4.5.1. This is the latest version of the .NET Framework at the time of writing, so if you can, use it.

  3. Make sure to select Windows Desktop and then select WPF Application. Give the project the name SignalRChat.WPF and then click on the OK button:

    SignalR: Real-Time Application Development - Second Edition

Setting up the packages

We will need some packages to get started properly. This process is described in detail in Chapter 1, The Primer. Let’s start off by adding SignalR, which is our primary framework that we will be working with to move on. We will be pulling this using NuGet, as described in Chapter 1, The Primer:

  1. Right-click on the References in Solution Explorer and select Manage NuGet Packages, and type Microsoft.AspNet.SignalR.Client in the Search dialog box. Select it and click on Install.

  2. Next, we’re going to pull down something called as Bifrost.

    Bifrost is a library that helps us build MVVM-based solutions on WPF; there are a few other solutions out there, but we’ll focus on Bifrost.

  3. Add a package called Bifrost.Client.
  4. Then, we need the package that gives us the IOC container called Ninject, working together with Bifrost. Add a package called Bifrost.Ninject.

Observables

One of the things that is part of WPF and all other XAML-based platforms is the notion of observables; be it in properties or collections that will notify when they change. The notification is done through well-known interfaces for this, such as INotifyPropertyChanged or INotifyCollectionChanged. Implementing these interfaces quickly becomes tedious all over the place where you want to notify everything when there are changes.

Luckily, there are ways to make this pretty much go away. We can generate the code for this instead, either at runtime or at build time. For our project, we will go for a build-time solution. To accomplish this, we will use something called as Fody and a plugin for it called PropertyChanged. Add another NuGet package called PropertyChanged.Fody.

If you happen to get problems during compiling, it could be the result of the dependency to a package called Fody not being installed. This happens for some versions of the package in combination with the latest Roslyn compiler. To fix this, install the NuGet package called Fody explicitly.

Now that we have all the packages, we will need some configuration in code:

  1. Open the App.xam.cs file and add the following statement:
    using Bifrost.Configuration;
  2. The next thing we will need is a constructor for the App class:
    public App()
    {
        Configure.DiscoverAndConfigure();
    }
    

    This will tell Bifrost to discover the implementations of the well-known interfaces to do the configuration.

  3. Bifrost uses the IoC container internally all the time, so the next thing we will need to do is give it an implementation. Add a class called ContainerCreator at the root of the project. Make it look as follows:
    using Bifrost.Configuration;
    using Bifrost.Execution;
    using Bifrost.Ninject;
    using Ninject;
    
    namespace SignalRChat.WPF
    {
        public class ContainerCreator : ICanCreateContainer
        {
            public IContainer CreateContainer()
            {
                var kernel = new StandardKernel();
                var container = new Container(kernel);
                return container;
            }
        }
    }
    

    We’ve chosen Ninject among others that Bifrost supports, mainly because of familiarity and habit. If you happen to have another favorite, Bifrost supports a few. It’s also fairly easy to implement your own support; just go to the source at http://github.com/dolittle/bifrost to find reference implementations.

  4. In order for Bifrost to be targeting the desktop, we need to tell it through configuration. Add a class called Configurator at the root of the project. Make it look as follows:
    using Bifrost.Configuration;
    
    namespace SignalRChat.WPF
    {
        public class Configurator : ICanConfigure
        {
            public void Configure(IConfigure configure)
            {
                configure.Frontend.Desktop();
            }
        }
    }
    

Summary

Although there are differences between creating a web solution and a desktop client, the differences have faded over time. We can apply the same principles across the different environments; it’s just different programming languages. The SignalR API adds the same type of consistency in thinking, although not as matured as the JavaScript API with proxy generation and so on; still the same ideas and concepts are found in the underlying API.

Resources for Article:


Further resources on this subject:


NO COMMENTS

LEAVE A REPLY

Please enter your comment!
Please enter your name here