1

我有一个我目前正在使用的 Silverlight 应用程序,它为其 MVVM 框架实现了 Caliburn.Micro。一切正常,但我注意到一些绑定中的一些有趣之处。我拥有的是处理应用程序导航的 ShellViewModel 和 ShellView。ShellViewModel 有一个为应用程序加载的 ViewModel 的列表。ShellViewModel 继承自 Conductor,因此它可以处理所有的激活和停用。

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

当我创建此 ViewModel 的子项和关联的 View 时,我注意到 ListBox(在这种情况下)仅在我更改 ShellViewModel 级别的 ActiveItem 时才刷新绑定。因此,当应用程序最初加载并且此视图是默认的活动视图时,您不会在列表中看到任何内容(我正在调用 Ria 服务来获取此列表的数据)。但是,如果我单击 ShellViewModel/ShellView 上的另一个 ViewModel,然后单击返回,它将显示列表中的项目。这也适用于将项目添加到列表或删除它们。除非我切换活动视图,否则它不会刷新。这对我来说似乎很奇怪,我似乎无法像我想的那样想办法让它绑定。还有一件事要注意,当我添加/删除项目时;我调用 Refresh 方法,

有人对这里可能发生的事情有任何想法吗?或者关于我如何尝试调试它的任何想法?

先感谢您!

这是 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


}
4

1 回答 1

0

不确定您的问题是否与此有关,来自文档

由于 IConductor 的所有 OOTB 实现都继承自 Screen,这意味着它们也有一个生命周期,并且该生命周期级联到它们正在执行的任何项目。因此,如果一个导体被停用,它的 ActiveItem 也将被停用。如果您尝试关闭导体,则只有当它所执行的所有项目都可以关闭时,它才能关闭。事实证明,这是一个非常强大的功能。我注意到有一个方面经常让开发人员绊倒。如果您在导体中激活一个本身未激活的项目,则该项目实际上不会被激活,直到导体被激活。当您考虑时,这是有道理的,但有时会导致头发拉扯。

编辑:我想我明白你在做什么,不过有几个问题:

  1. ShellViewModel的是 Conductor<IViewModel<V,M>>.Collection.OneActive,目录什么时候激活?我认为您希望将目录添加到项目,然后激活它。
  2. 使用BaseConductorViewModel,它继承自从 Screen 继承的 Conductor,它在绑定时获取对其视图的引用。我不确定您添加的 View 属性是做什么用的。
  3. CM 可以为您处理设置所选项目。因此,对于您拥有 ItemsControl 的主详细信息情况,CM 将设置 SelectedItem 并且您可以从中填充详细信息。
于 2011-09-16T23:20:25.593 回答