DevCity.NET -
WinForms and WPF Interop - The Best of Both Worlds
Ged Mead

Ged Mead (XTab) is a Microsoft Visual Basic MVP who has been working on computer software and design for more than 25 years. His journey has taken him through many different facets of IT. These include training as a Systems Analyst, working in a mainframe software development environment, creating financial management systems and a short time spent on military laptop systems in the days when it took two strong men to carry a 'mobile' system.

Based in an idyllic lochside location in the West of Scotland, he is currently involved in an ever-widening range of VB.NET, WPF and Silverlight development projects. Now working in a consultancy environment, his passion however still remains helping students and professional developers to take advantage of the ever increasing range of sophisticated tools available to them.

Ged is a regular contributor to forums on vbCity and authors articles for DevCity. He is a moderator on VBCity and the MSDN Tech Forums and spends a lot of time answering technical questions there and in several other VB forum sites. Senior Editor for DevCity.NET, vbCity Developer Community Leader and Admin, and DevCity.NET Newsletter Editor. He has written and continues to tutor a number of free online courses for VB.NET developers.

by Ged Mead
Published on 7/12/2008

  Whenever I see a question along the lines of "How can I include an image alongside each item in a list of items or use more than one font, or varying background colors?" I usually find myself muttering "WPF!".    This is a lot more sociable and positive than many of the things I mutter as I sit here each day, but finally I've realised that I need to do something about it.

  The result is this article, which works through the steps needed to harness the rich UI features of WPF to the more familiar paradigm of Windows Forms.

   The example used will show you how easy it can be to use a WPF control in a Windows Forms application and you can use this article as the stepping stone to creating your own.  The ListBox layout shown below will be created in WPF and ported over to be used in a Windows Forms application.  

WPF ListBox Control Example

Introduction, Code and Markup

  So here's the kind of scenario I have in mind.   You have a Windows Forms project on the go and you want to create a ListBox that has more than just text in it.  You want to have an image at the left hand side and two sets of text at the right, each string in a different size font and a different color.

   Of course you could use Owner Draw, MeasureString, DrawString, DrawImage and a bunch of other hokey stuff to do this in WinForms.   And before WPF came on the scene that was all you had in your toolbox.

   But now that WPF is here you can create ListBoxes that are far more complex than this one.  Mind-boggling complexity and knock-your-eyes-out design characteristics, actually.   To be truthful though you  really need Expression Blend as well as Visual Studio 2008 (or VB 2008)  if you want to create anything that has a lot of sub-elements and/or many variations of colors, such as rotated gradients.

  If you're not ready to abandon Windows Forms totally (and why should you?) then you can get most of the best of both worlds and port those fancy design WPF controls into your Windows Forms. 

   On the assumption that not everyone has Blend or the full edition of VS 2008 I'm going to limit the ListBox  to something quite basic and will leave it to you to take it from there upwards in the design stakes.   The demo project for this article is written using VB  2008 Express Edition.  This article isn't going to be a full primer on creating WPF controls, but I hope to show you enough detail for you to be able to try the approach out yourself - pushing the envelope as your WPF knowledge grows.

1. The Windows Forms Project

   Start up a new Windows Forms project.   Add a Panel to the default Form1, set    its Dock property to Right.   Set its Width  to 300.

   Go the Toolbox and find the WPFInteroperability Tab.  


Select the ElementHost


 Open it and select the ElementHost control.  Drag it on to the form and drop it inside the Panel.  The Smart Tab dialog will open and invite you to SelectHostedContent from a combobox.   Of course, at the moment, the combobox will be empty as we haven't reached that stage yet.

  But before you close down the SmartTab, do select the Dock in Parent Container checkbox.   

  That's all we need do with the WinForms project at this stage. 

2.  WPF UserControl Library Project

(i)  The UserControl

 The next step is to add a WPF User Control Application to the Solution.  Now, if you are using the Express Edition and you don't see the name of your Solution at the top of the Solution Explorer then you need to make a change to your IDE options.

  Go to Tools > Options in the IDE Menu and select Projects and Solutions > General.

   Over to the right hand side list of checkboxes you will find "Always Show Solution" as an option.   


Show the Solution Name

   Check this option and the Solution name will be displayed in Solution Explorer.

   Right click on the Solution name in SolutionExplorer and from the context menu that appears select Add > New Project.   Select the WPF User Control Library template and click to add this to the Solution.  You will see that a new project with a default UserControl file pair (xaml and xaml.vb files) will have been added.

    Show the Solution Name


   The next step is create the XAML Markup for the ListBox UserControl.  For the purposes of this article I'm going to begin by showing you all the XAML so that you can simply copy and paste it from this article into your project.  On the following pages I will walk through that markup and explain how it works in some detail.

 The default code in  the UserControl1.XAML file should be replaced with the following:

