Blog Archives

WPF: Accessing Resources From An External Project

A friend asked me recently if it was possible to access resources that were created and stored in a separate assembly. Although I was sure it could be done, I’d never tried it, so I was interested in experimenting with this idea.  It turns out that the Pack URL holds the key.  Here’s an example.

Access a single ResourceDictionary

In a ResourceDictionary file named Dictionary1.xaml inside a standard WPF Application project named ‘Resources’, I have the following markup:

 

    1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    2    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    3     <LinearGradientBrush x:Key="MyBrush">

    4         <GradientStop Color="Black" Offset="0"/>

    5         <GradientStop Color="Yellow" Offset="1"/>

    6     </LinearGradientBrush>

    7 

    8   <Style x:Key="RedButton" TargetType="{x:Type Button}">

    9     <Setter Property="Background" Value="Red"/>

   10     <Setter Property="Foreground" Value="Yellow"/>

   11     <Setter Property="Margin" Value="12,3"/>

   12     <Setter Property="Padding" Value="3"/>

   13     <Setter Property="MinWidth" Value="75"/>

   14     <Setter Property="MinHeight" Value="34"/>

   15   </Style>

   16 </ResourceDictionary>

 

Very basic resources, I know, but sufficient to demo the process. The next step is to create the ‘client’ project, the one that will use this resource.  You can either add a new project to the current solution or start a completely separate solution. In my first example, I added a WPF Application project to the solution and named it ‘ResourceUser’.

In either case, you need to add a reference to the ‘Resource’ project in the Reference section of the ResourceUser project. If you add a second project to the current solution, the project name will be available to you in the Projects tab of the Add reference dialog (as shown in the screenshot below).  If you use a separate solution, you’ll have to use the Browse tab and navigate your way to the required assembly.

 

ResDictonaries001

 

This enables you to point to the source file, i.e. the ResourceDictionary named Dictionary1.xaml in the Resource project.  The way you point to that resource is to create a ResourceDictionary in the Application.xaml file of the client project, the project that I named ResourceUser.  You set its Source property to the Dictionary1.xaml file.  As I said at the start, you use the pack URL syntax to do this. Here’s the syntax that does the job:

 

    1 <Application x:Class="Application"

    2    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    3    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    4    StartupUri="Window1.xaml">

    5     <Application.Resources>

    6 

    7         <ResourceDictionary Source="pack://application:,,,/Resources;component/Dictionary1.xaml"/>

    8 

    9   </Application.Resources>

   10 </Application>

 

To avoid confusion, I probably should have named the first project something like ‘ResourceDictionarySource’, but anyway I’m sure you’ll understand that the word ‘Resources’ in that Source property refers to the name of the project.  You’ll also see that the Dictionary1.xaml file name is included as the final item.  The general syntax for the pack URL approach is:

application:,,,/<Name of project you are referencing>;component/<Name of specific file you want to access>

Once you’ve rebuilt the project, you can use the referenced resources just as you would if they existed in the client project.  To use the gradient brush and the button styles from my example above, you can do something like this in the Window of the ResourceUser project:

 

   12     <Button Style="{StaticResource RedButton}" Content="Demo" Margin="52,21,127,0" Grid.Row="1" Height="22" VerticalAlignment="Top" />

   13     <Rectangle Margin="52,28,127,17" Name="Rectangle1" Stroke="Black"

   14               Fill="{StaticResource MyBrush}"/>

Access multiple ResourceDictionaries

If the project that has the resources contains more than one ResourceDictionary, then you can use a MergedDictionaries block in the Application.xaml file of the client project. The following example assumes that the Resources project now contains two separate ResourceDictionary files named Dictionary1.xaml and Dictionary2.xaml:

 

    5     <Application.Resources>

    6 

    7     <ResourceDictionary>

    8       <ResourceDictionary.MergedDictionaries>

    9         <ResourceDictionary Source="pack://application:,,,/Resources;component/Dictionary1.xaml"/>

   10         <ResourceDictionary Source="pack://application:,,,/Resources;component/Dictionary2.xaml"/>

   11       </ResourceDictionary.MergedDictionaries>

   12     </ResourceDictionary>

   13 

   14   </Application.Resources>

 

What if you have ResourceDictionaries in the client project too?

This doesn’t present any kind of problem.  You simply add the ResourceDictionary to the MergedDictionaries collection in the usual way.  For example, if you have a ResourceDictionary named ResourceDictionary3.xaml in the ResourceUser project, you can use this syntax in Application.xaml:

 

    7     <ResourceDictionary>

    8       <ResourceDictionary.MergedDictionaries>

    9         <ResourceDictionary Source="pack://application:,,,/Resources;component/Dictionary1.xaml"/>

   10         <ResourceDictionary Source="pack://application:,,,/Resources;component/Dictionary2.xaml"/>

   11         <ResourceDictionary Source="Dictionary3.xaml" />

   12       </ResourceDictionary.MergedDictionaries>

   13     </ResourceDictionary>

 

 

What about resources that use their own resources?

So what happens if for example you have a ControlTemplate that uses images that are stored as resources in the Resource project?  Well, this isn’t a problem at all because the whole Resource project is referenced, so if the template calls for a resource file in that project when it is used in the client project, that file is available.

If that explanation isn’t as clear as you’d like, here’s an example.  In the Dictionary2.xaml ResourceDictionary, you have a Control Template for a CheckBox that uses three images.  Here’s the first part of the markup for the CheckBox Control Template:

 

    6 <ControlTemplate x:Key="ThumbCheckBox" TargetType="{x:Type CheckBox}">

    7         <Border Name="OuterBorder"

    8               BorderBrush="Black" BorderThickness="1">

    9             <Canvas Name="MainCanvas">

   10                 <Border Name="ImagesBorder" BorderBrush="DarkGray" BorderThickness="1"

   11                       Width="24" Height="32" Margin="1,1,0,0" >

   12                     <Canvas Name="ImagesCanvas">

   13                         <Image Name="ThumbDown" Source="ThumbDownSmall.jpg" Width="18" Height="26"

   14                             Canvas.Left="2" Canvas.Top="2"/>

   15                         <Image Name="ThumbUp" Source="ThumbUpSmall.jpg" Width="18" Height="26"

   16                              Margin="1"  Canvas.Left="2" Canvas.Top="2"

   17                              Visibility="Hidden"/>

   18                         <Image Name="Indeterminate" Source="QuestionMark.jpg" Width="18" Height="26"

   19                              Margin="1"  Canvas.Left="2" Canvas.Top="2"

   20                              Visibility="Hidden"/>

   21                     </Canvas>

 

You can see that there are three jpgs involved in the creation of that template.

They are all stored in the project with a Build Action of Resource.  One of the images is named ThumbDownSmall.jpg  and you can see it shown in the Solution Explorer, together with its properties in the Properties pane:

ResDictonaries002

 

If you assign this ControlTemplate to a CheckBox instance in the Window of the client project, the required images will be pulled from the Resources project as and when they are needed.  The following XAML in the client project uses the template:

 

    <CheckBox Name="chkApproval" Margin="52,14,111,36" Grid.Row="2"

         Template="{StaticResource ThumbCheckBox}"

         Content="Not Approved"

         IsChecked="{x:Null}"

         IsThreeState="True">

    </CheckBox>

 

And the result is the CheckBox you can see at the bottom of the Window:

 

ResDictonaries003

