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

Implementing Edit and Format Features in a WPF RichTextBox

In previous blogs, I looked at how to access a plain text file through a RichTextBox and then how to open and save content in RTF format.   In the second blog, I also looked at some core editing features which are available as keyboard shortcuts.

This time I want to look at how to create a simple text editor based around the RichTextBox.  I also want to cover the scenario where you load a file of one type, such as a plain  text file, make changes and then save it as an RTF file.

Because there is quite a lot of content here, I’ve numbered the sections and listed them:

  1. Introduction to creating a WPF User Control in a library
  2. The skeleton of the RTBEditor user control
  3. Referencing the user control from another project
  4. Creating the File Menu Item of the user control
  5. Creating and coding the File >New menu item
  6. Creating and coding the File > Open menu item.
  7. Creating and coding the File > Save As Text menu item
  8. Creating and coding the File > Save As Rtf menu item
  9. Creating and coding the File > Exit menu item
  10. Creating the Edit menu item
  11. Creating the Format and Align menu items

 

1.  Creating a WPF User Control

Instead of adding a RichTextBox directly into a Window, I’m going to create a WPF UserControl.   In the interests of reusability and portability, I’ll store this in a WPF User Control Library.  To do this from your currently open WPF Solution:

  1. Select File > Add > New Project in the Visual Studio IDE main menu.
  2. Select ‘Windows’ from the list of Project types.
  3. Select WPF User Control Library from the list of templates.
  4. Rename it.  I named the library ‘WPFUserControls’.
  5. Click OK.

To add a new User Control to the library:

  1. Select Project > Add New Item from the IDE menu.
  2. Select WPF from the Categories list.
  3. Select User Control (WPF) from the Templates list.
  4. Rename it.  I named it RTBEditor.xaml.
  5. Click ‘Add’.

2.  Skeleton of the User Control

To create the skeleton of the User Control, do the following:

  1. Delete the default Grid in the UserControl markup.
  2. Replace it with a DockPanel.
  3. Inside the DockPanel, add a Menu element.
  4. Set its DockPanel.Dock property to Top.
  5. Set its MinHeight property to 25.
  6. Inside the DockPanel, but outside the markup for the Menu, add a RichTextBox.
  7. Name it ‘RTB’.
  8. Set its DockPanel.Dock property to Bottom.
  9. Set its MinHeight and MinWidth properties to 200.

The markup below includes the steps described above, plus a few other less important property settings I’m using for demo purposes:

  <DockPanel>

    <Menu Margin="0,0,0,5" Name="Menu1" DockPanel.Dock="Top" MinHeight="25" >

 

    </Menu>

    <RichTextBox Name="RTB" BorderThickness="2" DockPanel.Dock="Bottom"

                MinHeight="200" MinWidth="200"

                 Background="#FFEDEAEA">

    </RichTextBox>

  </DockPanel>

3.  Referencing the User Control

When I’m building a User Control, I like to insert an instance of it in a Window, so I can see how things are progressing.

Because the User Control is stored in a library, it’s necessary to reference this library from any project that wants to use it.    In the Solution I’m using, I have two projects; the Library, named WPFUserControls and a standard WPF project named WpfRichTextBox.   You can see this from the screenshot below:

RTB003

I need to add a reference to the library in the WPF Window Application project so that I can access and use the RTBEditor user control in one or more Windows.  To do this:

  1. Ensure you have the WPF Window Application project selected in the Solution Explorer.
  2. Select Project > Add Reference form the IDE main menu.
  3. In the Add Reference dialog box, click on the Projects tab.
  4. There will only be one Project Name in the list, so ensure this is selected and click OK.

In WPF, another step is needed.  You have to add details of the namespace to the markup of the Window where you want to use the user control.   I’m using a file named Window2 to host the text editor control, so I need to add the following markup to the xml namespaces at the top of the file:

xmlns:uc="clr-namespace:WPFUserControls;assembly=WPFUserControls"

You can type that in by hand if you want to, but it’s not the easiest syntax to remember.  Far easier is to type as far as the “uc=” point and then use the Intellisense list to select the library project. 

RTB004

In this case, you want to select the ‘WPFUserControls in assembly WPFUserControls’ item, because this is the name of the project you are referencing.   When you click on it, the correct syntax, as shown in the earlier XAML snippet, will be created for you automatically.

