Follow @PDSAInc October 2012 - Posts - Paul Sheriff's Blog for the Real World

Paul Sheriff's Blog for the Real World

This blog is to share my tips and tricks garnered over 25+ years in the IT industry

Paul's Favorites

October 2012 - Posts

Creating a XAML Tile Control

One of the navigation mechanisms used in Windows 8 and Windows Phone is a Tile. A tile is a large rectangle that can have words and pictures that a user can click on. You can build your own version of a Tile in your WPF or Silverlight applications using a User Control. With just a little bit of XAML and a little bit of code-behind you can create a navigation system like that shown in Figure 1.

Figure 1: Use a Tile for Navigation

Figure 1: Use a Tile for navigation. You can build a Tile User Control with just a little bit of XAML and code.

The WPF application shown in Figure 1 uses a WrapPanel to display a series of Tile objects. There are two styles defined in this Window to give us a large tile and a small tile. These styles and the usage of the Tile will be shown later, but first let’s look at how you can create this tile user control.

The User Control

In a WPF or Silverlight application you can create user controls which are a composite of other controls grouped together as a single unit. This user control can then be dragged and dropped onto a Window or User Control from the Visual Studio Toolbox. To create a “Tile” you need a Border, Grid, Image and a TextBlock control. Of course you will need to style these to get the appearance you saw in Figure 1. You will also need to use a Visual State Manager to highlight the tile the user is currently hovering over. The complete XAML for the tile is shown below:

<Border x:Name="borMain"
        Style="{StaticResource pdsaTileBorderStyle}"
        MouseEnter="OnMouseEnter"
        MouseLeave="OnMouseLeave"
        MouseLeftButtonDown="OnMouseLeftButtonDown">
  <VisualStateManager.VisualStateGroups>
    <VisualStateGroup Name="MouseStates">
      <VisualState Name="MouseEnter">
        <Storyboard>
          <ColorAnimation
            To="{StaticResource
                    pdsaTileBorderHighlightColor}"
            Duration="00:00:00"
            Storyboard.TargetName="borMain"
            Storyboard.TargetProperty="BorderBrush.Color" />
        </Storyboard>
      </VisualState>
      <VisualState Name="MouseLeave" />
    </VisualStateGroup>
  </VisualStateManager.VisualStateGroups>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="*" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Image Grid.Row="0"
            Name="imgMain"
            Style="{StaticResource pdsaTileImageStyle}"
            Source="{Binding TileImageUri}" />
    <TextBlock Grid.Row="1"
                Name="tbText"
                Style="{StaticResource
                           pdsaTileTextBlockStyle}"
                Text="{Binding TileText}" />
  </Grid>
</Border>

The Border, the Image and TextBlock all have a style applied to them. A set of default styles are contained in a resource dictionary that comes with the user control. The user control and the resource dictionary are located in a DLL named PDSA.WPF. You can override the default resource dictionary with one of your own to create a different look and feel for your tiles. You only need to keep the names of the styles the same.

The Visual State Manager has just a single ColorAnimation when the mouse enters the Border. This ColorAnimation will change the border brush color to the value specified in the style named pdsaTileBorderHighlightColor. The Border will respond to the MouseEnter and MouseLeave events and call the Visual State Manager to move to the states defined in the XAML as shown in the code below:

private void OnMouseEnter(object sender, MouseEventArgs e)
{
  VisualStateManager.GoToState(this, "MouseEnter", true);
}

private void OnMouseLeave(object sender, MouseEventArgs e)
{
  VisualStateManager.GoToState(this, "MouseLeave", true);
}

Creating the Click Event

In addition to the MouseEnter and MouseLeave events, the user control must also raise a Click event. The MouseLeftButtonDown event is defined on the Border control. When this event procedure is fired an instance of a class called PDSATileEventArgs is created and a Click event is raised. Here is the code for the MouseLeftButtonDown event.

private void OnMouseLeftButtonDown(object sender,
 MouseButtonEventArgs e)
{
  PDSATileEventArgs args = new PDSATileEventArgs();

  args.Text = this.Text;
  args.ViewName = this.ViewName;
  if(ImageUri != null)
    args.ImageUri = this.ImageUri.ToString();
  if(ToolTip != null)
    args.ToolTip = this.ToolTip.ToString();

  RaiseClick(args);
}

public delegate void TileClickEventHandler(object sender,
  PDSATileEventArgs e);

public event TileClickEventHandler Click;

protected void RaiseClick(PDSATileEventArgs e)
{
  if (null != Click)
    Click(this, e);
}

