WPF ComboBox

本文介绍了一个自定义的WPF ComboBox控件,该控件支持占位符文本、清除按钮等功能,并详细展示了如何通过XAML样式和C#代码实现这些功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ScrollViewer样式参考http://blog.youkuaiyun.com/oneonce/article/details/77160487


效果


Sytle

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ctrls="clr-namespace:ImageButtonTest01.Control"
    xmlns:converter="clr-namespace:ImageButtonTest01.Control.Converter">

    <converter:BoolToVisibility x:Key="BTV"/>
    <converter:PlaceholderFontSizeConverter x:Key="PHFSC"/>
    <converter:ImageTextBoxMarginLeftConverter x:Key="ITBMLC"/>
    <converter:BoolOppositeConverter x:Key="BOC"/>

    <SolidColorBrush x:Key="ComboToggleButton.MouseMove.Foreground" Color="#FF7AC0EE"/>
    <SolidColorBrush x:Key="ComboToggleButton.Checked.Foreground" Color="#801AC0EE"/>
    
    <Style x:Key="ComboToggleButtonStyle" TargetType="{x:Type ToggleButton}">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="BorderBrush" Value="Transparent"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ToggleButton}">
                    <Border Background="Transparent">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <TextBlock x:Name="txt" Grid.Column="1" RenderTransformOrigin="0.5,0.5"
                                       HorizontalAlignment="Center" VerticalAlignment="Center"
                                       FontFamily="/ImageButtonTest01;component/Resources/#iconfont"
                                       Text=""
                                       Foreground="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}">
                                <TextBlock.RenderTransform>
                                    <RotateTransform x:Name="transIcon" Angle="0"/>
                                </TextBlock.RenderTransform>
                            </TextBlock>
                        </Grid>
                    </Border>

                    <ControlTemplate.Triggers>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="IsChecked" Value="true"></Condition>
                            </MultiTrigger.Conditions>

                            <Setter TargetName="txt" Property="Foreground" Value="{StaticResource ComboToggleButton.Checked.Foreground}"/>

                            <MultiTrigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation Storyboard.TargetName="transIcon" Storyboard.TargetProperty="Angle" To="180" Duration="0:0:0.2" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </MultiTrigger.EnterActions>
                            <MultiTrigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation Storyboard.TargetName="transIcon" Storyboard.TargetProperty="Angle" To="0" Duration="0:0:0.2" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </MultiTrigger.ExitActions>
                        </MultiTrigger>
                        
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="txt" Property="Foreground" Value="{StaticResource ComboToggleButton.MouseMove.Foreground}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="EditableTextBoxStyle" TargetType="{x:Type TextBox}">
        <Setter Property="Margin" Value="1"/>
        <Setter Property="Padding" Value="0"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="Background" Value="{x:Null}"/>
        <Setter Property="MaxLength" Value="2048"/>
        <Setter Property="Foreground" Value="Black"/>
        <Setter Property="ContextMenu" Value="{DynamicResource TextBoxContextMenu}"/>
        <Setter Property="SelectionBrush" Value="Bisque"/>
        <Setter Property="FontSize" Value="16"/>
        <Setter Property="Focusable" Value="True"/>
        <Setter Property="CaretBrush" Value="Black"/>
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="SnapsToDevicePixels" Value="True"/>
        <Style.Triggers>
            <Trigger Property="IsReadOnly" Value="True">
                <Setter Property="Opacity" Value="0.8"/>
            </Trigger>
            <Trigger Property="IsEnabled" Value="False">
                <Setter Property="Opacity" Value="0.4"/>
            </Trigger>
        </Style.Triggers>
    </Style>

    <SolidColorBrush x:Key="PlaceholderComboBox.Static.BorderBrush" Color="#FF909090"/>
    <SolidColorBrush x:Key="PlaceholderComboBox.Static.Foreground" Color="#FFAAAAAA"/>
    <SolidColorBrush x:Key="PlaceholderComboBox.Focused.BorderBrush" Color="#FF007ACC"/>
    <SolidColorBrush x:Key="PlaceholderComboBox.MouseOver.BorderBrush" Color="#FF1E1E1E"/>


    <ControlTemplate x:Key="placeholderComboBoxTemplate" TargetType="{x:Type ctrls:PlaceholderComboBox}">
        <Border x:Name="border"
                Background="{TemplateBinding Background}"
                BorderBrush="{StaticResource PlaceholderComboBox.Static.BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}"
                HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                CornerRadius="{Binding CornerRadius, RelativeSource={RelativeSource TemplatedParent}}">
            <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <Grid.ColumnDefinitions>
                    <!--左圆角-->
                    <ColumnDefinition Width="{Binding CornerRadius.TopLeft, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ITBMLC}, ConverterParameter=0.4}"/>
                    <!--内容显示-->
                    <ColumnDefinition Width="*"/>
                    <!--清除按钮-->
                    <ColumnDefinition Width="Auto"/>
                    <!--下拉按钮-->
                    <ColumnDefinition Width="Auto"/>
                    <!--右圆角-->
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>

                <TextBlock x:Name="PART_Placeholder" Grid.Column="1" Margin="3,0,0,0"
                           IsHitTestVisible="False"
                           HorizontalAlignment="Left" VerticalAlignment="Center"
                           SnapsToDevicePixels="True"
                           Visibility="Collapsed" Opacity="0.6"
                           TextAlignment="Left"
                           Text="{Binding Path=PlaceHolder, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                           FontSize="{Binding FontSize, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource PHFSC}}"/>

                <ctrls:ImageButton x:Name="PART_ClearButton" Grid.Column="2"
                                   Margin="2" IsTabStop="False" IsEnabled="True"
                                   FontFamily="/ImageButtonTest01;component/Resources/#iconfont" FontSize="20"
                                   Content=""
                                   HorizontalAlignment="Right"
                                   VerticalAlignment="Center"
                                   Style="{StaticResource ImageButtonTransparent}"
                                   Visibility="{Binding ShowClearButton,RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BTV}}"/>

                <Grid Grid.Column="1" x:Name="ContentSite">
                    <ContentPresenter x:Name="PART_SelectedItem" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
                                      ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
                                      Content="{TemplateBinding SelectionBoxItem}"
                                      ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
                                      HorizontalAlignment="Stretch" Margin="2,0,2,0"
                                      IsHitTestVisible="False" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    
                    <TextBox  x:Name="PART_EditableTextBox" Style="{StaticResource EditableTextBoxStyle}"
                              Visibility="Collapsed" IsHitTestVisible="True"
                              SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                              HorizontalAlignment="Stretch" VerticalAlignment="Center"
                              HorizontalContentAlignment="Left" TextAlignment="Left"
                              IsReadOnly="{TemplateBinding IsReadOnly}"
                              FontFamily="{TemplateBinding FontFamily}"
                              Foreground="{TemplateBinding Foreground}"
                              Text="{TemplateBinding Text}"
                              FontSize="{TemplateBinding FontSize}"/>
                </Grid>

                <ToggleButton x:Name="PART_DropDownToggle" Grid.Column="1" Grid.ColumnSpan="3"
                              BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"
                              Background="{Binding Background,RelativeSource={RelativeSource TemplatedParent}}"
                              Foreground="Black" FontWeight="Medium"
                              IsEnabled="{Binding Path=IsReadOnly,RelativeSource={RelativeSource TemplatedParent},Mode=OneWay, Converter={StaticResource BOC}}"
                              IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                              Style="{StaticResource ComboToggleButtonStyle}">

                    <ToggleButton.RenderTransform>
                        <RotateTransform x:Name="transIcon" Angle="0"/>
                    </ToggleButton.RenderTransform>
                </ToggleButton>

                <!--弹出下拉控件-->
                <Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False"
                       IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"
                       PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"
                       Placement="Bottom">
                    <Grid Background="#FFFFFFFF"
                          Width="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}"
                          MaxHeight="{Binding MaxDropDownHeight, RelativeSource={RelativeSource TemplatedParent}}">
                        <Border x:Name="PopupBorder" BorderThickness="{TemplateBinding BorderThickness}" HorizontalAlignment="Stretch"
                                Height="Auto" BorderBrush="{TemplateBinding BorderBrush}"
                                Background="Transparent">
                        </Border>
                        <ScrollViewer x:Name="DropDownScrollViewer"
                                      HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                                      VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
                                      Style="{StaticResource scrollviewerDefaultStyle}">
                            <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" />
                        </ScrollViewer>
                    </Grid>
                </Popup>
            </Grid>
        </Border>

        <ControlTemplate.Triggers>
            <DataTrigger Binding="{Binding Text, RelativeSource={RelativeSource Self}}" Value="">
                <Setter TargetName="PART_Placeholder" Property="Visibility" Value="Visible" />
                <Setter TargetName="PART_ClearButton" Property="IsEnabled" Value="False" />
            </DataTrigger>

            <Trigger Property="IsEditable" Value="True">
                <Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
                <Setter TargetName="PART_SelectedItem" Property="Visibility" Value="Collapsed"/>
                <Setter TargetName="PART_DropDownToggle" Property="Background" Value="Transparent"/>
                <Setter TargetName="PART_DropDownToggle" Property="Grid.Column" Value="3"/>
                <Setter TargetName="PART_DropDownToggle" Property="Grid.ColumnSpan" Value="1"/>
                <Setter Property="IsTabStop" Value="false"/>
                <Setter TargetName="PART_DropDownToggle" Property="Focusable" Value="False"/>
            </Trigger>

            <Trigger Property="IsMouseOver" Value="True">
                <Setter TargetName="border" Property="BorderBrush" Value="{StaticResource PlaceholderComboBox.MouseOver.BorderBrush}"/>
                <Setter TargetName="PART_DropDownToggle" Property="Foreground" Value="{StaticResource PlaceholderComboBox.MouseOver.BorderBrush}"/>
            </Trigger>
            
            <Trigger Property="IsFocused" Value="True">
                <Setter TargetName="border" Property="BorderBrush" Value="red"/>
            </Trigger>
            
            <Trigger Property="IsKeyboardFocusWithin" Value="True">
                <Setter TargetName="border" Property="BorderBrush" Value="{StaticResource PlaceholderComboBox.Focused.BorderBrush}"/>
            </Trigger>

            <Trigger Property="IsEnabled" Value="False">
                <Setter TargetName="border" Property="Opacity" Value="0.4"/>
            </Trigger>

            <Trigger Property="IsStylusCaptured" Value="True">
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>


    <Style TargetType="{x:Type ctrls:PlaceholderComboBox}">
        <Setter Property="AllowDrop" Value="true"/>
        <Setter Property="Background" Value="WhiteSmoke"/>
        <Setter Property="BorderThickness" Value="2"/>
        <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
        <Setter Property="BorderBrush" Value="{StaticResource ImageTextBox.Static.BorderBrush}"/>
        <Setter Property="Template" Value="{StaticResource placeholderComboBoxTemplate}"/>
    </Style>
