2011-09-16 51 views
1

我有一个Silverlight应用程序,我目前正在为其MVVM框架实现Caliburn.Micro。事情工作正常,但我注意到一些绑定中的一些funniness。我拥有的是一个ShellViewModel和一个处理应用程序导航的ShellView。 ShellViewModel有一个为应用程序加载的ViewModel的列表。 ShellViewModel从Conductor继承,以便它可以处理所有的激活和停用。Caliburn Entity DataBinding Funniness

我也有一种ViewModel基类,名为BaseConductorViewModel,它也继承自Conductor。这是基本上Master-Detail视图的ViewModel。对于这些BaseConductorViewModels,我有一个名为Items的BindableCollection。想法是将此集合绑定到ListBox或其他ItemsControl。

当我创建此ViewModel的一个子项和一个关联的视图时,我注意到ListBox(在本例中)只是在ShellViewModel级别更改ActiveItem时刷新了绑定。所以当应用程序最初加载时,这个视图是默认的活动视图,你不会在列表中看到任何东西(我正在调用Ria服务来获取这个列表的数据)。但是,如果我单击ShellViewModel/ShellView上的另一个ViewModel,然后单击它将显示列表中的项目。这也适用于将项目添加到列表或删除它们。它不会刷新,除非我切换活动视图。这对我来说似乎很奇怪,我似乎无法找到一种方法来让它像我所愿意的那样绑定。还有一件事要注意,当我添加/删除项目;我调用Refresh方法,目前我没有使用NotifyOfPropertyChange方法,尽管我之前尝试过使用相同的结果。

有没有人有任何想法可能会发生在这里?或者有关我如何去尝试调试的想法?

预先感谢您!

这里是ShellViewModel

public abstract class ShellViewModel<V,M>:Conductor<IViewModel<V, M>>.Collection.OneActive, IViewModelCatalogShell<V,M> 
    where V:IView 
    where M:IModel 
{ 
    #region Properties/Members 
    public ViewModelSelectedItemList<V, M> Catalog { get; set; } 
    #endregion 

    #region Constructors 
    public ShellViewModel() 
    { 
     Catalog = new ViewModelSelectedItemList<V, M>(); 
    } 
    #endregion 

} 

这里是BaseConductorViewModel

public abstract class BaseConductorViewModel<T,V,M>:Conductor<T>, IViewModel<V, M> 
    where V:IView 
    where M:IModel 
{ 
    #region Properties/Members 
    protected Guid _id=Guid.Empty; 
    public Guid Id 
    { 
     get{return _id;} 
     set 
     { 
      _id =value; 
      NotifyOfPropertyChange("Id"); 
     } 
    } 

    protected string _name=string.Empty; 
    public string Name 
    { 
     get { return _name; } 
     set 
     { 
      _name = value; 
      NotifyOfPropertyChange("Name"); 
     } 
    } 

    public string TypeName 
    { 
     get 
     { 
      return this.GetType().FullName; 
     } 
    } 

    protected string _description = string.Empty; 
    public string Description 
    { 
     get { return _description; } 
     protected set 
     { 
      _description = value; 
      NotifyOfPropertyChange(() => Description); 
     } 
    } 

    protected V _view; 
    public V View 
    { 
     get { return _view; } 
     set 
     { 
      _view = value; 
      NotifyOfPropertyChange("View"); 
     } 
    } 

    protected M _model; 
    public M Model 
    { 
     get { return _model; } 
     set 
     { 
      _model = value; 
      NotifyOfPropertyChange("Model"); 
     } 
    } 

    protected SelectedItemList<T> _items; 
    public SelectedItemList<T> Items 
    { 
     get { return _items; } 
     set 
     { 
      _items = value; 
      NotifyOfPropertyChange(() => Items); 
     } 
    } 

    protected Guid _lastModifiedBy = Guid.Empty; 
    public Guid LastModifiedBy 
    { 
     get { return _lastModifiedBy; } 
     set 
     { 
      _lastModifiedBy = value; 
      NotifyOfPropertyChange("LastModifiedBy"); 
     } 
    } 

    protected DateTime _lastModifiedOn = DateTime.Today; 
    public DateTime LastModifiedOn 
    { 
     get { return _lastModifiedOn; } 
     set 
     { 
      _lastModifiedOn = value; 
      NotifyOfPropertyChange("LastModifiedOn"); 
     } 
    } 

    protected string _imageSource = string.Empty; 
    public string ImageSource 
    { 
     get { return _imageSource; } 
     protected set 
     { 
      _imageSource = value; 
      NotifyOfPropertyChange("ImageSource"); 
     } 
    } 
    #endregion 

    #region Constructors 
    public BaseConductorViewModel() 
    { 
     _items = new SelectedItemList<T>(); 
     Items.SelectItemChanged += new SelectedItemChangedEvent(Items_SelectItemChanged); 
     Items.SelectedIndexChanged += new SelectedIndexChangedEvent(Items_SelectedIndexChanged); 


     LoadData(); 
    } 
    public BaseConductorViewModel(V view, M model) 
     :this() 
    { 
     _items = new SelectedItemList<T>(); 
     View = view; 
     Model = model; 

     Items.SelectItemChanged += new SelectedItemChangedEvent(Items_SelectItemChanged); 
     Items.SelectedIndexChanged += new SelectedIndexChangedEvent(Items_SelectedIndexChanged); 

     LoadData(); 
    } 
    #endregion 

    #region Methods 
    public abstract void LoadData(); 
    #endregion 

    #region Event Handlers 
    private void Items_SelectItemChanged() 
    { 
     ChangeActiveItem(Items.SelectedItem, true); 
     OnActiveItemChanged(); 
    } 
    private void Items_SelectedIndexChanged(int index) 
    { 
     ChangeActiveItem(Items.SelectedItem, true); 
     OnActiveItemChanged(); 
    } 
    #endregion 
} 

