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

WPF Toolkit TimePicker手动输入时间不更新Value,仅失焦触发PropertyChanged

解决WPF Toolkit TimePicker手动输入不触发PropertyChanged的问题

我之前也碰到过一模一样的问题——用小键盘手动输入时间时,ResTime的绑定完全没反应,PropertyChanged事件也不触发。这其实是因为Xceed的TimePicker控件内部逻辑的问题:默认情况下,手动输入文本时,控件不会即时把输入内容解析并同步到Value属性,哪怕你在绑定里设置了UpdateSourceTrigger=PropertyChanged。

下面是几个亲测有效的解决方案:

方案一:在TextChanged事件里手动更新绑定

既然控件自己不触发,我们就手动帮它触发绑定源更新。在你已经绑定的TimePicker_TextChanged事件里添加以下代码:

private void TimePicker_TextChanged(object sender, TextChangedEventArgs e)
{
    var timePicker = sender as Xceed.Wpf.Toolkit.TimePicker;
    if (timePicker == null) return;

    // 获取TimePicker内部的TextBox控件(默认模板里的名称是PART_TextBox)
    var textBox = timePicker.Template.FindName("PART_TextBox", timePicker) as TextBox;
    if (textBox != null)
    {
        // 先更新TextBox的Text绑定源,再同步Value属性的绑定
        textBox.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
        timePicker.GetBindingExpression(TimePicker.ValueProperty)?.UpdateSource();
    }
}

这个方法最直接,不需要修改模板或者额外写类,适合快速解决问题。

方案二:自定义Style修改内部TextBox的绑定

如果不想在后台写事件逻辑,可以通过自定义Style来修改TimePicker内部TextBox的Text绑定,把它的UpdateSourceTrigger设为PropertyChanged:

<Window.Resources>
    <Style TargetType="{x:Type xctk:TimePicker}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type xctk:TimePicker}">
                    <!-- 这里复制TimePicker的默认模板代码,找到PART_TextBox并修改其Text绑定 -->
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <Grid>
                            <TextBox x:Name="PART_TextBox"
                                     Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, UpdateSourceTrigger=PropertyChanged}"
                                     HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                     VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                     Margin="3"/>
                            <ToggleButton x:Name="PART_ToggleButton"
                                          HorizontalAlignment="Right"
                                          VerticalAlignment="Center"
                                          IsChecked="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"/>
                        </Grid>
                    </Border>
                    <!-- 其他默认模板元素(比如下拉面板)可以直接复制原模板的代码 -->
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

注意:需要确保复制的模板和你当前使用的Toolkit版本的默认模板一致,不然可能会丢失一些功能。如果觉得复制麻烦,方案一其实更省心。

方案三:用附加属性实现通用解决方案

如果你的项目里有多个TimePicker需要解决这个问题,可以写一个附加属性来封装逻辑,避免重复代码:

public static class TimePickerSyncHelper
{
    public static readonly DependencyProperty SyncValueOnTextChangedProperty =
        DependencyProperty.RegisterAttached(
            "SyncValueOnTextChanged", 
            typeof(bool), 
            typeof(TimePickerSyncHelper), 
            new PropertyMetadata(false, OnSyncValueOnTextChangedChanged));

    public static bool GetSyncValueOnTextChanged(DependencyObject obj)
    {
        return (bool)obj.GetValue(SyncValueOnTextChangedProperty);
    }

    public static void SetSyncValueOnTextChanged(DependencyObject obj, bool value)
    {
        obj.SetValue(SyncValueOnTextChangedProperty, value);
    }

    private static void OnSyncValueOnTextChangedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var timePicker = d as Xceed.Wpf.Toolkit.TimePicker;
        if (timePicker == null) return;

        if ((bool)e.NewValue)
        {
            timePicker.Loaded += TimePicker_Loaded;
        }
        else
        {
            timePicker.Loaded -= TimePicker_Loaded;
            var textBox = timePicker.Template.FindName("PART_TextBox", timePicker) as TextBox;
            textBox?.TextChanged -= TextBox_TextChanged;
        }
    }

    private static void TimePicker_Loaded(object sender, RoutedEventArgs e)
    {
        var timePicker = sender as Xceed.Wpf.Toolkit.TimePicker;
        var textBox = timePicker.Template.FindName("PART_TextBox", timePicker) as TextBox;
        textBox?.TextChanged += TextBox_TextChanged;
    }

    private static void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        var textBox = sender as TextBox;
        var timePicker = textBox.TemplatedParent as Xceed.Wpf.Toolkit.TimePicker;
        timePicker?.GetBindingExpression(TimePicker.ValueProperty)?.UpdateSource();
    }
}

然后在XAML里直接给TimePicker加上这个附加属性就行:

<xctk:TimePicker Name="timePicker"
                 local:TimePickerSyncHelper.SyncValueOnTextChanged="True"
                 StartTime="{Binding MpStartTime,UpdateSourceTrigger=PropertyChanged}"
                 EndTime="{Binding MpEndTime,UpdateSourceTrigger=PropertyChanged}"
                 Value="{Binding ResTime,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
                 Format="Custom" FormatString="hh:mm tt"/>

方案四:升级Toolkit版本

这个问题可能是旧版本Xceed WPF Toolkit的bug,你可以尝试从NuGet安装最新版本的Xceed.Wpf.Toolkit包,看看官方是否已经修复了这个手动输入不更新绑定的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:44:45