KentW

What¡¯s the best approach to build a combo box with a multiple column list view as dropdown

TIA

Kent



Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

cheesenhomer

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




Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

ivolved_Mike_Brown

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 }






Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

ivolved_Mike_Brown

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.






Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

KentW

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





Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

neil mosafi

For lining up the columns, you could use a Grid in the DataTemplate with a named SharedSizeGroup on each column. Then set the Grid.IsSharedSizeScope="True" property on the ComboBox. This will force each column to be the same size within the combo box.

Another more thorough approach, but which would require quite a bit of work, would be to use a GridViewRowPresenter in the ItemTemplate. This will require you to subclass a ComboBox and add a Columns property of
type GridViewColumnCollection. You would then have to retemplate the ComboBoxItem to set the Columns property of the GridViewRowPresenter to the Columns defined on the ComboBox

I have done this before to create a hybrid TreeListView and got the idea from here

http://blogs.msdn.com/atc_avalon_team/archive/2006/03/01/541206.aspx

I hope the link helps to get you started if you choose to go down that route!

Regards
Neil




Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

ivolved_Mike_Brown

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






Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

neil mosafi

Hmm sounds like a good idea mike... be interested to see that!

Regards
Neil




Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

lee d

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






Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

ivolved_Mike_Brown

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.






Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

lee d

Are you writing a custom control




Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

ivolved_Mike_Brown

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.






Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

Paul Czywczynski

 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.



Did the WPFToolbelt get updated yet




Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

ivolved_Mike_Brown

D'oh...didn't do the check-in...I'll put it in there tonight, also I'll blog a bit about it on BrowniePoints.




Re: Windows Presentation Foundation (WPF) How to create a multi-column combo box with WPF?

leemon99

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>