What¡¯s the best approach to build a combo box with a multiple column list view as dropdown
TIA
Kent
What¡¯s the best approach to build a combo box with a multiple column list view as dropdown
TIA
Kent
Interesting question...I guess the simplest method would be to set the ItemsTemplate to be a StackPanel with a horizontal orientation. Here is a sample...it's a little rough though...the big problem is getting the columns to align. I'll leave that up to you to solve.
Here's the XAML.
1 <Window x:Class="MultiColumnListBox.Window1"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 Title="MultiColumnListBox" Height="300" Width="300"
5 >
6 <Window.Resources>
7 <DataTemplate x:Key="ComboTemplate">
8 <StackPanel Orientation="Horizontal">
9 <Border BorderThickness="1" BorderBrush="Black">
10 <TextBlock Text="{Binding Path=FirstName}"/>
11 </Border>
12 <Border BorderThickness="1" BorderBrush="Black">
13 <TextBlock Text="{Binding Path=LastName}"/>
14 </Border>
15 </StackPanel>
16 </DataTemplate>
17 </Window.Resources>
18 <StackPanel>
19 <ComboBox x:Name="cmbPeople" ItemTemplate="{StaticResource ComboTemplate}" />
20 </StackPanel>
21 </Window>
Here's the code-behind.
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Windows;
5 using System.Windows.Controls;
6 using System.Windows.Data;
7 using System.Windows.Documents;
8 using System.Windows.Input;
9 using System.Windows.Media;
10 using System.Windows.Media.Imaging;
11 using System.Windows.Shapes;
12
13
14 namespace MultiColumnListBox
15 {
16 /// <summary>
17 /// Interaction logic for Window1.xaml
18 /// </summary>
19
20 public partial class Window1 : System.Windows.Window
21 {
22
23 public Window1()
24 {
25 InitializeComponent();
26 List<Person>people=new List<Person>();
27 people.Add(new Person("Mike","Brown"));
28 people.Add(new Person("Josh","Smith"));
29 people.Add(new Person("Lee", "D"));
30 cmbPeople.ItemsSource=people;
31 }
32 private class Person
33 {
34 public Person(String firstName, String lastName)
35 {
36 this.firstName=firstName;
37 this.lastName=lastName;
38 }
39 private String firstName;
40 public String FirstName
41 {
42 get { return firstName; }
43 set { firstName = value; }
44 }
45 private String lastName;
46
47 public String LastName
48 {
49 get { return lastName; }
50 set { lastName = value; }
51 }
52
53 }
54 }
55 }
cheesenhomer wrote:
Hi Kent,
the cheapest approach (maybe the best ) is the DataTemplate below. Use this as ItemTemplate of your ComboBox
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Width="50" Text="{Binding Path=Property1}" />
<TextBlock Width="50" Text="{Binding Path=Property2}" />
</StackPanel>
</DataTemplate>
Uas as many TextBlock, as many columns you need. The column-width is determined by the width of the TextBlocks.
Greetings
cheesenhomer
Jinx.
Thanks, Cheesenhomer.
I agree this approach is an inexpensive one, it doesn't support sorting like ListView though. ;-)
But I think I got the idea.
Thanks
Kent
I took the ComboBox ControlTemplate sample and modified it to use a ListView within the popup. No subclassing necessary. It's a pretty long listing though.
Another alternative is to use an attached property. I know I talk about them a lot but since the popup in the ComboBox has a TemplatedPartAttribute, it would be possible to attach a property to the combobox that looks for the popup within the ComboBox's control template and replaces it with another. One that contains a ListView.
Let me play around with it a little bit tonight and I'll show you what I'm talking about.
--Mike
I think all you need to do is take the sample template in
ms-help://MS.MSSDK.1033/MS.NETFX30SDK.1033/wpf_conceptual/html/b0662fa1-16d7-4320-b26b-c1804e565a44.htm
and replace the StackPanel in the popup with listview and handle mousedown or doubleclick on the listviewitem to set the selecteditem of the combobox to the datacontext of the selecteditem in the listview and set the IsDropDownOpen property of the ComboBox to false. that should do it
Lee: That's what I mentioned above. I set the SelectionMode of the ListView to Single and created a Binding between the ListView's SelectedItem property and the ComboBox's SelectedItem property. Unfortunately there is no way to change a Boolean on an event within XAML or all of it could be done within XAML so you will need to add an event handler for the SelectedItemChanged event and have that handler set the IsDropDownOpen property to false.
Neil: I'm halfway through the code now. I will finish the rest this evening. It's actually going to be part of the WPFToolbelt.
No it's not a custom control per se. It's a DependencyObject that can be attached to a Control and set to replace a named template part with another. So for example, the ComboBox has a named part called PART_Popup (as specified by the TemplatePartAttribute). The class provides an attached property that can be set on a control to replace the part with one that the user provides.
ivolved_Mike_Brown wrote:
Lee: That's what I mentioned above. I set the SelectionMode of the ListView to Single and created a Binding between the ListView's SelectedItem property and the ComboBox's SelectedItem property. Unfortunately there is no way to change a Boolean on an event within XAML or all of it could be done within XAML so you will need to add an event handler for the SelectedItemChanged event and have that handler set the IsDropDownOpen property to false.
Neil: I'm halfway through the code now. I will finish the rest this evening. It's actually going to be part of the WPFToolbelt.
Sooo - did anyone figure this out
I got as far as modifying the simple style template below so that a GridView shows up when you click the combo box, and the selected item is bound to the combo box. But I'm not really clear if now I have two collections being created - how do you indicate that the GridView/ListView is to replace the ItemsPresenter of the ComboBox And how to I hook up the event to close when the GridView is clicked
<!--
SimpleStyles: ComboBox --><
ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}"><
Grid><
Grid.ColumnDefinitions><
ColumnDefinition /><
ColumnDefinition Width="20" /></
Grid.ColumnDefinitions><
Border x:Name="Border" Grid.ColumnSpan="2" CornerRadius="2" Background="{StaticResource NormalBrush}" BorderBrush="{StaticResource NormalBorderBrush}" BorderThickness="1" /><
Border Grid.Column="0" CornerRadius="2,0,0,2" Margin="1" Background="{StaticResource WindowBackgroundBrush}" BorderBrush="{StaticResource NormalBorderBrush}" BorderThickness="0,0,1,0" /><
Path x:Name="Arrow" Grid.Column="1" Fill="{StaticResource GlyphBrush}" HorizontalAlignment="Center" VerticalAlignment="Center" Data="M 0 0 L 4 4 L 8 0 Z"/></
Grid><
ControlTemplate.Triggers><
Trigger Property="ToggleButton.IsMouseOver" Value="true"><
Setter TargetName="Border" Property="Background" Value="{StaticResource DarkBrush}" /></
Trigger><
Trigger Property="ToggleButton.IsChecked" Value="true"><
Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" /></
Trigger><
Trigger Property="IsEnabled" Value="False"><
Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" /><
Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" /><
Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/><
Setter TargetName="Arrow" Property="Fill" Value="{StaticResource DisabledForegroundBrush}" /></
Trigger></
ControlTemplate.Triggers></
ControlTemplate><
ControlTemplate x:Key="ComboBoxTextBox" TargetType="{x:Type TextBox}"><
Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}" /></
ControlTemplate><
Style x:Key="{x:Type ComboBox}" TargetType="{x:Type ComboBox}"><
Setter Property="SnapsToDevicePixels" Value="true"/><
Setter Property="OverridesDefaultStyle" Value="true"/><
Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/><
Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/><
Setter Property="ScrollViewer.CanContentScroll" Value="true"/><
Setter Property="MinWidth" Value="120"/><
Setter Property="MinHeight" Value="24"/><
Setter Property="Template"><
Setter.Value><
ControlTemplate TargetType="{x:Type ComboBox}"><
Grid><
ToggleButton Name="ToggleButton" Template="{StaticResource ComboBoxToggleButton}" Grid.Column="2" Focusable="false" IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press"></
ToggleButton><
ContentPresenter Name="ContentSite" IsHitTestVisible="False" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Margin="3,3,23,3" VerticalAlignment="Center" HorizontalAlignment="Left" /><
TextBox x:Name="PART_EditableTextBox" Style="{x:Null}" Template="{StaticResource ComboBoxTextBox}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="3,3,23,3" Focusable="True" Background="Transparent" Visibility="Hidden" IsReadOnly="{TemplateBinding IsReadOnly}"/><
Popup Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="False" Focusable="False" PopupAnimation="Slide"><
Grid Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}"><
Border x:Name="DropDownBorder" Background="{StaticResource WindowBackgroundBrush}" BorderThickness="1" BorderBrush="{StaticResource SolidBorderBrush}"/><!--
<ScrollViewer SnapsToDevicePixels="True">
<VirtualizingStackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
--><
ListView ItemsSource="{TemplateBinding Property=ItemsSource}" SelectionMode="Single" SelectedItem="{Binding RelativeSource={RelativeSource TemplatedParent},Path=SelectedItem, Mode=TwoWay}
"><
ListView.View><
GridView><
GridViewColumn DisplayMemberBinding="{Binding XPath=code}" Header="Code"/><
GridViewColumn DisplayMemberBinding="{Binding XPath=name}" Header="Name"/></
GridView></
ListView.View></
ListView></
Grid></
Popup></
Grid><
ControlTemplate.Triggers><
Trigger Property="HasItems" Value="false"><
Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/></
Trigger><
Trigger Property="IsEnabled" Value="false"><
Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/></
Trigger><
Trigger Property="IsGrouping" Value="true"><
Setter Property="ScrollViewer.CanContentScroll" Value="false"/></
Trigger><
Trigger Property="IsEditable" Value="true"><
Setter Property="IsTabStop" Value="false"/><
Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/><
Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/></
Trigger></
ControlTemplate.Triggers></
ControlTemplate></
Setter.Value></
Setter><
Style.Triggers></
Style.Triggers></
Style><!--
SimpleStyles: ComboBoxItem --><
Style x:Key="{x:Type ComboBoxItem}" TargetType="{x:Type ComboBoxItem}"><
Setter Property="SnapsToDevicePixels" Value="true"/><
Setter Property="OverridesDefaultStyle" Value="true"/><
Setter Property="Template"><
Setter.Value><
ControlTemplate TargetType="{x:Type ComboBoxItem}"><
Border Name="Border" Padding="2" SnapsToDevicePixels="True"><
ContentPresenter /></
Border><
ControlTemplate.Triggers><
Trigger Property="IsHighlighted" Value="true"><
Setter TargetName="Border" Property="Background" Value="{StaticResource SelectedBackgroundBrush}"/></
Trigger><
Trigger Property="IsEnabled" Value="false"><
Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/></
Trigger></
ControlTemplate.Triggers></
ControlTemplate></
Setter.Value></
Setter></
Style>