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

Posted

技术标签:

【中文标题】WPF XAML 绑定中的自动字段更新 - 简单示例不起作用【英文标题】:Automatic field update in WPF XAML binding - simple example not working 【发布时间】:2021-12-09 18:06:17 【问题描述】:

我是 C#、WPF、XAML 和绑定方面的新手,为了学习这个良好的环境,我自己创建了这个小练习:我想显示一个包含增值税和不包含增值税的价格。但是我希望在更改字段后立即自动更新另一个字段(所以只要我点击 Tab)。 我已经创建了一个类(感谢我在 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 的类来计算数字:

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 设置,但这似乎并没有改变任何东西..

【问题讨论】:

默认情况下,当控件失去焦点时会更新 xaml 绑定,...您可以通过指定 UpdateSourceTrigger 来更改该行为。对于您的示例: Text="Binding IncludeVat,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged" "默认情况下,当控件失去焦点时会更新 xaml 绑定" - true,但仅适用于 TextBox 的 Text 属性。当目标属性更改时,所有其他绑定都会更新其源属性。另请注意,Text 属性默认绑定 TwoWay。显式设置Mode=TwoWay 是多余的。 我已经尝试过 UpdateSourceTrigger=PropertyChanged 设置,但这似乎并没有改变任何东西...... @Tjerk 好的,还有一件事。 UpdateSourceTrigger 告诉 wpf 绑定系统何时将新值推回视图模型。现在您已设置含增值税的价格,但您必须通知不含增值税的价格已更改。只需调用 NotifyPropertyChanged(nameof(ExcludeVat));在 IncludeVat 属性设置器的末尾。 为了通知多个同时发生的属性更改,您可以使用空或 null PropertyName 触发一次 PropertyChanged 事件。这会将所有绑定通知到源对象的所有属性。 【参考方案1】:

假设您想在从 UI 更改 IncludeVat 时更新 ExcludeVat。 您需要在 IncludeVat 的 setter 中为这两个属性提高 notify 属性。

 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 来更新。你是 ExcludeVat 的吸气剂:get => m_IncludeVat - Tax;现在将获得更新后的值,并且 UI 将反映这一点。

【讨论】:

以上是关于WPF XAML 绑定中的自动字段更新 - 简单示例不起作用的主要内容,如果未能解决你的问题,请参考以下文章

如何自动更新 WPF DataGrid 和 xml 之间的绑定

WPF XAML DataGrid 和 SelectAll

XAML 中的 WPF ListView 绑定 ItemsSource

C#/XAML/WPF 绑定部分工作,仅显示列表中的第一项

wpf绑定--学习笔记2

WPF - 从 XAML 中的 StringFormat 绑定属性访问属性值