Introduction
This article looks at creating a Silverlight custom control when inheriting ItemsControl
does not fit the requirement. The control displays a number of items determined by the dimensions of the control, not the number of items in the data source.
Background
When I first started creating this control, I inherited from ItemsControl
but quickly realised that the ItemsControl
was not going to behave in a manner I desired. I also realised that overriding some of the ItemsControl
methods/properties wasn't going to work either. So I was forced to create, what is essentially an ItemsControl
without the inheritance.
Using the Code
Using the control is like any other Silverlight ItemsControl
. It has ItemsSource
and DisplayMemberPath
properties used to bind data to the control. If the ItemSource
implements INotifyPropertyChanged
, the control will flicker through characters when a value is changed. The control also has a UpdateRow
method that can be used when the control is not bound.
Points of Interest
Creating your own custom ItemsControl
, which does not inherit from ItemsControl
, is not as hard as you may think. The two dependency properties you must implement are:
ItemSource Dependency Property
public static DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(FlickerBoard),
new PropertyMetadata(new PropertyChangedCallback(OnItemsSourceChanged)));
public IEnumerable ItemsSource
{
get
{
return (IEnumerable)base.GetValue(ItemsSourceProperty);
}
set
{
if (value != ItemsSource) base.SetValue(ItemsSourceProperty, value);
}
}
private static void OnItemsSourceChanged
(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FlickerBoard table = d as FlickerBoard;
table.Bind();
}
DisplayMemberPath Dependency Property
public static DependencyProperty DisplayMemberPathProperty =
DependencyProperty.Register("DisplayMemberPath", typeof(string),
typeof(FlickerBoard), null);
public string DisplayMemberPath
{
get
{
return (string)base.GetValue(DisplayMemberPathProperty);
}
set
{
if (value != DisplayMemberPath)
base.SetValue(DisplayMemberPathProperty, value);
}
}
You may wish it implements other ItemsControl
methods and properties but the ItemsSource
and DisplayMemberPath
properties are all that is required to successfully bind to an Enumerable data source.
However there is more to it than just creating the binding. The UIElements
that display the individual items also need to be bound. Once again, you will need a DisplayMemberPath
property and also an Item
property to maintain a reference to the item being represented. As you will see in the following snippet, you will also need to determine if the item implements INotifyPropertyChanged
.
public object Item
{
get
{
return base.GetValue(ItemProperty);
}
set
{
if (value != Item)
{
var newValue = value as INotifyPropertyChanged;
var oldValue = Item as INotifyPropertyChanged;
if (oldValue != null)
oldValue.PropertyChanged -=
new PropertyChangedEventHandler(Item_PropertyChanged);
base.SetValue(ItemProperty, value);
if (newValue != null)
newValue.PropertyChanged +=
new PropertyChangedEventHandler(Item_PropertyChanged);
else
DisplayText(this.Text);
}
}
}
void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
DisplayText(this.Text);
}
Add some reflection to get the DisplayMemberPath
value:
System.Reflection.PropertyInfo pi = Item.GetType().GetProperty(DisplayMemberPath);
And there you have it, the nuts and bolts of creating your own ItemsControl
custom control. In most cases, you will not need to go to this effort to implement an ItemsControl
. This approach is only required when items in the control need to “pad” out the display within the control.
History
- 8th November, 2010: Initial version