With this in place, you can add an instance of the user control to the Window.  Here’s the markup for the Window so far:

 

    1 <Window x:Class="Window2"

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

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

    4    xmlns:uc="clr-namespace:WPFUserControls;assembly=WPFUserControls"

    5    Title="Using the Text Editor " Height="500" Width="400">

    6 

    7   <Grid>

    8     <uc:RTBEditor />

    9 

   10   </Grid>

   11 </Window>

There’s not much to see yet, but we can build up the user control and watch its progress.

4.  The File Menu of the User Control

WPF Menus are mainly composed of MenuItems.   The two key properties of a MenuItem are its Header (the text the user sees)  and its Click property (the link to the event handler for the Click event in the code-behind).

MenuItems can be nested, so in this case several sub items are nested inside the main File menu.  The following XAML placed inside the Menu1 Menu will create the standard list of File items:

    6       <MenuItem Header="_File" Name="FileMenuItem" >

    7         <MenuItem Header="_New" Name="mnuItemNew" Click="mnuItemNew_Click" />

    8         <MenuItem Header="_Open" Name="mnuItemOpen" Click="mnuItemOpen_Click" />

    9         <MenuItem Header="Save as _Text" Name="mnuItemTxtSave" Click="mnuItemTxtSave_Click" />

   10         <MenuItem Header="Save as _RTF" Name="mnuItemRTFSave" Click="mnuItemRTFSave_Click" />

   11         <Separator />

   12         <MenuItem Header="E_xit" Name="mnuExit" Click="mnuExit_Click" />

   13       </MenuItem>

The only points to note here are that you can use accelerator keys, such as Alt & F for the File Menu, or – more usefully – Alt & F & N for File > New.   Placing the underscore immediately in front of the letter you want as the hotkey is all that’s needed.
You’ll see that I also added a Separator element between the Exit menu item and the others.

5.   The New File Item

For our purposes, we don’t really need to create a new file at this point.  What the user wants is a blank RichTextBox in which to enter some content.   So we can just the use the Clear method to do this:

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

        RTB.Document.Blocks.Clear()

    End Sub

In a production application, you would of course want to build in a feature that asks the user to confirm what is effectively a total deletion of the current content. 

6.  The Open File Item

WPF currently doesn’t have its own OpenFileDialog, but you can use the old Win32 version.  Add the following two Imports statements to the code-behind:

Imports Microsoft.Win32

Imports System.IO

The first is for the OpenFileDialog; the second for the FileStream I’m just about to use to open and read the file.

Here is the code for this menu item:

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

   14         'Let the user select a file

   15         Dim ofd As New OpenFileDialog

   16         With ofd

   17             '  Set preferred file types

   18             .Filter = "Text Files(*.txt;*.rtf)|*.txt;*.rtf" & _

   19             "| All files (*.*)|*.*"

   20 

   21             If .ShowDialog() = True Then

   22                 '  Identify the chosen file extension

   23                 Dim dataformat As String

   24                 Select .FileName.Substring(.FileName.Length – 3)

   25                     Case "rtf"

   26                         dataformat = DataFormats.Rtf

   27                     Case Else

   28                         dataformat = DataFormats.Text

   29                 End Select

   30 

   31                 '  Then load the file using the appropriate data format

   32                 Dim fs As New FileStream(.FileName, FileMode.Open, FileAccess.Read)

   33                 Using fs

   34                     'Create a TextRange that comprises the start and end points of the RichTextBox text

   35                     Dim RTBText As New TextRange(RTB.Document.ContentStart, RTB.Document.ContentEnd)

   36                     RTBText.Load(fs, dataformat)

   37                 End Using

   38             End If

   39         End With

   40     End Sub

The OpenFileDialog code is straightforward and is no different from what you do in Windows Forms.   The Filter aims to encourage users to select text files that are editable in the RichTextBox, but allows them the freedom to choose others.   If they choose anything other than an Rtf file, the data format is set to Text.   This will allow actual text format files to be displayed properly; any others will simply display their binary form content.

The file load code is the same as I used in the earlier blogs, except that I use a variable named ‘dataformat’ instead of passing in the actual DataFormats object as the second parameter of the Load method of the TextRange.

