When building any kind of application, it's important to start with a good architecture, a set of reusable helper classes, and design patterns. In this first of a multi-part series of articles, you'll learn to use a message broker to eliminate strong coupling between classes. You'll see how to display status and informational messages to the user while resources are loading. Instead of having a ton of open windows, you'll learn to load user controls onto a single window and how to aggregate controls and build large screens.

This article is the first in a multi-part series on how to create a WPF business application. Instead of starting completely from scratch, I've created a starting architecture that you can learn about by reading the blog post entitled “An Architecture for WPF Applications” located at https://pdsa.com/blog. Download the samples that go along with the blog post to follow along step-by-step with this article. This series of articles is also a Pluralsight.com course that you may view at https://bit.ly/2SjwTeb.

Create a Message Display Area

On the main window, you're going to create a Border control with a couple of text block controls on which you can display messages, as shown in Figure 1. You can use this message area for displaying messages while your application loads. You can also use it to display informational messages that automatically disappear after a few seconds.

Figure 1: Two text blocks are used to display informational messages on the main window.
Figure 1: Two text blocks are used to display informational messages on the main window.

Add Properties to Display Messages

If you haven't already done so, download the samples for this article so you can follow along. Open the solution file included in the sample files, go to the project WPF.Sample.ViewModelLayer, and open the MainWindowViewModel.cs file. This View Model class is bound to the MainWindow class, so any messages to display or controls you need to manipulate require you to add properties in this View Model class.

As seen in Figure 1, a large and a small message may be displayed within the message area. These messages are displayed within a border that appears in the middle of the MainWindow. Add three properties (Listing 1) to this class; one to control the visibility of the border (IsInfoMessageVisible), a message for the large title message (InfoMessageTitle), and the small message (InfoMessage), as shown in Figure 1.

Listing 1: Add three properties to help you display messages on the main window

private bool _IsInfoMessageVisible = true;
private string _InfoMessageTitle = string.Empty;
private string _InfoMessage = string.Empty;

public bool IsInfoMessageVisible 
{
    get { return _IsInfoMessageVisible; }
    set 
    {
        _IsInfoMessageVisible = value;
        RaisePropertyChanged("IsInfoMessageVisible");
    }
}

public string InfoMessage 
{
    get { return _InfoMessage; }
    set 
    {
        _InfoMessage = value;
        RaisePropertyChanged("InfoMessage");
    }
}

public string InfoMessageTitle 
{
    get { return _InfoMessageTitle; }
    set
    {
        _InfoMessageTitle = value;
        RaisePropertyChanged("InfoMessageTitle");
    }
}

Set the Starting Message to Display

After entering the properties in the MainWindowViewModel class, build the project. Open the MainWindow.xaml file and locate the <Window.Resources> element. Add the property, InfoMessageTitle, on the View Model object created in the <Window.Resources> element. Set the value of the InfoMessageTitle property to "Please Wait While Loading Application...". This message displays in the large text block in the message area.

<Window.Resources>
  <vm:MainWindowViewModel x:Key="viewModel" 
                          InfoMessageTitle="Please Wait While Loading Application..."
                          StatusMessage="Sample of Business Application Screens" />
</Window.Resources>

Add XAML to Display Messages

Add a <Border> element after the closing </Menu> element. The <Border> is the splash screen/informational message area used to display messages to the user. In the next code snippet is the XAML to enter.

<!-- Informational Message Area -->
<Border Grid.Row="1" 
        Panel.ZIndex="2" 
        Visibility="{Binding Path=IsInfoMessageVisible, Converter={StaticResource visibilityConverter}}"
        Style="{StaticResource infoMessageArea}">
  <StackPanel>
    <TextBlock FontSize="40" Text="{Binding Path=InfoMessageTitle}" />
    <TextBlock FontSize="24" Text="{Binding Path=InfoMessage}" />
  </StackPanel>
</Border>

Try It Out

Save all of your changes and run the application. When the application starts, you should see a screen that looks like Figure 2. Once you verify that the message is displayed, close the application and return to Visual Studio.

Figure 2: When you run the application, the informational message title is displayed.
Figure 2: When you run the application, the informational message title is displayed.