You can download the sample solution I used for this blog post.  It's available as a blog attachment or can be downloaded from here.

Moving From WinForms to WPF

I’ve written lots of blog posts about WPF and in most cases the selection of topics has been a bit random.   Essentially, if it interests me at the time I’d write about it.  The problem with that approach is that it might assume a more basic grasp of WPF than many readers have.

In a recent post on VB City, someone said (in a discussion about WPF): “I don't know where to start and a WPF primer would be good”.  Not knowing where to start is probably a common problem, so I plan to write a few blog posts that will take a WPF 101 approach.  I’ll still be writing the higher level blogs in between times, but hopefully this series will be useful to those who are familiar with WinForms and want to see the specific differences between WinForms and WPF.

To make the blogs’ content accessible to the widest range of readers, I’ll base the samples on the VB Express version.  Today we’re still at VB2008, but VB2010 will be along in a few weeks and I’ll switch to that when it happens. I’ll also try and flag up any important differences between the 2008 and 2010 versions.

So let’s get started.

Create a new WPF project

Start a new project and select the WPF Application template:

WPF101_1_001

I’ve named it WPF101_1.

Check out the IDE

The view in the IDE is similar to the one for WinForms, but there are some obvious differences.

WPF101_1_002

The first major change from WinForms is the way that the design area is split.  The top half still shows the design view of the ‘Form’ (now a Window in WPF, but very similar in role to a Form). Below that, there’s the XAML pane.

XAML is a version of XML that you can use in WPF to design elements. We’ll be looking at XAML in much more detail as we progress, but for now remember that you don’t have to be an expert in XAML to be able to create some very useful and good WPF applications. In fact you could create everything using Visual Basic, although this isn’t the most efficient way of doing it.

The list of items in the Solution Explorer is also slightly different. There are new file types listed – specifically by default Application.xaml and Window1.xaml.  Both those XAML files have a plus sign next to them. When you click on the plus sign, a second file with the extension of xaml.vb appears. In general, the role of the XAML files is to hold the markup that represents the look of the elements in the project. The xaml.vb file, which is usually referred to as the code-behind file, generally deals with the behaviour. Their roles are a lot more flexible than that, but it’s a reasonable way of thinking of them at this early stage.

The Toolbox at the left hand side of the IDE is similar to the WinForms one.

WPF101_1_003

Many of the names of the controls are the same in both WinForms and WPF. This is both a good and bad thing. It’s good because you’ll be familiar with them. It’s bad because there are often differences that will catch you out if you’re not aware of them.

Create a Hello World! Window

1.  Add a Button to the Window

As a start point, I’ll create the canonical Hello World! app.  From the ‘Common’ tab of the Toolbox, select and drag a Button on to the surface of the Window. A key difference between WPF and Winforms when you do this is that you will see that some additional XAML is added to the XAML pane below the Designer.

WPF101_1_004

Your version won’t be exactly the same. The Margin values, for instance, are likely to be different. Also, I’ve split up the original into three lines just to reduce the width of the screenshot.

The XAML markup you see there between the opening and closing tags creates an instance of a Button. Here’s the Button in the Design pane.

WPF101_1_005

 

Looking at the things you see in the Design and XAML panes, these bring up various WPF specific points :

Nested Content  There are two blue bars at the left and top of the Window, each with a number in them. To understand what these represent, you need to look back at the XAML that was automatically created for this Window. On line 5 you see there is an opening tag for a Grid. On Line 9, there is a closing tag for this Grid. Whenever you create a new Window in the IDE, a Grid will be placed inside the Window in this way. It’s placed there is to act as a container for the remaining content.

One of the features of WPF is that many of the visual elements can only contain a single child. Now, although you may think it’s bizarre at this stage, a WPF Window is one of the elements that can contain only one child element. If that were literally true, the options for useful Windows and user-friendly applications would be very limited. It is true, but the get out clause is that each child element may content sub-elements. And each sub-element may contain its own children and so on. Therefore this isn’t really a limitation at all. We’ll look at why WPF layout is structured in this way later.

In the example so far, the Window has a child element of a Grid and the Grid has a Button as its child.

Grid Markers  Going back to the blue bars, these mark the outside edge of the Grid.  The values represent the height and width of the Grid. You will see in a later blog post how you can the Grid’s blue bars to add columns and rows to a grid.

Resolution Independence   The Button has been given a fixed Height value of 23 and a Width of 75 pixels. You should note that these aren’t the standard screen pixels that you are used to in Winforms. They are what is known as ‘device independent units’.  Confusingly, they are often referred to as ‘device independent pixels’, which almost inevitably results in them sometimes being referred to as just plain old ‘pixels’. But they are different from the pixels of your Winforms applications.

The device independent pixel of WPF respects the Dots Per Inch (DPI) setting of the system it is using. What this means in reality is that if you create your WPF button and deploy the application to a system that has a higher DPI setting, WPF will use more than ‘your’ 23 and 75 pixels to render it.   The effect of this is that the button will be as crisp on a machine with a different DPI setting as it is on yours. You don’t get that scrunching up of the elements that you often see in this situation. 

What happens is that WPF’s rendering engine makes full use of the available pixels on the deployed system. So if the machine the application is deployed on has a System DPI of, say, 120 then WPF would use 29 of those higher density pixels for the height and 94 for the width (approximately), rather than the original 23 and 75.   This is different from the blocky “fill in the extra pixels and get a jaggy result” effect that you will have seen elsewhere. Because WPF renders everything on screen using vector graphics, you simply get a higher density version of the original.

Margins   The XAML markup shows the Margin property for the Button. The four values represent the distance in device independent units that this button will always be from the edges of its parent. In the current example, it is 48 units from the left edge of the Grid and 40 units from the top. If you drag the button around in the Design pane, then let go, you’ll see that the values of the Margin property settings will change to reflect where you’ve left the Button.  The four values in the Margin setting represent Left, Top, Right, Bottom – always in that order. 

Alignment   You’ll also have noticed the two arrow pointing from the Button to the edge of the Grid. These signify that the Button is horizontally aligned to the left and vertically aligned to the top. This is also confirmed in the properties shown in the XAML. You can think of this as being the same as the Anchor properties in Winforms. WPF however offers much more subtle options than just plain anchoring.

if you did try dragging the button around in the Design pane, you’ll probably have found that, quite confusingly, not only do the top and left margin values change, but you might also see either the HorizontalAlignment, the VerticalAlignment, or both of them, disappear from the XAML. The reason for this is that WPF has a very sophisticated layout toolset that you can use directly in the Design pane. (You can of course manually make changes to the XAML, but because this is Winforms –> WPF, I’m going to try and use minimal hand coding of XAML.)

Looking at my original screenshot of the Window, you’ll see that at the right and bottom of the Button there is a small circle. Hover over the one at the right and you’ll see a hand icon appear.  It can be a bit fiddly, so make sure you have the hand, not the double-headed arrow.  If necessary, use the excellent Zoom scaling tool in the top left hand corner of the Design pane to increase the size of the display.

WPF101_1_006

 

Now, click on the circle and three things will happen. The circle will go and be replaced by another arrow, this one pointing to the right hand edge of the grid.  The third value in the Margin property down in the XAML pane will change from  to a new value.  Finally, the HorizontalAlignment property will disappear from the XAML.

The result of this change will be that if you run the project and change the size of the Window, the Button’s width will change so that it is always the same distance from the left and right edges. So at this point you have three-way anchoring.

