Follow @PDSAInc November 2009 - 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

November 2009 - Posts

PALS Learning Event: WPF Part 1 - November 18, 2009 from 8am - 12:30pm PST

PALS Seminar 

Hi All,

I will be giving 4 hours of real-world training on WPF this Wednesday (Nov. 18th, 2009). If you are new to WPF and wanting to learn how to use WPF to create business applications, this is a great place to start. The cost for this seminar is only $99. You can sign up at http://www.pdsa.com/learning.

I hope to see you there!

Paul D. Sheriff

Posted: Nov 16 2009, 05:48 PM by psheriff | with no comments
Filed under: ,
PDC BOF Session on Exception Management: Wed. Oct. 18th from 4:30pm - 5:30pm

If anyone is at PDC this week, I will be moderating a session on Exception Management. If you would like to discuss this topic with your other peers at PDC, please stop by!

Hope to see you there!

Paul

Posted: Nov 16 2009, 02:17 PM by psheriff | with 2 comment(s)
Filed under: , ,
Change Templates Dynamically in WPF

WPF has the flexibility to modify the look of your controls dynamically at runtime with just a few lines of code. Take a look at Figures 1 and 2 and you will see two different views of the same list box and data. To accomplish this, you simply need to setup two different resources of XAML code that you can switch between at runtime.

More Detail 
Figure 1: More detail

Less Detail
Figure 2: Less detail

The Basic Form and the Data

This XAML form uses an XML file to fill in the ListBox control with the product data. There is a folder called \Images where the pictures for the various products are located. Below is a fragment of the XML file that contains the product data.

<Products>
  <Product>
    <ProductId>1</ProductId>
    <ProductName>PDSA .NET Productivity Framework</ProductName>
    <Price>5000</Price>
    <Logo>/Images/Framework.gif</Logo>
  </Product>
  ...
  ...
</Products>

An XmlDataProvider control is used to load this file and supply the data for the list box. The XAML code shown below is the setup of this XmlDataProvider. This XmlDataProvider is located within the Window.Resources section of this WPF Window.

<XmlDataProvider x:Key="xProducts"
                 Source="/Product.xml"
                 XPath="Products/Product" />

As you can see in the above code the Product.xml file is located in the root of the project. You use the XPath attribute to read to the node level where the actual data is within the XML file. Then you will be able to specify the element name of where the data is for displaying in the list box.

Create the XAML Templates

The next items in the Window.Resources section of this WPF Window are the <DataTemplates>. There are two data templates created as resources in this window. One has a key of “tmplMore” and one has a key of “tmplLess”. Below is the XAML code that is used to create the list box that looks like Figure 1. You can see the use of a Horizontal StackPanel control, and an Image control as the first control within that StackPanel.

<DataTemplate x:Key="tmplMore">
  <StackPanel Orientation="Horizontal">
    <Image Margin="8"
           Source="{Binding XPath=Logo}"
           Width="100"></Image>
    <Label Width="250"
           Content="{Binding XPath=ProductName}"></Label>
    <Label Width="100"
           Content="{Binding XPath=Price}"></Label>
  </StackPanel>
</DataTemplate>

Notice the data binding of the Source property of the image and the Content properties of the two labels. The Binding uses XPath attribute to signify the name of the element within the XML file. Remember that the XmlDataProvider has the first part of the XPath query, while each Binding is the element where the data is located that will be displayed in the list box.

The next data template created within the Window.Resources section has the key of “tmplLess” and looks like the XAML below. You can see that the Image control is missing from this template.

<DataTemplate x:Key="tmplLess">
  <StackPanel Orientation="Horizontal">
    <Label Width="250"
           Content="{Binding XPath=ProductName}"></Label>
    <Label Width="100"
           Content="{Binding XPath=Price}"></Label>
  </StackPanel>
</DataTemplate>

In the WPF Window is the definition of the list box control shown in the XAML listing below. Notice that the ItemsSource attribute is set to bind to the xProducts XmlDataProvider control. Also notice that the ItemTemplate is bound to the “tmplMore” data template resource. This is the default data template that is used when the window is first displayed.

<ListBox Margin="10,10,0,0"
         Height="300"
         Name="lstData"
         ItemsSource="{Binding Source={StaticResource xProducts}}"
         IsSynchronizedWithCurrentItem="True"
         ItemTemplate="{StaticResource tmplMore}">
</ListBox>

Switch Between the XAML Templates