As you can see in the MouseLeftButtonDown event you create a new instance of a PDSATileEventArgs class. You gather the dependency properties from the user control and place those into this new PDSATileEventArgs object. Next, you call the RaiseClick method passing in this object. The Click event is raised from this method passing in the current tile object and the instance of the PDSATileEventArgs class. The PDSTileEventArgs class is shown below:

public class PDSATileEventArgs : EventArgs
{
  public PDSATileEventArgs() : base()
  {
    ViewName = string.Empty;
    Text = string.Empty;
    ImageUri = string.Empty;
    ToolTip = string.Empty;
  }

  public string ViewName { get; set; }
  public string Text { get; set; }
  public string ImageUri { get; set; }
  public string ToolTip { get; set; }
}

Create a Tile in your Application

After you have built this user control you can add a reference to the DLL that contains the user control. This user control will now show up in the Visual Studio Toolbox. Drag and drop a Tile control onto a window and set the appropriate properties via the Property Window or directly in the XAML. Below is the XAML for the “Computer Cleaner” tile shown in the upper left hand corner of Figure 1.

<my:PDSAucTile
        Name="tileComputerCleaner"
        Text="Computer Cleaner"
        ViewName="ComputerCleanerView"
        ToolTip="Click here to run the Computer Cleaner"
        ImageUri="/Images-Tiles/ComputerCleaner.png"
        Click="tile_Click"       
        Style="{StaticResource tileLarge}" />

Responding to the Click Event

When you click on a tile a Click event will fire. This event has a normal event procedure signature where you are passed the object that fired the event and an event argument object. The event argument object is an instance of the PDSATileEventArgs class. This event argument object contains the Text, ViewName, ImageUri and the ToolTip properties that you set in the XAML. In the sample code below these values are simply displayed in text blocks on the main window.

private void tile_Click(object sender, PDSATileEventArgs e)
{
  tbText.Text = e.Text;
  tbViewName.Text = e.ViewName;
  tbImageUri.Text = e.ImageUri;
  tbToolTip.Text = e.ToolTip;
}

In your application you might use a switch statement on the ViewName property to figure out which view to display as shown below:

private void tile_Click(object sender, PDSATileEventArgs e)
{
  switch (e.ViewName)
  {
     Case "ComputerCleanerView":
       // Display the Computer Cleaner View
       break;

     Case "LicenseView":
       // Display the License View
       break;

      ...  etc.
  }
}

You should assign a unique ViewName to each tile on your window in order to easily determine which tile was clicked upon and thus what action your program needs to take.

Summary

A Windows Phone or Windows 8 tile is very easy to create in XAML. In this blog post you learned how just a few lines of XAML and some event wire-ups make short work of creating a list of Tile objects. In the sample that comes with this blog post a WrapPanel is used to allow the tiles to be moved around fairly easy. You could put a ScrollViewer control around the WrapPanel to allow the set of Tiles to grow in any direction you wish.

NOTE: You can download the sample code for this article by visiting my website at http://www.pdsa.com/downloads. Select “Tips & Tricks”, then select “Creating a XAML Tile Control” from the drop down list.

Posted: Oct 22 2012, 10:13 AM by psheriff | with no comments
Filed under: , ,
A WPF Image/Text Button

Some of our customers are asking us to give them a Windows 8 look and feel for their applications. This includes things like buttons, tiles, application bars, and other features. In this blog post I will describe how to create a button that looks similar to those you will find in a Windows 8 application bar.

In Figure 1 you can see two different kinds of buttons. In the top row is a WPF button where the content of the button includes a Border, an Image and a TextBlock. In the bottom row are four individual user controls that have a Windows 8 style look and feel. The “Edit” button in Figure 1 has the mouse hovering over it so you can see how it looks when the user is about to click on it.

Figure 1: It is best to create a custom user control to get a more polished look and feel for a button control

Figure 1: It is best to create a custom user control to get a more polished look and feel for a button control.

If you read my previous blog post on creating a custom Button user control, you will find this blog post very similar.

There are many ways to create custom buttons and there are advantages and disadvantages to each way. The purpose of this blog post is to present one method that is easily understood by almost any XAML programmer, and hopefully to those new to XAML as well. 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. This gives you great reusability.

The User Control

