You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WPF如何实现带多可点击列的时分秒选择ComboBox

WPF单ComboBox实现时分秒三列选择方案

原生WPF无需第三方控件即可实现,网上多数失效方案的核心问题是错误将独立ComboBox嵌入下拉面板,会触发焦点抢占导致下拉自动关闭,以下是可直接运行的实现:

第一步:定义时间数据实体

新建用于绑定选中值的实体类,重写ToString方法控制选中后的显示文本:

public class TimeSelectItem
{
    public int Hour { get; set; }
    public int Minute { get; set; }
    public int Second { get; set; }
    public override string ToString()
    {
        return $"{Hour:D2}:{Minute:D2}:{Second:D2}";
    }
}

第二步:编写ComboBox控件模板

核心是重写ComboBox的下拉弹出面板,用三列ListBox分别承载时、分、秒选项,不要嵌入独立ComboBox控件:

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Loaded="Window_Loaded"
        Title="时间选择Demo" Height="200" Width="400">
    <Grid Margin="20">
        <ComboBox x:Name="TimeSelectComboBox" Width="120" HorizontalAlignment="Left">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding}" />
                </DataTemplate>
            </ComboBox.ItemTemplate>
            <ComboBox.Template>
                <ControlTemplate TargetType="ComboBox">
                    <Grid>
                        <ToggleButton x:Name="ToggleBtn" 
                                      IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                                      Content="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}}"
                                      Width="{TemplateBinding Width}" Padding="4" BorderThickness="1" BorderBrush="#ccc"/>
                        <Popup x:Name="Part_Popup" IsOpen="{Binding IsChecked, ElementName=ToggleBtn}" StaysOpen="False" AllowsTransparency="True" Placement="Bottom">
                            <Border Background="White" BorderBrush="#ccc" BorderThickness="1" Margin="0 2 0 0" Padding="4">
                                <Grid Width="180">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="60"/>
                                        <ColumnDefinition Width="60"/>
                                        <ColumnDefinition Width="60"/>
                                    </Grid.ColumnDefinitions>
                                    <ListBox x:Name="HourList" Grid.Column="0" BorderThickness="0">
                                        <ListBox.ItemContainerStyle>
                                            <Style TargetType="ListBoxItem">
                                                <Setter Property="HorizontalContentAlignment" Value="Center"/>
                                                <EventSetter Event="PreviewMouseLeftButtonDown" Handler="TimeItem_Click"/>
                                            </Style>
                                        </ListBox.ItemContainerStyle>
                                    </ListBox>
                                    <ListBox x:Name="MinuteList" Grid.Column="1" BorderThickness="1 0" BorderBrush="#eee">
                                        <ListBox.ItemContainerStyle>
                                            <Style TargetType="ListBoxItem">
                                                <Setter Property="HorizontalContentAlignment" Value="Center"/>
                                                <EventSetter Event="PreviewMouseLeftButtonDown" Handler="TimeItem_Click"/>
                                            </Style>
                                        </ListBox.ItemContainerStyle>
                                    </ListBox>
                                    <ListBox x:Name="SecondList" Grid.Column="2" BorderThickness="0">
                                        <ListBox.ItemContainerStyle>
                                            <Style TargetType="ListBoxItem">
                                                <Setter Property="HorizontalContentAlignment" Value="Center"/>
                                                <EventSetter Event="PreviewMouseLeftButtonDown" Handler="TimeItem_Click"/>
                                            </Style>
                                        </ListBox.ItemContainerStyle>
                                    </ListBox>
                                </Grid>
                            </Border>
                        </Popup>
                    </Grid>
                </ControlTemplate>
            </ComboBox.Template>
        </ComboBox>
    </Grid>
</Window>

第三步:编写后台逻辑

初始化可选值、处理列项点击事件,注意要从模板中找到三列对应的ListBox控件:

public partial class MainWindow : Window
{
    private ListBox HourList, MinuteList, SecondList;
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        // 从控件模板中获取三列选择列表
        HourList = TimeSelectComboBox.Template.FindName("HourList", TimeSelectComboBox) as ListBox;
        MinuteList = TimeSelectComboBox.Template.FindName("MinuteList", TimeSelectComboBox) as ListBox;
        SecondList = TimeSelectComboBox.Template.FindName("SecondList", TimeSelectComboBox) as ListBox;

        // 初始化时、分、秒可选范围
        for (int i = 0; i < 24; i++) HourList.Items.Add($"{i:D2}");
        for (int i = 0; i < 60; i++)
        {
            MinuteList.Items.Add($"{i:D2}");
            SecondList.Items.Add($"{i:D2}");
        }

        // 设置默认选中值 00:00:00
        var defaultTime = new TimeSelectItem { Hour = 0, Minute = 0, Second = 0 };
        TimeSelectComboBox.SelectedItem = defaultTime;
        HourList.SelectedItem = "00";
        MinuteList.SelectedItem = "00";
        SecondList.SelectedItem = "00";
    }

    private void TimeItem_Click(object sender, MouseButtonEventArgs e)
    {
        // 阻止ListBox默认选中逻辑冒泡
        e.Handled = true;
        var clickedValue = (sender as ListBoxItem).Content.ToString();
        var parentList = ItemsControl.ItemsControlFromItemContainer(sender as ListBoxItem) as ListBox;
        var currentTime = TimeSelectComboBox.SelectedItem as TimeSelectItem ?? new TimeSelectItem();

        // 根据点击的列更新对应时间字段
        if (parentList.Name == nameof(HourList))
        {
            currentTime.Hour = int.Parse(clickedValue);
            HourList.SelectedItem = clickedValue;
        }
        else if (parentList.Name == nameof(MinuteList))
        {
            currentTime.Minute = int.Parse(clickedValue);
            MinuteList.SelectedItem = clickedValue;
        }
        else if (parentList.Name == nameof(SecondList))
        {
            currentTime.Second = int.Parse(clickedValue);
            SecondList.SelectedItem = clickedValue;
        }

        // 刷新ComboBox显示值
        TimeSelectComboBox.SelectedItem = currentTime;
    }
}

避坑说明

  • 不要在下拉Popup中嵌入独立ComboBox控件:控件获取焦点时会触发Popup的自动关闭逻辑,无法完成连续选择
  • 点击列项时必须将PreviewMouseLeftButtonDown事件标记为已处理,否则ListBox默认选中路由逻辑会干扰时间值更新
  • 选中项的显示格式直接修改TimeSelectItem.ToString()方法即可
  • 对接MVVM模式时,直接将TimeSelectComboBox.SelectedItem绑定到ViewModel对应属性,点击事件中更新绑定值即可,无需额外自定义依赖属性

内容的提问来源于stack exchange,提问作者Peter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 12:06:19