Your SlideShare is downloading. ×
Creating a combo box with individual tooltips for each item
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Creating a combo box with individual tooltips for each item

1,417
views

Published on


0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
1,417
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
7
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. 12/26/12 Creating a ComboBox with Individual ToolTips for each Item Should LastPass remember this passw ord? DevCity.NET - http://devcity.net Creating a ComboBox with Individual ToolTips for each Item http://devcity.net/Articles/391/1/article.aspx by Ged Mead Published on 2/24/2010 Ged Mead Ged Mead In this article, Ged Mead demonstrates an easy way to use WPF and Windows Forms Interop to create and use a ComboBox that (XTab) is a displays ToolTips for individual ComboBox items. 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 Interop with a WPF UserControl the days when it took two strong men to carry a mobile Introduction system. 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 Based in an idyllic lochside database along with all the other data. location in the West of Scotland, he is currently 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 involved in an ever-widening complicated way of solving a relatively simple request. My immediate reaction was to think that there is probably an easy way of range of VB.NET, WPF and producing this result in WPF and hosting the resulting ComboBox-With-Tooltip control inside a Windows Form. Silverlight development projects. Now working in a 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 consultancy environment, his made a rather strange discovery which I’ll describe later. Here’s the end result of the ComboBox: 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 The above screenshot is from a WPF application, but the task requires for the ComboBox to be hosted in a Windows Forms application, to tutor a number of free online so let’s deal with that first. courses for VB.NET developers. 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.devcity.net/PrintArticle.aspx?ArticleID=391 1/7
  • 2. 12/26/12 Creating a ComboBox with Individual ToolTips for each Item 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.devcity.net/PrintArticle.aspx?ArticleID=391 2/7
  • 3. 12/26/12 Creating a ComboBox with Individual ToolTips for each Item 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: <UserControl x:Class="ComboWithToolTips" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 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:devcity.net/PrintArticle.aspx?ArticleID=391 3/7
  • 4. 12/26/12 Creating a ComboBox with Individual ToolTips for each Item 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: Imports System.ComponentModel Imports System.Collections.ObjectModel Public Class Person Implements INotifyPropertyChanged Sub New(ByVal personname As String, ByVal personstatus As String, ByVal personsgroup As String) Me.FullName = personname Me.Status = personstatus Me.Category = personsgroup End Sub Private _name As String Public Property FullName() As String Get Return _name End Get Set(ByVal value As String) _name = value OnPropertyChanged(New PropertyChangedEventArgs("FullName")) End Set End Property Private _status As String Public Property Status() As String Get Return _status End Get Set(ByVal value As String) _status = value OnPropertyChanged(New PropertyChangedEventArgs("Status")) End Set End Property Private _Category As String Public Property Category() As String Get Return _Categorydevcity.net/PrintArticle.aspx?ArticleID=391 4/7
  • 5. 12/26/12 Creating a ComboBox with Individual ToolTips for each Item End Get Set(ByVal value As String) _Category = value OnPropertyChanged(New PropertyChangedEventArgs("Category")) End Set End Property Public Sub OnPropertyChanged(ByVal e As PropertyChangedEventArgs) If Not PropertyChangedEvent Is Nothing Then RaiseEvent PropertyChanged(Me, e) End If End Sub 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")) Return GP End Function End Class 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 End Sub End Class 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. DataBinding and DataTemplate DataBinding and DataTemplate Now that we have a data source – the DataContext – the ComboBox can be bound to it. The syntax is very simple and requires only that you point the ItemsSource property of the ComboBox to the Binding. ItemsSource="{Binding}" The word ‘Binding’ is placed inside the curly braces to signify that this is a markup extension in XAML. If you were to run the project now you would actually have some bound data. However, it isn’t yet in a format that is useful to users: This will be an issue you are familiar with if you have done data binding in the past. At this stage, all that is capable of being displayed is the ToString representation of the Person instances. Without more detailed instructions, the application has no way of knowing which specific part(s) of the data should be listed in the ComboBox. The easiest way of doing this is to create a DataTemplate for the ComboBoxItems. You make this as plain or fancy as you like. For demo purposes I have just placed the string representation of the FullName property in a TextBlock and then surrounded this with a Border. Here is the XAML used to create the first part of the DataTemplate, which I’ve placed in the Resources collection of the Grid: <Grid.Resources> <DataTemplate x:Key="StatusToolTip"> <TextBlock Text="{Binding Path=FullName}"> </TextBlock> </DataTemplate> </Grid.Resources> The TextBlock is bound to the FullName property of those Person instances.devcity.net/PrintArticle.aspx?ArticleID=391 5/7
  • 6. 12/26/12 Creating a ComboBox with Individual ToolTips for each Item The next step is to point the ItemTemplate property of the ComboBox to this DataTemplate, which has a Key named StatusToolTip. The final version of the markup for the ComboBox looks like this: <ComboBox Height="23" HorizontalAlignment="Left" Name="ComboPerson" VerticalAlignment="Top" Width="100" ItemsSource="{Binding}" ItemTemplate="{StaticResource StatusToolTip}"/> Running the application again will produce the list of names in the ComboBox: All that remains to do is to add a ToolTip to the TextBlock: <TextBlock.ToolTip> <Border BorderBrush="Navy" BorderThickness="2" CornerRadius="4"> <TextBlock Text="{Binding Path=Status}" Margin="3"/> </Border> </TextBlock.ToolTip> This time the Binding Path points to the Status property of those Person instances. Now when the application runs and the mouse hovers over the name of a Person, the ToolTip showing the status of that Person will display: Here is the complete XAML for the UserControl: <UserControl x:Class="ComboWithToolTips" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="100" Height="70"> <Grid Height="70"> <Grid.Resources> <DataTemplate x:Key="StatusToolTip"> <TextBlock Text="{Binding Path=FullName}"> <TextBlock.ToolTip> <Border BorderBrush="Navy" BorderThickness="2" CornerRadius="4"> <TextBlock Text="{Binding Path=Status}" Margin="3"/> </Border> </TextBlock.ToolTip> </TextBlock> </DataTemplate> </Grid.Resources> <ComboBox Height="23" HorizontalAlignment="Left" Name="ComboPerson" VerticalAlignment="Top" Width="100" ItemsSource="{Binding}" ItemTemplate="{StaticResource StatusToolTip}"/> </Grid> </UserControl> The glitch that caught me out was that I thought it would be logical to use a ComboBoxItem in the DataTemplate and have a TextBlock inside that ComboBoxItem. However when I did that, the names and the ToolTips would appear, but the ComboBox wouldn’t collapse if the user clicked on the actual name. The mouse had to be clicked on a blank area anywhere to the right of the name. This obviously would be confusing and unacceptable to most users, so I had to get some outside help on this - Thanks, Sirga and Jie Wang for showing me the workaround. Surprisingly, it turns out that if you include a ComboBoxItem in the DataTemplate then it stops working correctly in this kind of scenario. The markup above will cause the ToolTip to display whenever the mouse hovers over a name (that is specifically the string representation of the name, but not any of the blank space to the right on the line). If you want the ToolTip to appear, no matter where on the ComboBox line the mouse hovers, then this alternative markup will produce the desired result: <Grid Height="70"> <Grid.Resources> <DataTemplate x:Key="StatusToolTip">devcity.net/PrintArticle.aspx?ArticleID=391 6/7
  • 7. 12/26/12 Creating a ComboBox with Individual ToolTips for each Item <TextBlock Text="{Binding Path=FullName}" /> </DataTemplate> <Border x:Key="ToolTipContent" BorderBrush="Navy" BorderThickness="2" CornerRadius="4"> <TextBlock Text="{Binding Path=Status}" Margin="3"/> </Border> </Grid.Resources> <ComboBox Height="23" HorizontalAlignment="Left" Name="ComboPerson" VerticalAlignment="Top" Width="100" ItemsSource="{Binding}" ItemTemplate="{StaticResource StatusToolTip}"> <ComboBox.ItemContainerStyle> <Style TargetType="{x:Type ComboBoxItem}"> <Setter Property="ToolTip" Value="{StaticResource ToolTipContent}" /> </Style> </ComboBox.ItemContainerStyle> </ComboBox> </Grid> As the highlighting shows, The ToolTip has been pulled out of the DataTemplate. The Border and TextBlock that comprise the ToolTip visuals are stored as a Resource and given a Key. This keyed resource is then used directly in the ComboBox’s ItemContainerStyle to style the individual ComboBoxItems. Apart from my little tussle with the ComboBoxItem template, this was a very easy solution overall. A few lines of XAML together with the use of an ElementHost in the Windows Form and the job’s done. Another good example of how WPF and Windows Forms Interop can help achieve GUI enhancements more easily than trying to hack or extend WinForms controls.devcity.net/PrintArticle.aspx?ArticleID=391 7/7