ScreenShot of the XAML Markup

    If you try and copy paste from the above screenshot, sadly you will be disappointed.   The reason that I have gone the screenshot route is not to make life difficult for you.  The problem is that for some reason I just can't get the Article Editor we use here on devCity to behave itself and display the XAML correctly.   So here's what I've done as an alternative (before you lose patience and give up, as I almost did!). 

   I have attached a text file to this article that contains all the above markup.   It is named UserControl1.txt.  (I won't bore you with the reasons why I haven't made it a XAML file).   All you need to do is download this small file, open it in Notepad or Visual Studio, copy all the content and paste it into the default UserControl1.XAML file in Solution Explorer, ensuring that you delete all the original default code from that file as you do so. 

  Once you have pasted it in, the IDE will instruct you to Reload the Designer, which you do by clicking on the message bar at the top of the IDE Designer pane.

  Because there is no data to display, you might be perplexed by what you see in the Design pane in the XAML file.   It hardly gives you any idea of what the finished product will look like, does it? 


ScreenShot of the Design Pane

  But don't worry, once we wire it up with some data and display a complete ListBox in our WinForm it will all fall into place.

  I probably should mention here that unless you have some experience of creating UIs in XAML directly in Visual Studio, then you will spend an awful lot of time tweaking it around, running it, tweaking it again, etc.  Creating the look of a UserControl like this is relatively much easier to do via Expression Blend (although in my experience you  still spend more time than you think you will messing about with it!)

(ii)  The Data

   It would be perfectly possible for me to create the data inside the UserControl itself.  But if you were planning to go to the trouble of devising and creating a DataTemplate it makes more sense (and is good practice) to separate the UI from the data source.  So what I've done is create a simple class that creates and returns a bundle of relevant data.

  The scenario for this particular project is that the listbox will contain images and text that represent different types of camping accommodation, so there will be data on tent, caravan, etc.   The class I've created for this data is called the Accommodation class.  Here is the code, which you will need to add to the WPFUser Control Library project in a separate class file, just as you would add a class file to any .NET project.


Code Copy

Class Accommodation
    Private m_name As String = String.Empty
    Private m_imagePath As String = String.Empty
    Private m_description1 As String = String.Empty
    Private m_cost As Decimal = 0

    Public Sub New(ByVal accnName As String, ByVal imagepath As String, ByVal MainInfo As String, ByVal Fee As Decimal)
        m_name = accnName
        m_imagePath = imagepath
        m_description1 = MainInfo
        m_cost = Fee
    End Sub
    Property Name() As String
            Name = m_name
        End Get
        Set(ByVal value As String)
            m_name = value
        End Set
    End Property

    Property ImagePath() As String
            ImagePath = m_imagePath
        End Get
        Set(ByVal value As String)
            m_imagePath = value
        End Set
    End Property

    Property MainDescription() As String
            MainDescription = m_description1
        End Get
        Set(ByVal value As String)
            m_description1 = value
        End Set
    End Property

    Property Cost() As Decimal
            Cost = m_cost
        End Get
        Set(ByVal value As Decimal)
            m_cost = value
        End Set
    End Property

    Public Overrides Function ToString() As String
        Return Me.Name
    End Function

    Public Shared Function GetAllCampers() As List(Of Accommodation)
        Dim AllCampers As New List(Of Accommodation)

        With AllCampers
            .Add(New Accommodation("Tent", "Images/TentOK.jpg", String.Format("FreeStanding tent - excludes Trailer Tents.{0}No size limit.", vbCrLf), 9))
            .Add(New Accommodation("Motorhome", "Images/Motorhome1.jpg", String.Format("Motorhome,{0}Gross Weight < 3 Tonne,{0}Length < 18 ft", vbCrLf), 18))
            .Add(New Accommodation("Small Caravan", "Images/Caravan2.jpg", "Towed Caravan, Single Axle, Max Length: 14 ft", 14))
            .Add(New Accommodation("Medium Caravan", "Images/Caravan.jpg", "Towed Caravan, Single Axle, Max Length: 18.5 ft", 15))
            .Add(New Accommodation("Large Caravan", "Images/DoubleAxle.jpg", String.Format("Towed Caravan,{0} Double or Single Axle,{0} Length: > 18.5 ft", vbCrLf), 17))

        End With

        Return AllCampers

    End Function



  I'm sure you'll be pleased to know that you can just copy and paste the above code if you want to.  As with the XAML, if you need a walkthrough explanation of what it does, then I will be covering that on the following pages.   For now, we just want to see that it all works!

  That said, it won't actually work until we add the image files to the project and also link the data source to the ListBox.  The images I have used are available as a downloadable zip file (or you can grab them from the demo solution which is also available with this article).  

  Create a new folder named "Images" in the WPF UserControl Library project in the Solution Explorer.   Right click on the project name and select Add > New Folder.  Once this folder appears in the Explorer's list Right click on the folder name and select Add > Existing Item.  You then navigate to where you have saved those images and select them.

  If you're new to all this you might be thrown by the fact that the images don't seem to be where you know you saved them.  This problem is easily resolved.  You need to ensure that "Objects of Type" in the Add Existing Item pane is showing "All Files" (the default is vb files).    You can use Shift-Click to add all the files in one go. 

   Once they are visible in Solution Explorer, select them all - again you can use Shift-Click.  Move your mouse down to the Properties Window, ensure that the Build Action  is set to "Resource", then click on the next item down (Copy To Output) and set this to "Copy If Newer".

  Finally, we need a little code behind.   This code in the xaml.vb file of the UserControl is all that's needed:


Code Copy
    Public Sub New()
    End Sub

    '  A List collection to hold the various accn info
    Dim AllCampers As List(Of Accommodation)

    Private Sub AccnListBox_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
        Me.DataContext = Accommodation.GetAllCampers
    End Sub

  Before moving on, run the Build > Rebuild Solution option from the main IDE menu.

3.  Back to the WinForms Project

   We have all we need now, so it's time to wire up the WPF and Windows Forms projects together. 

   Go back into the WinForms project, right click on the project name and select Add Reference.   Once you get the Add Reference screen up, select the Projects tab and click on the WPF UserControl Library1 and close the screen.

  If you check out your Toolbox, you should see that the UserControl is now available there, (although you may need to rebuild the project again first).  So you could drag the user control on to the form and drop it in the ElementHost. 

UserControl in Toolbox 

   However I am going to recommend that you don't do that, and instead suggest that you create the control in the code behind.

  To do this, go to the code window of Form1 and add the following code :

Code Copy
Imports WpfControlLibrary1

Class Form1

    '  Create an instance of the referenced WPF UserControl
    Dim CamperList As New UserControl1

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        ' Place the UserControl in the ElementHost
        Me.ElementHost1.Child = CamperList

    End Sub


   Rebuild it one more time and run it and if everything has gone to plan you will see the WPF UserControl sitting quite happily on that Windows Form.

   You can drag Windows Forms controls on to the form, although you can't have them overlapping with the WPF one.   Because this amalgamation of the two technologies is something of a fudge, it isn't perfect, but it definitely does open the door to much more interesting GUIs in Windows Forms, with far less effort, than was previously possible. 

   With this particular  WPF control on my mid range system, for example, I did notice a short delay while the UI was rendered.  If this is a problem that you want to deal with, then there are several workrounds and I have included some in the downloadable demo solution.

  The final Form looks like this:


WinForms ListBox and WPF ListBox on a Windows Form    

    On the next page I will look at the markup and the code and walk through the key things you need to know to create similar WPF UserControls and have them hosted in Windows Forms.



Analyzing the Markup and Code

  I haven't set out to make this a complete tutorial on XAML, but I thought it might be useful if I explained what the various elements do and also where you should make changes for your own purposes.