7.  The Save As Text MenuItem

The code to save the content to a .txt file is very similar to that used in the earlier blog.  The only difference is that again I have added a file dialog to give the user the choice of file name and save location.    This time, of course, it’s a SaveFileDialog. 

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

   43         Dim sfd As New SaveFileDialog

   44         sfd.Filter = "Text Files(*.txt)|*.txt"

   45 

   46         If sfd.ShowDialog = True Then

   47             Dim fs As FileStream = File.OpenWrite(sfd.FileName)

   48             Using fs

   49                 Dim RTBText As New TextRange(RTB.Document.ContentStart, RTB.Document.ContentEnd)

   50                 RTBText.Save(fs, DataFormats.Text)

   51             End Using

   52         End If

   53     End Sub

 

8.  The Save As RTF Menu Item

It isn’t really necessary to have two separate procedures for the two different formats.    The above procedure can easily be edited to identify which of the menu items called it and then switch the Filter of the SaveFileDialog and the DataFormats value accordingly.  But for now we’ll just live with a bit of unnecessary duplication:

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

   56         Dim sfd As New SaveFileDialog

   57         sfd.Filter = "Rtf Files(*.Rtf)|*.Rtf"

   58 

   59         If sfd.ShowDialog = True Then

   60             Dim fs As FileStream = File.OpenWrite(sfd.FileName)

   61             Using fs

   62                 Dim RTBText As New TextRange(RTB.Document.ContentStart, RTB.Document.ContentEnd)

   63                 RTBText.Save(fs, DataFormats.Rtf)

   64             End Using

   65         End If

   66     End Sub

9.  The Exit MenuItem

I’ll be honest: I included this out of habit and then realised that this is a user control, not a Window or a Form.   So you wouldn’t usually want the Exit menu item to unilaterally close down the parent container.  So in this case I’ll use ‘Exit’ as meaning ‘Clear the current content’.   In which case, this now familiar Clear method will do nicely:

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

   69         RTB.Document.Blocks.Clear()

   70     End Sub

10.   The Edit Menu Item

This is another container for nested menu items.   For this example, I’ve chosen to use the Cut, Copy and Paste commands.  Here’s the markup:

   14      <MenuItem Header="_Edit">

   15         <MenuItem Header="Cut" Command="Cut"/>

   16         <MenuItem Header="Copy" Command="Copy" />

   17         <MenuItem Header="Paste" Command="Paste" />

   18         <Separator />

   19         <MenuItem Header="Undo" Command="Undo" />

   20         <MenuItem Header="Redo" Command="Redo" />

   21       </MenuItem>

You’ll notice that I haven’t used the underscores with these menu items.  I could have, but as you will see if you run this project you already have the standard Ctrl & X, Ctrl & C, etc and I thought that a different set with Alt & whatever would just be confusing.

There are several key things to notice in this markup.   The first is that there is no Click property pointing to an event handler in the code-behind.   In fact, there is no code-behind needed for this functionality.  Because all five of these are built-in Application Commands, selecting any of them will cause that action to take place (maybe – more on this in a minute).   So, simply by adding the ‘Command=Undo’  to the markup, everything is in place to ensure that selecting that menu item will automatically cause the user’s last action to be undone.

Note that this is an Application wide command, so it will undo whatever user action the application was last aware of.  In other words, if this user control is surrounded by other controls in the Window and you apply an action to one of those other controls and then use the RTBEditor’s Undo command, it will undo the action you took on the other control.   It’s an application command, not a RichTextBox one.

Next, these commands are smart.  If it’s not appropriate for them to be available, then they won’t be.   What I mean by that is if, for example, you haven’t selected any content and so there is nothing on the clipboard, then the Paste command won’t be available.   The same logic applies to all these commands.   Here’s a screenshot:

RTB005

As you can see, there is no text in the RichTextBox and nothing has been selected.  Therefore the Cut and Copy commands (and their wired up menu items) are disabled.  In short, these commands know when they can or cannot execute.   (If you’re wondering, Paste is available because I had some other content still on the clipboard – it’s an Application wide command, remember).

Notice also that because these are Application commands with built-in shortcut key combinations, those combos are automatically displayed next to each menu item for you. 