Access View Model in the Code Behind

WPF creates an instance of your MainWindowViewModel class because you created an element referencing it in the <Window.Resources> element. You're going to want to use that instance of the MainWindowViewModel from the code-behind of the MainWindow class. You can do that by opening the MainWindows.xaml.cs file and adding a new Using statement to reference the View Model namespace. Besides that namespace, also add one to System and to System.Windows.Threading. You're going to use classes from these namespaces as well.

using System;
using System.Windows.Threading;
using WPF.Sample.ViewModelLayer;

Add a private variable in the MainWindow class that is of the type MainWindowViewModel, as shown in the following code snippet.

// Main window's view model class
private MainWindowViewModel _viewModel = null;

Modify the MainWindow constructor to set this private variable to the instance of the View Model created by XAML. Access the Resources collection of the main window and locate the key with the name of viewModel. Cast the resource returned as a MainWindowViewModel class and assign it to the _viewModel variable.

public MainWindow()
{
    InitializeComponent();
    
    // Connect to instance of the view model created by the XAML
    _viewModel = (MainWindowViewModel)this.Resources["viewModel"];
}

Remove Messages After a Few Seconds

To remove the messages after a few seconds, you'd think you could just add a Thread.Sleep(2000) to the Window_Loaded event procedure, then set the IsInfoMessageVisible property to false to make the informational message area disappear as shown below.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    // NOTE: This does NOT work
    System.Threading.Thread.Sleep(2000);
    _viewModel.IsInfoMessageVisible = false;
}

However, because WPF has only one thread of execution, the UI hasn't completely rendered fully when the Loaded event runs. So, if you execute the above code, you'll see the main window, but not the informational message area.

To solve this problem, use the Dispatcher class. The Dispatcher class allows you to invoke a separate thread of execution in the background. This allows the UI to continue to draw, while other code is now executing on a separate thread. This separate thread is where you may now sleep for two seconds, then set the IsInfoMessageVisible property to false. Of course, in a real application, it's in this code that you load any resources you need or perform other tasks while the user is seeing the message on the screen. Open the MainWindow.xaml file and add a Loaded event.

Loaded="Window_Loaded"

Write the following code in the Window_Loaded event to display a message for two seconds. Because you're using the await keyword, you need to mark the Window_Loaded event procedure with the async keyword.

private async void Window_Loaded(object sender, RoutedEventArgs e)
{
    await Dispatcher.BeginInvoke(new Action(() => {
        System.Threading.Thread.Sleep(2000);
        _viewModel.IsInfoMessageVisible = false;
    }), DispatcherPriority.Background);
}

Try It Out

Run the application again and you should now see the message displayed, and then the message disappears after a couple of seconds.

Load Resources in the Background

As mentioned previously, there's only one thread of execution. Thus, if you were to start making a bunch of calls to load data from a database, the UI wouldn't be able to redraw. You want to be able to inform the user of what you're doing while loading data in the background. To do this, you're going to take advantage of the Dispatcher class again. However, let's first create some methods that simulate loading of data. Open the MainWindowViewModel.cs file and add a constant for how long you want to simulate a load process happening.

private const int SECONDS = 1500;

Add three methods to simulate loading some global resources. It's in here that you eventually write code to hit a database, or maybe call a Web API to retrieve data. To keep this article simple, simulate the retrieval process by just sleeping the number of milliseconds you put in the constant SECONDS.

public void LoadStateCodes() 
{
    // TODO: Write code to load state codes
    System.Threading.Thread.Sleep(SECONDS);
}

public void LoadCountryCodes() 
{
    // TODO: Write code to load country codes
    System.Threading.Thread.Sleep(SECONDS);
}

public void LoadEmployeeTypes() 
{
    // TODO: Write code to load employee types
    System.Threading.Thread.Sleep(SECONDS);
}

Add a method to clear the informational message properties after all the global resources have been loaded. This method also makes the informational message area invisible by setting the IsInfoMessageVisible property to false.

public void ClearInfoMessages() 
{
    InfoMessage = string.Empty;
    InfoMessageTitle = string.Empty;
    IsInfoMessageVisible = false;
}