Vertically, there will be no change if you alter the size of the Window, because the HorizontalAlignment is still set to Top and there is a constant Margin of 39 units between the top of the Button and the top of the Grid. Obviously, if you click on the circle at the bottom of the button, this will make a similar change to the vertical settings.

If you click on any of the arrows, they will change to circles.  This is a feature you need to experiment with in order to become totally familiar with it.

Button Content  You’ll see the word ‘Button’ located between the opening and closing tags of the Button element markup. The same word also appears on the face of the button in the Design pane. In a similar way to how Winforms assigns a default Text property of ‘Button1’ to a button, WPF assigns a default Content property of a string containing the word ‘Button’.  Note that it’s not a Text property, as in Winforms, even though in this case it does happen to be text.  In WPF, Content is everything. A WPF button can have almost any content. The limitations of text and an image are gone. If you remember my explanation above of nested content, this applies equally to buttons. So a button can contain layers of nested content. I’ll be covering the possibilities of this later, but for now just keep in mind that it’s Content and not Text.

WPF controls have default properties and the default property of a button is Content. Because of this, the XAML is displayed in the way you first saw above. I think the IDE maybe does you a disservice here by doing this. Another way of more specifically identifying the Content is to include its property value within the opening tag:

WPF101_1_007

 

So, even with something as basic as a button, you’ll see that there are already important differences between Winforms and WPF.

2.  Add a TextBlock to the Window

Expand the ‘Controls’ tab in the Toolbox and scroll down until you reach the TextBlock. Drag one on to the Window’s surface.

WPF101_1_008

 

Again you will see the alignment arrows and, as before with the button, you change these settings.

The default Property of a TextBlock is the Text property. When first created, the TextBlock is empty. You can enter text for the TextBlock either by placing it between the opening and closing tags or by specifically assigning a value to the Text property.

<TextBlock Height="21" Margin="39,0,62,38" Name="TextBlock1"
VerticalAlignment="Bottom" Text="This is a TextBlock" />

For the sake of completeness, I should tell you that it is possible to include other sub-elements inside a TextBlock. And these don’t even have to be text. That said, there are many better options for cases where you want to mix text and other graphical elements, so my advice is to stick to using the TextBlock for plain text. Well, not plain text – WPF offers you a lot of options for enhancing the look and style of text.  I’ll cover these choices later.

The WPF TextBlock is a Read Only control. If you want to take text input from a user, a TextBox, similar to the WinForms one, is available. You may have noticed that there is a Label element available in the Toolbox. I could have used this and it would have worked fine, but I chose the TextBlock because for most purposes in WPF apps it’s a better option.

3.   Add the Button’s Event Handling Code

In many cases, you can do what you probably do with a Winforms button in order to create and access its Click event. You double-click on the button in the Design pane.  This will automatically create the event handler code which include the familiar ‘Handles’ clause at the end of the signature line. In this example, ‘Handles Button1.Click’. It also brings up the xaml.vb code-behind file in the IDE to allow you to start adding code.

There is also an alternative. Back in the XAML for the Button, you can add a Click property inside the Button’s opening tag. When type in ‘Click=’ you will get a context menu:

 

WPF101_1_009 

 

In the above screenshot, the menu only contains the one choice of New Event Handler. If the code-behind file contains other procedures, these will also be listed as choices. When you double-click on the New Event handler choice an event handler will be created for you in the code-behind. This procedure won’t include the Handles clause.  The Handles clause isn’t needed for our Button1 instance, because it is already wired up via the Click property in the XAML.

As for the VB code in the event handler, this will be:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)

        TextBlock1.Text = "Hello World!"

    End Sub

Apart from the new WPF control, the syntax will be familiar, i.e. assigning a value to the Text property. The code differences between Winforms and WPF unfortunately won’t always be this simple, but I’ll try and cover many of the Gotchas as we come to them.

Change the Heading of the Window

In Windows Forms, you set the Text property of a Form to change what is shown as its heading. In WPF, the Window has a Title property that does the same thing. You’ll find this within the opening tag of the Window in the XAML pane.

WPF101_1_010

You can edit it there or you can select the Window in either the Design or XAML panes and then change the value of the Title property in the Properties pane. 

Run the Project

When you run the project and click on the Button, the event handler in the code-behind will populate the TextBlock with the Hello World string.

WPF101_1_011

Other Points

  Reloading.  You’ll often find that you see a gold bar at the top of the Design pane that says “An assembly or related document has been updated which requires the designer to be reloaded. Click here to reload”. You need to click on this before continuing. This message will appear whenever you make a change (usually in the code-behind or elsewhere in the project) and the layout engine needs to figure out how that affects what it’s going to show you as a result of the change. 

  Properties Pane.   The Properties pane is available to you in the same way as in Winforms.  You can click on an element either in the Design pane or the XAML pane and its properties will appear in the Properties pane, where you can view or edit them.  So you don’t even need to edit the XAML in most cases if you’re more comfortable using the Properties pane. 

 

 

 

Summary

In this first article, you’ve seen some of the differences between WPF and Windows Forms. There’s still an awfully long way to go and many differences to learn about, but at least we’ve made a start.

Some coverage of the following topics has been included:

 

  • Alignment
  • Button
  • Code-behind
  • Event Handling
  • Grid
  • Label
  • Margins
  • Nested content
  • Properties Pane
  • Reloading Message
  • Resolution Independence
  • TextBlock
  • TextBox
  • Window Title
  • XAML pane

WPF: Using a VirtualizingStackPanel to Improve ComboBox Performance

Introduction
In this earlier blog, I looked at how to use a ComboBox to display a list of all the system fonts, displaying each font name in its own font style.

I mentioned there that fonts are something of a special case, in that this collection of fonts is automatically cached for you after you first use it. The result is that when you run the application for the second and subsequent times there is minimal delay between the time the user clicks the ComboBox down arrow and the appearance of the list of fonts.