11.   The Format and Align Menu Items

If you’ve groked the idea of how commands are used here, you’ll have no problem with the next two sets of menu items – Format and Align.   Here’s the markup:

 

   22       <MenuItem Header="Format">

   23         <MenuItem Header="_Bold" Command="{x:Static EditingCommands.ToggleBold}" />

   24         <MenuItem Header="_Italic" Command="{x:Static EditingCommands.ToggleItalic}" />

   25         <MenuItem Header="_Underline" Command="{x:Static EditingCommands.ToggleUnderline}" />

   26       </MenuItem>

   27       <MenuItem Header="Align">

   28         <MenuItem Header="Left" Command="{x:Static EditingCommands.AlignLeft}" />

   29         <MenuItem Header="Center" Command="{x:Static EditingCommands.AlignCenter}" />

   30         <MenuItem Header="Right" Command="{x:Static EditingCommands.AlignRight}" />

   31       </MenuItem>

The key difference here (and I’ll admit that it caught me out at first) is that you need to use a markup extension to get at these commands.   You can see that the first three commands have the prefix of ‘Toggle’, as in ‘Toggle Bold’, and so on.   And the commands do work in exactly that way.  Click them once to toggle the effect on, click again to toggle it off.   You can select text and apply one of the formats or you can toggle a setting and then begin to type.  The setting you have just made will then be applied.   That’s why you will find that they are not disabled even when nothing is selected.

You’ll see that the markup extension requires the fully qualified name of the command.   There are other groups of commands, other than ApplicationCommands and EditingCommands and I will be looking at some of those in the next blog.   I also plan to include other commands and features, such as the ability to colour text and tweak the fonts.   At some stage I also want to bring the whole thing completely into the world of WPF by using a FlowDocument as the content container inside the RichTextBox.

Summary

In the meantime, you have the makings of a useful text editor here.   There are many other Application and Editing commands and you may well want to add some of those to the menu.    WPF’s Commands can make many of these common tasks very easy to implement.

Entity Framework Quickstart Quick Start

I was flitting through various beginner examples on Entity Framework and queries and landed on the MSDN Quickstart here.  It works fine and the step by step instructions are clear enough.   Although there are various linked follow-on articles that cover some of the background, I did wonder if some beginners might find it all to be a fairly steep learning curve and be tempted to give up almost at the start.    So I thought that maybe it might be a help to some beginners if I tried to fill in some of the background to what is going on in that initial Quickstart article.

This blog article will look at the items highlighted in the MSDN listing below:

Quickstart-1

I’m not going to replicate every step from that Quickstart and what I’ve done is home in on areas where I think clarification might help.  So, each of the numbered sections below equates to the MSDN URL that has the same title.  (I’ve hyperlinked the section titles so you can’t really go wrong). 

Within those numbered sections, I’ve used lower case letters a, b, c, etc for each subsection and again I’ve used the same heading in this article as they use in the Quickstart.

If you already have SQL Server installed and are familiar with how to run the script provided in the first steps of the Quickstart then you can skip the first two sections below and go straight to Section 3 (Creating the Course Manager Application).

1.  Quickstart

a.   What You Will Learn
The second paragraph in this section tells you that you can download the completed Course Manager by following the link given there.  That’s an incorrect link and you can actually download the CourseManager.zip file from here.

b.   Requirements
Although the Requirements section advises you to download the ADO.NET Entity Framework runtime and tools from the Microsoft Download Center, I strongly recommend that you don’t do this now.  This requirement has been overtaken by the availability of SP1 of Visual Studio 2008.  If you haven’t already installed this Service Pack, here’s your opportunity.

2.   Creating the School Sample Database

a.  To create the School database and schema
It’s possible that you may not have either SQL Server 2005 or SQL Server 2005 Express Edition installed.  You can download the Express Edition from here.

Because the Quickstart uses a Transact-SQL script to create the database and populate it with some demo data for you, you should also install the SQL Server Management Studio Express if you don’t already have this, or the full version of SSMS.  You can then execute the script from the Management Studio.

If you have any problems installing and initially setting up SQL Server Express Edition or the Management Studio, you can navigate your way through various help pages and articles on MSDN here.

 