The XAML for this kind of Windows 8 application bar style-button is a little more complicated than the simple buttons shown in my previous blog posts. However, the basics are shown below:

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>
  <Border Grid.Row="0"
          Name="borMain"
          Style="{StaticResource
                   pdsaButtonImageTextBorderStyle}"
          MouseEnter="borMain_MouseEnter"
          MouseLeave="borMain_MouseLeave"
          MouseLeftButtonDown="borMain_MouseLeftButtonDown"
          ToolTip="{Binding Path=ToolTip}">

    <VisualStateManager.VisualStateGroups>
    ... MORE XAML HERE ...
    </VisualStateManager.VisualStateGroups>

    <Image Source="{Binding Path=ImageUri}"
           Style="{StaticResource
                   pdsaButtonImageTextImageStyle}" />
  </Border>
  <TextBlock Grid.Row="1"
             Name="tbText"
             Style="{StaticResource
                     pdsaButtonImageTextTextBlockStyle}"
             Text="{Binding Path=Text}" />
</Grid>

There is a Grid, a Border, an Image and a TextBlock control all combined to form the buttons shown in row 2 of Figure 1. The above XAML is fairly easy to understand as this is just combining standard controls into a format that gives you the look required for your button. The Border, the Image and the TextBlock have a named style applied to them. The definition for this user control is in a DLL named PDSA.WPF. A default resource dictionary is included in the DLL where this user control is located to give you a default look and feel; however, you can make a copy of this resource dictionary and change the look to meet your needs.

Adding the Visual State Manager

In the original blog post on creating a button user control I wrote code to change a button’s state using C#. In this blog post I have replaced most of this code with XAML in the form of the Visual State Manager. A Visual State Manager (VSM) is a container for a storyboard in which you specify a series of actions to perform on different attributes of your controls. To give the user feedback when they hover over a button you use the Visual State Manager to change attributes of controls.

In the following VSM there are two visual states; MouseEnter and MouseLeave. The MouseLeave is empty which tells the VSM to return all properties changed during the MouseEnter back to their original values. In the MouseEnter state is where you modify three properties of the Border control. First you modify the BorderBrush color to the color specified in the style named “pdsaButtonImageTextBorderHoverColor”. You also modify the Background color of the border to the color specified in the style name “pdsaButtonImageTextBackHoverColor”. Finally, the Margin property of the Border control is modified slightly in order to make the button appear to move up.

<VisualStateManager.VisualStateGroups>
  <VisualStateGroup Name="MouseStates">
    <VisualState Name="MouseEnter">
      <Storyboard>
        <ColorAnimation
           To="{StaticResource
                pdsaButtonImageTextBorderHoverColor}"
           Duration="0:0:00.1"
           Storyboard.TargetName="borMain"
           Storyboard.TargetProperty="BorderBrush.Color" />
        <ColorAnimation
           To="{StaticResource
                pdsaButtonImageTextBackHoverColor}"
           Duration="0:0:00.1"
           Storyboard.TargetName="borMain"
           Storyboard.TargetProperty="Background.Color" />
        <ThicknessAnimation
           To="{StaticResource
                pdsaButtonImageTextHoverThickness}"
           Duration="0:0:00.1"
           Storyboard.TargetName="borMain"
           Storyboard.TargetProperty="Margin" />
      </Storyboard>
    </VisualState>
    <VisualState Name="MouseLeave" />
  </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

The XAML below shows the default styles used in the Visual State Manager. These styles come from the PDSAButtonStyles.xaml resource dictionary contained in the PDSA.WPF dll.

<!-- Border color while hovering over button -->
<Color x:Key="pdsaButtonImageTextBorderHoverColor">
  Gray
</Color>
<!-- Background color while hovering over button -->
<Color x:Key="pdsaButtonImageTextBackHoverColor">
  Gray
</Color>
<!-- Thickness while hovering over button -->
<Thickness x:Key="pdsaButtonImageTextHoverThickness">
  4,2,4,4
</Thickness>

Writing the Mouse Events

To trigger the Visual State Manager to run its storyboard in response to the specified event, you respond to the MouseEnter event on the Border control. In the code behind for this event call the GoToElementState() method of the VisualStateManager class exposed by the user control. To this method you will pass in the target element (“borMain”) and the state (“MouseEnter”). The VisualStateManager will then run the storyboard contained within the defined state in the XAML.

private void borMain_MouseEnter(object sender,
 MouseEventArgs e)
{
  VisualStateManager.GoToElementState(borMain,
    "MouseEnter", true);
}

Write code in the MouseLeave event and call the VisualStateManager’s GoToElementState method and specify “MouseLeave” as the state to go to.

private void borMain_MouseLeave(object sender,
 MouseEventArgs e)
{
  VisualStateManager.GoToElementState(borMain,
    "MouseLeave", true);
}

