WPF XAML 绑定中的自动字段更新 - 简单示例不起作用

Automatic field update in WPF XAML binding - simple example not working

我是 C#、WPF、XAML 和绑定的新手,为了学习这个不错的环境,我自己创建了这个小练习:我想显示包含增值税和不含增值税的价格。但是我想让另一个字段在我更改字段后立即自动更新(所以只要我点击 Tab)。 我已经创建了一个 class(感谢我在 Stack Overflow 中发布的另一个问题的帮助)来进行数学运算。但我现在向我们提出的问题是这些字段没有得到更新。我使用 INotifyPropertyChange 接口,但由于某种原因,它不起作用。 这是正在发生的事情:

我使用以下 XAML 代码:

<Window x:Class="BTWv2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BTWv2"
        mc:Ignorable="d"
        Title="MainWindow" Height="150" Width="250">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20" />
            <ColumnDefinition Width="auto" />
            <ColumnDefinition Width="auto" />
            <ColumnDefinition Width="auto" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="20" />

        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="20"/>

        </Grid.RowDefinitions>

        <Label Grid.Column="1" Grid.Row="1" Content="Amount inc VAT:" VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBox 
                 Grid.Column="2" Grid.Row="1" 
                 VerticalAlignment="Center" HorizontalAlignment="Center" 
                 Width="100" Text="{Binding IncludeVat,Mode=TwoWay}"/>

        <Label Grid.Column="1" Grid.Row="3" Content="Amount excl VAT:" VerticalAlignment="Center" HorizontalAlignment="Center"/>

        <TextBox Text="{Binding ExcludeVat,Mode=TwoWay}"
               Grid.Column="2" Grid.Row="3" 
               VerticalAlignment="Center" HorizontalAlignment="Center" 
               MinWidth="100"
               />
    </Grid>
</Window>

这里是实际的 C# 代码(我使用名为 CalculateVAT 的 class 来计算数字:

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace BTWv2
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            CalculateVAT Price = new CalculateVAT();
            Price.IncludeVat = 100;
            DataContext = Price;

        }

        public class CalculateVAT : INotifyPropertyChanged
        {
            private decimal m_IncludeVat; // <- decimal is a better choice for finance

            // To compute VAT we should know the percent; let it be known
            public const decimal Percent = 21.0m;


            public decimal IncludeVat
            {
                get => m_IncludeVat;
                set
                {
                    // negative cash are usually invalid; if it's not the case, drop this check
                    if (value < 0)
                        throw new ArgumentOutOfRangeException(nameof(value));
                    m_IncludeVat = value;
                    Tax = Math.Round(m_IncludeVat / 100 * Percent, 2);
                    NotifyPropertyChanged();
                }
            }

            public decimal ExcludeVat
            {
                get => m_IncludeVat - Tax;
                set
                {
                    if (value < 0)
                        throw new ArgumentOutOfRangeException(nameof(value));
                    m_IncludeVat = Math.Round(value / 100 * (100 + Percent), 2);
                    Tax = m_IncludeVat - value;
                    NotifyPropertyChanged();
                }
            }

            // Let's be nice and provide Tax value as well as IncludeVat, ExcludeVat 
            public decimal Tax { get; private set; }

            public override string ToString() =>
              $"Include: {IncludeVat:f2}; exclude: {ExcludeVat:f2} (tax: {Tax:f2})";



            public event PropertyChangedEventHandler? PropertyChanged;

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

    }
}

有人知道我在这里遗漏了什么吗? 我已经在绑定中尝试了 UpdateSourceTrigger=PropertyChanged 设置,但这似乎没有任何改变..

假设您想在从 UI 更改 IncludeVat 时更新 ExcludeVat。 您需要为 IncludeVat 的 setter 中的两个属性提出通知 属性 已更改。

 public decimal IncludeVat
            {
                get => m_IncludeVat;
                set
                {
                    // negative cash are usually invalid; if it's not the case, drop this check
                    if (value < 0)
                        throw new ArgumentOutOfRangeException(nameof(value));
                    m_IncludeVat = value;
                    Tax = Math.Round(m_IncludeVat / 100 * Percent, 2);
                    NotifyPropertyChanged(nameof(IncludeVat));
                    NotifyPropertyChanged(nameof(ExcludeVat));
                }
            }

这将做的是,当您从 UI 更改 IncludeVat 时,setter 现在会引发 属性 更改为 ExcludeVat。 UI 将尝试通过调用 ExcludeVat 的 getter 进行更新。您 getter 的 ExcludeVat :获取 => m_IncludeVat - 税;现在将获得更新后的值,UI 将反映出来。