INotifyPropertyChanged 和 ObservableCollection WPF

Posted

技术标签:

【中文标题】INotifyPropertyChanged 和 ObservableCollection WPF【英文标题】:INotifyPropertyChanged and ObservableCollection WPF 【发布时间】:2011-12-17 13:36:51 【问题描述】:

现在我有一个只显示一个月的日历(我过去的月份)。我试图让用户从组合框中选择月份和年份并更新日历。我正在使用我熟悉的 observablecollection 进行绑定。我不知道 INotifyPropertyChanged 是如何工作的。我以前从未使用过它。非常感谢任何帮助或建议。这是我目前所拥有的:

public class Schedule : INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    public void Update(int propertyName)
    
        if (propertyName != null)
        
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                 handler.Invoke(this, new PropertyChangedEventArgs(propertyName.ToString()));
        
    


   // public void UpdateCal(PropertyChangedEventArgs e)
   // 
    //    if (PropertyChanged != null)
    //        PropertyChanged(this, e);
  //   
    public string MonthWeek  get; set; 
    public string Year  get; set; 
    public string Month  get; set; 
    public string day  get; set; 
    public string WeekOfYear  get; set; 
    public string dayofweek  get; set; 

   // public string month 
    //    get return Month; 
    //    set
    //    
     //       UpdateCal(new PropertyChangedEventArgs("month"));
      //  
   // 
    public int WeekNo  get; set; 
    public int WeekDay  get; set; 
    public DateTime Date  get; set; 
 

---这是另一个计算每个日期在网格上的位置的类----

           public SchedulePage(MainWindow parentForm)
    
        InitializeComponent();

        pick = Convert.ToInt32(comboMonth.SelectedItem) + 1;
        _parentForm = parentForm;
        // DateTime date = new DateTime(year, month, day);
        var t = new List<Schedule>();
        DateTime curr = DateTime.Now;
      //  comboMonth.Items.Add(curr.Month);
        DateTime newcurr = new DateTime(2011, pick, 1);
     //   pickdate = datePickercal.SelectedDate;
      //  DateTime newcurr = new DateTime(curr.Year, curr.Month, 1);
        var cal = System.Globalization.DateTimeFormatInfo.CurrentInfo.Calendar;
        var ms = cal.GetWeekOfYear(new DateTime(newcurr.Year, newcurr.Month, 1), System.Globalization.CalendarWeekRule.FirstDay, System.DayOfWeek.Sunday);
        for (var i = 1; newcurr.Month == pick; newcurr = newcurr.AddDays(1))
        
            var sched = new Schedule();
            var month_week = (newcurr.Day / 7) ;
            sched.MonthWeek = newcurr.GetWeekOfMonth().ToString();
            sched.Month = newcurr.Month.ToString();
            sched.Year = newcurr.Year.ToString();
            sched.day = newcurr.Day.ToString();
            sched.WeekOfYear = cal.GetWeekOfYear(newcurr, System.Globalization.CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString();
            sched.dayofweek = newcurr.DayOfWeek.ToString();
            t.Add(sched);

                _parentForm.bindings.schedule.Add(new Schedule  WeekNo = newcurr.GetWeekOfMonth()-1, WeekDay = (int)newcurr.DayOfWeek, day = newcurr.Day.ToString() );

        
        lblDate.Content = (newcurr.Month -1) + "/" + newcurr.Year;

         DataContext = _parentForm.Bindings;

---这个类使observablecollections-----

           public partial class BindingCamper 
  // This class assist in binding campers from listview to the textboxes on the camperspage
    public ObservableCollection<Camper> Campers  get; set; 
    public ObservableCollection<Staff> StaffMembers  get; set; 
    public ObservableCollection<Schedule> schedule  get; set; 
    public BindingCamper()
    
        Campers = new ObservableCollection<Camper>();
      StaffMembers = new ObservableCollection<Staff>();
      schedule = new ObservableCollection<Schedule>();
    

【问题讨论】:

【参考方案1】:

这就是您通常实现INotifyPropertyChanged 的方式:

public class Schedule : INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    

    private string _monthWeek;
    public string MonthWeek
    
        get  return _monthWeek; 
        set
        
            if (value != _monthWeek)
            
                _monthWeek = value;
                OnPropertyChanged("MonthWeek");
            
        
    

    // And so on for other properties...


基本上,您只需要在每次更新属性时触发PropertyChanged 事件,因此每个setter 都必须调用OnPropertyChanged。请注意,您不能使用自动实现的属性来执行此操作,因为您需要在 setter 中添加自定义逻辑。

【讨论】:

您可以使用自动实现的属性来执行此操作,但建议不要这样做。最好是自包含 OnPropertyChanged,而不是依靠代码更改它们来触发它。不过,这实际上只适用于 OneWay 属性,因为 UI 不应该能够触发 OnPropertyChanged。 @m-y - 如何使用自动实现的属性实现 INotifyPropertyChanged? @my,当然,您可以从类中的任何位置调用 OnPropertyChanged,但在这种情况下,不能保证会引发事件(例如,如果从类外部更新属性) @ThomasLevesque, CodeNaked:这就是我的意思...你可以在类中的任何其他位置调用 OnPropertyChanged,但这仅对 OneWay 属性有点用处(只有 getter 很重要)并且 setter 是私有的/受保护的)。我从来没有使用过这种方法,但这可能是重点。 我不在 setter 中调用 OnPropertyChanged 的​​唯一情况是用于计算且没有 getter 的属性。在这种情况下,当更新它所依赖的属性时,我为此属性调用 OnPropertyChanged【参考方案2】:

当您绑定到某个属性(即使该属性是 ObservableCollection)时,对 PROPERTY(不是属性的内容)的任何更改都应该引发 PropertyChanged 事件。

在引发 CollectionChanged 事件时,ObservableCollection 是自包含的,因此不必担心会为 ItemsSource 项本身触发事件。

XAML:

<!-- This says that ItemsSource is bound to the Campers property... -->
<ItemsControl ItemsSource="Binding Path=Campers, Mode=OneWay" />

类:

public class TheViewModel()

   private ObservableCollection<Camper> _campers;
   public ObservableCollection<Camper> Campers
   
       get  return _campers; 
       set
       
           if (Equals(_campers, value)) return;

           _campers = value;
           RaisePropertyChanged("Campers"); //Or however you implement it
       
   

   private void SomeFunc()
   
       var bindingCamper = new BindingCamper();

       Campers = bindingCamper.Campers; //This will fire the event
       //etc.
   


或者,如果您的 BindingCamper 您的 ViewModel,那么您可以在其中执行相同的操作。

【讨论】:

【参考方案3】:

当您从后面的代码更改属性并且想要更新您的 UI 时,您可以使用 INotifyPropertyChanged 接口。正如我所见,您实现了接口,甚至设置了一个助手来使用它,只是您使用 int 作为参数,您应该使用字符串。如果您设置了属性,那么只需使用正确的 PropertyName 调用您的助手,您就可以开始了。

像这样:

 public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName)
    
        PropertyChangedEventHandler handler = PropertyChanged;
        if (null != handler)
        
            handler(this, new PropertyChangedEventArgs(propertyName));
        
    

并触发事件通知UI:

NotifyPropertyChanged("YourPropertyName");

也许您也需要设置 TwoWay 绑定,但仅当您也想从 UI 更改属性时才适用。

【讨论】:

以上是关于INotifyPropertyChanged 和 ObservableCollection WPF的主要内容,如果未能解决你的问题,请参考以下文章

使用属性... INotifyPropertyChanged

NHibernate 和 INotifyPropertyChanged

IObservable<T> 和 INotifyPropertyChanged - 是不是存在连接

INotifyPropertyChanged 不起作用

[译]WPF MVVM 架构 Step By Step(添加actions和INotifyPropertyChanged接口)

INotifyPropertyChanged WPF