WPF 中 UpdateSourceTrigger=PropertyChanged 和 StringFormat 的问题

发布于 2024-09-08 06:51:38 字数 6824 浏览 2 评论 0原文

我的应用程序中有一个文本框,它将数据绑定到我的类中的小数字段,并且绑定模式是两种方式。我使用 StringFormat={0:c} 进行货币格式设置。

只要我不触摸“UpdateSourceTrigger”,这就可以正常工作。如果我设置 UpdateSourceTrigger=PropertyChanged ,它会停止格式化我输入的文本。

这是我的代码示例

Employee.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace Converter
{
    public class Employee : INotifyPropertyChanged
    {
        int _employeeNumber;
        string _firstName;
        string _lastName;
        string _department;
        string _title;
        decimal _salary;

        public Employee()
        {
            _employeeNumber = 0;
            _firstName =
                _lastName =
                _department =
                _title = null;
        }
        public int EmployeeNumber
        {
            get { return _employeeNumber; }
            set 
            { 
                _employeeNumber = value;
                OnPropertyChanged("EmployeeNumber");
            }
        }
        public string FirstName
        {
            get { return _firstName; }
            set 
            { 
                _firstName = value;
                OnPropertyChanged("FirstName");
            }
        }

        public string LastName
        {
            get { return _lastName; }
            set 
            { 
                _lastName = value;
                OnPropertyChanged("LastName");
            }
        }

        public string Department
        {
            get { return _department; }
            set 
            { 
                _department = value;
                OnPropertyChanged("Department");
            }
        }

        public string Title
        {
            get { return _title + " salary: " + _salary.ToString(); }
            set 
            { 
                _title = value;
                OnPropertyChanged("Title");
            }
        }

        public decimal Salary
        {
            get { return _salary; }
            set
            {
                _salary = value;                
                OnPropertyChanged("Salary");
                OnPropertyChanged("Title");
            }
        }

        public override string ToString()
        {
            return String.Format("{0} {1} ({2})", FirstName, LastName, EmployeeNumber);
        }


        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChangedEventArgs args = new PropertyChangedEventArgs(propertyName);
                this.PropertyChanged(this, args);
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}

EmployeeList.cs:

using System.Collections.ObjectModel;

namespace Converter
{
    public class EmployeeList : ObservableCollection<Employee>
    {
    }
}

Window1.xaml:

<Window x:Class="Converter.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Converter"
    Title="Window1" Height="500" Width="500">
    <Window.Resources>
        <local:EmployeeList x:Key="myEmployeeList">
            <local:Employee EmployeeNumber="1" FirstName="John" LastName="Dow" Title="Accountant" Department="Payroll" Salary="25000.00" />
            <local:Employee EmployeeNumber="2" FirstName="Jane" LastName="Austin" Title="Account Executive" Department="Customer Management" Salary="25000.00" />
            <local:Employee EmployeeNumber="3" FirstName="Ralph" LastName="Emmerson" Title="QA Manager" Department="Product Development" Salary="25000.00" />
            <local:Employee EmployeeNumber="4" FirstName="Patrick" LastName="Fitzgerald" Title="QA Manager" Department="Product Development" Salary="25000.00" />
            <local:Employee EmployeeNumber="5" FirstName="Charles" LastName="Dickens" Title="QA Manager" Department="Product Development" Salary="25000.00" />            
        </local:EmployeeList>
        <local:StringToDecimalCurrencyConverter x:Key="StringToDecimalCurrencyConverter"></local:StringToDecimalCurrencyConverter>
    </Window.Resources>
    <Grid DataContext="{StaticResource myEmployeeList}">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="240" />
            <RowDefinition Height="45" />
        </Grid.RowDefinitions>
        <ListBox Name="employeeListBox"  ItemsSource="{Binding Path=., Mode=TwoWay}" Grid.Row="0" />
        <Grid Grid.Row="1"  DataContext="{Binding ElementName=employeeListBox, Path=SelectedItem, Mode=TwoWay}">
            <Grid.RowDefinitions>
                <RowDefinition Height="40" />
                <RowDefinition Height="40" />
                <RowDefinition Height="40" />
                <RowDefinition Height="40" />
                <RowDefinition Height="40" />
                <RowDefinition Height="40" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="80" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Label Grid.Row="0" Grid.Column="0">First Name</Label>
            <Label Grid.Row="1" Grid.Column="0">Last Name</Label>
            <Label Grid.Row="2" Grid.Column="0">Title</Label>
            <Label Grid.Row="3" Grid.Column="0">Department</Label>
            <Label Grid.Row="4" Grid.Column="0">Salary</Label>

            <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Mode=TwoWay, Path=FirstName}" />
            <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Mode=TwoWay, Path=LastName}" />
            <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Mode=TwoWay, Path=Title}" />
            <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Mode=TwoWay,  Path=Department}" />
            <TextBox Grid.Row="4" Grid.Column="1"  Text="{Binding Mode=TwoWay, StringFormat=\{0:c\}, UpdateSourceTrigger=PropertyChanged, Path=Salary}" />
            <TextBlock Grid.Row="5" Grid.Column="1"  Text="{Binding Mode=OneWay, Converter={StaticResource StringToDecimalCurrencyConverter}, Path=Salary}" />
        </Grid>        
    </Grid>