的ViewModelSelectedItemList仅仅是一个类型的这个类的版本

public class SelectedItemList<T>:IObservableCollection<T> 
{ 
    #region Properties/Members 
    protected BindableCollection<T> _items = new BindableCollection<T>(); 

    protected bool _isReadOnly = false; 

    protected bool _isNotifying = true; 
    public bool IsNotifying 
    { 
     get 
     { 
      return _isNotifying; 
     } 
     set 
     { 
      _isNotifying = value; 
     } 
    } 

    public int Count 
    { 
     get { return _items.Count; } 
    } 

    protected int _selectedIndex = -1; 
    public int SelectedIndex 
    { 
     get { return _selectedIndex; } 
     set 
     { 
      _selectedIndex = value; 
      NotifyOfPropertyChange("SelectedIndex"); 
      FireSelectedIndexChangedEvent(_selectedIndex); 
     } 
    } 

    public T SelectedItem 
    { 
     get 
     { return _items[_selectedIndex]; } 
     set 
     { 
      _selectedIndex = _items.IndexOf(value); 
      NotifyOfPropertyChange("SelectedItem"); 
      FireSelectedItemChangedEvent(); 
     } 
    } 

    #endregion 

    #region Events 
    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 
    public event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged; 
    public event SelectedIndexChangedEvent SelectedIndexChanged; 
    public event SelectedItemChangedEvent SelectItemChanged; 
    #endregion 

    #region Constructors 
    #endregion 

    #region Methods 
    public void AddRange(System.Collections.Generic.IEnumerable<T> items) 
    { 
     if (!_isReadOnly) 
     { 
      foreach (T item in items) 
      { 
       _items.Add(item); 
      } 

      if (_isNotifying) 
      { 
       NotifyOfPropertyChange("Count"); 
      } 
     } 
    } 
    public void RemoveRange(System.Collections.Generic.IEnumerable<T> items) 
    { 
     if (!_isReadOnly) 
     { 
      foreach (T item in items) 
      { 
       _items.Remove(item); 
      } 
      if (_isNotifying) 
      { 
       NotifyOfPropertyChange("Count"); 
      } 
     } 
    } 

    public int IndexOf(T item) 
    { 
     return _items.IndexOf(item); 
    } 
    public void Insert(int index, T item) 
    { 
     if (!_isReadOnly) 
     { 
      _items.Insert(index, item); 
      if (_isNotifying) 
      { 
       NotifyOfPropertyChange("Count"); 
      } 
     } 
    } 
    public void RemoveAt(int index) 
    { 
     if (!_isReadOnly) 
     { 
      _items.RemoveAt(index); 
      if (_isNotifying) 
      { 
       NotifyOfPropertyChange("Count"); 
      } 
     } 
    } 

    public T this[int index] 
    { 
     get 
     { 
      return _items[index]; 
     } 
     set 
     { 
      _items[index] = value; 
     } 
    } 