It will be obvious to some, but maybe not so obvious to everybody that once that is all set up, the instructions  in this section of the Quickstart which are headed:

“On the File menu, point to New, and then click Database Engine Query.”

refer to what you need to do in the Management Studio, not Visual Studio. 

When you run the Management Studio, you are invited to connect to a server of your choice

Quickstart000

  If you’ve only just installed everything, you’ll likely only have one choice.

 

Quickstart001

 

  Follow the three steps in the ‘To create the School database and schema’ section.   If the script executes properly, you will see a message at the bottom of the window:

Quickstart002

 

b.   To review tables in the School database
The third step in this section might confuse novices because it says:

“In the Object Explorer window, expand the node for the newly connected instance, then expand Databases, expand School, expand Tables, and review the list of table objects in the database.”

It then shows you a database diagram.   There are actually two separate steps involved here.

(1)   To review the list of table objects as instructed, you need to expand the items as described there and you will see something like the following:

Quickstart003

(2)  To generate your own set of database diagrams like those shown in the Quickstart, you need to select the ‘Database Diagrams’ folder in that Object Explorer list:

Quickstart003A

 

The first time you do this, you might see a short message as shown below:

Quickstart004

Click the default choice of ‘Yes’.

You can then Right-click on the Database Diagrams folder and select ‘New Database Diagram’.

Select all the Tables from the database:

Quickstart005

Add them to the diagram and then Close the dialogue.   You will then see a diagram similar to the one shown in the Quickstart created in your Management Studio.

 

3.  Creating the Course Manager Application
I don’t think there’s anything in the instructions in this section that will cause you any problems.   So you should have a Windows Form something like this:

Quickstart006

 

4.   Generating the School Entity Data Model

a.  To add the ADO.NET Entity Data Model item template
If you can’t find the ADO.NET Entity Data Model template in the list of available templates, this probably means you haven’t successfully installed Visual Studio SP1 yet. 


b.   To generate the EDM
When you get to Step 3 of this section, your dialog should look something like this:

Quickstart007

Note the setting of the Data source, which might not always be the default (and some of them do look very similar).

Instead of entering the server name, you may find it better to use the dropdown arrow and/or the Refresh button and then select the server instance on which you earlier ran the script.  

Alternatively, you can simply enter ‘(local)’ and pick the School database from the list:

Quickstart008

(If you’re feeling really lazy, you can in fact just enter a period (a ‘full stop’ as we Brits used to call it) and this is the same as entering (local).)

You shouldn’t need to type in the database name because if everything is going to plan so far you will see ‘School’ in the list of available databases.    If you can, then read on.

However, if you can’t get access to the database, have tried until you’re ready to throw the keyboard through the window and are just about to give up then don’t do that – instead navigate down to the end of this article where I show you an alternative way of accessing the School database that will be fine for the purposes of this Quickstart training.

 

c.   To view the EDM in the ADO.NET Entity Data Model Designer

You probably won’t need to double click the edmx file in the Solution Explorer, as per Step 1, because the newly created design is usually made the primary display for you as soon as it is initially created.

In Step 4: “From the View menu, select Other Windows, click Mapping Details, and then click an entity or association in the Entity Designer.” the ‘Associations’ referred to are shown as ‘Navigation Properties’ :

Quickstart011

 

The next section will be the Querying Entities and Associations part of the Quickstart.    As this is becoming a much longer piece than I expected, I’m going to split it here and you will be able to see the further information in Part 2.

 

—————————————————————————————————————————————————————————————————————-

If you can’t get the database created and connected

But what if it’s not going to plan and you’re getting pretty frustrated?  You want to be learning about Entity Framework but you can’t get the basic sample database connected.  Well, for learning purposes only, you could sidestep this issue and access the a copy of the School.mdf database file directly in Visual Studio.  It’s not really the best route in the long run, but if you want to try out the EF samples for now and work out the SQL Server problems later then at least it’s an option.

If you checked out the link I gave you for downloading the completed sample, you might have noticed that there is a file in that download list called School.mdf:

Quickstart010

I did download this file and tried the workaround I’m going to show you, but I got errors that make me think that this version is maybe for SQL Server 2008.  Anyhow, you can try downloading it and see if it usable for you;  if not, here’s an alternative download that is definitely OK for SQL Server 2005.  So you can download that copy and, if you want to keep your steps in sync with the alternative instructions I’ll show you in a moment, save it to a Folder named Databases on your C Drive – i.e. C:Databases.

