I have a Silverlight application that I am currently working that implements Caliburn.Micro for its MVVM framework. Things are working fine but I am noticing some funniness in some of the binding. What I have is a ShellViewModel and a ShellView that handle the navigation for the application. The ShellViewModel has a list of the loaded ViewModels for the application. The ShellViewModel inherits from Conductor so that it can handle all the activation and deactivation.
I also have a type of ViewModel base class called BaseConductorViewModel that also inherits from Conductor. This is for ViewModel’s that are basically Master-Detail views. For these BaseConductorViewModels I have a BindableCollection called Items. The idea being to bind this collection to a ListBox or other ItemsControl.
When I create an child of this ViewModel and an associated View I have noticed that the ListBox(in this case) only refreshes the binding when I change the ActiveItem at the ShellViewModel level. So when the application initially loads and this view is the default active view you won’t see anything in the list (I am calling Ria service to get the data for this list). But, if I click on another ViewModel on the ShellViewModel/ShellView and then click back it will show the items in the list. This also follows for adding items to the list or removing them. It won’t refresh unless I switch active views. This seems very odd to me and I can’t seem to figure out a way to get it bind as I would except. One more thing to note, when I am adding/removing items; I call the Refresh method, currently I am not using the NotifyOfPropertyChange method though I did try that previously to the same result.
Does anyone have any ideas of what might be going on here? Or any ideas on how I could go about trying to debug this?
Thank you in advance!
Here is the 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
}
And here is the 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
}
The ViewModelSelectedItemList is just a typed version of this class
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
}