</Window>

如果您从上面的代码中删除“UpdateSourceTrigger=PropertyChanged”,它就可以正常工作。

我也尝试过使用 Converter 而不是 StringFormat,但问题仍然没有解决。

I have a text box in my application which is data bound to a decimal field in my class and the binding mode is two way. I am using StringFormat={0:c} for currency formatting.

This works fine as long as I don't touch 'UpdateSourceTrigger'. If I set UpdateSourceTrigger=PropertyChanged , It stops formatting the text that I am entering.

here is my code example

Employee.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace Converter
{
    public class Employee : INotifyPropertyChanged
    {
        int _employeeNumber;
        string _firstName;
        string _lastName;
        string _department;
        string _title;
        decimal _salary;

        public Employee()
        {
            _employeeNumber = 0;
            _firstName =
                _lastName =
                _department =
                _title = null;
        }
        public int EmployeeNumber
        {
            get { return _employeeNumber; }
            set 
            { 
                _employeeNumber = value;
                OnPropertyChanged("EmployeeNumber");
            }
        }
        public string FirstName
        {
            get { return _firstName; }
            set 
            { 
                _firstName = value;
                OnPropertyChanged("FirstName");
            }
        }

        public string LastName
        {
            get { return _lastName; }
            set 
            { 
                _lastName = value;
                OnPropertyChanged("LastName");
            }
        }

        public string Department
        {
            get { return _department; }
            set 
            { 
                _department = value;
                OnPropertyChanged("Department");
            }
        }

        public string Title
        {
            get { return _title + " salary: " + _salary.ToString(); }
            set 
            { 
                _title = value;
                OnPropertyChanged("Title");
            }
        }

        public decimal Salary
        {
            get { return _salary; }
            set
            {
                _salary = value;                
                OnPropertyChanged("Salary");
                OnPropertyChanged("Title");
            }
        }

        public override string ToString()
        {
            return String.Format("{0} {1} ({2})", FirstName, LastName, EmployeeNumber);
        }


        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChangedEventArgs args = new PropertyChangedEventArgs(propertyName);
                this.PropertyChanged(this, args);
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}

EmployeeList.cs:

using System.Collections.ObjectModel;

namespace Converter
{
    public class EmployeeList : ObservableCollection<Employee>
    {
    }
}

Window1.xaml:

<Window x:Class="Converter.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Converter"
    Title="Window1" Height="500" Width="500">
    <Window.Resources>
        <local:EmployeeList x:Key="myEmployeeList">
            <local:Employee EmployeeNumber="1" FirstName="John" LastName="Dow" Title="Accountant" Department="Payroll" Salary="25000.00" />
            <local:Employee EmployeeNumber="2" FirstName="Jane" LastName="Austin" Title="Account Executive" Department="Customer Management" Salary="25000.00" />
            <local:Employee EmployeeNumber="3" FirstName="Ralph" LastName="Emmerson" Title="QA Manager" Department="Product Development" Salary="25000.00" />
            <local:Employee EmployeeNumber="4" FirstName="Patrick" LastName="Fitzgerald" Title="QA Manager" Department="Product Development" Salary="25000.00" />
            <local:Employee EmployeeNumber="5" FirstName="Charles" LastName="Dickens" Title="QA Manager" Department="Product Development" Salary="25000.00" />            
        </local:EmployeeList>
        <local:StringToDecimalCurrencyConverter x:Key="StringToDecimalCurrencyConverter"></local:StringToDecimalCurrencyConverter>
    </Window.Resources>
    <Grid DataContext="{StaticResource myEmployeeList}">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="240" />
            <RowDefinition Height="45" />
        </Grid.RowDefinitions>
        <ListBox Name="employeeListBox"  ItemsSource="{Binding Path=., Mode=TwoWay}" Grid.Row="0" />
        <Grid Grid.Row="1"  DataContext="{Binding ElementName=employeeListBox, Path=SelectedItem, Mode=TwoWay}">
            <Grid.RowDefinitions>
                <RowDefinition Height="40" />
                <RowDefinition Height="40" />
                <RowDefinition Height="40" />
                <RowDefinition Height="40" />
                <RowDefinition Height="40" />
                <RowDefinition Height="40" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="80" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Label Grid.Row="0" Grid.Column="0">First Name</Label>
            <Label Grid.Row="1" Grid.Column="0">Last Name</Label>
            <Label Grid.Row="2" Grid.Column="0">Title</Label>
            <Label Grid.Row="3" Grid.Column="0">Department</Label>
            <Label Grid.Row="4" Grid.Column="0">Salary</Label>

            <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Mode=TwoWay, Path=FirstName}" />
            <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Mode=TwoWay, Path=LastName}" />
            <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Mode=TwoWay, Path=Title}" />
            <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Mode=TwoWay,  Path=Department}" />
            <TextBox Grid.Row="4" Grid.Column="1"  Text="{Binding Mode=TwoWay, StringFormat=\{0:c\}, UpdateSourceTrigger=PropertyChanged, Path=Salary}" />
            <TextBlock Grid.Row="5" Grid.Column="1"  Text="{Binding Mode=OneWay, Converter={StaticResource StringToDecimalCurrencyConverter}, Path=Salary}" />
        </Grid>        
    </Grid>
