In part 1 of this article, you learned how to create a Windows 8 look and feel for your WPF applications. You were shown a high-level overview of the various components that made up the shell for navigating. In part 2 of this article you will learn to create a WPF Button user control, a Message Box you can style, and a simple Message Broker System. All of these components are used to create the “Windows 8 Style” WPF shell you learned about in part 1.

Create Your Own WPF Button User Control

In Figure 1 you see examples of standard WPF Button controls. You can add a drop shadow and you can change the color, but you can’t change much else without creating a whole new control template. For example, you are unable to modify the BorderBrush or the BorderThickness properties of the Button control. You might also want to use some animation other than the default, which again requires you to change the control template.

Figure 1: The normal WPF Buttons cannot be styled very well.

Sometimes all you want is just some simple buttons where you can modify the border brush and the thickness and have different color options via styles. I have found that instead of working with the whole control template thing, just creating a user control is sometimes much easier.

There are many ways to create custom buttons and there are advantages and disadvantages to each way. One method that is easily understood by almost any XAML programmer is to create a user control. User controls have been around since the Visual Basic 4.0 days. Most developers understand the value of using user controls. With XAML user controls you can put these controls into a WPF User Control Library or a Silverlight Class Library and reference those DLLs from any WPF or Silverlight application. Using a DLL gives you great reusability. Using resource dictionaries and styles can make your user controls very customizable as shown in Figure 1. The second row of buttons you see are the same button user control with just different styles applied.

The Simple Button User Control

The XAML for your Button user control is actually very simple. You use a single Border control and a TextBlock control within that Border as shown in the code snippet below.

< Border Name ="hoverBorder"
     Style="{DynamicResource hoverBorder}"
     MouseDown="hoverBorder_MouseDown"
     MouseUp="hoverBorder_MouseUp"
     MouseEnter="hoverBorder_MouseEnter"
     MouseLeave="hoverBorder_MouseLeave"
     MouseLeftButtonUp="hoverBorder_MouseLeftButtonUp">
  <TextBlock
     Text="{Binding Path=Text}"
     Name="tbText"
     Style="{DynamicResource hoverTextBlock}" />
</ Border >

The definition for this user control is in a DLL named PDSA.WPF. Notice that there are styles applied to both the Border and the TextBlock controls. These style definitions are in a default resource dictionary located in the same DLL as this user control. This resource dictionary or one you create can be used to theme your Button user control. You can see the three different themes we created in Figure 1. The Gray Style button uses a resource dictionary that is contained in the PDSA.WPF DLL. The other two styles are in the main project and can be referenced from your App.xaml or from within the window/user control where you need the button.

Changing Colors in Response to Mouse Events

When a user moves over a button or presses a button, you should give some visual feedback to that user. You can do this with animation using the Visual State Manager or Event Triggers in WPF. For this Button user control you might want to just respond to the various mouse events and change the Background property to a different color. When the mouse is pressed, you might also want to change the Foreground color of the Text in the TextBlock control. Listing 1 shows the code for each of the mouse events.

Use the FindResource method instead of accessing the this.Resources[] collection on the user control. Using FindResource allows you to set the resource dictionary at any level in your application, not just on this user control. The FindResource method searches upward through the UI tree looking for a resource that match the names you see that start with “pdsa”.

You will need a Click event that you can raise when the user clicks on the button. It is in this Click event that you can write code to respond to the user clicking on this button. Listing 2 shows the definition of the Click delegate and the RaiseClick method to raise the Click event.

The Default Resource Dictionary

Listing 3 shows the definition of the resource dictionary file contained in the PDSA.WPF DLL. You can use this dictionary as the default look and feel for any button control you add to a window. I have included two additional resource dictionaries in the main project as examples of how you can change the resources to give your buttons a different look. You need to keep the x:Key names the same, but you can change the color and thickness attributes. You can even change from Gradient colors to a SolidColorBrush as you can see I did when you look at the different resource dictionaries.

Using the Button Control

Once you make a reference to the PDSA.WPF DLL from your WPF application you will see the “PDSAucButton” control appear in your Toolbox. Drag and drop the button onto a window or user control in your application. I have not referenced the PDSAButtonStyles.xaml file within the control itself so you do need to add a reference to this resource dictionary somewhere in your application such as in the App.xaml as shown in the code snippet below.

< Application.Resources >
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary
      Source="/PDSA.WPF;component/
              PDSAButtonStyles.xaml" />
    </ResourceDictionary.MergedDictionaries>
  </ResourceDictionary>
</ Application.Resources >

