Click here to Skip to main content
16,005,206 members
Articles / Desktop Programming / WPF

WPF Twitter Client with Model-View-ViewModel Design Pattern Step by Step

Rate me:
Please Sign up or sign in to vote.
4.61/5 (13 votes)
29 Jun 2012CPOL13 min read 50.7K   2.4K   40   7
Details of the MVVM pattern.

Introduction

This article details a real world example of using a Model-View-ViewModel Design Pattern. I split the development process into steps. Each step is based on the previous one and adds some specific functionality. At the end of each step, we will have a working application.

As an example I chose Twitter Client. I hope that this application is comprehensible and useful. I’ll use TweetSharp library to make work with twitter API simple and not cumbersome.

The reader is supposed to have some experience of work with WPF, in particular, having investigated data binding.

I will not write here why you should use MVVM while working with WPF. Yet if you want to read in details about the motivation for using this design pattern, I would recommend you to read “WPF Apps with the Model-View-ViewModel Design Pattern” by Josh Smith. On my part, I would like to say that the design which smells is a big pain, especially if you use WPF.

You also may download a bit more developed twitter client on github: https://github.com/KirillLykov/AnhingaTwitterClient 

Background

A Twitter Client shows tweets grouped according to some criterias. It also allows submitting tweets and sends direct messages. Finally, it provides friends management – a user can “follow” somebody what means he or she will see the followings tweets in his or her tweetlist.

We start off with looking at an existing Twitter Client before writing code. This analysis will clarify how our client should look and what controls we should use.

WPFTwitterClientMVVM/pic_1.JPG

There are several bookmarks at the top of the window ­­- recent, replies, user, etc. Depending on the chosen bookmark, the different information is displayed in the main part of the window. For instance, if the recent bookmark  has been chosen, there will be the list of the last tweets. If the messages bookmark has been chosen, there are direct messages. 

This short analysis leads to the solution to use TabControl. It will display the pages with various contents and show the name of the chosen TabItem at the Top.

Brief Introduction to MVVM

First, I want to describe the situation when a developer tries to create an application using WPF but without using MVVM. It’ll provide the starting point and demonstrate benefits of using MVVM in a real- life example. 

Imagine you need to write a UserControl that displays tweets. Imagine also that you decided not to learn MVVM and prefer to do everything the same way, you would do it if you used a framework of the previous generation, like WinForms. Perhaps, you will press “Add UserControl” in VS, and then add controls you need in XAML. After that, you will add to the code behind a property which will store tweets. Finally, you will add to the code behind methods which will handle events like button mouse click, etc. At the end, you will have some XAML code which describes graphical design and code behind which stores data and contains events handlers. 

Let us have the first feature request– users need to see some other tweets, not those you store in your property. But they are ok with the design of your control. Perhaps, you will sort it out by adding a flag in your code behind. If it is true, than you show those tweets that you showed them one group tweet, otherwise another one. Not a great solution, but it works. 

Time passed and you’ve got the second feature request – some users think that every tweet contains too much redundant information. Thus, you should create a new look of tweets. At the same time, you don’t want to offend those users who like the old look… You definitely will create some sophisticated solution for this problem but it is not the last feature request you will get. And the more you get, the more difficult it is to solve them… 

The question is whether it is possible to design the application in the way which allows avoiding copy-paste and simplifying the code. What if we move the code behind user control to a separate class? In this case, we will have one class inherited from UserControl which represents the user interface. Call it View. And another class represents data directly connected with View, e.g., those data and event handlers which might be in code behind. Call it ViewModel.

This differentiation into View and ViewModel would easily solve the first feature request. We needed to display the new group of tweets in the same way as the first one. All the developer would need is to create a new ViewModel class which contains the new data and map it into the View he or she already have. 

The second feature request could be solved by writing a new View and using the old ViewModel.

The only question is how to create this mapping between View and ViewModel. The data binding is the tool which will provide this mapping. Imagine we have a View and a ViewModel which know nothing about one another. In the third class (I will call it Main View), where we are going to use View, we will set up the right DataContext for our View. It will not cause any changes in the code of View or ViewModel. I deliberately skip the technical details. I will describe them in the following paragraphs. 

If you like the UML class diagram, here we have one describing the typical application written with MVVM: 

