WPF + MVVM – a quick little status app (1 of 2)

Oh man, I’ve been like a kid in the candy story during my winter break from school. I dove into Entity Frameworks, Linq-to-Objects, MVC, and JQuery (I still owe a couple more posts on that – which will come in the next few days). Now, I’ve also jumped into WPF, MVVM, and some open-source components.

This first post will be about WPF, MVVM, and the app. The second post will be about the open source components.

School starts for my LAST semester (WOO HOO!) tomorrow. There is one more thing I wanted to try to do, while I still have some free time: I wanted to write a status app.

As discussed, I have a main monitor, and two little 7" MIMO USB-monitors on either side. On the right is my "twitter wall", which runs MetroTwit. On the left is Windows Live Mail, which shows me all my mail accounts. Ideally, I would like to have on ONE monitor:

  • Time/date
  • Temperature
  • Tweets (my home timeline)
  • My new e-mails

So that while I am on my work computer, this ONE 7" little monitor can show this status. Well opportunity knocked a few days ago when the left monitor quit unexpectedly. I thought, let’s just take a swing and see how far we get with just writing an app that is what I want – if it gets to be too much, then I don’t have to finish it.

Let me start with what I created, and that try to explain some of the really interesting things I learned!

image

and to better explain what this looks like in the real-world:

2011-01-16_20-27-54_649

WPF:
I’ve dabbled in WPF on several occasions and always struggled with it. A lot of things were very different from regular development (Windows, command-line, OR web). Earlier in the week, I had some insomnia and watched a few of these videos on learning WPF that I’d HIGHLY recommend. These get right to the point and are for .NET developers that know how to program, but "don’t get" some of these WPF conventions:

http://windowsclient.net/learn/videos_wpf.aspx

The ones by Pete Brown in particular are just great, very easy to understand. As a "regular" developer, here are some of the "Ohhhhhh!!" moments I had:

  • A "Canvas" is like a flow-layout container.
  • A "Grid" is like a table-layout container where items within it can have a RowSpan and ColSpan, etc.
  • You set the text of most things with the .Content property, not the .Text property.
  • The TextBlock is pretty much the best control to use to put any sort of text on the screen.
  • A ListBox is extremely powerful, you can very easily ‘layout’ what the contents of a single ListItem will look like. In my screenshots above, each tweet and each mail item are my own custom layouts – where I created a template for the ListItem, and the ListBox just shows the items, according to the template.
  • Don’t do things manually – use MVVM, you will be glad you did.

MVVM:
If you are new to these newer .NET technologies, you may have noticed the surge of some of these design patterns becoming mainstream, like MVC, MVP, and MVVM.

Well it’s for good reason, these are fundamentally "better" ways to create applications. I don’t mean that academically they are more proper, I mean these sorts of patterns require less code, make each component much easier to manage and extend and generally makes the application easier and faster to write. So please, give them a chance. I am talking through the MVC stuff in this blog series. Here, I’ll try to cover the basics of MVVM.

MVVM stands for Model-View-ViewModel. This is the notion of:

  • Model – that is, a data structure or collection of data structures that represent the data you will need for your screen. The Model JUST deals with holding data, it does no formatting or preparing.
  • View – that is, the actual UI. The UI shows (or in the case of WPF or Silverlight) "binds" to the Model. But wait, we don’t want to put logic in the UI and we said we aren’t putting logic in the Model – so how do we make the UI look good?
  • ViewModel – the view "binds" to the Model, but gets its data from the ViewModel. The ViewModel gets data from (or for) the view, and takes care or processing or formatting.

This is probably just as vague as everything else written up on MVVM, so let me try to make this a little more real. In the application above, there is a "Model" for Tweets. This is a class that has properties like From, FromImage, Text, TweetDate, etc.

There is a ViewModel called TwitterViewModel that actually GETS the data from Twitter (via the utter train-wreck that is "TweetSharp" – seriously, don’t get me started!). The ViewModel offers an ObservableCollection as a public property that UI can bind to. So the logic for getting the tweets, sorting the tweets, and offering them – this is done by the ViewModel.

Lastly, let’s talk about the View. WPF is really quite different than Windows or Web development in this respect. Although you COULD use WPF just like you do a Windows app, where you put code in Form_Load and things like that, WPF does have some extremely powerful features that you should consider. They may be confusing at first, but push through that, this is really worth learning – I swear!

For example, and this is important, every WPF form, control, or page has exactly one "DataContext". That is, a ViewModel, that it is going to use. The good part here is that you can "bind" controls to things within that ViewModel. Imagine a "customer" edit screen, your DataContext would be a ViewModel that offers up a Customer Model, if that helps.

