Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
/// <summary>
/// Room view model.
/// </summary>
public class RoomViewModel : ViewModelBase
{
    #region Variables
    private int _bedIndex = 1;
    private int _tableIndex = 1;
    #endregion

    #region Constructor & destructor
    /// <summary>
    /// Initializes a new instance of the <see cref="RoomViewModel"/> class.
    /// </summary>
    public RoomViewModel(Models.Room room)
    {
        // Store values
        Room = room;
        // Create commands
        AddTable = new Command(OnAddTableExecuted);
        AddBed = new Command(OnAddBedExecuted);
    }
    #endregion

    #region Properties
    /// <summary>
    /// Gets the title of the view model.
    /// </summary>
    /// <value>The title.</value>
    public override string Title { get { return "Room"; } }

    #region Models
    /// <summary>
    /// Gets or sets the room.
    /// </summary>
    [Model]
    public Models.Room Room
    {
        get { return GetValue<Models.Room>(RoomProperty); }
        private set { SetValue(RoomProperty, value); }
    }

    /// <summary>
    /// Register the Room property so it is known in the class.
    /// </summary>
    public static readonly PropertyData RoomProperty = RegisterProperty("Room", typeof(Models.Room));
    #endregion

    #region View model
    /// <summary>
    /// Gets or sets the name.
    /// </summary>
    [ViewModelToModel("Room")]
    public string Name
    {
        get { return GetValue<string>(NameProperty); }
        set { SetValue(NameProperty, value); }
    }

    /// <summary>
    /// Register the Name property so it is known in the class.
    /// </summary>
    public static readonly PropertyData NameProperty = RegisterProperty("Name", typeof(string));

    /// <summary>
    /// Gets or sets the table collection.
    /// </summary>
    [ViewModelToModel("Room")]
    public ObservableCollection<Models.Table> Tables
    {
        get { return GetValue<ObservableCollection<Models.Table>>(TablesProperty); }
        set { SetValue(TablesProperty, value); }
    }

    /// <summary>
    /// Register the Tables property so it is known in the class.
    /// </summary>
    public static readonly PropertyData TablesProperty = RegisterProperty("Tables", typeof(ObservableCollection<Models.Table>));

    /// <summary>
    /// Gets or sets the bed collection.
    /// </summary>
    [ViewModelToModel("Room")]
    public ObservableCollection<Models.Bed> Beds
    {
        get { return GetValue<ObservableCollection<Models.Bed>>(BedsProperty); }
        set { SetValue(BedsProperty, value); }
    }

    /// <summary>
    /// Register the Beds property so it is known in the class.
    /// </summary>
    public static readonly PropertyData BedsProperty = RegisterProperty("Beds", typeof(ObservableCollection<Models.Bed>));
    #endregion
    #endregion

    #region Commands
    /// <summary>
    /// Gets the AddTable command.
    /// </summary>
    public Command AddTable { get; private set; }

    /// <summary>
    /// Method to invoke when the AddTable command is executed.
    /// </summary>
    private void OnAddTableExecuted()
    {
        Tables.Add(new Models.Table(string.Format("Table {0}", _tableIndex++)));
    }

    /// <summary>
    /// Gets the AddBed command.
    /// </summary>
    public Command AddBed { get; private set; }

    /// <summary>
    /// Method to invoke when the AddBed command is executed.
    /// </summary>
    private void OnAddBedExecuted()
    {
        Beds.Add(new Models.Bed(string.Format("Bed {0}", _bedIndex++)));
    }
    #endregion
}

 

 

 

 As you can see, the view model can only be constructed by passing a Room model object. It is very important to be aware of this construction. The reason that there is no empty constructor is because there is no support for views that do not represent a Room model.

In the view model, the properties of the Room model are mapped by the use of the Model attribute and the ViewModelToModel attribute. Last but not least, commands are defined to be able to add new tables and beds to the Room model.

Info

Another way to add a new user control is to use the item templates

Now the model and the view model are fully set up, the last thing to do is to create the actual view. To accomplish this, add a new WPF user control to the project. First thing to do is to implement the code-behind, since that is the easiest to do:

Code Block
<summary>
/// Interaction logic for Room.xaml
/// </summary>
public partial class Room : UserControl
{
    /// <summary>
    /// Initializes a new instance of the <see cref="Room"/> class.
    /// </summary>
    public Room()
    {
        // Initialize component
        InitializeComponent();
    }
}

The only thing we changed from the default user control template is that the user control now derives from the generic UserControl control instead of the default System.Windows.Controls.UserControl control. Then, the view model that the user control should use is provided as generic argument. This is it for the code-behind, let’s move up to the view.

The last thing to do now is the actual xaml view. For the sake of simplicity, the actual content is left out (it’s just a grid with a textbox and itemscontrols for the children):

Code Block
<catel:UserControl x:Class="Catel.Articles._03___MVVM.Examples.NestedUserControls.Room"
                      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                      xmlns:catel="http://catel.codeplex.com"
                      xmlns:NestedUserControls="clr-namespace:Catel.Articles._03___MVVM.Examples.NestedUserControls">
    <!-- For the sake of simplicity, the content is left out -->
</catel:UserControl>

A few things are very important to notice in the xaml code shown above. The first thing to notice is that (like the code-behind), the base class is now catel:UserControl instead of UserControl.

That’s all that can be learned about solving the “nested user control” problem. We have set up the model, view model and finally the view. Now, let’s take a look at how it looks in a screenshot (and notice the construction time of the view model, they are really constructed on-demand):