为什么INotifyPropertyChanged只会在至少改变两个字符时触发?

时间:2009-04-02 08:53:10

标签: c# wpf silverlight xaml inotifypropertychanged

我终于得到了一个Silverlight MVVM示例,这样当我更改名字和姓氏文本框的值时,全名会自动更改。

然而,奇怪的是,我的模型继承自 INotifyPropertyChanged仅在我更改 至少2个字符 时通知姓。

  • 如果我将“Smith”改为“Smith1”,那么不会发生任何事件
  • 如果我将“Smith”更改为“Smith12”,则会按预期触发该事件

之前是否有人在Silverlight / XAML / INotifyPropertyChanged中遇到此问题?会是什么呢? 是否有某个设置指示文本框在将其自身更改为“已更改”之前需要更改多少?

以下是我正在使用的代码的主要部分:

Customer.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace TestMvvm345.Model
{
    public class Customer : INotifyPropertyChanged
    {
        public int ID { get; set; }
        public int NumberOfContracts { get; set; }

        private string firstName;
        private string lastName;

        public string FirstName
        {
            get { return firstName; }
            set
            {
                firstName = value;
                RaisePropertyChanged("FirstName");
                RaisePropertyChanged("FullName");
            }
        }

        public string LastName
        {
            get { return lastName; }
            set
            {
                lastName = value;
                RaisePropertyChanged("LastName");
                RaisePropertyChanged("FullName");
            }
        }

        public string FullName
        {
            get { return firstName + " " + lastName; }
        }

        #region INotify
        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        } 
        #endregion

    }
}

CustomerHeaderView.xaml:

<UserControl x:Class="TestMvvm345.Views.CustomerHeaderView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <StackPanel HorizontalAlignment="Left">
            <ItemsControl ItemsSource="{Binding}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBox x:Name="FirstName" 
                                Text="{Binding Path=FirstName, Mode=TwoWay}" 
                                Width="150" 
                                Margin="3 5 3 5"/>
                            <TextBox x:Name="LastName" 
                                Text="{Binding Path=LastName, Mode=TwoWay}" 
                                Width="150"
                                Margin="0 5 3 5"/>
                            <TextBlock x:Name="FullName" 
                                Text="{Binding Path=FullName, Mode=TwoWay}" 
                                Margin="0 5 3 5"/>
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </Grid>
</UserControl>

CustomerViewModel.cs:

using System.ComponentModel;
using System.Collections.ObjectModel;
using TestMvvm345.Model;

namespace TestMvvm345
{
    public class CustomerViewModel
    {
        public ObservableCollection<Customer> Customers { get; set; }

        public void LoadCustomers()
        {
            ObservableCollection<Customer> customers = new ObservableCollection<Customer>();

            //this is where you would actually call your service
            customers.Add(new Customer { FirstName = "Jim", LastName = "Smith", NumberOfContracts = 23 });
            customers.Add(new Customer { FirstName = "Jane", LastName = "Smith", NumberOfContracts = 22 });
            customers.Add(new Customer { FirstName = "John", LastName = "Tester", NumberOfContracts = 33 });
            customers.Add(new Customer { FirstName = "Robert", LastName = "Smith", NumberOfContracts = 2 });
            customers.Add(new Customer { FirstName = "Hank", LastName = "Jobs", NumberOfContracts = 5 });

            Customers = customers;
        }

    }
}

MainPage.xaml.cs中:

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    CustomerViewModel customerViewModel = new CustomerViewModel();
    customerViewModel.LoadCustomers();
    CustomerHeaderView.DataContext = customerViewModel.Customers;
}

更新:

我在WPF中重新制作了这个项目,它运行正常。也许这是一个Silverlight 3问题。

3 个答案:

答案 0 :(得分:2)

使用您的示例代码,它非常适合我。我进行单个字符更改然后移动焦点并正确地发生FullName更新

除此之外,您对NotifyPropertyChanged的使用存在缺陷

    public string FirstName
    {
        get { return firstName; }
        set
        {
            firstName = value;
            RaisePropertyChanged("FirstName");
            RaisePropertyChanged("FullName");
        }
    }

应该是:

    public string FirstName
    {
        get { return firstName; }
        set
        {
            if (firstName != value)
            {
                firstName = value;
                RaisePropertyChanged("FirstName");
                RaisePropertyChanged("FullName");
            }
        }
    }

您希望避免导致事件触发,并且在非更改时发生关联的重新绑定。

答案 1 :(得分:0)

我在Silverlight 2中发现了同样的问题。我认为这是一个错误。这是我的错误报告(目前尚未回答):

http://silverlight.net/forums/t/59207.aspx

答案 2 :(得分:-1)

您可能还希望在Binding语句中包含“UpdateSourceTrigger = PropertyChanged”。即

Text="{Binding Path=FirstName,UpdateSourceTrigger=PropertyChanged}"

这将确保在文本框中进行更改时所有值都会更新。