</ResourceDictionary>



CS:

public class PlaceholderComboBox : ComboBox
{
    private ImageButton mClearTextButton;

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

    public PlaceholderComboBox()
    {
    }

    public override void OnApplyTemplate()
    {
        mClearTextButton = GetTemplateChild("PART_ClearButton") as ImageButton;
        if (null != mClearTextButton)
        {
            mClearTextButton.Click += OnClearTextButtonClick;
        }

        base.OnApplyTemplate();
    }

    private void OnClearTextButtonClick(object sender, RoutedEventArgs e)
    {
        Text = "";
    }

    public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(nameof(ShowClearButton), typeof(bool),
                                                                    typeof(PlaceholderComboBox), new PropertyMetadata(true));
    public bool ShowClearButton
    {
        get { return (bool)GetValue(ShowClearButtonProperty); }
        set { SetValue(ShowClearButtonProperty, value); }
    }

    public static readonly DependencyProperty PlaceHolderProperty = DependencyProperty.Register(nameof(PlaceHolder), typeof(string), 
                                                                    typeof(PlaceholderComboBox), new PropertyMetadata("Placeholder"));
    public string PlaceHolder
    {
        get { return (string)GetValue(PlaceHolderProperty); }
        set { SetValue(PlaceHolderProperty, value); }
    }

