Thought Cloud UI #2: Summary view models

We’ve started building the Thought Cloud user interface. It won’t be long until we see something working.

image I like to build Silverlight user interfaces via composition. I construct each view as a separate UserControl. Then I compose them into a working application. Taking this approach, let’s create a Views folder in the application, and a HomeView UserControl.

I use Visual Studio to cerate all of my user controls, since Blend messes up the namespaces. But once it’s created, I switch to Blend for everything else.

Create sample data

The HomeView will be data bound to the HomeViewModel. The first step is to create sample data from that class. This will give us all of the properties that we can data bind to, and it will let Blend automatically generate some very useful things.

image

image

The home view model simply has a list of clouds and a command to add a new cloud. Each cloud in the list has a list of thoughts and a command to add a new thought. There’s a problem with that. On this view, we intend to show a summary of each cloud. The next view will let you edit thoughts. Let’s change our cloud view models to cloud summary view models.

public class CloudSummaryViewModel
{
    private Cloud _cloud;

    public CloudSummaryViewModel(Cloud cloud)
    {
        _cloud = cloud;
    }

    internal Cloud Cloud
    {
        get { return _cloud; }
    }

    public string Text
    {
        get
        {
            Thought centralThought = _cloud.CentralThought;
            if (centralThought == null)
                return "<New cloud>";
            string text = centralThought.Text;
            if (text == null)
                return "<New cloud>";
            return text;
        }
    }

    public override bool Equals(object obj)
    {
        if (obj == this)
            return true;
        CloudSummaryViewModel that = obj as CloudSummaryViewModel;
        if (that == null)
            return false;
        return _cloud == that._cloud;
    }

    public override int GetHashCode()
    {
        return _cloud.GetHashCode();
    }
}

The cloud summary view model displays the text of the central thought. If it has no central thought, or if the text is not yet initialized, then it displays “<New cloud>”. This is different than the “My thought” behavior that we implemented in the cloud view model. When the user is looking at a list of clouds, they might get confused if one of them claims to be a thought.

The cloud summary view model also returns the wrapped Cloud object, and implements Equals and GetHashCode. This pattern always arises for a view model that appears in a selectable list. The Cloud property let’s us correlate user selection with the model (it is internal so that we don’t accidentally data bind to it). And the Equals and GetHashCode methods make sure that the user selection is equal to a member of the list, even if a new view model is created.

Regenerate sample data

Since we changed the view model after generating the sample data, Blend warns us that it is incorrect.

image

imageThe easiest thing to do is delete it and recreate it. Then we can drag the Clouds property onto the art board. We end up with a list box filled with lorem ipsum text for several clouds.

What Blend has done is to create an item template that brings in the item properties. In this case, the list contains CloudSummaryViewModels, which has only the Text property. (Remember that we made the Cloud property internal so Blend would ignore it.) To see the generated item template, right-click the list, choose “Edit Additional Templates”, “Edit Generated Items (ItemTemplate)”, “Edit Current”.

image

This will dive into the list box and show you an item template with just one TextBlock. It is bound to the Text property.

Compose the application

To see the application running, we need to do a couple more things. First, drop a button on the HomeView and drag the AddCloud command onto it. This data binds the button to the command. Then open the MainPage and add a HomeView to the page. You can get to the HomeView by clicking the “Assets” toolbar button and selecting “Project”.

image

Now we need to give the view a view model. Recall from the last post that MainPage uses a view model locator to find a MainViewModel. So we can just expose a HomeViewModel from MainViewModel.

public HomeViewModel Home
{
    get
    {
        return _navigationModel.CurrentUser == null
            ? null
            : new HomeViewModel(_navigationModel.CurrentUser);
    }
}

This property will give us a home view model if the user is logged in. If not, it will return null. When the user logs in or out, CurrentUser changes (remember that it is Independent), so the view model will fire PropertyChanged. We haven’t implemented the log in logic, so let’s just fake it for now.

public class SynchronizationService
{
    ...

    public void Initialize()
    {
        ...

        _navigationModel.CurrentUser = _community.AddFact(new Identity("mike"));
    }
}

All that remains is to data bind the DataContext of the HomeView inside of the MainPage to the Home property. Generate sample data for MainViewModel, and drag the Home property onto the HomeView. We now have an app that will add “<New Cloud>” to a list box whenever you hit the “New Cloud” button.

Leave a Reply

You must be logged in to post a comment.