绑定TimePicker和CalendarDatePicker UWP时的不同行为

问题描述

这个问题更多是出于好奇,因为我已经找到了解决方法,但是我仍然想了解区别。我有一个简单的ContentDialog派生类,该类允许分别使用CalendarDatePicker和TimePicker选择两个日期和时间。我将它们的日期和时间属性绑定如下:

        <StackPanel Orientation="Vertical" Grid.Column="0" Grid.Row="0">
            <TextBlock>From</TextBlock>
            <CalendarDatePicker  Date="{x:Bind DateFromDate,Mode=TwoWay}" />
            <TimePicker Time="{x:Bind DateFromTime,Mode=TwoWay}" />
        </StackPanel>

        <StackPanel Orientation="Vertical" Grid.Column="1" Grid.Row="0">
            <TextBlock>To</TextBlock>
            <CalendarDatePicker  Date="{x:Bind DatetoDate,Mode=TwoWay}" />
            <TimePicker Time="{x:Bind DatetoTime,Mode=TwoWay}" />
        </StackPanel>

,后面带有以下代码

    private DateTimeOffset _dateFromDate;
    private TimeSpan _dateFromTime;
    private DateTimeOffset _datetoDate;
    private TimeSpan _datetoTime;


    private DateTimeOffset DateFromDate
    {
        get => _dateFromDate;
        set
        {
            _dateFromDate = value;
            NotifyPropertyChanged();
        }
    }
    private TimeSpan DateFromTime
    {
        get => _dateFromTime;
        set
        {
            if (value != _dateFromTime)
            {
                _dateFromTime = value;
                NotifyPropertyChanged();
            }    
        }
    }

    private DateTimeOffset DatetoDate
    {
        get => _datetoDate;
        set
        {
            _datetoDate = value;
            NotifyPropertyChanged();
        }
    }
    private TimeSpan DatetoTime {
        get => _datetoTime;
        set
        {
            if(value != _datetoTime)
            {
                _datetoTime = value;
                NotifyPropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") =>  PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));

您可能会注意到,在绑定到TimePickers的TimeSpan属性中,检查该属性是否已更改。如果删除属性,并将其保留在DateTimeOffset属性中,则每次我设置这些属性时都会引发StackOverflowException。如果您用谷歌搜索,您将遇到诸如this之类的问题,其中发生了非常明显的无限递归。如果我调试它,则确实会一次又一次调用setter,因此我的理论是,TimePicker背后的代码中的某个地方,当它收到Notification时,它不仅会获取属性,而且还会对其进行设置。有什么想法吗?

解决方法

这里发生了非常明显的无限递归。如果我对其进行调试,则确实会一次又一次地调用设置程序

我可以重现此问题,它看起来x:Bind TwoWay模型使绑定成为圆形,我可以确定这不是设计使然。当前,有许多解决方法可以解决此问题。

使用绑定替换x:Bind。

public TimeSpan DateToTime
{
    get => _dateToTime;

    set
    {

        _dateToTime = value;
        OnPropertyChanged();

    }
}
       
<TimePicker Time="{Binding DateToTime,Mode=TwoWay}" />

将TwoWay修改为OneWay。

<TimePicker Time="{x:Bind DateFromTime,Mode=OneWay}" />

最近提到的一个是使用条件语句停止此循环的。

请随时通过Windows反馈中心应用报告此情况。我将继续跟踪这个问题。

更新

请注意,NotifyPropertyChanged的文档明确指出仅在值实际更改时才触发NotifyPropertyChanged事件。 请注意,不遵循文档时,行为是不确定的。因此,在不遵循文档的情况下,由于两种行为均未定义,因此您无法假定不良情况下的行为将保持一致。

这里是INotifyPropertyChanged上的doc

仅当属性的设置程序无法防止在值等于先前值时触发PropertyChanged时,才会重现此问题。因此,无论行为如何不同,它们都是不确定的行为。如果您不遵循本文档,那么您会期望事情以奇怪的方式破坏,这再次是因为行为未定义。

为什么DatePicker可以在没有值等于先前值的情况下工作?

在某些情况下,不确定的行为可能会符合预期,但是由于它没有遵循文档,因此存在风险,因为它随时可能更改而不会发出警告。