INotifyPropertyChanged和ObservableCollection在WPF中的应用

3

目前我有一个只显示一个月份(无论我传入什么月份)的日历。我正在尝试让用户从下拉框中选择月份和年份,并更新日历。我正在使用可观察集合进行绑定,这方面我有点熟悉。但是我不知道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>();
    }
3个回答

7
这是您通常实现 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封装在自身中,而不是依赖于代码更改来触发它。虽然这只适用于单向属性,因为UI不应该能够触发OnPropertyChanged。 - myermian
@m-y - 你如何使用自动实现的属性来实现INotifyPropertyChanged? - CodeNaked
@m-y,当然,你可以在类的任何地方调用OnPropertyChanged,但在这种情况下,不能保证事件会被触发(例如,如果属性是从类外部更新的)。 - Thomas Levesque
2
我不调用setter中的OnPropertyChanged方法的唯一情况是对于计算属性且没有getter的属性。在这种情况下,当它所依赖的属性更新时,我会为此属性调用OnPropertyChanged方法。 - Thomas Levesque
那么,我应该如何连接包含我想要发送到OnPropertyChanged的月份的下拉列表框呢? - TMan
显示剩余2条评论

5

当你绑定到一个属性(即使该属性是一个ObservableCollection),任何对该属性的更改(而不是属性内容)都应该引发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,则可以在其中执行相同的操作。


另外,如果您的BindingCamper是您的ViewModel,则可以在其中执行相同的操作。


2
当你从代码后台更改属性并希望更新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");

如果您想从UI更改属性,则可能需要设置双向绑定。

但这只是真实的情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接