Multiple item combo box with headers?

2019-01-13 19:33发布

问题:

Is it possible to have "column headers" on a combo box bound to multiple items? For example a combo box that displays a persons name. The combo box would display John Doe. But I'd like to display column headers:

First   Last
John    Doe
Jane    Doe
Jimmy   Doe

Is this possible without the use of a data grid? What about a simple solution that includes the use of a data grid? I found one solution for embedding a data grid into a combo box but it looks difficult and requires MS Blend.

I'd be happy if I could just get some headers as the first row in the drop down.

G

Here is my xaml code with HB's attempt that produces a compile error as mentioned in the comments.

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
<ComboBox Name="cboPlaceNames" Grid.IsSharedSizeScope="True" ItemsSource="{DynamicResource items}" Height="22" Width="285" Margin="0,6,165,0" SelectedIndex="0" HorizontalAlignment="Right" VerticalAlignment="Top" SelectionChanged="cboPlaceNames_SelectionChanged">
  <ComboBox.Resources>
    <CompositeCollection x:Key="items">
      <ComboBoxItem IsEnabled="False">
        <Grid TextElement.FontWeight="Bold">
          <Grid.ColumnDefinitions>
            <ColumnDefinition SharedSizeGroup="A"/>
            <ColumnDefinition Width="5"/>
            <ColumnDefinition SharedSizeGroup="B"/>
            <ColumnDefinition Width="5"/>
            <ColumnDefinition SharedSizeGroup="C"/>
          </Grid.ColumnDefinitions>
          <Grid.Children>
            <TextBlock Grid.Column="0" Text="Name"/>
            <TextBlock Grid.Column="2" Text="CLLI"/>
            <TextBlock Grid.Column="4" Text="Street"/>
          </Grid.Children>
        </Grid>
      </ComboBoxItem>
      <Separator/>
      <CollectionContainer Collection="{Binding Source={x:Reference cboPlaceNames}, Path=DataContext.Data}"/>
    </CompositeCollection>

    <DataTemplate DataType="x:Type obj:PlaceName">
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition SharedSizeGroup="A"/>
          <ColumnDefinition Width="5"/>
          <ColumnDefinition SharedSizeGroup="B"/>
          <ColumnDefinition Width="5"/>
          <ColumnDefinition SharedSizeGroup="C"/>
        </Grid.ColumnDefinitions>
        <Grid.Children>
          <TextBlock Grid.Column="0" Text="{Binding Name}"/>
          <TextBlock Grid.Column="2" Text="{Binding CLLI}"/>
          <TextBlock Grid.Column="4" Text="{Binding Street}"/>
        </Grid.Children>
      </Grid>
    </DataTemplate>
  </ComboBox.Resources>
</ComboBox>      

回答1:

Example:

<ComboBox Name="cb" Grid.IsSharedSizeScope="True" ItemsSource="{DynamicResource items}">
    <ComboBox.Resources>
        <CompositeCollection x:Key="items">
            <ComboBoxItem IsEnabled="False">
                <Grid TextElement.FontWeight="Bold">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition SharedSizeGroup="A"/>
                        <ColumnDefinition Width="5"/>
                        <ColumnDefinition SharedSizeGroup="B"/>
                    </Grid.ColumnDefinitions>
                    <Grid.Children>
                        <TextBlock Grid.Column="0" Text="Name"/>
                        <TextBlock Grid.Column="2" Text="Occupation"/>
                    </Grid.Children>
                </Grid>
            </ComboBoxItem>
            <Separator/>
            <CollectionContainer Collection="{Binding Source={x:Reference cb}, Path=DataContext.Data}"/>
        </CompositeCollection>

        <DataTemplate DataType="{x:Type obj:Employee}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition SharedSizeGroup="A"/>
                    <ColumnDefinition Width="5"/>
                    <ColumnDefinition SharedSizeGroup="B"/>
                </Grid.ColumnDefinitions>
                <Grid.Children>
                    <TextBlock Grid.Column="0" Text="{Binding Name}"/>
                    <TextBlock Grid.Column="2" Text="{Binding Occupation}"/>
                </Grid.Children>
            </Grid>
        </DataTemplate>
    </ComboBox.Resources>
</ComboBox>

Note that getting the Collection-binding right is not that easy because there is neither DataContext nor VisualTree to rely on, ElementName and RelativeSource does not work, this is because CompositeCollection is just a collection, not a FrameworkElement.

Other than that the way this is done is via Grids that have shared size columns. The DataTemplate is applied automatically via the DataType.

Edit: Setting the header-ComboBoxItem's IsHitTestVisible property to False is not enough since it still can be selected using the keyboard. I now changed it to IsEnabled="False" which fades out the item a bit. You could probably re-template that item to not do that. Or if you find another way of disabling it from selection that would of course work out too.