This will give your buttons a default look and feel unless you override that dictionary on a specific window/user control or on an individual button. The “Gray Style” button shown in Figure 1 is what the default button looks like after setting the above code in your App.xaml and then dragging a button onto a window. If you wish to give a specific style to just a single button you can override the default by using the code below:

< my : PDSAucButton HorizontalAlignment ="Left"
                 Margin="6"
                 Text="Flat Blue Style"
                 x:Name="btn2"
                 Click="btn2_Click"
                 VerticalAlignment="Top">
  <my:PDSAucButton.Resources>
    <ResourceDictionary
       Source="FlatBlueButtonStyles.xaml" />
  </my:PDSAucButton.Resources>
</ my : PDSAucButton >

If you want to override a series of buttons within one specific StackPanel, or within a specific window or user control, you simply set the Resources section for that control as shown in Listing 4.

A WPF Message Box You Can Style

You can go to great pains to add styles, colors, gradients and a really cool look and feel to your WPF application only to have that ruined by the standard Windows message box as shown in Figure 2.

Figure 2: The normal Windows message box just does not look right on a styled WPF application.

It would be nice if Microsoft offered us a Message Box control that we can style, but they don’t. It is up to you to create a window that you can style and modify the look and feel. In Figure 3 and Figure 4 you can see examples of a message box that you can change to match your specific theme of your application.

Figure 3: Creating your own message box dialog from a window, a Text Block and a custom button control.
Figure 4: You can completely change the theme of your button through the use of resource dictionaries.

The PDSAMessageBoxView XAML

The first step in creating your own custom message box is to add a new window to your WPF project. You then need to set some styles to get a borderless window. You set the following attributes on your window:

  • WindowStyle="None"
  • ShowInTaskbar="True"
  • ResizeMode="NoResize"
  • AllowsTransparency="True"
  • Background="Transparent"

The WindowStyle attribute allows you to set a single border, 3-D border, or a Tool Window border. Setting this attribute to None eliminates the border altogether. The ShowInTaskbar attribute is optional, but if you are doing a dialog window you probably want this window to show up in the taskbar. Since this is a dialog window, you probably do not want to allow the user to resize this window, thus you set the ResizeMode to “NoResize.” The next two attributes, AllowsTransparency and Background, work together. You must set AllowsTransparency to True to allow the Background to be set to Transparent. If you do not set these two attributes, then the chrome on your message box will still show up.

Listing 5 shows the complete XAML for the PDSAMessageBoxView.xaml file. This XAML contains a Border, a Grid, a TextBlock for the message, and a StackPanel control with four PDSAucButton controls. All of the attributes for the border, the text block and the buttons are controlled via styles in a resource dictionary file. Using a resource dictionary allows you to create new resource dictionaries with different colors and other attributes to style the message box in any manner you see fit.

Styles for PDSAMessageBox Class

As mentioned previously, all of the styles for the PDSAMessageBoxView class are contained within a resource dictionary. There is a resource dictionary in the PDSA.WPF DLL where the PDSAMessageBoxView class is located. However, there is an additional resource dictionary in the main project with a blue theme as shown in Figure 4. You can look in the sample application that comes with this article to see the different resource dictionaries used to create both themes shown in Figure 3 and Figure 4. Feel free to modify any of the styles in the resource dictionaries to make the message box look like however you want.

The PDSAMessageBox Class

Just like .NET has the MessageBox class in order to display the dialog, I have created a PDSAMessageBox class with a Show method as well. I made the Show method with the same parameters as the MessageBox class in order to help you make an easier transition to the PDSAMessageBox class. I do not use any of the MessageBoxImage parameters, so you will need to remove any of these, or add an image to your message box window if you so desire. Listing 6 shows the PDSAMessageBox class with the static Show methods defined.

All of the Show methods end up calling just a single Show that takes care of displaying the PDSAMessageBoxView dialog. One thing I did change in my Show method is that if the user pops up a message box with just an “Ok” button, I do allow the user to navigate away from this dialog without pressing the Ok button. To me, it does not make sense to force the user to press a single button on the form. This could have side effects however, if you have code immediately following the call to the Show method because this is no longer a modal dialog. Again, feel free to modify this code if you do not like this functionality.

Calling the PDSAMessageBox

The code snippet below shows an example of calling the PDSAMessageBox. The Show method will return a MessageBoxResult enumeration. There was no need to change the return value from that of the normal MessageBox class. You can find the code in the sample that you download for this article (see the end of this article for download instructions).

private void btnOKOnly_Click(object sender,
 RoutedEventArgs e)
{
  PDSAMessageBox.Show(
     "This just displays an OK Button",
     "OK", MessageBoxButton.OK);
}
    
