自定义ComboBox.ItemsPanel

时间:2018-07-23 12:48:55

标签: c# wpf combobox

我想自定义从ComboBox下拉的面板。

例如,用于添加搜索功能或其他功能。

我了解EssentialObjects,这正是我所需要的,但是我想知道自己如何做。

小草稿

<ComboBox ...>
    <ComboBox.ItemsPanel>
        <ItemsPanelTemplate>
            <!-- This is how i can customize container for items. But it's not the way -->
            <!-- <UniformGrid Columns="2" /> -->

            <!-- But i need something like this -->
            <Grid>
                <!-- Whatever i want -->
                <TextBox ... />
                <Button ... />
                <Button ... />

                <!-- Container for items -->
                <UniformGrid Columns="2"/>
            </Grid>

        </ItemsPanelTemplate>
    </ComboBox.ItemsPanel>
</ComboBox>

是否可以调整标准ComboBox,还是必须创建自己的控件?

1 个答案:

答案 0 :(得分:2)

我的建议是创建自己的ComboBox,以扩展标准版本。让我们看看如何。首先是控件类:

public class CustomComboBox : ComboBox
{
    public static readonly DependencyProperty HeaderTemplateProperty =
        DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(CustomComboBox), new PropertyMetadata(null));

    public static readonly DependencyProperty FooterTemplateProperty =
        DependencyProperty.Register("FooterTemplate", typeof(DataTemplate), typeof(CustomComboBox), new PropertyMetadata(null));

    static CustomComboBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomComboBox), new FrameworkPropertyMetadata(typeof(CustomComboBox)));
    }

    public DataTemplate HeaderTemplate
    {
        get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
        set { SetValue(HeaderTemplateProperty, value); }
    }

    public DataTemplate FooterTemplate
    {
        get { return (DataTemplate)GetValue(FooterTemplateProperty); }
        set { SetValue(FooterTemplateProperty, value); }
    }
}