Display Messages While Loading Resources

Let's now create a method to call each of the three methods you added to the View Model class. Open the MainWindow.xaml.cs file and add a new using statement.

using System.Threading.Tasks;

Add a method named LoadApplication() in the MainWindow class. You'll find it in Listing 2.

Listing 2: Create an asynchronous method to call your loading methods in your view model

public async Task LoadApplication()
{
    _viewModel.InfoMessage = "Loading State Codes...";
    await Dispatcher.BeginInvoke(new Action(() => {
        _viewModel.LoadStateCodes();
    }), DispatcherPriority.Background);
    
    _viewModel.InfoMessage = "Loading Country Codes...";
    await Dispatcher.BeginInvoke(new Action(() => {
        _viewModel.LoadCountryCodes();
    }), DispatcherPriority.Background);

    _viewModel.InfoMessage = "Loading Employee Types...";
    await Dispatcher.BeginInvoke(new Action(() => {
        _viewModel.LoadEmployeeTypes();
    }), DispatcherPriority.Background);
}

In Listing 2, set the InfoMessage property to "Loading State Codes...". Use the Dispatcher class to call the LoadStateCodes() method in the view model. As this takes place on a separate thread of execution, the message can update on the message area of the main window. Next, the InfoMessage property is set to "Loading Country Codes...", followed by another call through the Dispatcher class. You can perform this code over and over until all your global resources are loaded.

Call the LoadApplication() method from the Window_Loaded event. Delete the other code within the Window_Loaded event and replace it with the code shown in the following code snippet.

private async void Window_Loaded(object sender, RoutedEventArgs e)
{
    // Call method to load resources application
    await LoadApplication();
    
    // Turn off informational message area
    _viewModel.ClearInfoMessages();
}

Try It Out

Run the application and you should see each of your messages appear in the informational message area. Each message disappears after about 1.5 seconds.

Load User Controls Dynamically in WPF

As your application grows larger, you're going to have a lot of user controls and/or windows to display. Instead of having a large switch...case statement to load each of these individually, you can take advantage of Reflection to dynamically load each. Let's add three user controls that can be called from the menu system. The complete name of the control is going to be stored in the Tag property of each menu item.

As your application grows larger, you're going to have a lot of user controls and/or windows to display.

Add a Login Control

Right mouse-click on the WPF.Sample project and add a new folder named UserControls. Right mouse-click on the new UserControls folder and select Add > User Control... from the menu. Set the name of this new user control to LoginControl.xaml and press the Add button. Add a Style attribute to the <UserControl> element as shown below.

<UserControl x:Class="WPF.Sample.UserControls.LoginControl" 
             xmlns="..."
             ...
             Style="{StaticResource screenStyle}">
  <Grid>

  </Grid>
</UserControl>

Remove the <Grid> element in the user control and replace it with a Border control. Within the Border control, add a TextBlock control to display the name of this user control. Later in this article series, you're going to build a complete log in UI to ask a user to log in with. For now, let's just add a placeholder with some text to display.

<Border Style="{StaticResource screenBorder}">
  <TextBlock Text="Login Screen" FontSize="18" Margin="20,80" />
</Border>

Add a User Feedback Control

Right mouse-click on the UserControls folder and select Add > User Control... Set the name to UserFeedbackControl.xaml and press the Add button. Add a Style attribute to the <UserControl> element just like you did with the Login user control.

Style="{StaticResource screenStyle}"

Remove the <Grid> element in the user control and replace it with a Border control. Within the Border control, add a TextBlock control to display the name of this user control. Later in this article series, you're going to build a complete UI for this user feedback screen.

<Border Style="{StaticResource screenBorder}">
  <TextBlock Text="User Feedback Screen" FontSize="18" Margin="20,80" />
</Border>

Add User Maintenance Control

Right mouse-click on the UserControls folder and select Add > User Control... Set the name to UserMaintenanceControl.xaml and press the Add button. Add the Style attribute to the <UserControl> element just like you did with the previous two controls.

Style="{StaticResource screenStyle}"