Now, this application is not a great example (or maybe it is?) – because I have several ViewModels to manage. I have Models and ViewModels for: Twitter, Mail, and Weather data If my main page can only handle exactly one ViewModel – what do I do? Well, I create a container ViewModel. For example:

    public class StatusViewModel

    {

 

        DateTime lastUpdateMail = DateTime.MinValue;

        DateTime lastUpdateWeather = DateTime.MinValue;

        DateTime lastUpdateTwitter = DateTime.MinValue;

 

        void updateTimer_Tick(object sender, EventArgs e)

        {

            // TODO: Get newest tweets, check mail, update weather

        }

 

        DispatcherTimer updateTimer = new DispatcherTimer();

 

        public MailViewModel MailItems { get; set; }

 

        public TwitterViewModel Tweets { get; set; }

 

        public WeatherViewModel Weather { get; set; }

    }

To be clear, I don’t know if this is the right way to do it, but this is how I did it, and this works pretty well. So the top-level ViewModel has properties for the sub-ViewModels that are needed on this screen. I also have the top-ViewModel control the timing of updates, just because it seemed simpler. Mail updates are once per 15-minutes, twitter updates are once per 150 seconds, and weather updates are once-per-hour, because they only update the data once per hour.

But how is MVVM fundamentally different though? Ok, so the window/control/page/whatever has a "DataContext", and you "bind" to elements within it. For example, here is some XAML from the main form:

<Window x:Class="SederSoftware.Status.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;

        Title="SederSoftware.Status" Height="800" Width="480" Loaded="Window_Loaded"

        xmlns:svm="clr-namespace:SederSoftware.Status.Support.ViewModels;assembly=SederSoftware.Status.Support">

    <Window.DataContext>

        <svm:StatusViewModel />

    </Window.DataContext>

So I declare the "svm" prefix to point to the Status.Support namespace, and StatusViewModel is a class within it. StatusViewModel as you can see from the snippet above is just a regular class, doesn’t need to inherit or implement anything.

OK, so on that page, if I want to "bind" to something, I use this special syntax. For example, the TwitterViewModel has a public ObservableCollection called Tweets. I could add a ListBox, and bind to the collection.

<ListBox ItemsSource="{Binding Path=Tweets.Tweets}" />

"How does it know what Tweets is?" – binding and Path statements are always relative to the current DataContext. These are not great names, but StatusViewModel has a property called Tweets that is a TwitterViewModel – and that TwitterViewModel has an ObservableCollection property called Tweets.

You can bind, for example, a label to a specific property too – but in this case, we want to bind a list to collection. Now by default, this won’t show correctly, because we need to create a "DataTemplate" for how to show each individual tweet. You do that by doing something like this:

    <ListBox Margin="6"

        Name="listBox1"

        ItemsSource="{Binding Path=Tweets.Tweets}">

        <ListBox.ItemTemplate>

            <DataTemplate>

                <local:TwitterItemTemplate Width="{Binding ActualWidth, ElementName=listBox1, Mode=OneWay}"  />

            </DataTemplate>

        </ListBox.ItemTemplate>

    </ListBox>

So to get a little more technical, I have a separate User Control where I have this ListBox, and this is (basically) how it’s defined. A couple of things to note here. I bind the "ActualWidth" of the ListBox (the visible area) to the Width of the ListItem. Otherwise, each ListItem can be whatever size it wants. Also, you might be able to guess that we are saying "there is a data template for this, but it’s not here – go load the TwitterItemTemplate". That looks like this:

    <Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="20*" />

            <RowDefinition Height="50*" />

        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="68*" />

            <ColumnDefinition Width="354*" />

            <ColumnDefinition Width="111*" />

        </Grid.ColumnDefinitions>

        <Image Source="{Binding Path=FromImage}" Grid.RowSpan="2">

            <Image.BitmapEffect>

                <DropShadowBitmapEffect />

            </Image.BitmapEffect>

        </Image>

        <TextBlock Text="{Binding Path=From}"

                Grid.Column="1"  Padding="2" FontWeight="Bold" />

        <TextBlock Text="{Binding Path=Text}"

                    TextWrapping="Wrap"

                Grid.Column="1"

                Grid.Row="1" TextTrimming="CharacterEllipsis" Grid.ColumnSpan="2"/>

        <TextBlock Grid.Column="2" Name="textBlock1" Text="{Binding Path=TimePassed}" FontSize="10" />

    </Grid>

