Tentokrát si upravíme kombo tak, aby po otevření zobrazovalo více sloupců než jen jeden a uživatel tak měl snazší výběr hodnoty.
![]() |
| vícesloupcové kombo |
Dalo by se to řešit přepsáním ControlTemplate, a nahradit stávající ItemsPresenter za nějakou jinou kontrolku podporující zobrazení kolekcí po sloupcích. Jakýkoliv zásah do ControlTemplate je ale poměrně pracný, navíc je potřeba přepsat všechny varianty (Classic, Aero, Luna, ...) aby nový kontrol nepůsobil jak pěst na oko v jiném tématu.
Svoje řešení jsem tedy postavil na změně DataTemplate, kdy si připravím dvakrát ItemTemplate - jeden pro zavřené kombo a jeden pro otevřené (prvky v Popup okně) a ty nastavuji pomocí triggeru ve stylu pro dané kombo. Sloupce v jednotlivých řádcích jsou zarovnané díky nastavenému Grid.IsSharedSizeScope v rodičovském kombu a pojmenovaným skupinám sloupečků v ItemTemplate (SharedSizeGroup). Drobná úprava stylu pro ComboBoxItem v rámci komba pak vyhazuje přebytečný padding a rámeček vybraného záznamu, které vizuelně rušily díky vlastnímu paddingu a rámečkům v jednotlivých buňkách.
Výběr správného ItemTemplate zajišťuje trigger, jak už je uvedeno výše a fígl jak vybrat ten správný spočívá v tom, že v otevřeném kombu uvnitř Popup okna jsou jednotlivé prvky uzavřené v kontejneru ComboBoxItem, zatímco tzv. SelectionBox tento kontejner nad sebou nemá. DataTrigger tedy zkouší mezi svými předky najít kontrol ComboBoxItem a pokud ho nenajde (výsledek je null) použije DataTemplate pro zavřené kombo, v opačném případě pro otevřené.
V ukázce používám jednoduchý ViewModel s kolekcí lidí (IList
Třída Person a ukázkový viewmodel vypadá následovně:
public class Person
{
public Person(string name, string surname, bool male, DateTime birthDate)
{
Name = name;
Surname = surname;
Male = male;
Age = (int)(DateTime.Now - birthDate).TotalDays / 365;
}
public string Name { get; private set; }
public string Surname { get; private set; }
public bool Male { get; private set; }
public int Age{ get; private set; }
public string FullName { get { return string.Format("{0} {1}", Surname, Name); } }
}
public class MainViewModel
{
public MainViewModel()
{
People = new ObservableCollection<Person>
{
new Person("Zdeněk", "Srstka", true, new DateTime(1935, 9, 26)),
new Person("Dana", "Morávková", false, new DateTime(1971, 7, 29)),
new Person("Jiří", "Pomeje", true, new DateTime(1964, 12, 13))
};
CurrentPerson = People[0];
}
public IList<Person> People { get; private set; }
public Person CurrentPerson { get; set; }
}
XAML view pak takto:
<Window x:Class="WpfApplication1.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:WpfApplication1.ViewModels"
xmlns:converters="clr-namespace:WpfApplication1.Converters"
mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:MainViewModel}"
Title="MainView" Height="640" Width="480" >
<Window.Resources>
<!-- konverter převádějící bool hodnotu na uvedený řetězcový zápis -->
<converters:BooleanToObjectConverter x:Key="BooleanToObjectConverter" TrueValue="♂" FalseValue="♀" />
<!-- DataTemplate pro zavřené kombo (SelectionBox) -->
<DataTemplate x:Key="SelectionBoxItemTemplate" DataType="viewModels:Person">
<TextBlock Text="{Binding Path=FullName}" />
</DataTemplate>
<!-- DataTemplate prvky uvnitř popupu (4 sloupečky) -->
<DataTemplate x:Key="PopupItemTemplate" DataType="viewModels:Person">
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="Silver" />
<Setter Property="BorderThickness" Value="0.25" />
<Setter Property="Padding" Value="5, 2" />
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A" />
<ColumnDefinition SharedSizeGroup="B" />
<ColumnDefinition SharedSizeGroup="C" />
<ColumnDefinition SharedSizeGroup="D" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0">
<TextBlock Text="{Binding Path=Surname}" />
</Border>
<Border Grid.Column="1">
<TextBlock Text="{Binding Path=Name}" />
</Border>
<Border Grid.Column="2">
<TextBlock Text="{Binding Path=Male, Converter={StaticResource BooleanToObjectConverter}}" />
</Border>
<Border Grid.Column="3">
<TextBlock Text="{Binding Path=Age, StringFormat={}{0} let}" />
</Border>
</Grid>
</DataTemplate>
<!-- styl pro upravené kombo -->
<Style x:Key="PeopleComboBoxStyle" TargetType="{x:Type ComboBox}">
<Style.Resources>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Padding" Value="0" />
<Setter Property="BorderThickness" Value="0" />
</Style>
</Style.Resources>
<Setter Property="Grid.IsSharedSizeScope" Value="True" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<ContentControl x:Name="cnt" Content="{Binding}" ContentTemplate="{StaticResource PopupItemTemplate}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBoxItem}}}" Value="{x:Null}">
<Setter TargetName="cnt" Property="ContentTemplate" Value="{StaticResource SelectionBoxItemTemplate}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<ComboBox ItemsSource="{Binding Path=People}"
SelectedItem="{Binding Path=CurrentPerson}"
Style="{StaticResource PeopleComboBoxStyle}" />
</Grid>
</Window>