Remove the <Grid> element in the user control and replace it with a Border control. Within the Border control, add a TextBlock control to display the name of this user control.

<Border Style="{StaticResource screenBorder}">
  <TextBlock Text="User Maintenance Screen" FontSize="18" Margin="20,80" />
</Border>

Because you created each of these user controls within the UserControls folder, the fully qualified type of each control is the following:

  • WPF.Sample.UserControls.LoginControl
  • WPF.Sample.UserControls.UserFeedbackControl
  • WPF.Sample.UserControls.UserMaintenanceControl

To instantiate each of these controls using Reflection, you're going to use each of the above names.

Dynamically Create User Controls

Each control in WPF has a Tag property. This property is of the data type Object and isn't used by WPF. This means that you can use it for whatever you want. You're going to use it for either a command to execute or for the name of a user control to create dynamically.

First, add a Tag property to the Exit menu and set it to simply “exit”. This is a specific command to execute. Open the MainWindow.xaml and add the Tag property as shown below.

<MenuItem Header="E_xit" Tag="exit" Click="MenuItem_Click" />

For each user control you're going to create, locate the appropriate menu item, and add the Tag property. Place the complete namespace and control name within the Tag property.

<MenuItem Header="Users" Click="MenuItem_Click" Tag="WPF.Sample.UserControls.UserMaintenanceControl" />
<MenuItem Header="Feedback" Click="MenuItem_Click" Tag="WPF.Sample.UserControls.UserFeedbackControl" />
<MenuItem Header="Login" HorizontalAlignment="Right" Click="MenuItem_Click" Tag="WPF.Sample.UserControls.LoginControl" />

Display User Controls on the Main Window

Open the MainWindow.xaml file and notice that just below the Border control you added for the information messages, there's a Grid control with the name set to contentArea.

<!-- Content Area -->
<Grid Grid.Row="1" Panel.ZIndex="1" Name="contentArea" Style="{StaticResource contentAreaStyle}" />

It's into this area you're going to display all of your screens. Instead of having multiple windows open and cluttering your desktop, you're going to keep all screens displayed within the main window. Each screen you create is going to be a user control. Thus, each WPF application you create will generally have many user controls and only a few windows.

Display a User Control

Open the MainWindow.xaml.cs file and add a new method named DisplayUserControl(). This method takes an instance of a user control and adds it to the Grid control named contentArea on the main window.

public void DisplayUserControl(UserControl uc)
{
    // Add new user control to content area
    contentArea.Children.Add(uc);
}

Close a User Control

If you have a method to display a user control, you're going to need a method to close a user control. Add a new method named CloseUserControl(). This method clears the Children collection in the Grid control named contentArea on the main window.

private void CloseUserControl()
{
    // Remove current user control
    contentArea.Children.Clear();
}

Load a User Control Using Reflection

Now that you know how to display and remove a user control from the main window, write code to take the value from the Tag property and create an instance of that user control. Add a new method named LoadUserControl(), as shown in Listing 3. This method uses reflection to load the user control name specified in the Tag property.

Listing 3: The LoadUserControl() method loads a user control using reflection

private void LoadUserControl(string controlName)
{
    Type ucType = null;
    UserControl uc = null;
    
    // Create a Type from controlName parameter
    ucType = Type.GetType(controlName);
    if (ucType == null) 
    {
        MessageBox.Show("The Control: " + controlName + " does not exist.");
    }
    else 
    {
        // Close current user control in content area
        CloseUserControl();
        
        // Create an instance of this control
        uc = (UserControl)Activator.CreateInstance(ucType);
        if (uc != null) 
        {
            // Display control in content area
            DisplayUserControl(uc);
        }
    }
}

The code in Listing 3 attempts to get a .NET type by calling Type.GetType() on the string that contains the namespace and user control name. If that's successful, any current control within the content area is closed. Next, the Activator.CreateInstance() method is called with the type returned from the Type.GetType() method to create an instance of this user control. If the user control is created successfully, the DisplayUserControl() method is called, passing in the newly created user control.

Execute a Command or Load a User Control

