Проблема с UpdateSourceTrigger = PropertyChanged и StringFormat в WPF - PullRequest
2 голосов
/ 08 июля 2010

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

Я также пытался использовать конвертервместо StringFormat и до сих пор проблема не решена.

1 Ответ

5 голосов
/ 08 июля 2010

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

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

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

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

...