    public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(nameof(CornerRadius), typeof(CornerRadius),
        typeof(PlaceholderComboBox), new PropertyMetadata(new CornerRadius(0, 0, 0, 0)));
    public CornerRadius CornerRadius
    {
        get { return (CornerRadius)GetValue(CornerRadiusProperty); }
        set { SetValue(CornerRadiusProperty, value); }
    }
}


converter:

public class BoolOppositeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (null == value)
        {
            return false;
        }

        bool bv = (bool)value;

        return !bv;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class PlaceholderFontSizeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        double v = (double)value;

        return (v * 3.5) / 5.0;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class ImageTextBoxMarginLeftConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is double)
        {
            double v = (double)value;
            if (null != parameter)
            {
                return v * double.Parse(parameter.ToString());
            }
            return v * 2.0 / 5.0;
        }
        return 0;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class BoolOppositeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (null == value)
        {
            return false;
        }

        bool bv = (bool)value;

        return !bv;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}



使用:

<control:PlaceholderComboBox Width="300" Height="45" FontSize="30"
                                ShowClearButton="False"
                                PlaceHolder="请选择/输入"
                                IsEditable="False"
                                CornerRadius="0">
    <ComboBoxItem>11111111111</ComboBoxItem>
    <ComboBoxItem>22222222222</ComboBoxItem>
    <ComboBoxItem>3333333333333</ComboBoxItem>
    <ComboBoxItem>4444444444444444444444444</ComboBoxItem>
</control:PlaceholderComboBox>
<control:PlaceholderComboBox Width="300" Height="45" FontSize="30" Margin="5"
                                ShowClearButton="True"
                                PlaceHolder="请选择/输入"
                                IsEditable="True"
                                CornerRadius="0">
    <ComboBoxItem>aaaaaaaaaaaaa</ComboBoxItem>
    <ComboBoxItem>bbbbbbbbbbbbbb</ComboBoxItem>
    <ComboBoxItem>ccccccc</ComboBoxItem>
    <ComboBoxItem>ddddddddddddddddd</ComboBoxItem>
