Article Options
Premium Sponsor
Premium Sponsor

 »  Home  »  .NET Framework  »  WinForms and WPF Interop - The Best of Both Worlds
 »  Home  »  .NET Framework  »  Framework 2.0  »  WinForms and WPF Interop - The Best of Both Worlds
 »  Home  »  .NET Framework  »  Framework 3.0  »  WinForms and WPF Interop - The Best of Both Worlds
 »  Home  »  .NET Intermediate  »  WinForms and WPF Interop - The Best of Both Worlds
 »  Home  »  Visual Studio 2008  »  WinForms and WPF Interop - The Best of Both Worlds
 »  Home  »  Windows Development  »  Interop  »  WinForms and WPF Interop - The Best of Both Worlds
 »  Home  »  Windows Development  »  Win Forms  »  WinForms and WPF Interop - The Best of Both Worlds
 »  Home  »  Windows Development  »  Windows Presentation Foundation  »  WinForms and WPF Interop - The Best of Both Worlds
WinForms and WPF Interop - The Best of Both Worlds
by Ged Mead | Published  07/12/2008 | .NET Framework Framework 2.0 Framework 3.0 .NET Intermediate Visual Studio 2008 Interop Win Forms Windows Presentation Foundation | Rating:
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.

 

View all articles by Ged Mead...
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

Public
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
        Get
            Name = m_name
        End Get
        Set(ByVal value As String)
            m_name = value
        End Set
    End Property

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

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

    Property Cost() As Decimal
        Get
            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

End
Class

 

  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()
        InitializeComponent()
    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

Public
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

End
Class

   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.

 

Sponsored Links