The WPF UserControl

   As the UserControl (and it's DataTemplate) is probably what you are least familiar with, let me start with that.

   Here's the screenshot of the code again:

XAML for UserControl

  1.  The Grid

 The first element is the Grid.   This is the overall container for the UserControl.  I have set its Background to Transparent simply for display purposes.  If you change this to a visible color it will have the effect of adding a further outside border to the control when it is used.

  The Grid is the default root element of UserControl and so is inserted for you.  As it happens, the Grid isn't needed in this particular control, but I've left it in anyway.  If you wanted to create a UserControl that contained more controls than just the DockPanel/ListBox combination then the Grid would become useful again.

2.   The DockPanel

  The DockPanel is the real container for the ListBox.  By setting its Background to Blue and setting a Margin value the markup creates that light blue border around the whole thing. 

3.   DockPanel.Resources

  Resources are a key feature of WPF.  When you create a Resource, you are making a set of reusable markup.  The "scope" of the Resource is controlled by where you place it.  In this case the Resource will be accessible by any control that resides inside this DockPanel.

  Clearly then it follows that if you chose to move the Resource further up the tree - to the Grid, the UserControl or even to the whole Application - then you widen the accessibility of the resource.  And conversely if you moved it down the tree - to make it a Resource of an individual control that is contained inside the DockPanel, then you reduce its accessibility or scope.

4.  DataTemplate

  This is a great feature of WPF!  In non-technical terms a DataTemplate is a set of visual blocks put together in the layout that you want and then saved for later re-use by means of the Key name you assign to it. 

  In our example here, the visual blocks include the image at the left side and the textblocks at the right, plus some other stuff.

  Once you have created the template, it can be applied to controls in a similar sort of way that you can use CSS with HTML. 

  In the graphic below I've shown a breakdown of how this DataTemplate has been put together.


Breakdown of the DataTemplate

 a.  Border.

The outermost element is a Border.  I've given this a BorderBrush color of LightSteelBlue.  Again you can change this.   Rather strangely to my mind a Border is more than just a border - it's really a border and an interior, and the interior can be a different color from the border itself.

   So with that in mind, you'll realise that it's possible to set the Background color also.   Often this will be  a solid color, although in this case I've gone one step further and created a gradient effect. 

b.  LinearGradientBrush

Without going too deeply into this kind of brush, you need to know that it consists of a Start point, an End point and a number of GradientStops. 

The GradientStops represent points along the line (it is "linear", remember) where a new color is placed.  WPF calculates how to change one color to another in an even linear fashion between these stops.

  The sample here uses Hex values because I was able to pick the exact shades I wanted in Expression Blend's color picker.  Alternatively you can manually type in values and keep tweaking them until you're happy - sounds painful I know, but that's what I used to do.   Or the much easier approach is to type in the names of known colors. 

The Start and End points decree where the gradient starts and ends - which may sound obvious but in fact is quite a sophisticated attribute.  By changing these two points you can substantially alter the look of gradient and change it from the default of diagonal to horizontal or vertical or in fact any angle in between. 

The default setting is (0,0) for the StartPoint - which represents the top left corner - and (1,1), the bottom right.  My advice is play around with the settings and see what effects you get, until you find one you like.  Or use Expression Blend, in which case the gradient world is almost unlimited and very, very easy.

c.  Outer Stack Panel

 Because we need to have the image and two lots of text all bundled together somehow and neatly lined up, we have to put them all in the same container.  (There will be more sub-containers inside this one).

  There is actually another reason why we need this StackPanel.  Something you have to get used to with WPF is learning which controls will hold only one child and which will hold many.  Because the Border will only hold one, and we need to stuff several separate things in there, we solve this dilemma by making a StackPanel the single child -  and that StackPanel can then hold several children of its own.

  The StackPanel has its Orientation property set to Horizontal,  meaning that all its children will be placed side by side from left to right.  The Margin property of 3 is there to put a bit of space between this StackPanel and the Border.

d.  Image

   Image placement is straightforward enough.  As it is the first child of the horizontally oriented StackPanel it will automatically be placed at the left hand edge of the panel.

  The Source property however is a little more technical.  As you will see when I deal with the DataContext later, this DataTemplate is (not surprisingly) bound to a data source.  Because this is all being built for a specific purpose (to list Accommodation class objects in a ListBox) we obviously know in advance that the data source will have an image and we know that the Property that will hold that image is the ImagePath property.

  So the markup of:

Binding Path=ImagePath

  binds the ImagePath property to the Image itself.

  In other words, each item in that list is an instance of the Accommodation Class and the path and file name assigned to each instance's ImagePath property will be used to ensure that the correct image appears.

e.  Inner StackPanel

This is the StackPanel at the right hand side of the list item. The reason for adding in yet another StackPanel is that I  have two lots of text, one above the other, but each text item has different font properties and text color. 

  So in order to get two separate TextBlocks to sit on one top of the other I've employed a StackPanel, this time with Vertical Orientation. 

  If you refer back to the graphical breakdown above, you'll see how this fits in to the overall layout.

f.  Upper TextBlock

TextBlocks are WinForms Labels on Steroids!  You have tremendous control over their appearance and use.  As you can see, I have set several properties so that it has exactly the look I wanted.

  As with the Image, this control is bound to the data source; this time it will be hooked into the Name property.

g.  Lower TextBlock

Similar to its sibling above, this TextBlock is databound to the MainDescription property.  It has various properties set so that more text can be shown and also to make it obviously different from the Name that is shown in the TextBlock above it. 

Also note that because I know there will be more than one line of text to be displayed, the TextWrapping property is set to deal with this.

Another point to note is that I set a width property on this control.  Try deleting it and then run the project and you will see why.

  I hope there is enough detail above for you to see how you can create DataTemplates of your own.  So the remaining markup for this UserControl is in fact the ListBox itself.

5.  The ListBox

This ListBox has had a value assigned to its Name property and also it has been set to dock to the bottom of the containing DockPanel.  To be honest, these are both redundant in the example used in this article and are there because the original sample was more complex: The ListBox was referred to elsewhere in the code behind (and therefore a name is needed for a reference), and there were other sub-controls contained within the DockPanel (and therefore the docked position of each was important for the correct layout).

  For now just file away the thought that your UserControl can contain multiple sub-controls if that is what is needed.

  ItemTemplate is crucial for this example to work.  Remember back at the start I described that a DataTemplate has a Key that can be used to assign the template to an element?  Well, this is where it takes place.  The ItemTemplate property set here ensures that each ListItem in the ListBox is formatted using the DataTemplate named "AccnDataTemplate" that we just looked at in detail above.

 Another very important element in this ListBox is the ItemsSource.  Again this will fall more into place when I deal with the DataContext, but keep in mind that this property is what binds this ListBox to the Accommodation Class objects that it will display.


WPF UserControl - Code Behind

  The code was quite simple:

Code Copy
Class UserControl1
    Public Sub New()
    End Sub

    '  A List collection to hold the various accn info
    Dim AllCampers As List(Of Accommodation)

    Private Sub AccnListBox_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
        Me.DataContext = Accommodation.GetAllCampers
    End Sub

  A constructor to enable New instances to be created, a type safe List to hold the info we created in the Accommodation Class and a line in the Loaded event that assigns the specific Accommodation instances to the DataContext for this UserControl.

  Just to mention in passing: it isn't necessary to assign the DataContext to the UserControl.   There may be situations where you want to use more than one DataContext within a single UserControl, in which case you simply assign the DataContext further down the tree. 

  In this project, for example, you could assign it to the DockPanel and it will still run as expected.  The reason it works is that each control looks for its own DataContext.  So in this example, if the DockPanel did have its own DataContext, then that is what it would use.  Because there isn't one, it next looks to see if the Grid has one;  it doesn't either.  So it moves up the tree again until it finds the DataContext assigned to the complete UserControl.  Very powerful stuff when you stop and think about the possibilities.

Windows Forms Project - Form Code

  I think that this too is fairly straightforward:

Code Copy
Imports WpfControlLibrary1

Class Form1

    '  Create an instance of the referenced WPF UserControl
    Dim CamperList As New UserControl1

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        ' Place it in the ElementHost
        Me.ElementHost1.Child = CamperList

    End Sub


  The Imports statement is optional, but allows you to refer to UserControl1 in the abbreviated way. 

  The next main code  line creates an instance of the WPF UserControl.  The reason I have used this approach and not just dragged an instance from the Toolbox is that now I have a means of referring to this control in code if I should need to.   If I had used the designer and the toolbox I only have code access to the ElementHost.   In fact  the ElementHost's properties will appear in the Properties Window, but the UserControl will not.   This  makes perfect sense when you think about it; the Windows Form has no real knowledge of the UserControl, it only knows about the ElementHost.

  Finally in the Form's Load event this new instance of the UserControl is set as the Child of ElementHost1.  ElementHost, you will remember, is the control that allows this interop between Win32 and WPF, and acts as a container for WPF controls.  Thi syntax is different from the standard Windows Forms Add method which adds a new control to a container; in WPF containers have children or in some cases a single Child. 


  And that's pretty much all there is to it.   This ListBox is very basic, as WPF controls go.   It was tempting to create something much more complex and include various animations when an item is selected or realtive to mouse events, but I decided to keep it as simple as possible so that you can use the basics to build your own versions.

  I hope there is enough information here to help you to do that.  Feel free to post follow up questions on any aspect.  If the question (and answer) involve code or markup it is far better to post the question in the vbCity Forums.  The Comments boxes here don't have any kind of formatting. 

  If you don't get a swift answer from a forum post, please post a link to it as a comment - I get immediate notifications of all comments to the article so if I'm around I will try and check out your questions as soon as I can.