Here are the complete steps to generate the EDM using a workaround:

  • Select Generate from database in the Choose Model Contents dialog box. Then click Next.

  • Click the New Connection button.

  • In the Choose Data Source dialog box, select your data source and then click Continue.

  • In the Connection Properties dialog box, select the Microsoft SQL Server Database File (SQL Client) option as the Data source, then browse to the C:DatabasesSchool.mdf file that you have just downloaded and saved. 

 

Quickstart009A

  • Press the Test Connection button and you should be rewarded with a reassuring message that the connection succeeded. 
  • Click the OK button.
  • The Choose Your Database Objects dialog box is displayed.

  • Ensure that all tables and stored procedures are selected and that the value of Model namespace is SchoolModel, and then click Finish to complete the wizard.

 

The Model will be created for you and you can then follow the steps shown in the section of the Quickstart entitled ‘To view the EDM in the ADO.NET Entity Data Model Designer’.

———————————————————————————————————————————————————————————–

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: 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.

How To Create and Use a Custom Command in WPF

  I've got to be honest and admit up front that initially I wasn't completely sold on the idea that WPF Commands are the great leap forward that they are sometimes billed as. OK, so I get that they can reduce the repetition of event handling code, but even their most ardent supporters aren't going to be able to claim that the required code is particularly intuitive. I will agree though that the availability of CanExecute is useful sometimes when multiple controls are bound to a Command. As a WinForms developer moving to WPF, I still find that in many cases I'm happy to use event handlers and, where necessary, a utility method or two.

So, having got that caveat out of the way, let's look at a basic situation where we want a range of different user actions to trigger the same task.

I've decided to use the scenario that I covered in this blog. It's maybe not the most realistic scenario, but it does give an opportunity to use a wide range of user inputs and gestures to move the image across the screen. It also allows the use of the CanExecute event.

In the earlier blog, the user had to click on an image to make it move from left to right across a Canvas. This can soon become tiresome, so by means of a Command we'll give her several other options. In order to make the use of CanExecute realistic, we will say that once the image reaches the far right edge of the Canvas, it must stay there. So the image move will be allowed to happen (i.e. CanExecute) as long as it hasn't reached the right hand edge of the canvas.

Creating the User Interface
The following XAML will create an updated version of the Canvas-and-Image UI used previously. This version includes various controls that will be bound to the Command. 

<Window x:Class="Window3"

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

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

 

   Title="Using A Command" Height="300" Width="300">

 

 

    <Grid x:Name="MainGrid">

    <Grid.RowDefinitions>

      <RowDefinition Height="Auto" />

      <RowDefinition Height="218*" />

      <RowDefinition Height="Auto" />

    </Grid.RowDefinitions>

 

 

      <!–  Canvas in the main middle section –>

    <Canvas x:Name="MainCanvas" Grid.Row="1">

      <Image x:Name="MoveableImage" Width="55" Source="questionmark2.jpg"

 

        Canvas.Left="0" Canvas.Top="0" />

    </Canvas>

 

    <!– Menu at the top –>

    <Menu Grid.Row="0" Margin="3">

      <MenuItem Header="Move It" Margin="5"

 

               ></MenuItem>

    </Menu>

 

    <ToolBar Grid.Row="2" >

      <Button Content="Move It"

              />

    </ToolBar>

 

      <Button Grid.Row="1" VerticalAlignment="Bottom"

             HorizontalAlignment="Right" Width="100"

             Height="33" Content="Move It" Margin="0,0,4,2"

              />

  </Grid>

</Window>

 (If you've looked at the markup closely and think that the order of elements is haphazard, you would be wrong.   Try moving the MenuItem to what seems a more logical position above the Canvas and you will get an Error when the code is completed.  As you will see, the code behind for the MenuItem will eventually have a link to the Image.   So therefore the Image must be created first in the top down XAML file, before the MenuItem can know about it .  I often think that it's these little potential Gotchas that make the WinForms-to-WPF learning curve so difficult.)

This is what it should look like:

  