The Default Resource Dictionary

Below is the definition of the resource dictionary file contained in the PDSA.WPF DLL. This dictionary is used as the default look and feel for any Image/Text Button control you add to a window or user control.

<ResourceDictionary  ...>
  <!-- ****************************** -->
  <!-- ** Image/Text Button Styles ** -->
  <!-- ****************************** -->
  <!-- Image/Text Button Border -->
  <Style TargetType="Border"
         x:Key="pdsaButtonImageTextBorderStyle">
    <Setter Property="Margin"
            Value="4" />
    <Setter Property="BorderBrush"
            Value="White" />
    <Setter Property="BorderThickness"
            Value="2" />
    <Setter Property="HorizontalAlignment"
            Value="Center" />
    <Setter Property="CornerRadius"
            Value="50" />
    <Setter Property="Width"
            Value="32" />
    <Setter Property="Height"
            Value="32" />
    <Setter Property="Background"
            Value="Transparent" />
  </Style>
  <!-- Image/Text Button Image -->
  <Style TargetType="Image"
         x:Key="pdsaButtonImageTextImageStyle">
    <Setter Property="Margin"
            Value="0" />
  </Style>
  <!-- Image/Text Button TextBlock -->
  <Style TargetType="TextBlock"
         x:Key="pdsaButtonImageTextTextBlockStyle">
    <Setter Property="Margin"
            Value="2" />
    <Setter Property="Foreground"
            Value="White" />
    <Setter Property="HorizontalAlignment"
            Value="Center" />
    <Setter Property="FontSize"
            Value="9" />
  </Style>
  <!-- Border color while hovering over button -->
  <Color x:Key="pdsaButtonImageTextBorderHoverColor">
    Gray
  </Color>
  <!-- Background color while hovering over button -->
  <Color x:Key="pdsaButtonImageTextBackHoverColor">
    Gray
  </Color>
  <!-- Thickness while hovering over button -->
  <Thickness x:Key="pdsaButtonImageTextHoverThickness">
    4,2,4,4
  </Thickness>
</ResourceDictionary>

Feel free to modify this resource dictionary, or copy it and modify your new copy in order to give another look and feel to these buttons. Keep the “x:Key” name the same, other than that, you can modify any other attribute.

Using the Button Control

Once you make a reference to the PDSA.WPF DLL from your WPF application you will see the “PDSAucButtonImageText” 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 add a reference to this resource dictionary in your Application Resources section defined in App.xaml.

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

Your buttons now have a default look and feel unless you override the application resource dictionary on a specific Window/User Control or on an individual button. After you have given a global style to your application and you drag your image/text button onto a window, the following will appear in your XAML window.

<my:PDSAucButtonImageText ... />

There will be some other attributes set on the above XAML, but you just need to set the x:Name, the Text, ToolTip and ImageUri properties. You will also want to respond to the Click event procedure in order to associate an action with clicking on this button. In the sample code you download for this blog post you will find the declaration of the Edit button to be the following:

<my:PDSAucButtonImageText
     Name="btnEdit"
     ImageUri="/PDSA.WPF;component/Images/Edit_White.png"
     Text="Edit"
     Click="btnEdit_Click" />

The Text and ImageUri properties are dependency properties in the PDSAucButtonImageText user control. The x:Name and the ToolTip we get for free. Since a Border control does not have a Click event you will create one by using the MouseLeftButtonDown on the border to fire your custom “Click” event. Code the “Click” event in the PDSAucButtonImageText user control using the code shown below:

private void borMain_MouseLeftButtonDown(object sender,
 MouseButtonEventArgs e)
{
  RaiseClick(e);
}

public delegate void ClickEventHandler(object sender,
  RoutedEventArgs e);
public event ClickEventHandler Click;

protected void RaiseClick(RoutedEventArgs e)
{
  if (null != Click)
    Click(this, e);
}

Summary

This blog post built upon the previous posts where I explained how to build a button user control. The user control presented in this post adds both text and an image and adds a little XAML to the storyboard in the Visual State Manager. With the appropriate styles applied you can get a Windows 8 look and feel for these application bar buttons. Feel free to modify the styles to take on any look you want for your buttons.

NOTE: You can download the sample code for this article by visiting my website at http://www.pdsa.com/downloads. Select “Tips & Tricks”, then select “A WPF Image/Text Button” from the drop down list.

Posted: Oct 01 2012, 09:03 AM by psheriff | with no comments
Filed under: , ,
More Posts