我们只是添加两个dependecy属性来管理页眉和页脚。现在,使用ILSpy可以检索默认的ComboBox样式。我们需要使用我们的控制模板:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:cbd="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Classic"
                    xmlns:local="clr-namespace:WpfApp1">

    <Geometry x:Key="Û">M 0 0 L 3.5 4 L 7 0 Z</Geometry>

    <Style x:Key="Ü" TargetType="{x:Type ToggleButton}">
        <Setter Property="FrameworkElement.MinWidth" Value="0" />
        <Setter Property="FrameworkElement.MinHeight" Value="0" />
        <Setter Property="FrameworkElement.Width" Value="Auto" />
        <Setter Property="FrameworkElement.Height" Value="Auto" />
        <Setter Property="Control.Background" Value="#00FFFFFF" />
        <Setter Property="Control.BorderBrush" Value="{x:Static cbd:ClassicBorderDecorator.ClassicBorderBrush}" />
        <Setter Property="Control.BorderThickness" Value="2" />
        <Setter Property="Control.Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ToggleButton}">
                    <DockPanel Background="{TemplateBinding Control.Background}" LastChildFill="False" SnapsToDevicePixels="True">
                        <cbd:ClassicBorderDecorator x:Name="Border" BorderStyle="AltRaised" DockPanel.Dock="Right" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" BorderThickness="{TemplateBinding Control.BorderThickness}" BorderBrush="{TemplateBinding Control.BorderBrush}">
                            <Path Fill="{TemplateBinding Control.Foreground}" HorizontalAlignment="Center" VerticalAlignment="Center" Data="{StaticResource Û}" />
                        </cbd:ClassicBorderDecorator>
                    </DockPanel>
                    <ControlTemplate.Triggers>
                        <Trigger Property="ToggleButton.IsChecked" Value="true">
                            <Setter TargetName="Border" Property="cbd:ClassicBorderDecorator.BorderStyle" Value="AltPressed" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="UIElement.IsEnabled" Value="False">
                <Setter Property="Control.Foreground" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" />
            </Trigger>
        </Style.Triggers>
    </Style>

    <Style BasedOn="{StaticResource {x:Type ComboBox}}" TargetType="{x:Type local:CustomComboBox}">
        <Setter Property="Control.Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ComboBox}">
                    <Border Background="{TemplateBinding Control.Background}" BorderThickness="{TemplateBinding Control.BorderThickness}" BorderBrush="{TemplateBinding Control.BorderBrush}" SnapsToDevicePixels="True">
                        <Grid>
                            <cbd:ClassicBorderDecorator x:Name="Border" Background="{TemplateBinding Control.Background}" BorderBrush="{x:Static cbd:ClassicBorderDecorator.ClassicBorderBrush}" BorderThickness="2" BorderStyle="Sunken">
                                <Popup Name="PART_Popup" AllowsTransparency="True" Placement="Bottom" IsOpen="{TemplateBinding ComboBox.IsDropDownOpen}" Focusable="False" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}">
                                    <cbd:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MaxHeight="{TemplateBinding ComboBox.MaxDropDownHeight}" MinWidth="{Binding ElementName=Border, Path=ActualWidth}">
                                        <Border Name="DropDownBorder" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" BorderThickness="1" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}">
                                            <ScrollViewer Name="DropDownScrollViewer">
                                                <Grid RenderOptions.ClearTypeHint="Enabled">
                                                    <Canvas Height="0" Width="0" HorizontalAlignment="Left" VerticalAlignment="Top">
                                                        <Rectangle Name="OpaqueRect" Height="{Binding ElementName=DropDownBorder, Path=ActualHeight}" Width="{Binding ElementName=DropDownBorder, Path=ActualWidth}" Fill="{Binding ElementName=DropDownBorder, Path=Background}" />
                                                    </Canvas>
                                                    <StackPanel>
                                                        <ContentPresenter Margin="1,1,1,1" ContentTemplate="{TemplateBinding local:CustomComboBox.HeaderTemplate}" />
                                                        <ItemsPresenter Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
                                                        <ContentPresenter Margin="1,1,1,1" ContentTemplate="{TemplateBinding local:CustomComboBox.FooterTemplate}" />
                                                    </StackPanel>
                                                </Grid>
                                            </ScrollViewer>
                                        </Border>
                                    </cbd:SystemDropShadowChrome>
                                </Popup>
                            </cbd:ClassicBorderDecorator>
                            <DockPanel Margin="2">
                                <FrameworkElement DockPanel.Dock="Right" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" />
                                <Border Name="SelectedItemBorder" Margin="{TemplateBinding Control.Padding}">
                                    <ContentPresenter Margin="1,1,1,1" Content="{TemplateBinding ComboBox.SelectionBoxItem}" ContentTemplate="{TemplateBinding ComboBox.SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemsControl.ItemTemplateSelector}" ContentStringFormat="{TemplateBinding ComboBox.SelectionBoxItemStringFormat}" VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
                                </Border>
                            </DockPanel>
                            <ToggleButton Margin="2" MinWidth="0" MinHeight="0" Width="Auto" Focusable="False" Style="{StaticResource Ü}" ClickMode="Press" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" />
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="ComboBox.IsSelectionBoxHighlighted" Value="True" />
                                <Condition Property="ComboBox.IsDropDownOpen" Value="False" />
                            </MultiTrigger.Conditions>
                            <Setter Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" Property="Control.Foreground" />
                        </MultiTrigger>
                        <Trigger Property="ComboBox.IsSelectionBoxHighlighted" Value="True">
                            <Setter TargetName="SelectedItemBorder" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" Property="Border.Background" />
                        </Trigger>
                        <Trigger Property="ItemsControl.HasItems" Value="False">
                            <Setter TargetName="DropDownBorder" Property="FrameworkElement.MinHeight" Value="95" />
                        </Trigger>
                        <Trigger Property="UIElement.IsEnabled" Value="False">
                            <Setter Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" Property="Control.Foreground" />
                            <Setter Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Property="Control.Background" />
                        </Trigger>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="ItemsControl.IsGrouping" Value="True" />
                                <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="False" />
                            </MultiTrigger.Conditions>
                            <Setter Property="ScrollViewer.CanContentScroll" Value="False" />
                        </MultiTrigger>
                        <Trigger SourceName="PART_Popup" Property="Popup.HasDropShadow" Value="True">
                            <Setter TargetName="Shdw" Property="FrameworkElement.Margin" Value="0,0,5,5" />
                            <Setter TargetName="Shdw" Property="cbd:SystemDropShadowChrome.Color" Value="#71000000" />
                        </Trigger>
                        <Trigger SourceName="DropDownScrollViewer" Property="ScrollViewer.CanContentScroll" Value="False">
                            <Setter TargetName="OpaqueRect" Value="{Binding ElementName=DropDownScrollViewer, Path=VerticalOffset}" Property="Canvas.Top" />
                            <Setter TargetName="OpaqueRect" Value="{Binding ElementName=DropDownScrollViewer, Path=HorizontalOffset}" Property="Canvas.Left" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

我选择了 classic 主题,因此在这种情况下,需要将对PresentationFramework.classic的引用添加到项目中。

诀窍在于,我添加了两个ContentPresenter(前一个和后一个)ItemPresenter(称为 ItemsPresenter )。他们将显示我们的自定义DataTemplate。 此样式应放置在Themes \ generic.xaml文件中(将其生成操作设置为 Page )。

现在我们可以使用CustomComboBox:

<StackPanel>
    <local:CustomComboBox>
        <local:CustomComboBox.HeaderTemplate>
            <DataTemplate>
                <TextBlock Text="HEADER" />
            </DataTemplate>
        </local:CustomComboBox.HeaderTemplate>
        <local:CustomComboBox.FooterTemplate>
            <DataTemplate>
                <TextBlock Text="FOOTER" />
            </DataTemplate>
        </local:CustomComboBox.FooterTemplate>

        <ComboBoxItem>One</ComboBoxItem>
        <ComboBoxItem>Two</ComboBoxItem>
        <ComboBoxItem>Three</ComboBoxItem>
    </local:CustomComboBox>
</StackPanel>

希望它能对您有所帮助。