Creating the Command
Let's start by creating a new Command Class called MoveItCommand. It will have a single property named MoveIt, a backing field named _move it and a parameterless constructor. The property and field are of type RoutedUICommand, but apart from that are standard and really don't need any additional explanation: 

    Private Shared _moveit As RoutedUICommand

    Public Shared ReadOnly Property MoveIt() As RoutedUICommand

        Get

            Return _moveit

        End Get

    End Property

 The constructor is used to create the various keyboard and mouse gestures that can be used to move the image. For the purposes of demonstration, I've gone totally overboard on these and have included four variations in the final version. But for clarity, I am only showing one in the code snippet below:

     Shared Sub New()

        '  Add keyboard and mouse gestures

        Dim UserInputs As New InputGestureCollection()

        UserInputs.Add(New KeyGesture(Key.M, ModifierKeys.Alt))

 

         '  Assign these gestures to the _moveit field (and thereby to the MoveIt property)

        _moveit = New RoutedUICommand("Move Element", "Move", GetType(MoveItCommand), UserInputs)

 

    End Sub 

  • The constructor begins by creating a new empty collection of mouse gestures.
  • The second line creates and stores a gesture which takes the Alt and M keys as the combination to be used to fire this command. Notice the order of the keys used as arguments – effectively it is "M & Alt" which isn't the way we usually think of the combination. If you switch the order and place the Modifier key first, you will get a runtime error. Trust me, I've been there, done that!
  • Finally, a new RoutedCommand is created, containing descriptive text, its name, owner type (which is this custom command class – MoveItCommand), and the collection of Gestures that will work with this command.

 Note that all the members of this class are Shared, ensuring that only instance of the Command will be in use in the application when it runs.

Additional Gestures
If you are happy with how the Class and its Constructor works, I'll now add in those other gestures I mentioned. These include F12 on its own, a mouse click on its own and finally a combination of mouse click and key press. Here's the revised code:  

    Shared Sub New()

        '  Add keyboard and mouse gestures

        Dim UserInputs As New InputGestureCollection()

        UserInputs.Add(New KeyGesture(Key.M, ModifierKeys.Alt))

        UserInputs.Add(New KeyGesture(Key.F12, ModifierKeys.None))

        UserInputs.Add(New MouseGesture(MouseAction.RightClick, ModifierKeys.None))

        UserInputs.Add(New MouseGesture(MouseAction.LeftClick, ModifierKeys.Shift))

         '  Assign these gestures to the _moveit field (and thereby to the MoveIt property)

        _moveit = New RoutedUICommand("Move Element", "Move", GetType(MoveItCommand), UserInputs)

    End Sub

 

  • The first gesture is Alt and M, which I've already covered.
  • The second gesture is the F12 key on its own. Note that you must include Modifiers.None. if you don't, you will get a runtime error.
  • The third gesture will fire the command when the mouse is right clicked anywhere in the Window. Because of the way that WPF and RoutedCommands work, this effectively means that you can still click on the Image to make it move. If you wanted to limit the mouse click to the Image only, you could harness the power of Bubbling and Tunneling events to trap the mouse button press at the Image level. I haven't done that in this example.
  • The final gesture takes a combination of both the mouse left button click together with holding down the Shift key. Quite awkward to use and only included to demonstrate the range of available gestures.

 

Command Bindings
Having created the custom Command Class, if we want to use it in the XAML – and we do – then it is necessary to create an XML namespace mapping. Without this link, the XAML markup would have no idea about the existence of the class and its property. The syntax is fairly simple: 

   xmlns:local="clr-namespace:GetValue" 

The alias of 'local' is used as the key which is mapped to the namespace in which the MoveItCommand class resides. The namespace is the same as the name of the project and in this case the project is named 'GetValue'.

Given this namespace mapping, we can now set up the CommandBinding in the XAML pane:

 

  <Window.CommandBindings>

    <CommandBinding Command="local:MoveItCommand.MoveIt"

                   Executed="CommandBinding_Executed"

                   CanExecute="CommandBinding_CanExecute"/>

  </Window.CommandBindings>

 

As you see from the snippet above, you create a new CommandBinding inside a CommandBindings collection. The Command property of the CommandBinding points to that MoveIt property created in the MoveItCommand class.

The Executed and CanExecute properties point to two methods which we will create next in the code-behind.