    public void Add(T item) 
    { 
     if (!_isReadOnly) 
     { 
      _items.Add(item); 
      if (_isNotifying) 
      { 
       NotifyOfPropertyChange("Count"); 
       _items.Refresh(); 
      } 

      if (_items.Count == 1) 
      { 
       SelectedIndex = 0; 
      } 
     } 
    } 
    public bool Remove(T item) 
    { 
     if (!_isReadOnly) 
     { 
      if (_isNotifying) 
      { 
       NotifyOfPropertyChange("Count"); 
      } 
      return _items.Remove(item); 
     } 
     else 
     { 
      return false; 
     } 
    } 

    public void Clear() 
    { 
     _items.Clear(); 
    } 
    public bool Contains(T item) 
    { 
     return _items.Contains(item); 
    } 
    public void CopyTo(T[] array, int arrayIndex) 
    { 
     if (!_isReadOnly) 
     { 
      _items.CopyTo(array, arrayIndex); 
      if (_isNotifying) 
      { 
       NotifyOfPropertyChange("Count"); 
      } 
     } 
    }  

    public bool IsReadOnly 
    { 
     get { return _isReadOnly; } 
    } 
    public void Lock() 
    { 
     _isReadOnly = true; 
    } 
    public void Unlock() 
    { 
     _isReadOnly = false; 
    } 

    public System.Collections.Generic.IEnumerator<T> GetEnumerator() 
    { 
     return _items.GetEnumerator(); 
    } 
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return _items.GetEnumerator(); 
    } 

    public void NotifyOfPropertyChange(string propertyName) 
    { 
     FirePropertyChangedEvent(propertyName); 
    } 
    public void Refresh() 
    { 
     _items.Refresh(); 
    } 

    #region Helper Methods 
    protected void FirePropertyChangedEvent(string propertyName) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); 
     } 

    } 
    protected void FireCollectionChangedEvent(NotifyCollectionChangedAction action) 
    { 
     if (CollectionChanged != null) 
     { 
      CollectionChanged(this, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(action)); 
     } 
    } 
    protected void FireSelectedIndexChangedEvent(int index) 
    { 
     if (SelectedIndexChanged != null) 
     { 
      SelectedIndexChanged(index); 
     } 
    } 
    protected void FireSelectedItemChangedEvent() 
    { 
     if (SelectItemChanged != null) 
     { 
      SelectItemChanged(); 
     } 
    } 
    #endregion 

    #endregion 


} 
+0

您的ShellViewModel是继承自Conductor还是继承者 .Collection.OneActive?如果可能,发布ShellViewModel和BaseConductorViewModel的代码。 –

+0

好吧我已经发布了这些对象的代码 –

+0

因此,ViewModelSelectedItemList继承自BaseConductorViewModel? –

回答

0

不知道,如果你问题与此有关,从docs

由于IConductor的所有开箱即实现从屏幕继承它 意味着他们也有一个生命周期和生命周期级联到 他们进行任何项目。因此,如果导体被禁用,它的ActiveItem也将被禁用。如果您尝试关闭 售票员,只有在所有物品可以关闭的情况下才能关闭。这原来是一个非常强大的功能。 有一个方面,我注意到经常去 开发者。如果你激活一个本身不是 激活的导体中的物品,该物品将不会被激活,直到指挥得到 激活。这是有道理的,当你想到它,但可以 偶尔会导致头发拉。

编辑: 我想我看到你想要做什么,一对夫妇的问题,但:

  1. ShellViewModelConductor<IViewModel<V,M>>.Collection.OneActive,当目录 激活?我认为你想将目录添加到物品,然后 激活它。
  2. 随着BaseConductorViewModel,它继承自导体,其中 继承自屏幕,其中 绑定时获取其视图的引用。我不确定你添加的View属性是什么。
  3. CM可以处理为您设置选定的项目。因此,对于具有ItemsControl的详细情形,CM将设置 SelectedItem,并从中填充详细信息。
+0

感谢您的回应Derek。我也尝试过使用从Screen继承的基本ViewModel。在这种情况下,我检查确保ShellViewModel是活动的,它是。我还使用BaseConductorViewModel检查了活动状态,在这种情况下,ShellViewModel也处于活动状态。我将检查可以看到BaseConductorViewModel是否也处于活动状态,并发布我找到的内容。 –

+0

我会看看你的东西,因为你提到的主要/细节,你看过HelloScreens例子与View.Context附加属性? –

+0

谢谢,我已经看了HelloScreens示例和View.Context附加属性 –