Let us start with creating very simple combo box. In the following example,
- In stage 1: Let us use combo box items as inline with the control.
- In stage 2: Let us then replace these by placing them into window’s resource section.
- In stage 3: Let us bind to C# objects.
- In stage 4: Let us use DataContext
- In stage 5: Let us use DataTemplate.
- In stage 6: Let us Sort and Filter.
- In stage 7: Let us use Value Converters
- In Stage 8: Let us use XmlDataProvider to load combo bos items list.
- In stage 9: Let us use Triggers
- In stage 10: Let us use Styles
Note, when creating combo box in WPF,
we do not need to do it following each of these steps, however these steps are examined
here to demonstrate implementation of a combo box in any WPF application, starting
with the simplest way and then moving towards more advance implementation.
Stage 1
ComboBox items are inline.
ComboBox items are inline.
<Window x:Class="SimpleComboBoxExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350" Width="525">
<Grid>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="161,49,0,0" Name="comboBox1"
VerticalAlignment="Top"
Width="156"
>
<ComboBox.Items>
<ComboBoxItem>Payroll</ComboBoxItem>
<ComboBoxItem>Trading</ComboBoxItem>
<ComboBoxItem>Sales</ComboBoxItem>
</ComboBox.Items>
</ComboBox>
</Grid>
</Window>
Stage 2
ComboBox items are in window’s resource .
ComboBox items are in window’s resource .
<Window x:Class="SimpleComboBoxExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow"
Height="350" Width="525">
<Window.Resources>
<col:ArrayList x:Key="myArray">
<sys:String>Payroll</sys:String>
<sys:String>Trading</sys:String>
<sys:String>Sales</sys:String>
</col:ArrayList>
</Window.Resources>
<Grid>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="161,49,0,0" Name="comboBox1"
VerticalAlignment="Top"
Width="156"
ItemsSource="{StaticResource myArray}"
>
</ComboBox>
</Grid>
</Window>
Stage 3
ComboBox items are implemented using list of department objects.
ComboBox items are implemented using list of department objects.
Instead of list of static items
(as in the above examples), let us implement combo box items bound from a list
of C# objects. In this case let’s first create a class call “Department” and another
class called “Departments” with a public property called “AllDepartments” which
we’ll bind to ItemsSource property of the ComboBox.
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
namespace
SimpleComboBoxExample
{
public class Departments
{
public List<Department>
AllDepartments
{
get
{
return
new List<Department>()
{
new
Department{Name = "Payroll",
NumEmp=100, Location="New York"},
new
Department {Name="Trading",
NumEmp=500, Location="New York"},
new
Department{Name = "Sales",
NumEmp=1000, Location="Chicago"},
};
}
}
}
public class Department
{
private
string _name;
private
int _numEmp;
private
string _location;
public string Location
{
get
{ return _location; }
set
{ _location = value; }
}
public int NumEmp
{
get
{ return _numEmp; }
set
{ _numEmp = value; }
}
public string Name
{
get
{ return _name; }
set
{ _name = value; }
}
}
}
In XAML, we can create an instance of “Departments”
class within the resource section. Then we add xaml code to bind the “AllDepartments”
property to ItemsSource of ComboBox. Notice also the “DisplayMemberPath”
property is important, otherwise display member of combo box item will not show
properly.
<Window x:Class="SimpleComboBoxExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SimpleComboBoxExample"
Title="MainWindow"
Height="350" Width="525">
<Window.Resources>
<local:Departments x:Key="depts">
</local:Departments>
</Window.Resources>
<Grid>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="161,49,0,0" Name="comboBox1"
VerticalAlignment="Top"
Width="156"
ItemsSource="{Binding Source={StaticResource ResourceKey=depts}, Path=AllDepartments}"
DisplayMemberPath="Name"
>
</ComboBox>
</Grid>
</Window>
Stage 4
ComboBox items are implemented using DataContext
ComboBox items are implemented using DataContext
Instead of creating instance of “Departments”
class within resources section, let us create an instance of “Departments”
class with code behind (i.e. in MainWindow.xaml.cs class) and set to the
DataContext of this window.
public partial class MainWindow : Window
{
public
MainWindow()
{
InitializeComponent();
var
depts = new Departments();
this.DataContext
= depts.AllDepartments;
}
}
Now let us use this data context
for binding in xaml code.
<Window x:Class="SimpleComboBoxExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SimpleComboBoxExample"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="161,49,0,0" Name="comboBox1"
VerticalAlignment="Top"
Width="156"
ItemsSource="{Binding}"
DisplayMemberPath="Name"
>
</ComboBox>
</Grid>
</Window>
Stage 5
ComboBox items are displayed using DataTemplate
ComboBox's ItemSource is still bound to DataContext (as in method 4 above). Let us now use DataTemplate to format the display of each item in the combo box's drop down.
Inline DataTemplate:
<ComboBox Height="23" HorizontalAlignment="Left" Margin="161,49,0,0" Name="comboBox1"
VerticalAlignment="Top"
Width="156"
ItemsSource="{Binding}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=Name}"></TextBlock>
<TextBlock Grid.Column="1" Text="{Binding Path=NumEmp}"></TextBlock>
<TextBlock Grid.Column="2" Text="{Binding Path=Location}"></TextBlock>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
DataTemplate as resource:
In order to add filter we just add the following line of code as well and see how only items with employee count less than 800 is being displayed on the drop down list of the combo box.
Stage 8:
Using XmlDataProvider for the ComboBox items list
<Window.Resources>
<DataTemplate x:Key="myTemplt">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=Name}"></TextBlock>
<TextBlock Grid.Column="1" Text="{Binding Path=NumEmp}"></TextBlock>
<TextBlock Grid.Column="2" Text="{Binding Path=Location}"></TextBlock>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="161,49,0,0" Name="comboBox1"
VerticalAlignment="Top"
Width="156"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource myTemplt}"
>
</ComboBox>
</Grid>
Stage 6
Sort and Filter added for ComboBox items drop down list.
Sort and Filter added for ComboBox items drop down list.
XAML stays as is as in the above example
(method 5). We just get the view for bound collection and apply sort and filter
to that. For this we add the following code (in bold) in the code behind class.
public
MainWindow()
{
InitializeComponent();
var
depts = new Departments();
this.DataContext
= depts.AllDepartments;
ListCollectionView
view = (ListCollectionView) CollectionViewSource.GetDefaultView(this.DataContext);
view.SortDescriptions.Add(new SortDescription("Location", ListSortDirection.Ascending));
}
See how the dropdown items are
now sorted by “Location” column in alphabetically ascending order.
In order to add filter we just add the following line of code as well and see how only items with employee count less than 800 is being displayed on the drop down list of the combo box.
view.Filter = ((object
item) => { return ((Department)item).NumEmp
< 800; });
Stage 7
Value Converter added to ComboBox items to display different foreground color based on employee count.
Value Converter added to ComboBox items to display different foreground color based on employee count.
(First of all let us reset the filter from previous
example so all items in the drop down) //view.Filter = ((object
item) => { return ((Department)item).NumEmp < 800; }); // commented out
for now
In order to demonstrate the use data value
converter, let us use value converter where based on employee count, which is
of type integer, let us return value for property “Foreground” which is of
System.Data.Media.Color type. So let us add new class “EmpCountConverter.cs”
which implements IValueConverter interface.
namespace
SimpleComboBoxExample
{
public class EmpCountConverter
: IValueConverter
{
public object Convert(object
value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return
int.Parse(value.ToString()) > 800 ? new SolidColorBrush(Colors.Red) : new SolidColorBrush(Colors.SaddleBrown);
}
public object ConvertBack(object
value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw
new NotImplementedException();
}
}
}
Inside XAML code, let us update the to hook up this
value converter as follows.
<Window.Resources>
<local:EmpCountConverter x:Key="empCountConvert">
</local:EmpCountConverter>
<DataTemplate x:Key="myTemplt">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=Name}"></TextBlock>
<TextBlock Grid.Column="1"
Text="{Binding Path=NumEmp}"
Foreground="{Binding Path=NumEmp, Converter={StaticResource
empCountConvert}}"
></TextBlock>
<TextBlock Grid.Column="2" Text="{Binding Path=Location}"></TextBlock>
</Grid>
</DataTemplate>
</Window.Resources>
Stage 8:
Using XmlDataProvider for the ComboBox items list
Let us first create an XML file called
Departments.xml as below:
<Departments>
<Department Name="Payroll" NumEmp="100" Location="New York"></Department>
<Department Name="Trading" NumEmp="500" Location="New York"></Department>
<Department Name="Sales" NumEmp="1000" Location="Chicago"></Department>
</Departments>
In our XAML code let us start fresh and add
XmlDataProvider in resource section and indicate that as the DataContext for
the Grid which contains the ComboBox. IntemsSource will be the “{Binding}” like
before, however notice, the DisplayMemberPath=@Name. Here @ sign is in front "Name" because “Name” is an attribute in the “Department” node in
the Departments.xml file.
<Window x:Class="SimpleXmlDataSourceExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<XmlDataProvider x:Key="depts" Source="Departments.xml"
XPath="/Departments/Department">
</XmlDataProvider>
</Window.Resources>
<Grid DataContext="{StaticResource depts}">
<ComboBox Height="23" HorizontalAlignment="Left" Margin="161,49,0,0"
Name="comboBox1"
VerticalAlignment="Top"
Width="156"
ItemsSource="{Binding}"
DisplayMemberPath="@Name"
>
</ComboBox>
</Grid>
</Window>
When we run this application, we’ll see the
following comb box being displayed like before (in stage 1).
Now let us put back DataTemplate, ValueConverter and Sorting
back this example like before while we are using XmlDataProvider.
<Window x:Class="SimpleXmlDataSourceExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SimpleXmlDataSourceExample"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<XmlDataProvider x:Key="depts" Source="Departments.xml" XPath="/Departments/Department">
</XmlDataProvider>
<local:EmpCountConverter x:Key="empCountConvert">
</local:EmpCountConverter>
<DataTemplate x:Key="myTemplt">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding XPath=@Name}"></TextBlock>
<TextBlock Grid.Column="1"
Text="{Binding XPath=@NumEmp}"
Foreground="{Binding XPath=@NumEmp, Converter={StaticResource empCountConvert}}"
></TextBlock>
<TextBlock Grid.Column="2" Text="{Binding XPath=@Location}"></TextBlock>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid Name="myGrid" DataContext="{StaticResource depts}">
<ComboBox Height="23" HorizontalAlignment="Left" Margin="161,49,0,0" Name="comboBox1"
VerticalAlignment="Top"
Width="156"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource myTemplt}"
>
</ComboBox>
</Grid>
</Window>
Notice in the code behind the sort functionality needs to be
added after the xml data is loaded as combo box itels list. Therefore the
sorting code (via CollectionViewSource) has been moved under event handler
method for combo box’s “Loaded” event.
public partial class MainWindow : Window
{
public
MainWindow()
{
InitializeComponent();
this.comboBox1.Loaded
+= new RoutedEventHandler(ComboBoxLoaded);
}
private
void ComboBoxLoaded(object
sender, RoutedEventArgs e)
{
ListCollectionView
view =
(ListCollectionView)CollectionViewSource.GetDefaultView(
((XmlDataProvider)this.myGrid.DataContext).Data);
view.SortDescriptions.Add(new SortDescription("Location",
ListSortDirection.Ascending));
}
}
Therefore, when we run it will look like the following screen shot.
Hi, Nice code in wpf application with combobox tool.
ReplyDelete-Hari
Theosoft
Thanks.
ReplyDelete