Executing The CommandBinding The task of the CommandBinding_Executed method is to carry out whatever tasks we want to have actioned when the command is invoked. In this demonstration project, this task is simply to shunt the image a few units to the right. So the code is as follows:  

    Private Sub CommandBinding_Executed(ByVal sender As System.Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs)

        '  Move the image 5 units to the right

        Dim LeftPos As Double = MoveableImage.GetValue(Canvas.LeftProperty)

        MoveableImage.SetValue(Canvas.LeftProperty, LeftPos + 5)

 

    End Sub 

(If you are not sure about how the GetValue and SetValue functions work, you can read up on it in my blog here).

CanExecute
CanExecute is an optional feature which can be quite useful. In this demonstration, we will only allow the Command to work (and therefore only allow the Image to continue moving to the right) if it hasn't reached the right hand edge of the Canvas. More subtly, if CanExecute becomes set to False, any controls that are bound to the Command will automatically be disabled. I will add Command Bindings to the buttons, etc shortly.

First, here is the code for the CanExecute method: 

    Private Sub CommandBinding_CanExecute(ByVal sender As System.Object, ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs)

        '  Only allow execution of the command if the image

        '  has not yet reached the right hand edge.

        e.CanExecute = MoveableImage.GetValue(Canvas.LeftProperty) < (MainCanvas.ActualWidth – MoveableImage.Width)

 

    End Sub 

It tests the current position of the Image to see if it has reached the right hand edge of the Canvas. CanExecute will be set to True if there is still space to the right side of the Image, otherwise it will be set to False.

The complete code behind for the Window and the Custom Command class together now looks like this: 

Partial Public Class Window3

 

    Private Sub CommandBinding_Executed(ByVal sender As System.Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs)

        '  Move the image 5 units to the right

        Dim LeftPos As Double = MoveableImage.GetValue(Canvas.LeftProperty)

        MoveableImage.SetValue(Canvas.LeftProperty, LeftPos + 5)

 

    End Sub

 

    Private Sub CommandBinding_CanExecute(ByVal sender As System.Object, ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs)

        '  Only allow execution of the command if the image

        '  has not yet reached the right hand edge.

        e.CanExecute = MoveableImage.GetValue(Canvas.LeftProperty) < (MainCanvas.ActualWidth – MoveableImage.Width)

 

    End Sub

End Class

 

Public Class MoveItCommand

 

    Private Shared _moveit As RoutedUICommand

    Public Shared ReadOnly Property MoveIt() As RoutedUICommand

        Get

            Return _moveit

        End Get

    End Property

 

    Shared Sub New()

        '  Add keyboard and mouse gestures

        Dim UserInputs As New InputGestureCollection()

        UserInputs.Add(New KeyGesture(Key.M, ModifierKeys.Alt))

        UserInputs.Add(New KeyGesture(Key.F12, ModifierKeys.None))

        UserInputs.Add(New MouseGesture(MouseAction.RightClick, ModifierKeys.None))

        UserInputs.Add(New MouseGesture(MouseAction.LeftClick, ModifierKeys.Shift))

         '  Assign these gestures to the _moveit field (and thereby to the MoveIt property)

        _moveit = New RoutedUICommand("Move Element", "Move", GetType(MoveItCommand), UserInputs)

    End Sub

 

End Class 

At this point, you can run the project and use any of those four gestures to move the Image. Once the Image hits the right hand edge, it will stop moving. The menu item, the two buttons don't play any part in the action yet – that is, clicking them will have no effect and they won't become disabled when the Image reaches the right hand limit.

Binding the Command to the Controls
All that remains to do is to bind this custom command to the menu item and the two buttons. Now things really do become simple and you can begin to see the potential benefit of using a Command. The following piece of markup is added to the MenuItem, the Button in the Toolbar and the Button in the Canvas:  

        Command="local:MoveItCommand.MoveIt" 

With those bindings in place, you can move the Image by any of the seven methods included – the four input gestures and by clicking on any of the three controls. Note now that once the Image hits the right hand edge, those three controls all become disabled.

So, there is a fairly simple example of using your own Command. You can see the final version of the XAML markup here, and also the code behind here.

WP Like Button Plugin by Free WordPress Templates