</Window>

If you remove 'UpdateSourceTrigger=PropertyChanged' from the above code it works fine.

I have also tried using Converter instead of the StringFormat, and still the problem is not solved.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

鸠魁 2024-09-15 06:51:38

问题是,如果您使用转换器并更新更改的属性,那么 WPF 将在更新源属性的过程中忽略属性更改。由于 PropertyChanged 事件是从 setter 引发的,因此 WPF 会忽略它。

这样做的原因是,如果您在文本框中键入“1”,它将转换为十进制值 1.0,然后转换回字符串“$1.00”。这将更改文本框中的文本并重置光标,因此如果您尝试输入“12”,您最终会得到“2$1.00”。

请注意,您的 Employee 对象正在更新,问题只是 TextBox 没有获取新格式化的值。

如果您确实想要这种行为,可以在绑定上设置 IsAsync=True,WPF 将不再将其视为可重入更改并允许它。这在 .NET 4.0 中也发生了变化,因此如果您升级到该框架的最新版本,那么您应该会看到您期望的行为。

The problem is that if you use a converter and update on property changed then WPF will ignore property changes while it is the process of updating the source property. Since the PropertyChanged event is raised from the setter, WPF ignores it.

The reason it does this is that if you typed "1" in the text box, this would get converted to the decimal value 1.0 and then get converted back to the string "$1.00". This would change the text in the text box and reset the cursor, so if you tried to type "12" you'd end up with "2$1.00".

Note that your Employee object is getting updated, and the problem is just that the TextBox isn't getting a newly formatted value.

If you really do want that behavior, you can set IsAsync=True on the binding and WPF will no longer see it as a reentrant change and will allow it. This has also changed in .NET 4.0, so if you upgrade to the latest version of the framework then you should see the behavior you expect.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文