</control:PlaceholderComboBox>


<think>我们之前讨论的是分页和页码显示,现在用户询问的是WPFComboBox控件的使用教程。根据用户提供的引用,我们可以总结出以下关键点:1.ComboBox基本功能:提供下拉列表供用户选择或输入(引用[1])2.自定义选项:可以在ComboBoxItem中嵌入其他控件(如CheckBox)实现多选(引用[2][3])3.数据绑定:支持通过Dictionary等集合进行数据绑定(引用[5])4.样式设计:可以设置背景、字体等样式(引用[3])因此,我将按照以下结构回答:1.ComboBox基本用法(包含XAML和C#代码)2.数据绑定方法(ItemSource、DisplayMemberPath、SelectedValuePath)3.多选ComboBox实现(使用CheckBox)4.自定义项模板(ItemTemplate)5.样式设置6.事件处理(SelectionChanged事件)同时,我会根据用户提供的引用[1]到[5]来提供示例代码,并在末尾添加引用标识。最后,我会生成几个相关问题。</think> ### WPF ComboBox 使用教程 #### 1. 基本用法 ComboBoxWPF中的下拉列表控件,用于让用户从预设选项中选择。基本实现代码: ```xml <ComboBox x:Name="simpleComboBox" Width="200" Height="30"> <ComboBoxItem>选项一</ComboBoxItem> <ComboBoxItem>选项二</ComboBoxItem> <ComboBoxItem>选项三</ComboBoxItem> </ComboBox> ``` ```csharp // 获取选中值 var selected = simpleComboBox.SelectedItem as ComboBoxItem; Console.WriteLine(selected.Content); // 输出:选项二 ``` 特性说明: - `IsEditable=true`:允许用户输入文本[^1] - `SelectedIndex`:获取选中项的索引(从0开始) - `SelectedItem`:获取选中项对象 - `SelectedValue`:获取选中项的值(配合绑定使用) #### 2. 数据绑定 推荐使用数据绑定填充选项(MVVM模式)[^5]: ```csharp public class ViewModel { public Dictionary<int, string> Options { get; } = new() { {1, "北京"}, {2, "上海"}, {3, "广州"} }; public int SelectedOption { get; set; } = 1; } ``` ```xml <ComboBox x:Name="bindComboBox" ItemsSource="{Binding Options}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding SelectedOption}"/> ``` #### 3. 自定义项模板 使用`ItemTemplate`自定义选项显示方式[^3][^4]: ```xml <ComboBox Width="300"> <ComboBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Image Source="/icons/city.png" Width="20"/> <TextBlock Text="{Binding Name}" Margin="10,0"/> <CheckBox IsChecked="{Binding IsSelected}" Visibility="Collapsed"/> </StackPanel> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> ``` #### 4. 多选实现 通过嵌入CheckBox实现多选功能[^2]: ```xml <ComboBox x:Name="multiCombo" IsEditable="True"> <ComboBox.ItemTemplate> <DataTemplate> <CheckBox Content="{Binding}" IsChecked="{Binding IsSelected, Mode=TwoWay}"/> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> ``` ```csharp // 获取所有选中的值 var selectedItems = multiCombo.Items .Cast<dynamic>() .Where(item => item.IsSelected) .Select(item => item.Content); ``` #### 5. 样式设置 自定义ComboBox外观[^3]: ```xml <ComboBox Background="LightBlue" Foreground="DarkRed" FontFamily="Arial" FontSize="14" FontWeight="Bold"> <!-- 选项 --> </ComboBox> ``` #### 6. 常用事件处理 ```csharp // 选择改变事件 comboBox.SelectionChanged += (sender, e) => { var combo = sender as ComboBox; Debug.WriteLine($"选中: {combo.SelectedItem}"); }; // 下拉列表打开/关闭事件 comboBox.DropDownOpened += (s,e) => { /* 下拉列表打开 */ }; comboBox.DropDownClosed += (s,e) => { /* 下拉列表关闭 */ }; ``` #### 7. 最佳实践 1. **MVVM模式**:通过绑定实现数据驱动UI 2. **虚拟化**:大数据集使用`VirtualizingStackPanel`提升性能 ```xml <ComboBox.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel /> </ItemsPanelTemplate> </ComboBox.ItemsPanel> ``` 3. **验证机制**:配合`INotifyDataErrorInfo`实现输入验证 4. **水印提示**:使用`TextSearch.TextPath`添加搜索提示
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OneOnce

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值