If you remember, you added a Tag property to the Exit menu item with a value of exit. This isn't the name of a user control. You are going to need another method to process anything that isn't a user control name. Go to the top of the MainWindow class and add a new using statement:

using System.Windows.Controls;

Next, add a new method named ProcessMenuCommands() to the MainWindow class. This method is going to handle any commands within the Tag property. Create a switch statement within this method to process the exit command. As your application grows, you'll have other commands to process.

private void ProcessMenuCommands(string command)
{
    switch (command.ToLower()) 
    {
        case "exit":
        this.Close();
        break;
    }
}

Locate the MenuItem_Click() event and modify it to look like Listing 4. In this event procedure, check to see if the Tag contains a period. If it does, assume that you have a user control to load. Each fully qualified user control needs to be in the format of namespace.controlname; thus if you have a period in the Tag property, assume that you need to load a user control. Call the LoadUserControl() method passing in the value from the Tag property to display that user control. If there isn't a period, assume that it's a command like exit and call the ProcessMenuCommands() method passing in the value from the Tag property.

Listing 4: The MenuItem_Click event determines whether to process a command or load a user control

private void MenuItem_Click(object sender, RoutedEventArgs e)
{
    MenuItem mnu = (MenuItem)sender;
    string cmd = string.Empty;
    
    // The Tag property contains a command or the name of a user control to load
    if (mnu.Tag != null) 
    {
        cmd = mnu.Tag.ToString();
        if (cmd.Contains(".")) 
        {
            // Display a user control
            LoadUserControl(cmd);
        }
        else 
        {
            // Process special commands
            ProcessMenuCommands(cmd);
        }
    }
}

Try It Out

Now that you have a command and some user controls to process from the menu items on the main window, let's try it out. Open the MainWindowViewModel.cs file and modify the constant named SECONDS to a value of 500. This eliminates a little wait time when you're starting the application. Run the WPF application and click on each of the menu items to see each user control displayed. Click on the Exit menu item and the application should close.

Don't Load a User Control Twice

If the user clicks on the Feedback menu and then clicks on it again, you don't want to load the same control twice. Write a method, named ShouldLoadUserControl(), to check whether the current user control loaded into the content area is the same one the user is trying to load.

private bool ShouldLoadUserControl(string controlName)
{
    bool ret = true;
    
    // Don't reload a control already loaded.
    if (contentArea.Children.Count > 0) 
    {
        if (((UserControl)contentArea.Children[0]).GetType().FullName == controlName) 
        {
            ret = false;
        }
    }
    
    return ret;
}

Locate the LoadUserControl() method and wrap the code within an if statement. The if statement calls the ShouldLoadUserControl() method, passing in the control name. If it's not the same control, or the contentArea is empty, execute the code to load the new user control to display or the command to execute.

private void LoadUserControl(string controlName)
{
    Type ucType = null;
    UserControl uc = null;
    
    if (ShouldLoadUserControl(controlName)) 
    {
        // ALL OTHER CODE GOES HERE
    }
}

Close Screens from View Models

So far, you've learned how to display a user control on the main window. You also wrote code to close a user control, but from where do you close that control? The best place is from the view model. However, the view model doesn't know anything about the UI, so how do you communicate from the view model to the main window? The answer is a message broker. You can read more about a message broker in this blog post: https://weblogs.asp.net/psheriff/a-communication-system-for-xaml-applications.

Create Login View Model

Just like the main window creates an instance of a view model in XAML, you should do the same thing for all the user controls you add to your project. Right mouse-click on the WPF.Sample.ViewModelLayer project and select Add > Class... from the menu. Set the name of the class to LoginViewModel. Replace the code in this file with the following code snippet.

using Common.Library;

namespace WPF.Sample.ViewModelLayer
{
    public class LoginViewModel : ViewModelBase
    {
    }
}

Bind View Model to Login Control

After you add the view model, build the solution so the new View Model class is available for user. Open the LoginControl.xaml file and add an XML namespace to reference the view model project.

xmlns:vm="clr-namespace:WPF.Sample.ViewModelLayer;assembly=WPF.Sample.ViewModelLayer"

Add a <UserControl.Resources> element and create an instance of the newly created LoginViewModel class.

