Проблема с UpdateSourceTrigger = PropertyChanged и StringFormat в WPF

У меня есть текстовое поле в моем приложении, которое является данными, привязанными к десятичному полю в моем классе, и режим привязки является двусторонним. Я использую 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, но проблема не решена.


person Elangovan    schedule 08.07.2010    source источник
comment
у вас есть ошибки привязки?   -  person Kishore Kumar    schedule 08.07.2010
comment
нет, это не форматирование отображаемого текста. В остальном все работает нормально. вообще никакой ошибки.   -  person Elangovan    schedule 08.07.2010


Ответы (1)


Проблема в том, что если вы используете конвертер и обновляете свойство, измененное, то WPF будет игнорировать изменения свойств, пока это процесс обновления исходного свойства. Поскольку событие PropertyChanged возникает из установщика, WPF игнорирует его.

Причина в том, что если вы наберете «1» в текстовом поле, это будет преобразовано в десятичное значение 1.0, а затем будет преобразовано обратно в строку «$ 1.00». Это изменит текст в текстовом поле и сбросит курсор, поэтому, если вы попытаетесь ввести «12», вы получите «2 $ 1,00».

Обратите внимание, что ваш объект Employee обновляется, и проблема только в том, что TextBox не получает новое отформатированное значение.

Если вам действительно нужно такое поведение, вы можете установить IsAsync=True для привязки, и WPF больше не будет рассматривать это как повторное изменение и разрешит его. Это также изменилось в .NET 4.0, поэтому при обновлении до последней версии фреймворка вы должны увидеть ожидаемое поведение.

person Quartermeister    schedule 08.07.2010
comment
У меня такая же проблема в silverlight, и, к сожалению, в silverlight у вас нет атрибута IsAsync в привязке. Как мы можем добиться этого в Silverlight ?? - person Sajid Ali; 27.02.2013
comment
Установка Async = True, кажется, перемещает каретку обратно в начало строки при каждой модификации - не очень удобно - person Slugart; 04.05.2015