private void btnYesNo_Click(object sender,
 RoutedEventArgs e)
{
  MessageBoxResult result;
    
  result = PDSAMessageBox.Show("Do you want to Quit?",
      "Quit?", MessageBoxButton.YesNo);
    
  MessageBox.Show("Result is " + result.ToString());
}

A Communication System for XAML Applications

A goal to strive for in any application is to keep the coupling between your objects as loose as possible. Coupling happens when one class contains a property that is used in another class, or uses another class in one of its methods. If you have this situation, then this is called strong or tight coupling. If you couple classes together then you make it harder to reuse one class in another project without including the coupled class that you might not need. You can also inadvertently introduce memory leaks if one object does not release the other coupled object. One popular design pattern to help with keeping objects loosely coupled is called the Mediator design pattern. The basics of this pattern are very simple; avoid one object directly talking to another object, and instead use another class to mediate between the two. Creating a robust message broker is beyond the scope of this article, but this section will show you the basics of how most message brokers work.

IPDSAMessageBroker Interface

As with most implementations of a design pattern, you typically start with an interface or an abstract base class. In this particular instance, an Interface will work just fine. The interface for our message broker class contains a single method “SendMessage” and one event “MessageReceived” as shown in the following code.

public delegate void MessageReceivedEventHandler(
 object sender, PDSAMessageBrokerEventArgs e);
    
public interface IPDSAMessageBroker
{
  void SendMessage(PDSAMessageBrokerMessage msg);
    
  event MessageReceivedEventHandler MessageReceived;
}

PDSAMessageBrokerMessage Class

The SendMessage method requires a type of PDSAMessageBrokerMessage to be passed to it. This class has a MessageName that is a ‘string’ type and a MessageBody property that is of the type ‘object’ so you can pass whatever you want in the body. You might pass a string in the body, or a complete object such as a Customer or Employee. The MessageName property will help the receiver of the message know what is in the MessageBody property. You will see examples of the usage of this class later.

public class PDSAMessageBrokerMessage
{
  public PDSAMessageBrokerMessage() {
  }
    
  public PDSAMessageBrokerMessage(string name,
   object body) {
    MessageName = name;
    MessageBody = body;
  }
    
  public string MessageName { get; set; }
    
  public object MessageBody { get; set; }
}

PDSAMessageBrokerEventArgs Class

Whenever you raise an event you should create your own event argument class. The PDSAMessageBrokerEventArgs class inherits from the System.EventArgs class and adds a couple of additional properties: MessageName and Message. The MessageName property is simply a string value. The Message property is a type of a PDSAMessageBrokerMessage class.

public class PDSAMessageBrokerEventArgs : EventArgs
{
  public PDSAMessageBrokerEventArgs() {
  }
    
  public PDSAMessageBrokerEventArgs(string name,
    PDSAMessageBrokerMessage msg) {
    MessageName = name;
    Message = msg;
  }
    
  public string MessageName { get; set; }
    
  public PDSAMessageBrokerMessage Message { get; set; }
}

PDSAMessageBroker Class

Now that you have an interface class and a class to pass a message through an event, it is time to create the PDSAMessageBroker class (Listing 7). This class implements the SendMessage method and will also create the event handler for the delegate in your Interface.

The SendMessage method will take a PDSAMessageBrokerMessage object as an argument. It creates an instance of a PDSAMessageBrokerEventArgs class, passing to the constructor two items: the MessageName from the PDSAMessageBrokerMessage object and the object itself. It may seem a little redundant to pass in the message name when that same message name is part of the message, but it does make consuming the event and checking for the message name a little cleaner - as you will see in the next section.

Create a Global Message Broker

In your WPF application, create an instance of this message broker class in the App class located in the App.xaml file. Create a public property in the App class and create a new instance of that class in the OnStartUp event procedure as shown in the following code:

public partial class App : Application
{
  public PDSAMessageBroker MessageBroker { get; set; }
    
  protected override void OnStartup(StartupEventArgs e)
  {
    base.OnStartup(e);
    
    MessageBroker = new PDSAMessageBroker();
  }
}

Sending and Receiving Messages

Let’s assume you have a user control that you load into a control on your main window and you want to send a message from that user control to the main window. For example, you might have the main window display a message box, or put a string into a status bar as shown in Figure 5.

Figure 5: The main window can receive and send messages.

The first thing you do in the main window is to hook up an event procedure to the MessageReceived event of the global message broker. You do this in the constructor of the main window:

public MainWindow()
{
  InitializeComponent();
    
  (Application.Current as App).MessageBroker.
     MessageReceived +=
      new MessageReceivedEventHandler(
       MessageBroker_MessageReceived);
}