<UserControl.Resources>
  <vm:LoginViewModel x:Key="viewModel" />
</UserControl.Resources>

Locate the <Border> element you created earlier and move the TextBlock control inside a StackPanel control. Below the TextBlock control add a Button control and create the Click event procedure named CloseButton_Click as shown in the following code snippet.

<Border Style="{StaticResource screenBorder}">
  <StackPanel>
    <TextBlock Text="Login Screen" FontSize="18" Margin="20,80" />
    <Button Content="Close" HorizontalAlignment="Left" Click="CloseButton_Click" />
  </StackPanel>
</Border>

Modify Code-Behind

Open the LoginControl.xaml.cs file and add a using statement so you can use the LoginViewModel class.

using WPF.Sample.ViewModelLayer;

Create a private variable to hold an instance of the LoginViewModel class.

// Login view model class
private LoginViewModel _viewModel = null;

Modify the constructor to assign the instance of the UserFeedbackViewModel created by XAML to the local private variable named _viewModel.

public LoginControl()
{
    InitializeComponent();
    
    // Connect to instance of the view model
    _viewModel = (LoginViewModel)
    this.Resources["viewModel"];
}

Modify the CloseButton_Click() event to call the Close() method on the View Model class.

private void CloseButton_Click(object sender, RoutedEventArgs e)
{
    _viewModel.Close();
}

In the ViewModelBase.cs class, located in the Common.Library project, there's a method named Close(). This method uses the MessageBroker class to send a “Close User Control” message as shown below.

public virtual void Close(bool wasCancelled = true)
{
    MessageBroker.Instance.SendMessage(MessageBrokerMessages.CLOSE_USER_CONTROL, wasCancelled);
}

Respond to Close Message in Main Window

Most of the messages sent from user controls and view models are going to communicate with the MainWindow class. So write code to respond to the MessageReceived event in the MainWindow. Open the MainWindow.xaml.cs file and add a using statement.

using Common.Library;

Add the code shown below to the constructor. This code connects to the MessageReceived event and hooks it to an event procedure you create in this class.

public MainWindow()
{
    InitializeComponent();
    
    // Connect to instance of the view model
    _viewModel = (MainWindowViewModel)
    this.Resources["viewModel"];
    
    // Initialize the Message Broker Events
    MessageBroker.Instance.MessageReceived += Instance_MessageReceived;
}

Add the Instance_MessageReceived event and add the following code to respond to the “Close User Control” message.

private void Instance_MessageReceived(object sender, MessageBrokerEventArgs e)
{
    switch (e.MessageName) 
    {
        case MessageBrokerMessages.CLOSE_USER_CONTROL:
            CloseUserControl();
            break;
    }
}

When the CLOSE_USER_CONTROL message is received from the login view model class, the CloseUserControl() method you wrote earlier is called. This method clears the Children collection of the contentArea where the user controls are loaded.

Try It Out

Run the application and click on the Login menu. Click the Close button on this log-in screen and you should see the user control disappear. If you want, set a break point in the CloseButton_Click() and in the Instance_MessageReceived event procedure to see how the message is transmitted from the View Model base class to the main window.

Create Other User Controls and View Models

You now have a design pattern for instantiating a view model by a user control and closing the user control. Use this same design pattern for the user feedback control you created. Right mouse-click on the WPF.Sample.ViewModelLayer project and select Add > Class... from the menu. Set the name of the class to UserFeedbackViewModel. Replace the code in this new file with the following:

using Common.Library;

namespace WPF.Sample.ViewModelLayer
{
    public class UserFeedbackViewModel : ViewModelBase
    {
    }
}

Bind View Model to User Feedback Control

Build the solution and have the user feedback user control create an instance of the UserFeedbackViewModel class. Open the UserFeedbackControl.xaml file and add an XML namespace to reference the view model project.

xmlns:vm="clr-namespace:WPF.Sample.ViewModelLayer;assembly=WPF.Sample.ViewModelLayer"

Add a <UserControl.Resources> element and create an instance of the UserFeedbackViewModel class.