In this blog I want to look at the situation where you have a ComboBox that has a large number of other (possibly graphically complex) items to display.
Without the built-in caching that is available for fonts, you will often find that the display of large amounts of data in a ComboBox can be annoyingly slow. Furthermore, it will continue to be slow each time the ComboBox is recreated (although you can re-access the ComboBox and have shorter delays as long as you don't close the Window). However, if you close the Window that contains this ComboBox, then the whole display has to be rebuilt and the initial longer delay will reoccur.

The reason for the delay is that WPF creates the complete display for the ComboBox in advance,  regardless of the fact that it might only display a tiny proportion of the total number of items. It is this initial unnecessary creation of visual content that causes the problem. Let's create a sample project to demonstrate this.

Display of Large Amount of Text
First, I'll create some fairly uninspiring demo data in the code-behind: 

    Private Sub Window2_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Dim DemoList As New List(Of String)

        For i As Integer = 0 To 10000

            DemoList.Add("This is ItemCollection # " & i.ToString)

        Next

        Me.DataContext = DemoList

    End Sub 

This simply creates 10,000 Strings, stores them in a List (Of String) and sets this List as the DataContext for the Window.

Next, in the markup for the Window, create a ComboBox that will use those Strings for its display of items: 

    <ComboBox Height="23" Margin="27,10,10,0" Name="ComboBox1"

             VerticalAlignment="Top"

             ItemsSource="{Binding}">

    </ComboBox>  

When you run this application, there will be noticeable delay before the list appears. As I mentioned above, when you click for a second and subsequent times, the delay is shorter but still exists. If you close this Window and then subsequently show it again, the long delay will reoccur.

Improving the Display Time
The quick fix for this issue is to insert a VirtualizingStackPanel into the template for the ComboBox. This panel has the ability to assess how many items can be displayed, based on the measurements of the ComboBox, and automatically creates the visuals for that limited number of items only.

An easy way to implement this is to create an ItemsPanelTemplate as a Resource and reference it in the ComboBox markup.  

  <Window.Resources>

    <ItemsPanelTemplate x:Key="VSP">

      <VirtualizingStackPanel/>

    </ItemsPanelTemplate>

  </Window.Resources>

 

 

    <ComboBox Height="23" Margin="27,10,10,0" Name="ComboBox1"

             VerticalAlignment="Top"

             ItemsSource="{Binding}"

             ItemsPanel="{StaticResource VSP}">

    </ComboBox>  

Specifically, the ItemsPanel property of the ComboBox is set to that ItemsPanelTemplate Resource.

If you prefer, you can include the VirtualizingStackPanel right in the ComboBox creation markup: 

   <ComboBox Height="23" Margin="27,10,10,0" Name="ComboBox1"

             VerticalAlignment="Top"

             ItemsSource="{Binding}"

            >

      <ComboBox.ItemsPanel>

        <ItemsPanelTemplate>

          <VirtualizingStackPanel />

        </ItemsPanelTemplate>

      </ComboBox.ItemsPanel>

    </ComboBox>  

Personally, I like to try and keep as much of these kind of things as Resources as I can.

More Complex Items

  If your display is more complex – maybe using a DataTemplate and including nested panels and some images in each item:

    <DataTemplate x:Key="ImgAndText">

      <StackPanel Orientation="Horizontal">

        <Image Source="Timer.jpg" Height="89" Margin="3"></Image>

        <StackPanel>

          <Image Source="Stars.jpg" Width="152" Height="44" Stretch="None"></Image>

          <TextBlock Text="{Binding}" Margin="2,15,2,2" ></TextBlock>

        </StackPanel>

       </StackPanel>

    </DataTemplate>

 

then the delay can become very substantial indeed and you will certainly want to be sure to include a VirtualizingStackPanel in the markup for the ComboBox.

WPF: Using a VirtualizingStackPanel to Improve ComboBox Performance

Introduction
In this earlier blog, I looked at how to use a ComboBox to display a list of all the system fonts, displaying each font name in its own font style.

I mentioned there that fonts are something of a special case, in that this collection of fonts is automatically cached for you after you first use it. The result is that when you run the application for the second and subsequent times there is minimal delay between the time the user clicks the ComboBox down arrow and the appearance of the list of fonts.

In this blog I want to look at the situation where you have a ComboBox that has a large number of other (possibly graphically complex) items to display.
Without the built-in caching that is available for fonts, you will often find that the display of large amounts of data in a ComboBox can be annoyingly slow. Furthermore, it will continue to be slow each time the ComboBox is recreated (although you can re-access the ComboBox and have shorter delays as long as you don't close the Window). However, if you close the Window that contains this ComboBox, then the whole display has to be rebuilt and the initial longer delay will reoccur.

The reason for the delay is that WPF creates the complete display for the ComboBox in advance,  regardless of the fact that it might only display a tiny proportion of the total number of items. It is this initial unnecessary creation of visual content that causes the problem. Let's create a sample project to demonstrate this.

Display of Large Amount of Text
First, I'll create some fairly uninspiring demo data in the code-behind: 

    Private Sub Window2_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Dim DemoList As New List(Of String)

        For i As Integer = 0 To 10000

            DemoList.Add("This is ItemCollection # " & i.ToString)

        Next

        Me.DataContext = DemoList

    End Sub 

This simply creates 10,000 Strings, stores them in a List (Of String) and sets this List as the DataContext for the Window.

Next, in the markup for the Window, create a ComboBox that will use those Strings for its display of items: 

    <ComboBox Height="23" Margin="27,10,10,0" Name="ComboBox1"

             VerticalAlignment="Top"

             ItemsSource="{Binding}">

    </ComboBox>  

When you run this application, there will be noticeable delay before the list appears. As I mentioned above, when you click for a second and subsequent times, the delay is shorter but still exists. If you close this Window and then subsequently show it again, the long delay will reoccur.

Improving the Display Time
The quick fix for this issue is to insert a VirtualizingStackPanel into the template for the ComboBox. This panel has the ability to assess how many items can be displayed, based on the measurements of the ComboBox, and automatically creates the visuals for that limited number of items only.

An easy way to implement this is to create an ItemsPanelTemplate as a Resource and reference it in the ComboBox markup.  

  <Window.Resources>

    <ItemsPanelTemplate x:Key="VSP">

      <VirtualizingStackPanel/>

    </ItemsPanelTemplate>

  </Window.Resources>

 

 

    <ComboBox Height="23" Margin="27,10,10,0" Name="ComboBox1"

             VerticalAlignment="Top"

             ItemsSource="{Binding}"

             ItemsPanel="{StaticResource VSP}">

    </ComboBox>  

Specifically, the ItemsPanel property of the ComboBox is set to that ItemsPanelTemplate Resource.

If you prefer, you can include the VirtualizingStackPanel right in the ComboBox creation markup: 

   <ComboBox Height="23" Margin="27,10,10,0" Name="ComboBox1"

             VerticalAlignment="Top"

             ItemsSource="{Binding}"

            >

      <ComboBox.ItemsPanel>

        <ItemsPanelTemplate>

          <VirtualizingStackPanel />

        </ItemsPanelTemplate>

      </ComboBox.ItemsPanel>

    </ComboBox>  

Personally, I like to try and keep as much of these kind of things as Resources as I can.

More Complex Items

  If your display is more complex – maybe using a DataTemplate and including nested panels and some images in each item:

    <DataTemplate x:Key="ImgAndText">

      <StackPanel Orientation="Horizontal">

        <Image Source="Timer.jpg" Height="89" Margin="3"></Image>

        <StackPanel>

          <Image Source="Stars.jpg" Width="152" Height="44" Stretch="None"></Image>

          <TextBlock Text="{Binding}" Margin="2,15,2,2" ></TextBlock>

        </StackPanel>

       </StackPanel>

    </DataTemplate>

 

then the delay can become very substantial indeed and you will certainly want to be sure to include a VirtualizingStackPanel in the markup for the ComboBox.

WPF: ProgressBar With Marquee Text Display and TemplateBinding

 Introduction
In this blog I want to demonstrate several things. The first is how to reveal a text message in a ProgressBar. This is easy to do and you can get reasonably close to the moving marquee effect that you can see on some digital displays.
A more important topic that I'll cover is TemplateBinding in ControlTemplates. This makes it easy to use a core template, but allow individual instances of the control type that use the template to tweak selected properties. In this case I'll use the FontSize property.
Finally, you sometimes come across a situation where the control (in this case the ProgressBar) doesn't have a property that maps directly to a property that exists in the ControlTemplate. Although this might seem improbable, it actually happens quite often. We'll look at how we can work round this limitation and in the example I will show you can set a Content property on a ProgressBar control, even though the ProgressBar doesn't actually possess such a property.

If you've read my last three or four blogs you may be thinking that I'm a bit obsessed with ProgressBars! That's not the case and it's simply that they represent a good case study for ControlTemplates, they require named parts and I think they make a nice change from the demos that mostly seem to use Buttons.

The ControlTemplate
In the previous blogs on ProgressBars, you will have seen that I have stored the ControlTemplates in the Window.Resources collection. This time, the ControlTemplate is going to be stored in the Application.Resources collection. The thinking behind this is that this template is designed to be reused in several Windows across the application, with different values being placed on some properties in some instances. So having it in a central location clearly makes it available application-wide and makes sense.

Here is the markup for the initial version of the ControlTemplate: 

    <ControlTemplate x:Key="PBWordReveal" TargetType="{x:Type ProgressBar}">

      <Grid>

        <Border Name="PART_Track" 

          BorderThickness="2" CornerRadius="5"

          Background="LightSkyBlue" BorderBrush="Navy"  />

 

        <ContentControl Name="PART_Indicator"

           Content="  Loading   . . . .   Loading   . . . . .   Loading"

           Margin="4,0" FontSize="14"

           Foreground="Black" Background="LightSkyBlue"

           VerticalAlignment="Center"

           HorizontalAlignment="Left"/>

      </Grid>

    </ControlTemplate> 

If you are not familiar with the concept of named parts – specifically the PART_Track and PART_Indicator shown above – you get an explanation in my previous ProgressBar blogs.

The outer track is a Border element, while the Indicator uses a ContentControl. The key property is probably the Content property, which as you can see contains the text that will be displayed. The HorizontalAlignment property is also quite important to the look of the final effect.

ProgressBar Instance
To use this template, I will create a ProgressBar instance in a Window. 

    <ProgressBar Name="PBReveal"

     Template="{StaticResource PBWordReveal}"

            Width="200" Height="40"    /> 

To animate the ProgressBar in the code-behind of the Window (in this example, a Window named 'WordReveal': 

Imports System.Windows.Media.Animation

 

Partial Public Class WordReveal

 

    Private Sub WordReveal_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

         Dim a As New DoubleAnimation

        a.From = 0

        a.To = 100

        a.Duration = New TimeSpan(0, 0, 6)

 

        PBReveal.BeginAnimation(ProgressBar.ValueProperty, a)

    End Sub

End Class 

When you run this project, the text content in the ProgressBar will be revealed from left to right. The screenshot shows the bar with the animation approx 60% complete:

And this is the display when the animation has ended:

Effectively all that is happening is that the ContentControl, the PART_Indicator element of the template, is revealed over the period of the animation – the revelation commencing from left to right.

If you want more of a marquee type effect, you can change the HorizontalAlignment : 

  HorizontalAlignment="Right" 

This will make the text scroll from right to left:

Setting the HorizontalAlignment to either Center or Stretch will produce a different result again.

TemplateBinding
TemplateBinding allows you to hand back control of selected properties to the instance of the ProgressBar that is using this ControlTemplate. It is simple to implement and in this example only requires the change shown below to the PART_Indicator named part of the template:  

       <ContentControl Name="PART_Indicator"

           Content="  Loading   . . . .   Loading   . . . . .   Loading"

           Margin="4,0"

              FontSize="{TemplateBinding FontSize}"

           Foreground="Black" Background="LightSkyBlue"

           VerticalAlignment="Center"

           HorizontalAlignment="Right"

          /> 

The TemplateBinding points to a specific property – FontSize. This can now be set in an instance of the control that uses the template:  

    <ProgressBar Name="PBReveal"

     Template="{StaticResource PBWordReveal}"

     Width="200" Height="40"

        FontSize="22"/> 

Note that if you choose not to set a value on the FontSize property in the ProgressBar instance, the default value for that property will be used (12).

Mapping Between Properties
As you can see from the markup, the PART_Indicator uses a ContentPresenter. The ContentPresenter has a Content property and this is what is used to set the value of the text to be displayed. The basic ProgressBar however doesn't have such a property. So if you want to include TemplateBinding to allow different text to be used in different instances that use this template, you need to map the ContentPresenter property to some other property of the ProgressBar.

In most cases, if you have only one property that needs this kind of mapping, the solution is simple. You can use the Tag property: 

        <ContentControl Name="PART_Indicator"

             Content="{TemplateBinding Tag}"

           Margin="4,0"

           FontSize="{TemplateBinding FontSize}"

           Foreground="Black" Background="LightSkyBlue"

           VerticalAlignment="Center"

           HorizontalAlignment="Right"

          /> 

And you insert the required text in the markup for the ProgressBar instance : 

    <ProgressBar Name="PBReveal"

     Template="{StaticResource PBWordReveal}"

     Width="200" Height="40"

     FontSize="22"

       Tag="  Please Wait . . . . . . . . . ."/> 

These two templated changes result in the following display:

As you can see, both the FontSize and the Content have changed.

If you need to include more than one of these kind of cross property mappings, there is nothing (apart from common sense and experience) stopping you from hijacking other ProgressBar properties. You can identify a property that you absolutely, categorically know won't be used (and there's the start of the slippery slope right there) you can use this to map to.

The more professional alternative, of course, is to create a custom control of your own which contains all the properties you desire. I'll be looking at the steps involved in creating a custom control in a future blog.

WPF: How to Create a Reversing ProgressBar

 

 

In some of my earlier blogs – here, here and here, I looked at some rather colorful, non-rectangular versions of the ProgressBar. Although you probably wouldn't want to use some of those versions outside of a game environment, I hope it did show you how you can create and tweak non-rectangular versions. In this blog, I want to examine a less bizarre variation and along the way hopefully show you some more useful WPF basics.

Reversing the Default ProgressBar
The default ProgressBar is rectangular, has slightly rounded corners, a Gray Background, Linear Gradient Green Foreground and, most importantly, it traverses from left to right. I was wondering about how to change this and have a ProgressBar that traversed from right to left – a kind of a countdown effect instead of the usual fill-increasing effect. Because I was in ControlTemplate mode, I confess that I initially overlooked the obvious solution – total simplicity in WPF – and created a whole new template that did the job. I'll show you the obvious WPF solution shortly, but first let's just look at how this can be done as a ControlTemplate task.

ControlTemplate
I set about this by using a combination of Expression Blend and Visual Studio. I know I could have done it all in Blend using the Edit Template tools, but I came up this WPF route the hard way, from back in the early days when there were no tools and you had to become a XAML-head to get anywhere with this new-fangled thing called WPF. So I find that I still prefer to create my ControlTemplates in XAML in Visual Studio and use Blend when I want to do anything fancy with graphics or animation.

If you find it easier to do it all in Blend, I have no problem with that, but here's my way. First create a ControlTemplate, Key it as 'ReverseBasicPB' and set its TargetType to ProgressBar. The visual elements of the template will all be held inside a Grid. 

    <ControlTemplate x:Key="ReverseBasicPB" TargetType="{x:Type ProgressBar}">

      <Grid>

 

      </Grid>

    </ControlTemplate> 

As you will know from the earlier blogs, the ProgressBar requires two named parts – PART_Track and PART_Indicator. In this version, PART_Track will consist of a Rectangle inside a Border and PART_Indicator will be a Rectangle with a reasonable copy of the Green Linear Brush.
(As a side note, it is possible to dig into the WPF namespaces and get hold of the ProgressBarGlassyHighlight and ProgressBarTopHighlight Brushes, but I was content to go with my approximation.)

Here is the markup for the PART_Track: 

       <Border Name="PART_Track" BorderBrush="DarkGray"

               BorderThickness="2" CornerRadius="3" >

          <Rectangle Fill="LightGray"

               RadiusX="3" RadiusY="3"/>

        </Border> 

Note that to achieve the rounded corner effect, Border uses CornerRadius and Rectangle uses RadiusX and RadiusY.

The markup for the Indicator is just as simple: 

          <Rectangle Name="PART_Indicator" HorizontalAlignment="Right"

                    RadiusX="3" RadiusY="3" 

                    Margin="2,2,2,2">

          <Rectangle.Fill>

            <LinearGradientBrush EndPoint="0.5,0" StartPoint="0.5,1">

              <GradientStop Color="#FF5FBB23" Offset="0.525"/>

              <GradientStop Color="White"/>

              <GradientStop Color="#FF63BC28"/>

              <GradientStop Color="#FF95EDA5" Offset="0.542"/>

            </LinearGradientBrush>

          </Rectangle.Fill>

        </Rectangle> 

The key property here is the HorizontalAlignment. By setting it to Right we ensure that the ProgressBar Indicator will start at the right hand side and stretch out towards the left.

Demo
The following markup in a Window will create two ProgressBars, one default style and the other using this ControlTemplate: 

    <Grid>

    <Grid.RowDefinitions>

      <RowDefinition Height="87*" />

      <RowDefinition Height="175*" />

    </Grid.RowDefinitions>

 

    <ProgressBar x:Name="PBDefault" Width="150" Height="33">

 

    </ProgressBar>

    <ProgressBar Name="MyPB" Width="150" Height="33"

          Template="{StaticResource ReverseBasicPB}" Margin="22"

          Grid.Row="1"

          VerticalAlignment="Top" />

  </Grid> 

Add in the animation code-behind used in previous examples: 

    Private Sub ReverseBasic_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Dim a As New DoubleAnimation

        a.From = 0

        a.To = 100

        a.Duration = New TimeSpan(0, 0, 6)

 

        MyPB.BeginAnimation(ProgressBar.ValueProperty, a)

 

        a.From = 0

        a.To = 100

        a.Duration = New Duration(TimeSpan.Parse("0:0:6"))

 

        PBDefault.BeginAnimation(ProgressBar.ValueProperty, a)

    End Sub 

When you run this, completed ProgressBars are reasonably close in looks, the only major difference being that the templated one at the bottom runs right to left.

 

Although the creation of the ControlTemplate was only a few minutes work and opens the way for further visual changes (which I'll probably blog about later), if you just want the default ProgressBar reversed, the simple way is as follows:

Add a new ProgressBar to the Window, but this time include a LayoutTransform to rotate it 180 degrees. 

    <ProgressBar x:Name="PBDefaultReversed"

                 Width="150" Height="33"

                 Grid.Row="2" >

      <ProgressBar.LayoutTransform>

        <RotateTransform Angle="180" />

      </ProgressBar.LayoutTransform>

    </ProgressBar> 

(You'll also need to add the extra row to the Grid if you are following along and creating this sample yourself:) 

    <Grid.RowDefinitions>

      <RowDefinition Height="87*" />

      <RowDefinition Height="85*" />

      <RowDefinition Height="90*" />

    </Grid.RowDefinitions> 

And don't forget to animate it in the code-behind.

The final result, with the default ProgressBar at the top, the templated one in the middle and the transformed one at the bottom, looks like this:

I chose not to use a gradient for the background Gray, but of course you can implement this if you prefer.

WPF: Changing ProgressBar Appearance As Values Change

 

Introduction
In this earlier blog, I looked at how to create a non-rectangular ProgressBar. As the next step, I want to look at how you can change properties dynamically as the current Value of the ProgressBar changes.

I'll start with the easy (and fairly realistic) scenario where you want to tone down the ProgressBar once it has reached its maximum value.

Currently the ProgressBar coloring looks like this:

If you want to apply changes to the look of the ProgressBar as it is running, you can use a DataTrigger in XAML or you can simply use the ValueChanged event of the ProgressBar in the code-behind. I say "simply", but you do in fact have to understand how to dig into the ControlTemplate and drill down in order to make the code-behind approach work.

There's a trade-off between these two approaches – DataTrigger or code-behind. DataTrigger offers a slightly more concise syntax, but only as long as your requirements are very basic. Once you step beyond the absolute basics, the XAML becomes quite complex. And as you are no doubt more familiar with VB, what's the point in struggling just to prove the point that it can be created in XAML? Sometimes, common sense has to win over technical ego.

Overall, in most cases, code-behind is the easiest answer. But just to be sure you know what a DataTrigger is and how it works, let's run through it.

DataTrigger
A DataTrigger is fired (and as a result some predefined change happens) when a value is reached. In the scenario we are looking at here, a DataTrigger could be set to fire when the Value property of a ProgressBar reaches its maximum. So in the case of the ProgressBar defined below that would of course be when it reaches 100.  

    <ProgressBar x:Name="CurvyPB" Width="300" Height="60"

       Template="{StaticResource PBCurvy}"

       Foreground="{StaticResource BlueGreenRed}"

       Minimum="0" Maximum="100" /> 

The 'predefined change' might be to alter the color of the outside edge of the control and also to dim its Opacity, so that it can be more easily ignored now that its work is done.

The DataTrigger sits in a Triggers collection in the ControlTemplate for the ProgressBar. The syntax is as follows: 

      <ControlTemplate.Triggers>

        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Value}"

                    Value="100">

          <Setter TargetName="PART_Track" Property="Stroke" Value="DarkGray" />

          <Setter TargetName="PART_Track" Property="Opacity" Value="0.3" />

        </DataTrigger>

      </ControlTemplate.Triggers> 

(Note that this is only one section of the complete ControlTemplate.)

The DataTrigger Binding looks awkward and is one of those rather peculiar constructs that makes XAML hard to decipher (and create) sometimes. Essentially it translates to :
"Keep checking the Value property of the ProgressBar instance that is using this ControlTemplate. If and when it reaches a Value of 100, fire the trigger."

The Setters both target the Path named 'PART_Track'. The first changes the Stroke to DarkGray and the second one turns down the Opacity of that Path (which effectively reduces the Opacity of the templated ProgressBar).

Here is the full XAML for the Window which contains the ControlTemplate and the ProgressBar instance: 

<Window x:Class="CurvyWithTriggers"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:converter="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Luna"  

   Title="Curved ProgressBar" Height="200" Width="400">

  <Window.Resources>

    <ControlTemplate x:Key="PBCurvy" TargetType="{x:Type ProgressBar}">

      <Grid>

        <Path x:Name="PART_Track"

             Stroke="{StaticResource BlueGreenRed}"

             StrokeThickness="5"

             Data="F1 M46.802502,0.50000018 C59.803562,0.50000006 71.553123,3.7052743 79.942001,

              8.9014616 C88.330879,3.7052746 100.08044,0.5 113.0815,0.50000018 C125.92575,

              0.5 137.54851,3.6284194 145.9305,8.6908474 C154.3125,3.6284194 165.93524,

              0.50000006 178.7795,0.50000018 C204.35167,0.5 225.082,12.900593 225.082,

              28.1975 C225.082,43.494408 204.35167,55.895 178.7795,55.895 C165.93524,

              55.895 154.3125,52.766582 145.9305,47.704151 C137.54851,52.766582 125.92575,

              55.895 113.0815,55.895 C100.08044,55.895 88.330879,52.689728 79.942001,

              47.493538 C71.553123,52.689728 59.803562,55.895 46.802502,55.895 21.230335,

              55.895 0.5,43.494408 0.5,28.1975 0.5,12.900593 21.230335,0.5 46.802502,

              0.50000018 z"

             Stretch="Fill">

          <Path.Fill>

            <MultiBinding>

              <MultiBinding.Converter>

                <converter:ProgressBarBrushConverter />

 

              </MultiBinding.Converter>

              <Binding Path="Foreground" RelativeSource="{RelativeSource TemplatedParent}" />

              <Binding Path="IsIndeterminate" RelativeSource="{RelativeSource TemplatedParent}" />

              <Binding Path="ActualWidth" ElementName="PART_Indicator" />

              <Binding Path="ActualHeight" ElementName="PART_Indicator" />

              <Binding Path="ActualWidth" ElementName="PART_Track" />

 

            </MultiBinding>

          </Path.Fill>

        </Path>

        <Decorator x:Name="PART_Indicator" />

       </Grid>

 

      <ControlTemplate.Triggers>

        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Value}"

                    Value="100">

          <Setter TargetName="PART_Track" Property="Stroke" Value="DarkGray" />

          <Setter TargetName="PART_Track" Property="Opacity" Value="0.3" />

        </DataTrigger>

      </ControlTemplate.Triggers>

    </ControlTemplate>

 

  </Window.Resources>

  <Grid>

    <ProgressBar x:Name="CurvyPB" Width="300" Height="60"

       Template="{StaticResource PBCurvy}"

       Foreground="{StaticResource BlueGreenRed}"

       Minimum="0" Maximum="100" />

 

  </Grid>

</Window> 

This markup in the Application.xaml file creates the LinearGradientBrush: 

    <Application.Resources>

    <LinearGradientBrush x:Key="BlueGreenRed"

       EndPoint="1,0.5" StartPoint="0,0.5">

      <GradientStop Color="#FF2D3ADD" Offset="0"/>

      <GradientStop Color="#FFF13E14" Offset="1"/>

      <GradientStop Color="#FF9775D8" Offset="0.192"/>

      <GradientStop Color="#FF3F893B" Offset="0.481"/>

      <GradientStop Color="#FF2B9518" Offset="0.625"/>

      <GradientStop Color="#FECC7638" Offset="0.812"/>

    </LinearGradientBrush>

  </Application.Resources> 

The code-behind to animate the ProgressBar: 

Imports System.Windows.Media.Animation

 

Partial Public Class CurvyWithTriggers

 

    Private Sub CurvyWithTriggers_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Dim a As New DoubleAnimation

        a.From = 0

        a.To = 100

        a.Duration = New TimeSpan(0, 0, 8)

 

        CurvyPB.BeginAnimation(ProgressBar.ValueProperty, a)

    End Sub

End Class 

Apologies if you already have all that code and markup from the previous blog. Personally I hate it when someone says "I've used the same code as in my earlier blog", which means I then have to go and find that blog, dig through it and find the missing bits before I can test out what I'm working on now. So I much prefer to repeat it and make life easier for you, at the expense of a slightly longer blog entry.

After the ProgressBar completes its mission, it hits that Value of 100 and the look changes to this:

So if you're sat there saying to yourself that there's nothing there that you couldn't have easily done in code-behind, I almost agree with you. Certainly the Opacity change would be easy: 

        If progressBar1.Value = 100 Then

            progressBar1.Opacity = 0.3

        End If 

But what about changing the value of the Stroke property though? The ProgressBar doesn't have a Stroke property. That's tucked away inside the ControlTemplate and is a property of the Path named PART_Track. To get to that, we will need a way to access the ControlTemplate and then drill down into the Path.

Before we look at the code-behind approach, I just want to mention a problem with DataTriggers, especially as they relate to ProgressBars. The only arithmetic operator available to you in the DataTrigger in XAML is the equals operator. For fairly obvious reasons, the less-than and greater-then operator symbols have the potential to cause problems in a language that uses them as element delimiters.

The reason that this is a particular problem with the ProgressBar is that the algorithm that breaks the ProgressBar movement into its time chunks will rarely, if ever, space them across whole numbers. That is to say if the Minimum Value is 0 and the Maximum Value is 100 and the duration is 10 seconds, you might reasonably suppose that each new block will appear at intervals of 0, 10, 20, 30, etc. However it doesn't work that way and the values are more likely to be something like :
0
0.252538
0.299015
0.613981
: etc
: ending with
99.769844
99.890324
100

This makes it impossible for you to set a DataTrigger on a value that you can be certain will be matched exactly, except for the starting and ending values of 0 and 100. In theory, you could tweak the size settings of the PART_Track and PART_Indicator to ensure whole number partitions (these being the key factors in the breakdown), but none of my experimenting with this approach worked. An alternative approach, which I haven't tried, would be to create a ValueConverter which would then allow you to set value parameters, such as < 20 or > 40 and so on. However, I didn't really see any gain in going that route, as I can use the ValueChanged event of the ProgressBar directly, together with a bit of WPF delving, as we will now see.

FindResource and FindName
When you need to get at a ControlTemplate (or other Resource, for that matter), you can use the FindResource method of the FrameworkElement class. Before, I get into that, I am going to make two changes to the markup in the Window.
The first change is to add a Name property to the Window, placing this inside the opening tag of the Window class markup:  

 x:Name="CurvyValueChangedWindow" 

This is necessary in order for the Window to be accessed from the code-behind.

The second change is purely decorative – changing the Stroke property of the PART_Track path from the LinearGradientBrush to plain Yellow.  

        <Path x:Name="PART_Track"

             Stroke="Yellow" 

 The ProgressBar now looks like this at startup:

To locate that ControlTemplate, I use the following code, which I have placed in the ValueChanged event of the ProgressBar: 

        Dim ct As New ControlTemplate()

        Try

            ct = CType(CurvyValueChangedWindow.FindResource("PBCurvy"), ControlTemplate)

 

        Catch Ex As ResourceReferenceKeyNotFoundException

            '  Design time message

            Console.WriteLine("CT Not found")

            '  Quit gracefully if not found

            Exit Sub

        End Try 

The third line does all the work and you will see now why I added a name to the WPF Window. This Name is used to identify the container in which the Resource named PBCurvy (i.e. the ControlTemplate) should be found.
Note also that if due to a typo or other error the Resource can't be found, then no further action will be taken. At this stage, the Catch is superfluous. It is however important to avoid the application crashing as we move on to the next step.

FindName
So now we have got ourselves a reference to the ControlTemplate, but we still need to drill down into the Path named PART_Track which is a sub-element of that template. To do this, you can use the following code, placing it immediately below the code used to find the ControlTemplate resource:  

        Dim p As New Path

        p = CType(ct.FindName("PART_Track", PBCurvy), Path)

 

        If IsNothing(p) Then

            Console.WriteLine("Path Not found")

            Exit Sub

        End If 

This time the FindName function searches through that ControlTemplate (now referenced as 'ct') in order to find the named Path.
Once again, the test for IsNothing isn't necessary at this stage, because we have not yet tried to do anything with the Path variable 'p'. As before though, when we add further code, this checkpoint – and the Exit Sub if the Path isn't found – are important.

Changing The values
We've now reached the point where we can manipulate values of that Path according to the current values of the ProgressBar. This code placed immediately below the previous snippet will change the color and thickness of the Stroke: 

        Select Case PBCurvy.Value

            Case Is < 25

                p.Stroke = New SolidColorBrush(Colors.Yellow)

                p.StrokeThickness = 5

            Case 25 To 49

                p.Stroke = New SolidColorBrush(Colors.Green)

                p.StrokeThickness = 7

            Case 50 To 75

                p.Stroke = New SolidColorBrush(Colors.Orange)

                p.StrokeThickness = 9

            Case 76 To 99

                p.Stroke = New SolidColorBrush(Colors.Red)

                p.StrokeThickness = 11

            Case Else

                p.Stroke = New SolidColorBrush(Colors.DarkGray)

        End Select 

When you run the application, the colors will change as things progress:  

 

 I have chosen to make those particular changes for demo purposes, but you are of course not limited to those. The key take away points in this blog are the availability of the DataTrigger and the use of the very helpful FindResource and FindName methods when you want to trigger a change via the code-behind.

WPF: Displaying a Master-Detail Collection of In-Memory Objects In a TreeView

 Introduction
In this earlier blog I used an XML file containing nested data items as the data source of a TreeView. In this version I will use a collection of objects as the data source.
The details of the data are much the same as I used in the previous blog, with a couple of tiny changes to avoid any possible confusion caused by the use of Visual Basic keywords as field names:

These classes are saved in a VB file named SalesData.vb. Because it is fairly lengthy, filling about three screen display lengths, I've made it available from this link. (To a large extent, how the the data is created isn't really the topic of this blog – I'm more concerned with the Binding to a source and the templates used for displaying the data.)

Mapping to the local Assembly
In order to be able to access those SalesPerson, SalesOrder, etc classes and objects in the XAML file, it is necessary to create a mapping. The syntax for this is as follows:  

  xmlns:local="clr-namespace:HierarchicalDataTemplate" 

It isn't mandatory to use 'local' as the mapping alias, but it's a fairly traditional approach. In this case, of course 'HierarchicalDataTemplate' is the name of the project I am working on.

With the namespace mapping in place, any of the classes that currently exist in the code-behind files of the project become visible to the XAML file. If you view the code listing for the SalesData.vb file, you will see that the class which creates the demo data is called 'SalesPersonList'. It's now possible to create a new instance of that class in the XAML file : 

    <local:SalesPersonList x:Key="SalesPersonList"/> 

I generally place this in the Window.Resources collection.

Creating a TreeView and Binding its Data Source
In the markup for the Window itself, I'll create a TreeView which contains a single TreeViewItem.  

    <TreeView>

      <TreeViewItem ItemsSource="{Binding Source={StaticResource SalesPersonList}}"

          Header="Sales Figures" />

    </TreeView> 

The ItemsSource is the crucial property here. It identifies exactly where the TreeView should look for its data. In this case it looks into the SalesPersonList instance that I created a few moments ago. For the avoidance of doubt, the exact 'SalesPersonList' that is used is the StaticResource created earlier and identified by the Key. (I possibly should have used a different name for the Key and the underlying Class to avoid any confusion).

Because of the use of the Binding and the HierarchicalDataTemplates, it is only necessary to create this single TreeViewItem. The Binding engine will trawl through all the data and create as many TreeViewItem nodes as it needs to in order to display everything correctly. 

HierarchicalDataTemplates
This example uses three HierarchicalDataTemplates, plus a standard DataTemplate for the SalesItems. Here is the first one (which again I've placed in the Window.Resources collection):  

    <HierarchicalDataTemplate DataType="{x:Type local:SalesPerson}"

          ItemsSource="{Binding Path=Periods}">

      <TextBlock Text="{Binding Path=Name}"/>

    </HierarchicalDataTemplate> 

The DataType property identifies which type of object will be dealt with by this template. In the case of this first template, this will be the SalesPerson type. In other words, when the Binding that I placed on the TreeViewItem finds any instance of a SalesPerson inside that SalesPersonList, it will look at this template to discover how it should display SalesPerson details.
In this case, the Name property of the current SalesPerson will be shown in a TextBlock.
The ItemsSource property also uses a Binding, but it is only interested in knowing what needs to be shown as the child data of SalesPersons. As we know from the diagram above, this will be the Periods data.

If you are finding the DataType and the Bindings' Paths a bit tricky to grasp, it may help if you run the project as it currently stands. Here's the markup for the Window so far: 

<Window x:Class="OrdersListsDisplay"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:local="clr-namespace:HierarchicalDataTemplate"

   Title="Sales List Display" Height="300" Width="300">

  <Window.Resources>

    <local:SalesPersonList x:Key="SalesPersonList"/>

 

    <!–  Data Templates –>

    <HierarchicalDataTemplate DataType="{x:Type local:SalesPerson}"

          ItemsSource="{Binding Path=Periods}">

      <TextBlock Text="{Binding Path=Name}"/>

    </HierarchicalDataTemplate>

 

  </Window.Resources>

    <Grid>

    <TreeView>

      <TreeViewItem ItemsSource="{Binding Source={StaticResource SalesPersonList}}"

          Header="Sales Figures" />

    </TreeView>

  </Grid>

</Window> 

When you run this, you will first see:

Then, when you expand the first node, you will have:

Finally, clicking on either or both the SalesPerson nodes, you will see that the application has tried to display the children of the SalesPersons for you. In the absence of any instruction about formatting the Periods, it simply reverts to displaying the default ToString rendering of the class.

Hopefully though you can now see why the template points to SalesPerson as its DataType, but identifies the next level down the tree as the ItemsSource. (You may have noticed that this wasn't necessary with the XML data example in the earlier blog, where simply assigning a Binding without a Path will work.)

Displaying Periods and SalesOrders
Exactly the same approach is used for the next two templates: 

    <HierarchicalDataTemplate DataType="{x:Type local:Period}"

          ItemsSource="{Binding Path=SalesOrders}">

      <TextBlock Text="{Binding Path=Name}"/>

    </HierarchicalDataTemplate>

 

    <HierarchicalDataTemplate DataType="{x:Type local:SalesOrder}"

         ItemsSource="{Binding Path=SalesItems}">

      <TextBlock Text="{Binding Path=Name}"/>

    </HierarchicalDataTemplate> 

The three HierarchicalDataTemplates in place so far will produce this result:

which should come as no surprise, based on what I explained earlier.

ItemDetail DataTemplate
So that just leaves the template for the SalesItems and ItemDetail data. This time a standard DataTemplate will do, because we know there is no further data below ItemDetail.  

    <DataTemplate DataType="{x:Type local:SalesItem}">

      <TextBlock Text="{Binding Path=ItemDetail}"/>

    </DataTemplate> 

Just for the record, a HierarchicalDataTemplate would work but isn't necessary. With the final DataTemplate added, every level of the Master-Detail data can be accessed:

Formatting
You have many options for formatting the final presentation of the data. In this example, you can change the color, font size and weight, indentation, etc via the individual TextBlock properties. If you wanted to insert additional graphical detail (such as an icon), you simply wrap the TextBlock in a StackPanel with its Orientation set to Horizontal. You can then insert the icon image as an additional child of this StackPanel.

You can build on this basic example shown here to create a much more complex display.

WP Like Button Plugin by Free WordPress Templates