You will write code in each button’s click event procedures, shown below, to switch between the two XAML templates. In each event procedure you create a variable to refer to a DataTemplate object. You then use the FindResource method of the current window to find the resource named either “tmplMore” or “tmplLess”. Once you have the data template object you can assign that to the ItemTemplate property of the list box control.

One thing to note is that you should include error handling within this code because if the FindResource method does not find the resource, then it will throw an exception. I purposely left out the code for brevity in this example.

Visual Basic

Private Sub btnMore_Click(ByVal sender As System.Object, _
 ByVal e As System.Windows.RoutedEventArgs) Handles btnMore.Click
  Dim tmpl As DataTemplate

  tmpl = DirectCast(Me.FindResource("tmplMore"), DataTemplate)
  lstData.ItemTemplate = tmpl
End Sub

Private Sub btnLess_Click(ByVal sender As System.Object, _
 ByVal e As System.Windows.RoutedEventArgs) Handles btnLess.Click
  Dim tmpl As DataTemplate

  tmpl = DirectCast(Me.FindResource("tmplLess"), DataTemplate)
  lstData.ItemTemplate = tmpl
End Sub


C#

private void btnMore_Click(object sender, RoutedEventArgs e)
{
  DataTemplate tmpl;

  tmpl = (DataTemplate)this.FindResource("tmplMore");
  lstData.ItemTemplate = tmpl;
}

private void btnLess_Click(object sender, RoutedEventArgs e)
{
  DataTemplate tmpl;

  tmpl = (DataTemplate)this.FindResource("tmplLess");
  lstData.ItemTemplate = tmpl;
}

Summary

In this article you learned to change templates on a list box control dynamically at runtime using the FindResource method. You simply need to create a couple of XAML templates that you will use to switch between the different views. You create these XAML templates in the Window.Resources section of the window. You give each template a unique name, and then write a little code to find each of the resources at runtime. Once you have located the appropriate resource, you assign it to the ItemTemplate property of the list box and WPF will change the look of the control instantly.


NOTE: You can download the complete sample code at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then "WPF Change Templates" from the drop-down.

Good Luck With Your Coding,
Paul Sheriff

** SPECIAL OFFER FOR MY BLOG READERS **
Visit http://www.pdsa.com/Event/Blog for a free eBook on "Fundamentals of N-Tier".

Posted: Nov 16 2009, 12:43 PM by psheriff | with 5 comment(s)
Filed under: ,
Creating a Base Window Class in WPF

Unlike Windows Forms, there is no Visual Inheritance in WPF. Luckily you don’t need visual inheritance as you can use User Controls to get a re-usable UI. There are times, however, when you want a base Window class so you can have common functionality in all your WPF windows. To accomplish this you can create a base class from which all your WPF windows can inherit. To use the base class you will need to make one simple change in both the XAML and in your code-behind class.

<Window> Element and Window Class

When you add a new Window to a WPF application it builds a .XAML file and a code-behind file. The XAML file has a <Window> element and the code-behind file has a partial class that inherits from “Window” or “System.Windows.Window” to be more specific. Notice the x:Class=”<Namespace>.<ClassName>” syntax in the XAML (See Figure 1). This x:Class name attribute must have the same name as the partial class in the code-behind. At compile time, the .XAML file is converted into a partial class that has the same name as the name of the class in the code-behind and inherits from the name of the top level element in the XAML; in this case “Window”.


Figure 1: The Window class and the inherited Window must match.

As long as these two items match the two elements in the partial class in the code-behind the compiler will be happy. If either of these two elements does not match, you will receive a compiler error.

Inherit from the Window Class

Now that you understand the basics of how the XAML and the code-behind are put together, you can now create a base Window class and modify the XAML and code-behind to use this new class.

If you wish to follow along with this article, create a new WPF application in Visual Studio. Add a new class to your project named WindowBase. Modify this newly added class to inherit from the Window class. Add the following Imports/using statements at the top of the WindowBase class.

C#
using System.Windows;

Visual Basic
Imports System.Windows

The WindowBase class needs to inherit from the Window class by changing the code to look like the following:

C#
using System;
using System.Windows;

namespace WpfApplication1
{
  public class WindowBase : Window
  {
  }
}


Visual Basic
Imports System.Windows

Public Class WindowBase
  Inherits Window
End Class

Modify the code-behind of the Window1.xaml file to inherit from the WindowBase class you just created. Your Window1 code-behind should now look like the following:

C#
public partial class Window1 : WindowBase
{
  public Window1()
  {
    InitializeComponent();
  }
}