WPFTwitterClientMVVM/pic_2.PNG

We see View classes which use data from ViewModel classes. It is essential that View classes know nothing about Model. The Model encapsulates all business logic and provides data for ViewModel. ViewModel organizes data the way suitable for View. ViewModel knows nothing about Views use it.

We also have a special View and ViewModel classes I called “Main”. The Main View is not a widespread term, but I introduced it in order to simplify the description of the MVVM. It is the main UserControl which displays all others. It sets up data context for the View classes it uses. The Main ViewModel contains all ViewModels for particular UserControls.

Step 1. The Skeleton of the Application

Start with creating the application with TabControl containing only one tabItem. Call it “Recent”.

Create a new WPF application and add TabControl in the MainWindow:

XML
<TabControl Height="400"
	HorizontalAlignment="Left"
	Name="Tabs"
	VerticalAlignment="Top" Width="300">
  <TabItem Header="Recent" Name="RecentTab"/>
</TabControl>  

The MainWindow is the Main View in our application. It will contain all other Views which will be represented in the TabItems. It must have an appropriate Main ViewModel. Call it SimpleTwitterClientViewModel. It will contain necessary data and methods which handle the events like mouse button click. Point out that the Main View must get data from the Main ViewModel (by default, it gets them from the code behind). What we should do is to set up the DataContext:

XML
<Window x:Class="SimpleTwitterClient.MainWindow"
   x:Name="MainWindowInstance"
   xmlns:view="clr-namespace:SimpleTwitterClient.View"
   xmlns:viewModel="clr-namespace:SimpleTwitterClient.ViewModel"
   DataContext="{BindingViewModel,ElementName=MainWindowInstance}">
C#
public partial class MainWindow : Window
{
    private SimpleTwitterClientViewModel _viewModel;

    public SimpleTwitterClientViewModel ViewModel
    {
        get { return _viewModel; }
        set { _viewModel = value; }
    }

    public MainWindow()
    {
        _viewModel = new SimpleTwitterClientViewModel();
        InitializeComponent();
    }
}

Create a new UserControl, call it RecentView, and add a button to it. Create also a new class RecentViewModel.

SimpleTwitterClientViewModel is to hold ViewModels for all TabItems in the TabControl. Right now, we have only “Recent” so write the following code:

C#
public class SimpleTwitterClientViewModel
{
RecentViewModel _recentPage = new RecentViewModel();

public RecentViewModel RecentPage
    {
    get { return _recentPage; }
    set { _recentPage = value; }
}
}

Get our TabItem to display RecentView. First, set up the Content of the TabItem into the RecentPage:

XML
<TabItem Header="Recent" Name="RecentTab" Content="{Binding Path=RecentPage}"/>

Second, create a mapping between RecentView and RecentViewModel:

XML
 <Window.Resources>
    <DataTemplateDataType="{x:TypeviewModel:RecentViewModel}">
	    <view:RecentView />
    </DataTemplate>
</Window.Resources>  

After all these manipulations, we will have the application like the following:

WPFTwitterClientMVVM/pic_3.JPG

At this point, the applying of MVVM may seem a bit cumbersome but at the following steps, you will appreciate the merits of MVVM.

OAuth Authorization Process

The Twitter API uses the OAuth 1.0 protocol. In order to use an application with the Twitter API, we have to write some code which will perform the necessary authorization steps. If you are not interested in the detailed description of this code, you skip the text below but get the class called OAuthHandler from the enclosed code and use it. it is a simple singleton which does all the authorization work.

  1. ConsumerKey and ConsumerSecret. Go to the site http://dev.twitter.com/ and click register. Register as the developer there and then register your application. After that, you will see the page with information about your application. Get there ConsumerKey and ConsumerSecret and save them in the application’s settings.
  2. PIN (oauth_verifier). When your application will be launched for the first time, it must send a request with the ConsumerSecret and ConsumerKey to the server:
    C#
    FluentTwitter.SetClientInfo(
           new TwitterClientInfo
           {
               ConsumerKey = Settings.Default.ConsumerKey,
               ConsumerSecret = Settings.Default.ConsumerSecret
           });
    
        var twit = FluentTwitter.CreateRequest().Authentication.GetRequestToken();
    
        var response = twit.Request();
    
        var RequestToken = response.AsToken();
        twit = twit.Authentication.AuthorizeDesktop(RequestToken.Token);
    

    The last line of this code will open the default browser. After clicking “Allow” at the appeared page, you will see the PIN. You need it in your application so create a dialog which asks the PIN from the user. I call this dialog in the method getPinFromUser:

    C#
    string verifier = getPinFromUser();
    
  3. AccessToken. Now, send the request containing consumerKey, consumerSecret, and Pin. The service will return AccessToken which will be used to work with application further, so save it somewhere:
    C#
    twit.Authentication.GetAccessToken(RequestToken.Token, verifier);
    var response2 = twit.Request();
    
  4. Refreshing your AccessToken. After time passed, you need to get the new AccessToken.