There is more to it, because there is a rounded border and gradient background, but above is sort of the nuts and bolts. Now, if you follow this logic:

  • The main page has a DataContext that points to my container ViewModel (which has a property for the TwitterViewModel)
  • The TwitterFeedControl is a user control on the main page. TwitterFeedControl does NOT have a DataContext defined, it inherits from the parent window. The ListBox within binds to the Tweets ObservableCollection.
  • The ListBox on the TwitterFeedControl points to a DataTemplate (which is also just a user control) which also doesn’t have it’s own DataContext, it just piggy-backs on top of the DataContext from the main window.

This may be kind of confusing by now, but the point is here is that all the UI is doing, is worrying about: 1) where to bind and 2) what specifically it’s binding to. That’s it. You’ll see there is no code-behind, there is not for/next or conditional logic. The "View" is purely and wholly concerned with simply showing the data from the View, that is got from the ViewModel. This means that the UI has VERY little code, and the XAML it does have is all around formatting, gradients, effects, etc – which is what SHOULD be in the UI!

As you might imagine, that means that there is a lot of magic that must happen in that ViewModel and in the Model too. For example, for the twitter part, I wanted to show how old a Tweet is, in real-time (note in the top-right corner):

image

this means that when there is a new tweet, you will see "7 seconds ago", "8 seconds ago", etc. Think about how you might do this traditional programming in Windows. Now try to think how you’d do it in MVVM. The UI (View) wouldn’t get all involved in the data, it seems like a lot of work to have the ViewModel do it, because it would have to scroll through all the items to update that time. Why not just have the individual data structure (the View) update itself.

Hopefully you can see the magic of this – and the implications:

Keep in mind that WPF and Silverlight automatically respect INotifyPropertyChanged for anything that is bound. So in the screenshot about, the "4 minutes ago" is a TextBlock that is bound to Tweet.TimePassed.

That means, if a UI element is "bound" to a property of some class, and that property changes, the UI will update automatically.

Well then this is easy, all I need to do is create a new property, and wire up a timer. When the timer ticks, I fire the PropertyChanged event, so that the UI picks up the new value. Here is the top of the Tweet class (Tweet is the "Model" for this functionality):

    public class Tweet : INotifyPropertyChanged

    {

        DispatcherTimer updateTimer = new DispatcherTimer();

        public Tweet()

        {

            updateTimer.Interval = new TimeSpan(0, 0, 1);

            updateTimer.IsEnabled = true;

            updateTimer.Tick += new EventHandler(updateTimer_Tick);

            updateTimer.Start();

        }

 

        void updateTimer_Tick(object sender, EventArgs e)

        {

            if (PropertyChanged != null)

            {

                PropertyChanged(this, new PropertyChangedEventArgs("TimePassed"));

            }

        }

 

        public string TimePassed

        {

            get

            {

                TimeSpan timePassed = DateTime.Now.Subtract(TweetDate);

                if (timePassed.TotalSeconds < 60)

                    return timePassed.TotalSeconds.ToString("N0") + " seconds ago";

                else if (timePassed.TotalMinutes < 60)

                    return timePassed.TotalMinutes.ToString("N0") + " minutes ago";

                else if (timePassed.TotalHours < 24)

                    return timePassed.TotalHours.ToString("N1") + " hours ago";

                else

                    return timePassed.TotalDays.ToString("N1") + " days ago";

            }

        } 

Bottom Line:
So hopefully that helps explain the concept of MVVM as it applies to WPF and Silverlight. It’s a fundamentally different way to think about wiring up a UI. Having dug into this and building an entire application, I REALLY like this! There is such a crisp "separation of concerns". In other words, if there is a problem, it’s so easy to tell where it’s coming from. That coupled with how easy this was to code, has made for a relatively fast and easy app to write.

Well, that’s with the exception of the three components I used for: weather, twitter, and e-mail. I’ll cover that in the next post…

Tagged with: , ,
Posted in Uncategorized, WPF and MVVM
3 comments on “WPF + MVVM – a quick little status app (1 of 2)
  1. […] Rob Seder's Blog Incredibly interesting blog posts from a .NET duh-veloper Skip to content HomeAbout ← WPF + MVVM – a quick little status app (1 of 2) […]

    Like

  2. […] Seder| Leave a comment I’ve been continuing to dabble with my little status app (described here and here). I wanted to write down some of the lessons learned so far. Also, I think I have a decent […]

    Like

  3. Matt says:

    Awesome, i got a couple of “take-home” points out of this.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
Categories

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 5 other followers

%d bloggers like this: