This came up as a Windows Forms question a short while ago. Someone wanted to create a data bound ComboBox and have a relevant tooltip appear whenever the user hovered any of the items in the list. The strings for the tooltips would be stored in the database along with all the other data.
I’ve seen the proposed WinForms way of creating this result and, although I‘m sure it works fine, it really did look like a very complicated way of solving a relatively simple request. My immediate reaction was to think that there is probably an easy way of producing this result in WPF and hosting the resulting ComboBox-With-Tooltip control inside a Windows Form.
As it turned out, this is easily achievable with a very small amount of XAML . I did have an annoying little problem at one stage and made a rather strange discovery which I’ll describe later. Here’s the end result of the ComboBox:
The above screenshot is from a WPF application, but the task requires for the ComboBox to be hosted in a Windows Forms application, so let’s deal with that first.
Setting Up the Windows Forms Project
Start a new Windows Forms Application. In Form1, add some WinForms controls of your choice. I have added three buttons. They have no functionality and I only include them to emphasise that you can have WinForms and WPF hosted content in the same Form.
If you are using VB 2008 Express Edition, you should find a tab named ‘WPF Interoperability’ in the Toolbox. Inside this tab there will be control named ElementHost.
This tab may not be available in other editions. In the Pro edition, for example, you will need to add the ElementHost to the toolbox in the usual way:
Drag this control from the Toolbox and drop it on to your Form.
The smart tag will invite you to select the hosted content (currently set to none). As you haven’t created the content yet, just click anywhere on the Form’s surface to close the smart tag dialog box.
Add a WPF UserControl
To add a WPF UserControl to the Windows Forms project, Select Project > Add New Item… from the IDE main menu.
In the Add New Item dialog which appears, select WPF from the categories list, then click on User Control (WPF). Assign a name of your choice to this new user control.
Adding this WPF User Control automatically causes some new References to be added to your project – most notably PresentationCore and PresentationFramework. These are core WPF namespaces. If you select the UserControl by double clicking on it in the Solution Explorer, you will see that the Toolbox has now changed and you are offered a list of WPF controls instead of the Windows Forms ones.
Create the UserControl Content
Resize the UserControl boundaries so that it measures 100 x 70. You can do this in the XAML pane:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="100" Height="70">
Or in the Properties pane. The screenshot shows the Height setting. The Width property is also available, although the WPF Properties pane doesn’t offer you the Size property you may be used to in Windows Forms:
Drag a WPF ComboBox on to the surface of the WPF UserControl. It will be given some default values, not all of which suit our purposes, so manually change the XAML for the ComboBox so that it looks like the following:
<ComboBox Height="23" HorizontalAlignment="Left"
Name="ComboPerson" VerticalAlignment="Top" Width="100" />
The UserControl should now look like this in the Design Pane:
Assign the UserControl to the Windows Forms ElementHost
Now that you have a UserControl built, you can assign this to the ElementHost previously added to the Windows Form.
Rebuild the project to ensure that the UserControl is fully built. Select the Windows Form and click on the ElementHost. Click on the smart tag arrowhead and you will see the ElementHost Tasks dialog.
Click on the dropdown arrow at the right hand side of the ‘Select Hosted Content’ combo and you will see that the new UserControl has now been added to the list.
Select this control, then click anywhere on the Form to close the smart tag dialog. You will probably see an exclamation mark icon inside the ElementHost, possibly with some additional text, depending on the size of the host. Although this may make you think there is a problem, the UserControl will in fact display OK at run time. If you want to test things so far you can hit F5. The WPF ComboBox will be visible and will drop down when its arrow is clicked Of course as we haven’t bound the data yet or finished the look of the ComboBox there will be nothing to see.
Add a Data Source to the Application
The demonstration data that will be displayed in the ComboBox is provided by a simple class named ‘Person’. The class has three properties – FullName, Status and Category – although we only use two of these in the ComboBox. The class contains a function which creates a List of Type Person. You can of course use any data source you want, as long as it has at least two properties defined – one to populate the ComboBox list and one to be used as the ToolTip. Here is the class definition I have used:
Public Class Person
Sub New(ByVal personname As String, ByVal personstatus As String, ByVal personsgroup As String)
Me.FullName = personname
Me.Status = personstatus
Me.Category = personsgroup
Private _name As String
Public Property FullName() As String
Set(ByVal value As String)
_name = value
Private _status As String
Public Property Status() As String
Set(ByVal value As String)
_status = value
Private _Category As String
Public Property Category() As String
Set(ByVal value As String)
_Category = value
Public Sub OnPropertyChanged(ByVal e As PropertyChangedEventArgs)
If Not PropertyChangedEvent Is Nothing Then
RaiseEvent PropertyChanged(Me, e)
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Public Shared Function GetPersons() As List(Of Person)
Dim GP As New List(Of Person)
GP.Add(New Person("Neil Birch", "Available", "My Friends"))
GP.Add(New Person("Joe Brown", "On Site", "Work Colleagues"))
GP.Add(New Person("Larry Blake", "Available", "VB City"))
GP.Add(New Person("Fran Mead", "At Work", "Family"))
GP.Add(New Person("Elaine Javan", "On Vacation", "Work Colleagues"))
GP.Add(New Person("Matt Higginbotham", "On Line", "VB City"))
GP.Add(New Person("Zoe Flint", "On Site", "Work Colleagues"))
I’m not going to look at this class in detail because it isn’t really part of the ‘ComboBox with ToolTips’ concept. As I’ve mentioned, it doesn’t matter how you get the data, so long as it has two public properties you can use.
The GetPersons function at the bottom of the code generates a List of 7 Person objects. This function is called from the code-behind of the WPF UserControl:
Partial Public Class ComboWithToolTips
Dim ComboData As New List(Of Person)
Private Sub ComboWithToolTips_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
ComboData = Person.GetPersons
Me.DataContext = ComboData
The code snippet above creates an empty List (Of Person). When the user control is loaded, it then accesses the GetPersons function in the Person Class and populates that ComboData List with the details of the seven Persons. The final code line sets the DataContext of the user control to that List. This automatically links all the data in that List to any control or element inside the user control that wishes to be bound to it. You will see this data binding in action next.