Step 2. Displaying tweets

Fill in the RecentPage by the real data, i.e. the last tweets posted by the user’s followings and the user’s own tweets. In order to do that, we will use TweetSharp library. Add it to the list of references for your project.

Before starting, add the classes OAuthHandler and AskPinFromUserDialog from enclosed code to the Model folder. Add ConsumerKey, ConsumerSecret, OauthInfoFileName to settings. OauthInfoFileName contains the path to the config file where we store AccessToken. Perhaps, it is not the greatest solution but it is simple and works. Finally, add the creation of the OAuthHandler to the Main ViewModel:

C#
Model.OAuthHandler _oauthHandler = newModel.OAuthHandler();

Now, we can start working with RecentPage. First, add the container which will hold tweets to the RecentViewModel:

C#
public ObservableCollection<TwitterStatus> Tweets
{
 get; set;
}

We used ObservableCollection instead of regular .NET container like List because ObservableCollection synchronizes the data with the View automatically, i.e., we needn’t wait for event “data changed” in order to refresh View.

Now, download tweets. First, create a constructor for the RecentViewModel which gets the OAuthHandler as the parameter and save it into the _oauthHandler field. After that, add the LoadTweets method:

C#
public void LoadTweets()
{
    TwitterResult response = FluentTwitter
            .CreateRequest()
            .AuthenticateWith(
                Settings.Default.ConsumerKey,
                Settings.Default.ConsumerSecret,
                Model.OAuthHandler.Token,
                Model.OAuthHandler.TokenSecret)
            .Statuses()
            .OnHomeTimeline().AsJson().Request());

   var statuses = response.AsStatuses();
   foreach (TwitterStatus status in statuses)
   {
       Tweets.Add(status);
   }
}

For the first time, call LoadTweet right in the constructor of RecentViewModel:

C#
public RecentViewModel(Model.OAuthHandleroauthHandler)
{
    _oauthHandler = oauthHandler;
    Tweets = newObservableCollection<TwitterStatus>();
    LoadTweets();
}

Then create ListBox which will display tweets:

XML
<ListBoxx:Name="RecentTweetList"
   ItemsSource="{Binding Path=Tweets}"
   IsSynchronizedWithCurrentItem="True"/>

If you execute the application now, you will see the list with the result of invoking method TweetStatus.ToString(). It is because the elements of the ListBox don’t know how to display the objects of type TweetSharp. In order to do that, we need to add a DataTemplate to the UserControl.Resources.DataTemplate should contain the XAML code which determines the look of tweet. It should contain textbox for the text of the tweet, image for the userpic and so on. For every element like this one, we need to write binding to the appropriate property of the TweetSharp type object:

XML
<DataTemplate x:Key="TweetItemTemplate">
    <Grid x:Name="TTGrid">
       <Grid.RowDefinitions>
           <RowDefinition Height="5*" />
           <RowDefinition Height="2*" />
       </Grid.RowDefinitions>
       <Grid.ColumnDefinitions>
           <ColumnDefinition Width="5*" />
           <ColumnDefinition Width="3*" />
           <ColumnDefinition Width="17*" />
       </Grid.ColumnDefinitions>
       <Image Source="{Binding Path=User.ProfileImageUrl}"
        Name="UserAvatarImage" />
       <TextBlock
         Name="ScreenNameTextBlock"
         Text="{Binding Path=User.ScreenName}"
         Grid.Row="1"Grid.ColumnSpan="2"/>
       <TextBlock
         Text="{Binding Path=Text}"
         TextWrapping="Wrap"Grid.Column="1"Grid.ColumnSpan="2" />
    </Grid>