Visual Basic
Class Window1
  Inherits WindowBase

End Class

If you were to compile the WPF application right now, you will receive the error message “Base class 'System.Windows.Window' specified for class 'Window1' cannot be different from the base class 'WindowBase' of one of its other partial types.” The reason is because while you changed the code behind to inherit from the WindowBase class, you need to change the XAML markup to reflect this same change.

The change you need to make is you need to modify the <Window> element to be <WindowBase>. However, <WindowBase> is not a part of any of the imported xml namespaces that are a normal part of the XAML markup. Thus, you need to add a namespace reference to the assembly that contains the WindowBase class. If you created a default WPF application project, the namespace you will add to the XAML is the following:

xmlns:src="clr-namespace:WpfApplication1"

Once you have added this namespace with the prefix of “src” you can now change the <Window> element to <src:WindowBase>. Be sure you modify the closing </Window> element to </src:WindowBase>. The complete source code for the XAML is shown below.

C#
<src:WindowBase
  x:Class="WpfApplication1.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:src="clr-namespace:WpfApplication1"
  Title="Window1" Height="300" Width="300">
  <Grid></Grid>
</src:WindowBase>

Visual Basic
<src:WindowBase
  x:Class="Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:src="clr-namespace:WpfApplication1"
  Title="Window1" Height="300" Width="300">
  <Grid></Grid>
</src:WindowBase>

At this point you should be able to compile the application and everything should compile correctly.

Adding Functionality

Now that you have a base Window class you can add code to that class that can be used in all of your WPF Windows. Once piece of functionality you might need is to check if your Window is in design mode or runtime. Add the following property to your WindowBase class.

C#
public bool IsInDesignMode
{
  get
  {
    return System.ComponentModel.
            DesignerProperties.GetIsInDesignMode(this);
  }
}

Visual Basic
Public ReadOnly Property IsInDesignMode() As Boolean
  Get
    Return System.ComponentModel. _
            DesignerProperties.GetIsInDesignMode(Me)
  End Get
End Property

From within your Window that inherits from the WindowBase class you can test to see if you are in design mode or in runtime mode.

C#
if(IsInDesignMode)
  // Do Something

Visual Basic
If IsInDesignMode Then
  ' Do Something
End If

Another piece of code that might come in handy in your WPF windows is the ability to read a resource dictionary XAML file and load it into the Resources of the Window. Below is the code you would add to the WindowBase class to do this.

C#
using System.IO;
using System.Windows.Markup;

public void OpenResourceDictionary(string fileName)
{
  ResourceDictionary dic = null;

  if (File.Exists(fileName))
  {
    using (FileStream fs = new FileStream(fileName, FileMode.Open))
      dic = (ResourceDictionary)XamlReader.Load(fs);

    this.Resources.MergedDictionaries.Add(dic);
  }
  else
    throw new FileNotFoundException(
      "Can't open resource file: " + fileName +
      " in the method OpenResourceDictionary().");
}

Visual Basic
Imports System.IO
Imports System.Windows.Markup

Public Sub OpenResourceDictionary(ByVal fileName As String)
  Dim dic As ResourceDictionary = Nothing

  If File.Exists(fileName) Then
    Using fs As New FileStream(fileName, FileMode.Open)
      dic = DirectCast(XamlReader.Load(fs), ResourceDictionary)
    End Using

    Me.Resources.MergedDictionaries.Add(dic)
  Else
    Throw New FileNotFoundException( _
      "Can't open resource file: " & fileName & _
      " in the method OpenResourceDictionary().")
  End If
End Sub

When you wish to load a new resource dictionary file at runtime into your Window you may do so with some very simple code from within your Window.

C#
OpenResourceDictionary("D:\Samples\Green.xaml");

Visual Basic
OpenResourceDictionary("D:\Samples\Green.xaml")

Summary

So, that is all there is to creating your own base Window class from which all your Window objects can inherit. There is a lot of functionality that you will want to make available to your Window classes. Creating a base Window class is a great way to expose this functionality. It just takes a little bit of tweaking to your XAML and your code-behind to get all this great functionality.


NOTE: You can download the complete sample code at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then "WPF Window Inheritance" from the drop-down.

Good Luck With Your Coding,
Paul Sheriff

** SPECIAL OFFER FOR MY BLOG READERS **
Visit http://www.pdsa.com/Event/Blog for a free eBook on "Fundamentals of N-Tier".

Posted: Nov 02 2009, 03:42 PM by psheriff | with 19 comment(s)
Filed under: ,
More Posts