C# 在一个WPF ComboBox中使用三种不同的ComboBoxItem样式

C# 在一个WPF ComboBox中使用三种不同的ComboBoxItem样式,c#,wpf,xaml,combobox,C#,Wpf,Xaml,Combobox,因此,我试图为我正在开发的应用程序创建一个用户控件。它基本上是一个组合框旁边的切换按钮。在VS2015中,我能够以设计者想要的方式模拟用户控件的组合框部分,但我觉得我现在的方式并不是最好的方式 首先,这里是一个链接,指向它的屏幕截图: 为此,我创建了3种不同的ComboBoxItem样式。第一个将一个复选框、一个带有ContentPresenter的文本块和一个矩形。第二个只有一个分隔符,最后一个只有文本块和内容呈现器。这是我的XAML,它在UserControl.Resources部分声明:

因此,我试图为我正在开发的应用程序创建一个用户控件。它基本上是一个
组合框旁边的
切换按钮
。在VS2015中,我能够以设计者想要的方式模拟用户控件的
组合框
部分,但我觉得我现在的方式并不是最好的方式

首先,这里是一个链接,指向它的屏幕截图:

为此,我创建了3种不同的
ComboBoxItem
样式。第一个将一个
复选框
、一个带有
ContentPresenter
文本块
和一个
矩形
。第二个只有一个
分隔符
,最后一个只有
文本块
内容呈现器
。这是我的XAML,它在
UserControl.Resources
部分声明:

<Style x:Key="cbTestStyle" TargetType="{x:Type ComboBoxItem}">
    <Setter Property="SnapsToDevicePixels" Value="True"/>
    <Setter Property="HorizontalAlignment" Value="Stretch"/>
    <Setter Property="VerticalAlignment" Value="Stretch"/>
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ComboBoxItem">
                <Border Name="Border"
                        Padding="5"
                        Margin="2"
                        BorderThickness="2"
                        CornerRadius="0"
                        BorderBrush="Transparent">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="20"/>
                            <ColumnDefinition Width="75"/>
                            <ColumnDefinition Width="15"/>
                        </Grid.ColumnDefinitions>
                        <CheckBox Grid.Column="0"
                                  IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource TemplatedParent}}"/>
                        <TextBlock Grid.Column="1"
                                   TextAlignment="Left"
                                   Foreground="Black">
                            <ContentPresenter/>
                        </TextBlock>
                        <Rectangle Grid.Column="2"
                                   Stroke="Black"
                                   Width="15"
                                   Height="15"
                                   Fill="{TemplateBinding Foreground}"/>
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsHighlighted" Value="True">
                        <Setter TargetName="Border" Property="BorderBrush" Value="Gray"/>
                        <Setter TargetName="Border" Property="Background" Value="LightGray"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="cbSeparatorStyle" TargetType="ComboBoxItem">
    <Setter Property="SnapsToDevicePixels" Value="True"/>
    <Setter Property="HorizontalAlignment" Value="Stretch"/>
    <Setter Property="VerticalAlignment" Value="Stretch"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <Separator/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="cbResetStyle" TargetType="{x:Type ComboBoxItem}">
    <Setter Property="SnapsToDevicePixels" Value="True"/>
    <Setter Property="HorizontalAlignment" Value="Stretch"/>
    <Setter Property="VerticalAlignment" Value="Stretch"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ComboBoxItem">
                <Border x:Name="Border"
                        Padding="5"
                        Margin="2"
                        BorderThickness="2"
                        CornerRadius="0"
                        BorderBrush="Transparent">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="20"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="1">
                            <ContentPresenter/>
                        </TextBlock>
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsHighlighted" Value="True">
                        <Setter TargetName="Border" Property="BorderBrush" Value="Gray"/>
                        <Setter TargetName="Border" Property="Background" Value="LightGray"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
在本例中,我希望动态创建前三个项目,并使分隔符和“重置”项目保持静态。我对WPF还是比较陌生的。我觉得尝试在WinForms中创建此控件(此用户控件将在其中使用的应用程序)会复杂得多。另外,我正试图引导我们更多地使用WPF


如果您有任何在线帮助或其他问题或教程的链接,我们将不胜感激。

您可以使用DataTemplateSelector,其中包含XAML中定义的DataTemplates和绑定到的数据的某些项类型变量

public class StyleSelector : DataTemplateSelector
{
    public DataTemplate DefaultTemplate
    { get; set; }

    public DataTemplate SeparatorTemplate
    { get; set; }

    public DataTemplate ResetTemplate
    { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var type = item as SomeType;
        if (type != null)
        {
            switch (type.SomeItemTypeField)
            {
                case TypeENum.Separator: return SeparatorTemplate;
                case TypeENum.Reset: return ResetTemplate;
                default:
                    return DefaultTemplate;
            }
        }

        return base.SelectTemplate(item, container);
    }
}

解决方案1:

使用,以便可以使用数据绑定打开数据项,并使用常规XAML定义硬编码项:

<Window x:Class="WpfApplication31.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication31"
        Title="MainWindow" Height="350" Width="525"
        x:Name="view">
    <Window.Resources>
        <DataTemplate DataType="{x:Type local:DataItem}">
            <StackPanel Orientation="Horizontal">
                <CheckBox IsChecked="{Binding IsChecked}"/>
                <TextBlock Text="{Binding Text}"/>
                <Rectangle Stroke="Black" StrokeThickness="1"
                           Fill="{Binding Color}" Width="20"/>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>

    <Grid>
        <ComboBox VerticalAlignment="Center"
                  HorizontalAlignment="Center"
                  Width="100" x:Name="Combo">
            <ComboBox.Resources>
                <CompositeCollection x:Key="ItemsSource">
                    <CollectionContainer Collection="{Binding DataContext,Source={x:Reference view}}"/>
                    <Separator Height="10"/>
                    <Button Content="Clear All"/>
                </CompositeCollection>
            </ComboBox.Resources>

            <ComboBox.ItemsSource>
                <StaticResource ResourceKey="ItemsSource"/>
            </ComboBox.ItemsSource>
        </ComboBox>
    </Grid>
</Window>
数据项:

public class DataItem
{
    public bool IsChecked { get; set; }

    public string Text { get; set; }

    public string Color { get; set; }
}
结果:


我认为最好的办法是了解DataTemplate和DataTemplateSelector

下面是一篇博客文章,它将向您展示一个使用DataTemplate的简单示例


基本上,您可以将组合框绑定到对象集合,并使用DataTemplateSelector根据对象类型选择要使用的模板。

解决方案2:

使用,您可以为
组合框
控件修改此XAML以适应额外的视觉效果

您得到的XAML相当长,我不打算在这里发布它。您必须将其放入
ResourceDictionary
中,并在定义此
组合框的XAML中引用它

您需要编辑的相关部分是
弹出窗口

<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
    <Themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
        <Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
            <DockPanel>
                <Button Content="Clear All" DockPanel.Dock="Bottom"/>
                <Separator Height="2" DockPanel.Dock="Bottom"/>
                <ScrollViewer x:Name="DropDownScrollViewer">
                    <Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
                        <Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                            <Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
                        </Canvas>
                        <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </Grid>
                </ScrollViewer>
            </DockPanel>
        </Border>
    </Themes:SystemDropShadowChrome>
</Popup>
结果:


请注意,这种方法比我以前的解决方案和这里发布的其他答案要好得多,因为它没有将额外的视觉效果包装在
ComboBoxItem
s中,因此您没有为它们获得选择突出显示,这很奇怪。

感谢所有迄今为止为我提供了一些选项的人。我将尝试这些方法,看看哪种方法最适合我,然后向大家汇报。我决定尝试一下这种方法,但我有一个问题。我假设你在第一个解决方案中提出的一些建议会延续到这个解决方案中。显然,这里也使用了DataItem类和DataTemplate。XAML中是否还有其他需要继续执行的内容?第二个问题是,我是否需要保留整个
组合框
模板来修改弹出窗口中的一部分?或者我可以把其中的大部分去掉,因为这些默认样式都不会真正改变?@WERUreo 1:你需要另一个答案中的
DataItem
,然后用数据(我在代码隐藏中做了,但你可以做其他事情)和DataTemplate填充列表。2:控件模板在部分中不是真正可重用的,要么重写整个内容,要么不重写,所以您需要从Expression Blend获得完整的XAML。感谢您确认我对控件模板的怀疑。我开始意识到,试图将模板拼凑在一起并不能很好地工作。当我制作这个用户控件的原型时,我在
UserControl.Resources
中已经有了模板,但是当我将代码投入生产时,我可能会创建一个单独的
ResourceDictionary
。我使用了这个解决方案。我两种都试过了,老实说,我想我是在绕圈子,从一个跳到另一个,所以我基本上把所有的东西都去掉了,开始用这个解决方案。最终得到的东西看起来像我原来的截图。仍然需要解决一些问题,但对于这个问题,这个解决方案最适合我。再次感谢!我感谢你的建议。虽然我没有采用这种方法,但我肯定会研究DataTemplates和DataTemplateSelector。关于WPF,我还有很多需要学习的地方,所以非常感谢您提供任何新的信息。
public class DataItem
{
    public bool IsChecked { get; set; }

    public string Text { get; set; }

    public string Color { get; set; }
}
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
    <Themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
        <Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
            <DockPanel>
                <Button Content="Clear All" DockPanel.Dock="Bottom"/>
                <Separator Height="2" DockPanel.Dock="Bottom"/>
                <ScrollViewer x:Name="DropDownScrollViewer">
                    <Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
                        <Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                            <Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
                        </Canvas>
                        <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </Grid>
                </ScrollViewer>
            </DockPanel>
        </Border>
    </Themes:SystemDropShadowChrome>
</Popup>
<ComboBox ItemsSource="{Binding}"
          VerticalAlignment="Center"
          HorizontalAlignment="Center"
          Width="100"/>