</DataTemplate> 

Finally, point that the ListBox must use this DataTemplate:

XML
ItemTemplate="{StaticResourceTweetItemTemplate}" 

Launch the application. It should look like that:

WPFTwitterClientMVVM/pic_4.JPG

Step 3. More Pages

In this paragraph, we create an additional page which contains retweets.

The recent bookmark displays the last tweets being written by user’s followings or by the user him / her self. The method which downloads this group of tweet called HomeTimeline in TweetSharp. Yet it is not the only group of tweets that might be desirable for the user and in the class TwitterStatusesExtensions you may find methods that download other groups of tweets: OnFriendsTimeline, OnListTimeline, RetweetedByMe

For instance, we’ll choose all retweets posted by your friends. In order to do that, we should create class RetweetsViewModel which is to store all tweets obtained by RetweetedToMe() method. I’ll not write this code here, let it be a simple exercise. Note you don’t need to code a RetweetsView class. I will explain why at the end of the paragraph.

What we need to do now is to add the RetweetsViewModel object to the main ViewModel. Actually, we can do that by adding the new property for it and in the case of two pages, it’s ok. Yet imagine that we want to deal with many pages in TabControl. In this case, it makes sense to store pages in some container.

The RecentViewModel is very similar to the RetweetsViewModel. We can extract an interface IPageBase from the RecentViewModel and make the RetweetsViewModel class implement it. We also may make both of them have default constructors by a property:

C#
public interface IPageBase
{
   void LoadTweets();
   ObservableCollection<TwitterStatus> Tweets { get; set; }
}

Now, the time to change the code of the main ViewModel comes:

C#
ObservableCollection<IPageBase> _pages;

public ObservableCollection<IPageBase> Pages
{
   get { return _pages; }
   set { _pages = value; }
}

public SimpleTwitterClientViewModel()
{
   _pages = new ObservableCollection<IPageBase>();
   _pages.Add(new RecentViewModel());
   _pages.Add(new RetweetsViewModel());
   foreach (var page in _pages)
   {
      page.LoadTweets();
   }
} 

Finally, we shall add the new DataTemplate for the RetweetsPage in the Main View. Ok, there we get a problem. Before we have had only one TabItem which was bound to the property RecentPage in the Main ViewModel:

C#
<TabItem Header="Recent" Name="RecentTab" Content="{Binding Path=RecentPage}" /> 

Now we can’t be satisfied with such a solution because we need to have as many TabItems as the amount of pages there are. Fortunately, there is a smart property ItemsSource in the TabControl class so we can just bind your container to it.

Yet there is an issue. A TabItem has a Header which displays the name of the page but if our TabItems has been automatically generated by the ObservableCollection, it is not clear how to set TabItems headers. Don’t panic, first, add Name property to the IPageBase interface and implement it in every page class. Then create a style for the TabItem’s Header:

XML
<TabControlName="Tabs"
 ItemsSource="{Binding Pages}">
   <TabControl.ItemContainerStyle>
       <Style TargetType="TabItem">
           <Setter Property="Header" Value="{Binding Name}"/>
       </Style>
   </TabControl.ItemContainerStyle>
</TabControl> 

At the end, we will add View for our RetweetsViewModel. Of course, you may create a RetweetsView UserControl by copy-pasting code from the FriendsView. But there is a better solution. You may use RecentView as is:

XML
<DataTemplateDataType="{x:TypeviewModel:RetweetsViewModel}">
    <view:RecentView />
</DataTemplate> 

Finally, rename RecentView to TweetsView. That’s all. Now, our application should look like that:

WPFTwitterClientMVVM/pic_5.JPG

If you don’t like writing two similar DataTemplates, you might create a superclass for these ViewModels and specify only one DataTemplate for this superclass.

As an exercise, you may implement View and ViewModel for the list of user’s followings and followers.

Step 4. Sending tweets and ICommand

Once previous 3 steps are completed, a user gets an application which allows reading tweets. But user still can’t send tweets. In this paragraph, I will describe how to implement this feature according to MVVM.

