Caliburn Entity DataBinding Funniness - PullRequest
       25

Caliburn Entity DataBinding Funniness

1 голос
/ 17 сентября 2011

У меня есть приложение Silverlight, в котором я сейчас работаю, которое реализует Caliburn.Micro для своей инфраструктуры MVVM.Все работает хорошо, но я замечаю некоторую забавность в некоторых связывании.У меня есть ShellViewModel и ShellView, которые обрабатывают навигацию для приложения.ShellViewModel имеет список загруженных ViewModel для приложения.ShellViewModel наследуется от Conductor, так что он может обрабатывать все активации и деактивации.

У меня также есть тип базового класса ViewModel, называемый BaseConductorViewModel, который также наследуется от Conductor.Это для ViewModel, которые в основном являются представлениями Master-Detail.Для этих BaseConductorViewModels у меня есть BindableCollection с именем Items.Идея заключается в том, чтобы связать эту коллекцию с ListBox или другим ItemsControl.

Когда я создаю дочерний элемент этой ViewModel и связанного с ним представления, я заметил, что ListBox (в данном случае) обновляет привязку только при измененииActiveItem на уровне ShellViewModel.Поэтому, когда приложение изначально загружается и это представление является активным представлением по умолчанию, вы ничего не увидите в списке (я звоню в службу Ria, чтобы получить данные для этого списка).Но если я нажму на другую ViewModel в ShellViewModel / ShellView, а затем нажму назад, она покажет элементы в списке.Это также следует для добавления элементов в список или их удаления.Он не обновится, пока я не переключу активные виды.Это кажется мне очень странным, и я не могу придумать способ связать его, как я бы хотел.Еще одна вещь, которую нужно отметить, когда я добавляю / удаляю элементы;Я вызываю метод 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


}

1 Ответ

0 голосов
/ 17 сентября 2011

Не уверен, что если у вас проблема, как-то с этим связано, из документов :

Так как все реализации IConductor в OOTB наследуются от Screen it означает, что у них тоже есть жизненный цикл, и что жизненный цикл каскадов все предметы, которые они проводят. Итак, если проводник деактивирован, ActiveItem также будет деактивирован. Если вы попытаетесь закрыть проводник, он сможет закрыться только если все дирижер может закрыть. Это оказывается очень мощной функцией. В этом есть один аспект, который я часто замечал Разработчики. Если вы активируете предмет в проводнике, который сам не активен, этот элемент фактически не будет активирован, пока проводник не получит активируется. Это имеет смысл, когда вы думаете об этом, но можете изредка вызывает перетягивание волос.

редактирование: Я думаю, что вижу, что вы пытаетесь сделать, хотя пара вопросов:

  1. Ваш ShellViewModel Conductor<IViewModel<V,M>>.Collection.OneActive, когда каталог активируется? Я думаю, что вы хотите добавить каталог к ​​элементам, а затем Активируйте его.
  2. С BaseConductorViewModel наследуется от Проводника, который наследуется от Screen, который получает ссылку на свой вид, когда связаны. Я не уверен, для чего вы добавляете свойство View.
  3. CM может обработать установку выбранного элемента для вас. Так что для мастера подробно описать ситуацию, когда у вас есть ItemsControl, CM установит SelectedItem и от которого вы можете заполнить детали.
...