Table of Contents
Introduction
Some of the most common tasks in GUI programming are to select multiple items from the list, to modify properties of multiple items, to rearrange items in the list, or to copy / move items from one list to another.
I assume that you are using WPF and MVVM pattern.
Let's consider the following simple example. You want to switch on / off individual bits of the Hex
variable, like this:
You create properties on your ViewModel holding list(s) or collection(s) of items, and bind them to some ItemsControl
(s) on your view. You make an ItemTemplate
containing CheckBox
es, and bind them to properties of your items. Now what? How do you implement the needed functionality?
You have a few alternatives:
- Make view model class(es) for your items and put all the logic there, so every time the
Item.IsSelected
property is changed, you update the Hex
variable. Oops, the Hex
variable belongs to the parent VM, so your items will have to have a member variable holding the parent VM or a delegate callback.
- Make dumb items to hold data, make a bunch of converters, and implement logic in those converters / XAML, like so;
TextBox.Text = "{Binding CheckBox.IsChecked, Converter=....}"
Of course, you will need a pretty complicated FlagConverter
which can combine multiple flag values (I have a bunch of those in my WPF library).
Don't re-invent the wheel, and use the utility classes that I created to simplify things. With those utility classes, any task of updating / selecting / arranging / moving items is a snap.
Example
The XAML is simplicity itself:
<ListBox ItemsSource="{Binding Selectors}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected}"
Content="{Binding Caption}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The ViewModel cannot be simpler:
private NotifyParentObservableCollection<LookupItem> _selectors;
public NotifyParentObservableCollection<LookupItem> Selectors
{
get { return _selectors; }
private set
{
if (_selectors != null)
{
_selectors.ChildPropertyChanged -= OnSelectorsChildPropertyChanged;
}
_selectors = value;
if (_selectors != null)
{
_selectors.ChildPropertyChanged += OnSelectorsChildPropertyChanged;
}
OnPropertyChanged("Selectors");
}
}
private void OnSelectorsChildPropertyChanged(object sender,
PropertyChangedEventArgs e)
{
var item = sender as LookupItem;
if (e.PropertyName == "IsSelected")
{
if (item.IsSelected)
{
Hex |= (int) item.Value;
}
else
{
Hex &= ~(int) item.Value;
}
}
}
Selectors = new NotifyParentObservableCollection<LookupItem>
{
new LookupItem { Value = 1, Caption = "0x00000001" },
new LookupItem { Value = 2, Caption = "0x00000010" },
new LookupItem { Value = 4, Caption = "0x00000100" },
new LookupItem { Value = 8, Caption = "0x00001000" },
};
Voila, we are done with our task. Can you beat the simplicity?
NotifyParentObsevableCollection and LookupItem
public class LookupItem : INotifyPropertyChanged
LookupItem
is a utility class used as a generic ViewModel for an item when you need to update / select / arrange / move those items.
LookupItem
has a bunch of most useful for those scenarios properties, and it implements the INotifyPropertyChanged
interface to notify its parent NotifyParentObservableCollection<T>
when any of its properties change. If you need more properties, inherit from it and add whatever you need.
public class NotifyParentObservableCollection<T> : ObservableCollection<T>
NotifyParentObservableCollection<T>
attaches to the PropertyChanged
events of its children and raises the ChildPropertyChanged
event accordingly.
NotifyParentObservableCollection<T>
inherits from ObservableCollection<T>
, and overrides OnCollectionChanged()
and ClearItems()
to attach / detach handlers to children.
That's it. Again, can you beat the simplicity?
When I write code, my goal is to eliminate as much lines of code as possible, but no more.
My motto: The less lines of code, the less potential errors.
Caution
Since NotifyParentObservableCollection<T>
strongly attaches to the PropertyChanged
events of its children, you have to Clear()
the collection after you are done using it to eliminate potential memory leaks.
Of course, if the parent collection has the same life span as its children, you can safely ignore this advice, but if children can outlive the collection, don't forget to call Clear()
.
Revision History
- January 20, 2011 - Created the article.