Add TextBox and Button at the bottom of the Main View:

WPFTwitterClientMVVM/pic_6.JPG

The main question of this paragraph is how to handle events raised by control elements in the View. If you liked to have Send button click event handler in the code behind, you would click on this button right in the designer and would write code in the created function in the code behind. So far as we use MVVM, the method that handles the event must be handled in the Main ViewModel. It leads to a bit more tricky code.

First, add the Message property to the Main ViewModel. Then bind your tweet textbox to Message property:

XML
<TextBoxName="EnterTweetTextBox"
  Text="{BindingMessage}"/>

Write a method which will send tweet:

C#
private void SendTweet()
{
      var twitter = FluentTwitter.CreateRequest();
      twitter.AuthenticateWith(
                                Settings.Default.ConsumerKey,
                                Settings.Default.ConsumerSecret,
                                OAuthHandler.Token,
                                OAuthHandler.TokenSecret);
      twitter.Statuses().Update(Message);

      var response = twitter.Request();
      //you can verify the response here
} 

There is only one problem to solve – how to specify that SendTweet method should be called when the Send button is clicked. The Button class has a relevant property Command. It specifies the method which invokes when the button is pressed. Great, but Command has a type ICommand, while your SendMessage, of course, is just a function and it can’t implement this interface. The convenient solution is to use Adapter pattern from the “Design Patterns” book. It is easy but we don’t need to write this adapter because it has been already written by Josh Smith.

C#
internal class RelayCommand : ICommand
{
    #region Fields

    readonly Action _execute;
    readonly Func<bool> _canExecute;

    #endregion

    #region Constructors

    public RelayCommand(Action execute)
        : this(execute, null)
    {
    }

    public RelayCommand(Action execute, Func<bool> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }

    #endregion // Constructors

    #region ICommand Members

    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute();
    }

    public event EventHandler CanExecuteChanged
    {
        add
        {
            if (_canExecute != null)
                CommandManager.RequerySuggested += value;
        }
        remove
        {
            if (_canExecute != null)
                CommandManager.RequerySuggested -= value;
        }
    }

    public void Execute(object parameter)
    {
        _execute();
    }

    #endregion // ICommand Members
}

Class RelayCommand just adapts delegate to the interface ICommand. To use it in the code, you need to create a property which wraps the SendTweet with ICommand:

C#
RelayCommand _sendCommand;
public ICommand SendCommand
{
    get
    {
        if (_sendCommand == null)
        {
             _sendCommand = new RelayCommand(() => this.SendTweet());
        }
        return _sendCommand;
    }
}

Finally, let we make SendButton to use SendCommand as it’s handler:

XML
<Button Name="SendTweetButton"
Command="{Binding SendCommand}"/>

Not really tricky, isn’t it? That’s how the tweeter client looks like now:

WPFTwitterClientMVVM/pic_7.PNG

In order to make it look like the existing twitter clients, I added a stylistic effect. The textbox displays how many symbols user may enter.

As an exercise, you may implement the following feature. If user clicks on the userpic, the TabItem will be changed. It will display only tweets posted by this following.

License

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


Written By
Software Developer Institute of Computational Science
Switzerland Switzerland
I was Software Engineer and developed software using various languages and technologies, including C++, C#, Java, JavaScript. Now I'm Doctoral Assistant and make a study in computational chemistry.

Comments and Discussions

 
Questionupdates for vs 2012 - vs 2013 Pin
kiquenet.com10-Jul-14 4:42
professionalkiquenet.com10-Jul-14 4:42 
AnswerБольшое спасибо! Pin
Solovey198520-May-14 0:08
professionalSolovey198520-May-14 0:08 
QuestionWhy MVVM needed at all? Pin
Thornik5-Jul-12 6:16
Thornik5-Jul-12 6:16 
GeneralMy vote of 4 Pin
Erlend Håland29-Dec-10 9:11
Erlend Håland29-Dec-10 9:11 
GeneralMy vote of 2 Pin
SledgeHammer0129-Dec-10 4:51
SledgeHammer0129-Dec-10 4:51 
GeneralRe: My vote of 2 Pin
Kirill_Lykov29-Dec-10 19:36
Kirill_Lykov29-Dec-10 19:36 

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.