One piece of code you might not be familiar with is accessing a property defined in the App class of your XAML application. Within the App.Xaml file is a class named App that inherits from the Application object. You access the global instance of this App class by using Application.Current. You cast Application.Current to App prior to accessing any of the public properties or methods you defined in the App class. Thus, the code (Application.Current as App).MessageBroker, allows you to get at the MessageBroker property defined in the App class.

In the MessageReceived event procedure in the main window (shown below) you check to see if the MessageName property of the PDSAMessageBrokerEventArgs is equal to “StatusBar” and if it is, then display the message body in the status bar text block control on the window.

void MessageBroker_MessageReceived(object sender,
   PDSAMessageBrokerEventArgs e)
{
  switch (e.MessageName)
  {
    case "StatusBar":
      tbStatus.Text = e.Message.MessageBody.ToString();
      break;
  }
}

In the Page 1 user control’s Loaded event procedure you will send the message “StatusBar” through the global message broker to any listener using the following code:

private void UserControl_Loaded(object sender,
 RoutedEventArgs e)
{
  // Send Status Message
  (Application.Current as App).MessageBroker.
    SendMessage(
     new PDSAMessageBrokerMessage("StatusBar",
      "This is Page 1"));
}

Since the main window is listening for the message “StatusBar,” it will display the value, “This is Page 1” in the status bar at the bottom of the main window.

Sending a Message to a User Control

The previous example sent a message from the user control to the main window. You can also send messages from the main window to any listener as well. Remember that the global message broker is really just a broadcaster to anyone who has hooked into the MessageReceived event.

In the constructor of the user control named ucPage1 you can hook into the global message broker’s MessageReceived event as shown in the following code snippet:

public ucPage1()
{
  InitializeComponent();
    
  // Hook to the Global Message Broker
  (Application.Current as App).MessageBroker.
    MessageReceived += new MessageReceivedEventHandler(
      MessageBroker_MessageReceived);
}

You can then listen for any messages that are sent to this control by using a similar switch-case structure like that in the main window.

void MessageBroker_MessageReceived(object sender,
 PDSAMessageBrokerEventArgs e)
{
  // Look for messages intended for Page 1
  switch (e.MessageName)
  {
    case "ForPage1":
      MessageBox.Show(
        e.Message.MessageBody.ToString());
      break;
  }
}

Once the ucPage1 user control has been loaded into the main window you can then send a message from the main window using the following code:

private void btnSendToPage1_Click(object sender,
 RoutedEventArgs e)
{
  PDSAMessageBrokerMessage arg =
    new PDSAMessageBrokerMessage();
    
  arg.MessageName = "ForPage1";
  arg.MessageBody = "Message For Page 1";
    
  // Send a message to Page 1
  (Application.Current as App).MessageBroker.SendMessage(arg);
}

Since the MessageName matches what is in the ucPage1 MessageReceived event procedure, ucPage1 can do anything in response to that event. It is important to note that when the message gets sent it is sent to all MessageReceived event procedures, not just the one that is looking for a message called “ForPage1.” If the user control ucPage1 is not loaded and this message is broadcast, but no other code is listening for it, then it is simply ignored. For an alternative to using a broadcast approach for a message broker, please read the sidebar, Register Objects with Message Broker.

Remove Event Handler

In each class where you add an event handler to the MessageReceived event, you need to make sure to remove those event handlers when you are done. Failure to do so can cause a strong reference to the class and thus not allow that object to be garbage collected. In each of your user controls, make sure in the Unloaded event to remove the event handler.

private void UserControl_Unloaded(object sender,
 RoutedEventArgs e)
{
  if (_MessageBroker != null)
    _MessageBroker.MessageReceived -=
        _MessageBroker_MessageReceived;
}

Problems with Message Brokering

As with most “global” classes or classes that hook up events to other classes, garbage collection is something you need to consider. Just the simple act of hooking up an event procedure to a global event handler creates a reference between your user control and the message broker in the App class. This means that even when your user control is removed from your UI, the class will still be in memory because of the reference to the message broker. This can cause messages to still be handled even though the UI is not being displayed. It is up to you to make sure you remove those event handlers as discussed in the previous section. If you do not, then the garbage collector cannot release those objects.

Summary

In part 2 of this article, you learned how some of the various pieces of our overall Windows 8 look and feel were created. Creating a button out of a user control gives you a lot of flexibility on functionality and theming for your application. Using your own window to create a message box dialog allows you to avoid the ugly built-in Windows dialog. Use a message broker system to avoid strong coupling between the objects within your application. The message broker system presented in this article is very simplistic, but it does show the benefits of using such a system. In part 3 of this article you will see more of the user controls used to build the WPF Windows 8 shell.