2012-04-21 58 views
1

我试图在WPF ListVieuw中显示警报列表。为了做到这一点,我将Listbox数据绑定到了一个包含警报列表的属性。由于我使用了MVC编程范例,因此该属性位于控制器中,并且视图的datacontext被设置为该控制器。在MVC应用程序中结合ObservableCollection <T>和列表<T>

我注意到,当我向列表中添加一个闹钟时,视图没有显示新的闹钟。经过一番研究,我发现我需要使用ObservableCollection类来正确地做到这一点。

但是,显示警报列表不是唯一需要完成的事情,所以我不能/不想将列表的变量类型更改为ObservableCollection。

我现在试着做一个ObservableCollection类型的属性,但这也不起作用。这很正常,因为我没有将警报添加到属性中,所以我将它添加到仍然是List类型的变量中。

有没有办法在更新列表时告诉属性,还是其他/更好的方式来显示我的警报,并保持它们易于用于程序的其他部分?

编辑:

我的解决方法:我触发PropertyChanged事件在从我的报警变量PropertyChanged事件的事件处理程序清除我的财产FutureEvents。

我的代码: 类CMAIN { 私有静态挥发CMAIN实例; private static object syncRoot = new Object();

ObservableCollection<Alarm> alarms; 

    #region properties 
    /// <summary> 
    /// Returns the list of alarms in the model. Can't be used to add alarms, use the AddAlarm method 
    /// </summary> 
    public ObservableCollection<Alarm> Alarms 
    { 
     get 
     { 
      return alarms; 
     } 
    } 

    /// <summary> 
    /// Returns the ObservableCollection of future alarms in the model to be displayed by the vieuw. 
    /// </summary> 
    public ObservableCollection<Alarm> FutureAlarms 
    { 
     get 
     { 
      //Only show alarms in the future and alarm that recure in the future 
      var fAlarms = new ObservableCollection<Alarm>(alarms.Where(a => a.DateTime > DateTime.Now || (a.EndRecurrency != null && a.EndRecurrency > DateTime.Now))); 
      return fAlarms; 
     } 
    } 

    /// <summary> 
    /// Returns a desctription of the date and time of the next alarm 
    /// </summary> 
    public String NextAlarmDescription 
    { 
     get 
     { 
      if (alarms != null) 
      { 
       return alarms.Last().DateTimeDescription; 
      } 
      else 
      { 
       return null; 
      } 
     } 
    } 
    #endregion //properties 


    #region public 

    /// <summary> 
    /// Returns the instance of the singleton 
    /// </summary> 
    public static cMain Instance 
    { 
     get 
     { 
      if (instance == null) //Check if an instance has been made before 
      { 
       lock (syncRoot) //Lock the ability to create instances, so this thread is the only thread that can excecute a constructor 
       { 
        if (instance == null) //Check if another thread initialized while we locked the object class 
         instance = new cMain(); 
       } 
      } 
      return instance; 
     } 
    } 

    /// <summary> 
    /// Shows a new intance of the new alarm window 
    /// </summary> 
    public void NewAlarmWindow() 
    { 
     vNewAlarm newAlarm = new vNewAlarm(); 
     newAlarm.Show(); 
    } 

    public void AddAlarm(Alarm alarm) 
    { 
     alarms.Add(alarm);    
    } 

    public void RemoveAlarm(Alarm alarm) 
    { 
     alarms.Remove(alarm); 
    } 

    public void StoreAlarms() 
    { 
     mXML.StoreAlarms(new List<Alarm>(alarms)); 
    } 

    #endregion //public 

    #region private 

    //Constructor is private because cMain is a singleton 
    private cMain() 
    { 
     alarms = new ObservableCollection<Alarm>(mXML.GetAlarms()); 
     alarms.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(alarms_CollectionChanged); 
    } 

    private void alarms_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
    { 
     FutureAlarms.Clear(); //Needed to trigger the CollectionChanged event of FutureAlarms 
     StoreAlarms(); 
    } 


    #endregion //private 
} 

回答

0

WPF的PropertyChanged事件INotifyPropertyChanged界面的反应,所以你应该实现这个接口,当你在你的模型更改属性引发事件。 如果你这样做,你根本不需要使用ObservableCollection<T>。但请注意,如果您的财产是List并且您所做的唯一事情是添加或移除项目,WPF仍然会认为它是相同的列表并且什么也不做。因此,提高PropertyChanged事件之前,你需要属性设置你一个列表的新实例,这是很容易这样做:

MyList.add(newItem); 
MyList = new List<something>(MyList); 
#raise the event 
+0

一个列表没有实现INotifyPropertyChanged,所以如何提高PropertyChanged事件? – Bitbored 2012-04-21 20:15:11

+0

@Bitbored,你的**控制器**应该实现它。 – 2012-04-21 20:24:39

0

而是在每次获得与未来警报重新创建的ObservableCollection的,尝试直接更新采集当列表改变:

public ObservableCollection<Alarm> FutureAlarms { get; private set;} // initialize in constructor 

private void UpdateFutureAlarms() { 
    fAlarms.Clear(); 
    fAlarms.AddRange(
     alarms.Where(
      a => a.DateTime > DateTime.Now 
       || (a.EndRecurrency != null && a.EndRecurrency > DateTime.Now) 
     ) 
    ) 
} 

//... somewhere else in the code... 

public void Foo() { 
    // change the list 
    alarms.Add(someAlarm); 
    UpdateFutureAlarms(); 
} 

你也可以注册UpdateFutureAlarms作为一个事件处理程序,如果你有一个事件触发的List变化时。

0

你最好从ObservableCollection<T>中派生出你自己的类,并使用它来代替尝试像你一样封装两个现有类。至于为什么:

  • 第一,这将是更痛苦的,因为ObservableCollection<T>已经实现了List<T>支持的所有接口,所以你只需要实现你确实需要直接从List<T>的方法和WPF数据绑定将只是工作;
  • 第二,唯一现实的其他选择,INotifyPropertyChanged方法实施起来很麻烦(您将有效地重写ObservableCollection<T>),否则如果您在每次更改后用新的替换它们以获得较大的集合,将会导致较差的性能绑定更新。
+0

问题在于视图只是程序的一小部分。还有很多其他类和很多代码,全部使用List 来处理警报。如果我将主控制器中的列表类更改为ObservableCollection,最好的情况是我只失去一致性,最糟糕的情况是我需要更改很多代码才能使其正常工作。 – Bitbored 2012-04-21 20:30:43

+0

我也不确定是否将类型更改为ObservableCollection可以工作,因为我将警报添加到控制器的警报变量,但是我将listview绑定到属性FutureAlarms,该属性将处理结果,并且不会引发新的PropertyChanged事件。 – Bitbored 2012-04-21 20:36:20

+0

我明白了。但是,您的数据绑定困境不会消失,所以您可以尝试以下内容。直接使用List ,可以设置类别别名(例如“使用AlarmList = List ;”)或从List 中派生名为AlarmList的类,并全面更新所有旧列表引用。然后开始尝试使用一个新的AlarmList:ObservableCollection 类,知道返回列表只有几个字符。正如我所说,如果你想无障碍的数据绑定,(派生自)ObservableCollection 是最好的选择。 – Alan 2012-04-21 20:37:18

0

属性添加到报警

public bool Future 
{ get return (DateTime > DateTime.Now 
      || (EndRecurrency != null && EndRecurrency > DateTime.Now)); 
} 

当了更新报警呼叫未来NotifyPropertyChanged所有(或适当子集)。

然后使用DataTrigger或CollectionViewSource过滤器来隐藏它

<DataTrigger Binding="{Binding Path=Future, Mode=OneWay}" Value="False"> 
           <Setter Property="Visibility" Value="Collapsed"/> 
          </DataTrigger> 

过滤或隐藏是一种在表示层,所以应该离开你报警类别和报警收集全为业务层和数据层。

由于ObservableCollection实现的iList应该是兼容的。

与你目前的模型FurtureAlarms不妨是一个列表。可以缩短语法

(alarms.Where(a => a.DateTime > DateTime.Now || (a.EndRecurrency != null && a.EndRecurrency > DateTime.Now))).toList(); 
0

在WPF中,结合正确的收藏需要结合于实现集合INotifyCollectionChanged它具有CollectionChanged事件每当一个项目被添加或从集合中删除是应该被解雇。

所以建议您使用已经为您实现该接口的ObservableCollection <T>类。而关于您使用的列表<T>变量,我认为最好将它们切换到接口类型IList <T>,而不是由ObservableCollection实现,并且作为附加好处,不需要ObservableCollection通知的应用程序部分将不会需要添加额外的引用或了解Observable集合。

相关问题