Agile UI Development in .NET: View

Last time we started looking at sample code with the view-model class for the UI to send messages on channels (table of contents).

In this post, we continue with the next responsibility, the visualization.

The View

The view is responsible for visualizing the domain model to the user. We have seen in the last post that the view-model provides a simplified mini-model to the view. That means that the view does not have to care about the domain model as a whole with all its interactions and constraints. The view-model provides only the part of the domain model that is relevant to the current screen the user is seeing thus simplifying the job of the view.

Let’s jump into the cold water and look at the view that uses the view-model of the last post:

<UserControl x:Class="ProCollEE.WpfClient.MessageEntry.MessageEntryView"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:local="clr-namespace:ProCollEE.WpfClient.MessageEntry">
     <UserControl.DataContext>
         <local:MessageEntryViewModel Text="Hello ProCollEE World"/>
     </UserControl.DataContext>

     <StackPanel>
         <ContentControl Name="channelSelection" />
         <Grid>
             <Grid.ColumnDefinitions>
                 <ColumnDefinition Width="*"/>
                 <ColumnDefinition Width="Auto" MinWidth="47" />
         </Grid.ColumnDefinitions>
         <TextBox AutomationProperties.AutomationId="message"
                  Text="{Binding Path=Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
                  Margin="0,0,8,0"
                  AutoWordSelection="True"
                  TextWrapping="WrapWithOverflow" />
         <Button AutomationProperties.AutomationId="sendButton"
                 Grid.Column="1"
                 HorizontalAlignment="Right"
                 Padding="8,0,8,0"
                 Width="Auto"
                 Command="{Binding SendMessageCommand}"
                 IsDefault="True">Send</Button>
         </Grid>
    </StackPanel>

</UserControl>
/// <summary>
/// Interaction logic for MessageEntryView.xaml
/// </summary></pre>
public partial class MessageEntryView : IMessageEntryView
{
    /// <summary>
    /// Initializes a new instance of the <see cref="MessageEntryView"/> class.
    /// </summary>
    public MessageEntryView()
    {
        InitializeComponent();
    }

    /// <summary>
    /// Gets the user control displaying the message entry UI.
    /// </summary>
    /// <value>The user control displaying the message entry UI.</value>
    public UserControl AsUserControl
    {
        get { return this; }
    }

    /// <summary>
    /// Initializes this instance.
    /// </summary>
    /// <param name="viewModel">The view model.</param>
    /// <param name="channelSelectionView">The embedded channel selection view.</param>
    [Inject]
    public void Initialize(IMessageEntryViewModel viewModel, IChannelSelectionView channelSelectionView)
    {
        this.DataContext = viewModel;

        this.channelSelection.Content = channelSelectionView.AsUserControl;
    }
}

The markup and code are easy enough. Beside the layout information, there are three important parts:

  • place holder for the embedded channel-selection view
  • text box for message text entry
  • button to send the message

Place Holder for the Embedded Channel-Selection View

This view is dedicated to the entry of the message text. To send a message, we need additional information about which channels the message has to been sent on. This so called channel selection has its own view/view-model to separate these two responsibilities. The ContentControl is used to embed the channel-selection view at run-time when the application is started:

<ContentControl Name="channelSelection" />
this.channelSelection.Content = channelSelectionView.AsUserControl;

TextBox for Message Text Entry

The TextBox uses data-binding to bind the value shown in the control with the value on the view-model:

<TextBox Text="{Binding Path=Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"

The Mode is set to TwoWay to allow data-binding in both ways, from the view-model to the view and vice versa.

As you can see, the UpdateSourceTrigger is set to PropertyChanged. This causes data-binding whenever the user enters a key. This is used to enable or disable the Send button. We’ll come back to that when we have a look at the SendMessageUICommand later in this series.

To get data-binding running, we have to set the DataContext of the view. This is done in the Initialize method of the view:

this.DataContext = viewModel;

The Initialize method is decorated with the Inject attribute to tell Ninject to call this method when the instance is created and to pass the dependencies to the view.

Button to Send the Message

The button is data-bound to a command:

<Button Command="{Binding SendMessageCommand}" IsDefault="True">Send</Button>

The command takes care of enabling/disabling the button and performs the action to send the message. This command is the topic of the next post in this series.

Sample Data Context

As you can see in the XAML above, I set the data context in the XAML to some sample data. This data context gets override in the initialize method in the code-behind of the view.

The trick of this sample data is that it is shown in the XAML designer in Visual Studio and Expression Blend. Designing your views with sample data is much easier than without data to check the layout.

Conclusions

We have seen a simple enough view that allows us to enter a message and send it. The functionality to select the channels to send the message on is outsourced to an embedded view.

Data-binding connects the view-model with the view and a command is used to react to the click on the send button.

About the author

Urs Enzler

Add comment

By Urs Enzler

Recent Posts