<UserControl.Resources>
  <vm:UserFeedbackViewModel x:Key="viewModel" />
</UserControl.Resources>

Locate the <Border> element you created earlier and move the TextBlock control inside a StackPanel control. Below the TextBlock control, add a Button control, and create the Click event procedure named CloseButton_Click, as shown in the following code snippet.

<StackPanel>
  <TextBlock Text="User Feedback Screen" FontFamily="18" Margin="20,80" />
  <Button Content="Close" HorizontalAlignment="Left" Click="CloseButton_Click" />
</StackPanel>

Modify Code-Behind

Open the UserFeedbackControl.xaml.cs file and add a using statement so you can use the UserFeedbackViewModel class.

using WPF.Sample.ViewModelLayer;

Create a private variable to hold an instance of the UserFeedbackViewModel class.

private UserFeedbackViewModel _viewModel = null;

Modify the constructor to assign the instance of the UserFeedbackViewModel created by XAML to the local private variable named _viewModel.

public ucUserFeedback()
{
    InitializeComponent();
    
    // Connect to instance of the view model
    _viewModel = (UserFeedbackViewModel)
    this.Resources["viewModel"];
}

Modify the CloseButton_Click() event to call the Close() method on the view model class.

private void CloseButton_Click(object sender, RoutedEventArgs e)
{
    _viewModel.Close(true);
}

Try It Out

Run the application and click on the Feedback menu. Click the Close button and watch the control disappear.

Add User Maintenance Screen View Model

Right mouse-click on the WPF.Sample.ViewModelLayer project and select Add > Class... from the menu. Set the name of the class to UserMaintenanceViewModel. Replace the code in this new file with the following:

using Common.Library;

namespace WPF.Sample.ViewModelLayer
{
    public class UserMaintenanceViewModel : ViewModelBase
    {
    }
}

Bind View Model to User Maintenance Control

Build the solution and have the User Feedback user control create an instance of the UserMaintenanceViewModel class. Open the UserMaintenanceControl.xaml file and add an XML namespace to reference the view model project.

xmlns:vm="clr-namespace:WPF.Sample.ViewModelLayer;assembly=WPF.Sample.ViewModelLayer"

Add a <UserControl.Resources> element and create an instance of the UserMaintenanceViewModel class.

<UserControl.Resources>
  <vm:UserMaintenanceViewModel x:Key="viewModel" />
</UserControl.Resources>

Locate the <Border> element you created earlier and move the TextBlock control inside a StackPanel control. Below the TextBlock control add a Button control and create the Click event procedure named CloseButton_Click as shown in the following code snippet.

<StackPanel>
  <TextBlock Text="User Maintenance Screen" FontFamily="18" Margin="20,80" />
  <Button Content="Close" HorizontalAlignment="Left" Click="CloseButton_Click" />
</StackPanel>

Modify Code-Behind

Open the UserMaintenanceControl.xaml.cs file and add a using statement so you can use the UserMaintenanceViewModel class.

using WPF.Sample.ViewModelLayer;

Create a private variable to hold an instance of the UserMaintenanceViewModel class.

private UserMaintenanceViewModel _viewModel = null;

Modify the constructor to assign the instance of the UserMaintenanceViewModel created by XAML to the local private variable named _viewModel.

public ucUserFeedback()
{
    InitializeComponent();
    
    // Connect to instance of the view model
    _viewModel = (UserMaintenanceViewModel)
    this.Resources["viewModel"];
}

Modify the CloseButton_Click() event to call the Close() method on the View Model class.

private void CloseButton_Click(object sender, RoutedEventArgs e)
{
    _viewModel.Close(true);
}

Try It Out

Run the application and click on the Users menu. Click the Close button and watch the control disappear.

Summary

In this article, you added an area to your main window to display messages when an application first loads. Using the Dispatcher class, you can display messages while running other code to perform actions in the background. You learned to load user controls dynamically by using the Tag property of menu item controls. A message broker class is useful to send messages from user controls or view models to the main window in order to perform UI actions. This reduces coupling between classes and increases the reusability of your controls and classes. In the next article, you'll learn to display informational messages for a small amount of time and build a log in screen.