计算字段在 UI 中编辑之前不会更新

Calculated field not updating until edited in UI

作为新手程序员,我正在尝试使用 XAML 和 C# 测试数据绑定。我有两个绑定到属性的滑块,我想用滑块设置的两个属性值的总和来更新 TextBox。

我正在使用 INotifyPropertyChanged 并尝试更改我能找到的每个 属性 但我无法让文本框更新,直到我编辑文本框,此时,文本框更新为正确的值。使用 UpdateSourceTrigger=PropertyChanged 只会在我编辑文本框时更新文本框,而不是在我 select 另一个元素时更新文本框。我试过编写一个单独的事件处理程序,它不使用 [CallerNameMember] 并使用指定的 属性 但它似乎没有改变任何东西。

<Grid>
    <Grid.RowDefinitions>

    </Grid.RowDefinitions>

    <TextBox Grid.Row="0"
             Text="{Binding BoundNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
             FontSize="20"
             FontWeight="Bold"
             AllowDrop="False" />

    <Slider Grid.Row="1"
            Value="{Binding BoundNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            Maximum="100"
            Minimum="10"
            IsSnapToTickEnabled="True"
            TickFrequency="10" />
    <TextBox Grid.Row="2"
             Text="{Binding BoundNumber2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
             AllowDrop="False" />
    <Slider Grid.Row="3"

            Value="{Binding BoundNumber2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            Maximum="100"
            Minimum="10"
            IsSnapToTickEnabled="True"
            TickFrequency="10" />


    <TextBox Grid.Row="4"
            Name="MathBox"
             Text="{Binding QuickMath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}">
           </TextBox>

</Grid>

public partial class OrderScreen : INotifyPropertyChanged
{
    public OrderScreen()
    {
        DataContext = this;

        InitializeComponent();
     }

    private int quickMath;
    public int QuickMath
    {
        get { return _boundNumber + _boundNumber2; }
        set
        {

            if (value != quickMath)
            {
                quickMath = value;
                OnPropertyChanged();

            }
        }
    }
    private int _boundNumber;
    public int BoundNumber
    {
        get { return _boundNumber; }
        set
        {
            if (_boundNumber != value)
            {
                _boundNumber = value;
               // MathBox.Text = quickMath.ToString();
                OnPropertyChanged();

            }
        }
    }

    private int _boundNumber2;
    public int BoundNumber2
    {
        get { return _boundNumber2; }
        set
        {
            if (_boundNumber2 != value)
            {
                _boundNumber2 = value;
                MathBox.Text = quickMath.ToString();
                OnPropertyChanged();

            }
        }
    }

 public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {

        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

我可以让它与注释掉的 MathBox.Text = quickMath.ToString(); 一起工作,但我希望有更好的方法来使用数据绑定。感谢期待!

您尚未在任何地方初始化 PropertyChanged 事件,因此永远不会调用它。像这样声明和初始化它:

public event PropertyChangedEventHandler PropertyChanged = delegate { };

绑定机制订阅了DataSource对象的PropertyChanged事件,所以不需要"initialize"事件连同INPC实现,但是你可能已经注意到,QuickMath 属性 的 PropertyChanged 事件确实不会在 BoundNumberBoundNumber2 更改时触发。

您可以通过不同的方式修复它,例如为所有受影响的属性显式调用 OnPropertyChanged:

private int _boundNumber;
public int BoundNumber
{
    get { return _boundNumber; }
    set
    {
        if (_boundNumber != value)
        {
            _boundNumber = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(QuickMath));
        }
    }
}

请注意,这样您可以保留 QuickMath 属性 个 read-only。这种方法在其他情况下效果很好,例如使用 time-related 属性,假设您的数据源 属性 格式化字符串,如 "Edited 2 minutes ago" 基于记录时间戳和当前时间,你调用PropertyChanged作为定时任务。

public int QuickMath => _boundNumber + _boundNumber2;

或者,您可以在修改 BoundNumberBoundNumber2 的同时更新 QuickMath 以触发 QuickMath setter 内部的 OnPropertyChanged() 调用:

private int _boundNumber2;
public int BoundNumber2
{
    get { return _boundNumber2; }
    set
    {
        if (_boundNumber2 != value)
        {
            _boundNumber2 = value;
            OnPropertyChanged();
            QuickMath = BoundNumber + BoundNumber2;
        }
    }
}

如果 QuickMath 中的逻辑不允许将其变为 read-only 属性,这就有意义了。在这种情况下,您必须相应地调整 getter 并在那里使用 private 或 protected setter 以避免数据不一致和意外行为。

private int _quickMath;
public int QuickMath
{
    get { return _quickMath; }
    private set
    {
        if (value != _quickMath)
        {
            _quickMath = value;
            OnPropertyChanged();
        }
    }
}

在这两种情况下都不需要 two-way 绑定到 QuickMath:

<TextBlock Grid.Row="4" Text="{Binding QuickMath, Mode=OneWay}"/>

在 side-note 上并查看其余代码,真正值得一提的是绑定机制有望将 UI 从数据中分离出来,其中 XAML 知道数据源对象属性(名称和类型)而不是关于它的内部实现,而数据源对象可能根本不知道 XAML。所以

  1. 不应从数据对象调用 FrameworkElement,如 MathBox.Text
  2. 将数据对象 class 与页面或控件 class 完全分开被认为是一个很好的设计。

希望这对您有所帮助。