Introduction
This is the second part of my article on customizing controls in WPF. If you have not read the first part and are not familiar with Styles you may want to read part I http://windowsclient.net/blogs/airborneengineer/archive/2009/06/09/wpf-styles-and-templates-part-i.aspx . In this article we will focus on ComboBoxes. We make a ComboBox that is capable of displaying an Image and Text and also show how binding works when storing classes in ComboBoxes.
Customizing a ComboBox
In order to get an image and text to display in the ComboBox, we need to add a new Style to the ComboBox which will modify the ContentPresenter having it display the controls that we want it to display in this case a StackPanel with an Image and TextBlock inside. As described in the previous article, I created a copy of the existing template and began breaking it up into pieces. The first thing I did was located the ContentPresenter located in the ComboBox main template:
<ContentPresenter IsHitTestVisible="false"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"/>
This I broke up into a DataTemplate more to make it easier to read than anything else. The class that I will be binding to the ComboBox will have two properties that we are interested in, Image (string property which contains the path of the image) and Text (string property which contains the words that will be displayed below the image). The width and max properties are specified here to keep everything the same size so that when the item changes the control is not resizing itself.
<DataTemplate x:Key="DisplayImageWithText">
<Border Name="Border" Padding="2"
SnapsToDevicePixels="true" MaxHeight="65">
<StackPanel>
<Image Source="{Binding Image}" Stretch="Fill"
Width="42" Height="Auto"/>
<TextBlock Text="{Binding Text}"
TextAlignment="Center"
TextWrapping="Wrap" MaxWidth="50"/>
</StackPanel>
</Border>
</DataTemplate>
This DataTemplate must then be referenced as a resource in the ContentPresenter:
<ContentPresenter Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{StaticResource DisplayImageWithText}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Margin="3,3,23,3"
VerticalAlignment="Center"
HorizontalAlignment="Left"/>
This takes care of the ComboBox when it is closed. Next we will work on the ComboBoxItem; this is when the drop down is clicked. This is similar to the ComboBox itself but not exact. I use a StackPanel again and specify the size to ensure that all of the images are the same size.
<ControlTemplate TargetType="ComboBoxItem">
<Border Name="Border" Padding="2"
SnapsToDevicePixels="true">
<StackPanel>
<Image Source="{Binding Image}"
Stretch="Fill"
Width="60" Height="60"/>
<ContentPresenter HorizontalAlignment="Center"/>
</StackPanel>
</Border>
That completes the customization of the resource. In the source code attached to this article, I have the complete ResourceDictionary that has all of the colors and sections broken out for easy customization.
Binding Data
For this example I have two XML files with data but I chose to use two different methods to access the data to show multiple ways to accomplish the same thing. If we look at the window resources we see the following:
<Window.Resources>
<XmlDataProvider x:Key="StateData"
Source="Data\StateCodes.xml"
XPath="//State" />
<ObjectDataProvider x:Key="DropDownProvider"
ObjectType="{x:Type data:CodeItemLoader}"
MethodName="GetSampleData" />
</Window.Resources>
These are the data sources for our application. The first is just a XML data reader that will open the file StateCodes.xml located in the Data directory and return all of the nodes State.
The second data source is a data provider that creates a instance of the CodeItemLoader class and executes the method GetSampleData passing no parameters. If you open the class you will see the class opens an XML file called SampleData.xml and uses LINQ to parse the data and return a generic list of the class CodeItem.
Our first ComboBox is a general drop down list for states. The desire is the drop down list should show the entire state name while the ComboBox itself should only show the abbreviation. The data in the XML looks like this:
<State name="ALABAMA" id="AL" />
Since the data provider is returning back the State node, this is how we would code the ComboBox:
<ComboBox Name="cboStates"
ItemsSource="{Binding Source={StaticResource StateData}}"
SelectedValuePath="@id"
DisplayMemberPath="@name" />
If we want to display the code selected by the ComboBox, we do not need to write a bunch of code, all we need to do is use binding. The following TextBox is bound to the ComboBox:
<TextBox Text="{Binding ElementName=cboStates, Path=SelectedValue}" />
The second ComboBox be bound to our second data source and will have the Style that we created earlier to display an image with text. We add the MaxWidth and MaxHeight in here to control the drop down. You could make it longer or taller adjusting these values.
<ComboBox x:Name="cboImageSample"
ItemsSource="{Binding Source={StaticResource DropDownProvider}}"
Style="{StaticResource ImageComboBox}"
MaxWidth="350" MaxHeight="70" />
Since we have a list of CodeItem classes in our ComboBox, we can bind to each of the properties of the class as show below:
<TextBox
Text="{Binding ElementName=cboImageSample, Path=SelectedItem}" />
<TextBox
Text="{Binding ElementName=cboImageSample, Path=SelectedItem.Code}" />
<TextBox
Text="{Binding ElementName=cboImageSample, Path=SelectedItem.Text}" />
Summary
Microsoft’s approach to controls has really made WPF a powerful tool allowing you to do almost anything you want to a control. Use of binding allows code to be much leaner and easier to maintain.
Technorati Tags:
ComboBox,
Customization,
Introduction,
article,
archive,
ComboBoxes,
Image,
Text,
classes,
Style,
ContentPresenter,
StackPanel,
TextBlock,
template,
IsHitTestVisible,
SnapsToDevicePixels,
HorizontalAlignment,
HorizontalContentAlignment,
Margin,
VerticalAlignment,
VerticalContentAlignment,
Content,
SelectionBoxItem,
ContentTemplate,
SelectionBoxItemTemplate,
ContentTemplateSelector,
ItemTemplateSelector,
DataTemplate,
path,
words,
width,
size,
item,
DisplayImageWithText,
Border,
Name,
Source,
Stretch,
Fill,
Auto,
TextAlignment,
Center,
Wrap,
MaxWidth,
resource,
ContentSite,
False,
StaticResource,
Left,
ComboBoxItem,
images,
ControlTemplate,
TargetType,
code,
ResourceDictionary,
Data,
example,
files,
Resources,
XmlDataProvider,
StateData,
StateCodes,
XPath,
State,
ObjectDataProvider,
DropDownProvider,
ObjectType,
Type,
CodeItemLoader,
MethodName,
GetSampleData,
reader,
directory,
provider,
instance,
method,
SampleData,
LINQ,
CodeItem,
abbreviation,
ALABAMA,
node,
ItemsSource,
SelectedValuePath,
DisplayMemberPath,
TextBox,
ElementName,
SelectedValue,
ImageComboBox,
SelectedItem,
Summary,
Microsoft,
tool,
Combo,
Demo,
Templates,
pieces,
methods,
parameters,
easier,
itself,
cboStates,
cboImageSample
Windows Live Tags:
ComboBox,
Customization,
Introduction,
article,
archive,
ComboBoxes,
Image,
Text,
classes,
Style,
ContentPresenter,
StackPanel,
TextBlock,
template,
IsHitTestVisible,
SnapsToDevicePixels,
HorizontalAlignment,
HorizontalContentAlignment,
Margin,
VerticalAlignment,
VerticalContentAlignment,
Content,
SelectionBoxItem,
ContentTemplate,
SelectionBoxItemTemplate,
ContentTemplateSelector,
ItemTemplateSelector,
DataTemplate,
path,
words,
width,
size,
item,
DisplayImageWithText,
Border,
Name,
Source,
Stretch,
Fill,
Auto,
TextAlignment,
Center,
Wrap,
MaxWidth,
resource,
ContentSite,
False,
StaticResource,
Left,
ComboBoxItem,
images,
ControlTemplate,
TargetType,
code,
ResourceDictionary,
Data,
example,
files,
Resources,
XmlDataProvider,
StateData,
StateCodes,
XPath,
State,
ObjectDataProvider,
DropDownProvider,
ObjectType,
Type,
CodeItemLoader,
MethodName,
GetSampleData,
reader,
directory,
provider,
instance,
method,
SampleData,
LINQ,
CodeItem,
abbreviation,
ALABAMA,
node,
ItemsSource,
SelectedValuePath,
DisplayMemberPath,
TextBox,
ElementName,
SelectedValue,
ImageComboBox,
SelectedItem,
Summary,
Microsoft,
tool,
Combo,
Demo,
Templates,
pieces,
methods,
parameters,
easier,
itself,
cboStates,
cboImageSample
WordPress Tags:
ComboBox,
Customization,
Introduction,
article,
archive,
ComboBoxes,
Image,
Text,
classes,
Style,
ContentPresenter,
StackPanel,
TextBlock,
template,
IsHitTestVisible,
SnapsToDevicePixels,
HorizontalAlignment,
HorizontalContentAlignment,
Margin,
VerticalAlignment,
VerticalContentAlignment,
Content,
SelectionBoxItem,
ContentTemplate,
SelectionBoxItemTemplate,
ContentTemplateSelector,
ItemTemplateSelector,
DataTemplate,
path,
words,
width,
size,
item,
DisplayImageWithText,
Border,
Name,
Source,
Stretch,
Fill,
Auto,
TextAlignment,
Center,
Wrap,
MaxWidth,
resource,
ContentSite,
False,
StaticResource,
Left,
ComboBoxItem,
images,
ControlTemplate,
TargetType,
code,
ResourceDictionary,
Data,
example,
files,
Resources,
XmlDataProvider,
StateData,
StateCodes,
XPath,
State,
ObjectDataProvider,
DropDownProvider,
ObjectType,
Type,
CodeItemLoader,
MethodName,
GetSampleData,
reader,
directory,
provider,
instance,
method,
SampleData,
LINQ,
CodeItem,
abbreviation,
ALABAMA,
node,
ItemsSource,
SelectedValuePath,
DisplayMemberPath,
TextBox,
ElementName,
SelectedValue,
ImageComboBox,
SelectedItem,
Summary,
Microsoft,
tool,
Combo,
Demo,
Templates,
pieces,
methods,
parameters,
easier,
itself,
cboStates,
cboImageSample