This article is compatible with the latest version of Silverlight.
See also part 1, part 3 and the update.
Download full source code
What is the slider
Main part of the weather control we introduced is the slider. Actually, we planned the whole weather control as a demo for it. The slider is a separate control that has functionality similar to the ListBox control.
It has support of
- Items
- Defining items in XAML
- Binding items to data objects
- Template the items
- Template the containers for the items
- Selection
- Scrolling
- Mouse dragging
Almost all of the above are available in ListBox but there are differences with our slider. The main differences are that it uses animations for scrolling instead of instant transition and it provides better customization for the container. We called it AnimatedSlider and like the ListBox it inherits from ItemsControl.
Why inherit from ItemsControl
ItemsControl is the most convenient class to inherit from. To display the items we need a parent element that is visualized and ItemsControl provides a Panel. It has support for DataTemplate items and binding so this will shorten our work. When inheriting from ItemsControl we should override
There are some functions that handle the items and their containers that you need to override if you want to extend the functionality of the ItemsControl. For more information on the functions see the descriptions in the tables in our article on how to do that.
How does ItemsControl support binding
To support binding ItemsControl needs a source collection that implements INotifyCollectionChanged wed. This allows tracking changes in the data and updating the control. ItemsControl hooks on the event NotifyCollectionChangedEventHandler and handles the changes in the collection accordingly. You can see that in the private functions of the class ItemsSourceChanged and OnCollectionChanged in .Net Reflector. When you inherit from ItemsControl you can override OnItemsChanged to process the changed items.
Do you speak XAML
What do we mean by support of items in XAML?
We mean possibility to use syntax like this:
<
MyControl
>
<
SomeControl
/>
...
<
AntherControl
/>
</
MyControl
>
Probably the first thing you need to know is the XAML syntax and how the XAML declarations bond with your class. For our case we will explain in short but for more detailed information you should read the MSDN page on XAML syntax. XAML like this
<
MyControl
TextProperty
=
"TextValue"
/>
called Attribute syntax and XAML like this
<
MyControl
>
<
MyControl.ColorProperty
>ColorValue</
MyControl.Property
>
</
MyControl
>
called Property Element syntax are often used and seem obvious. It turns out our in case we need the Content Element syntax that is usually used to define the Parent-Child relationship.
<
Parent
>
<
Child
/>
</
Parent
>
This syntax is somewhat more special. It is used to assign value to a certain property and that is the property indicated by the ContentPropertyAttribute of the class. Here is an example of how it works.
Definition in code
[ContentProperty(
"SpecificProperty"
)]
public
class
MyControl
{
public
string
SpecificProperty
{
get
{
return
_specificProperty; }
set
{ _specificProperty = value; }
}
private
string
_specificProperty;
}
Use in XAML
<
MyControl
SpecificProperty
=
"Value"
/>
which is equivalent to
<
MyControl
>Value</
MyControl
>
Value could be anything from string to collection. In the case of collection the opening and closing tag for the collection can be omitted. In the ItemsControl ContentPropertyAttribute is the Items property, which is of type ItemCollection.
Implementing selection
To add selection functionality to an ItemsControl we use the helper class SelectionManager from our previous article to create the class ItemsSelector.
protected
ObservableCollection<ISelectable> _selectableItems;
private
SelectionManager _selectionManager;
public
event
EventHandler<SelectionChangedEventArgs> SelectionChange;
PrepareContainerForItem is called when an item is updated or added. We only add the container of the item in our list of items that are selectable.
protected
override
void
PrepareContainerForItemOverride( DependencyObject element,
object
item )
{
base
.PrepareContainerForItemOverride( element, item );
ISelectable item2 = element
as
ISelectable;
if
( item2 !=
null
)
{
this
._selectableItems.Add( item2 );
}
}
And tell the SeletionManager to manage our collection…
_selectionManager.SetCollectionToManage ( _selectableItems );
…then we get notified for change of the selection…
_selectionManager.SelectionChange +=
new
EventHandler<SelectionChangedEventArgs>( _selectManager_SelectionChange );
…and pass the event outside
void
_selectManager_SelectionChange(
object
sender, SelectionChangedEventArgs e )
{
if
(
this
.SelectionChange !=
null
)
this
.SelectionChange(
this
, e );
}
ItemsControl with templatable containers
We already showed you how to inherit from ItemsControl and implement containers but here we have some more thoughts on how we do it here. ItemsControl is designed to be extendable with containers for its items. That’s why you override GetContainerForItemOvveride() – you should return the type of your container. This is used in the private methods of ItemsControl. Container for the items can be any ContentControl. We chose to create the class ItemContainer that extends the ContentControl and has additional three properties. Two of them ContentWidth and ContentHeight allow you to choose the size of the items. Normally, they would have the size of the container. The other property (ContentStyle) sets the Style of the item. Normally, you would set only the DataTemplate of an item like in the ListBox. This allows you to set template for the container and for the item, and have DataTemplate for the item. The idea is to allow the user to template the items if they are defined in XAML without setting template for the containers.
The ItemContainersControl class is the class that implements containers. It inherits the ItemsSelector and defines four new properties. Three of them correspond to the three properties we talked about lastly. The other exposes the template of the container.
AnimatedSlider
The final step to finish our slider is to make the items scrollable. We do that by simply animating the Canvas.Left property of the ItemsPresenter. ItemsPresenter is the Panel containing the items. The control has two buttons to control the movement. The space between the buttons is the visible area. We use clip to hide the items that are outside of it.
void
AnimatedSlider_LayoutUpdated(
object
sender, EventArgs e )
{
this
.LayoutUpdated -=
new
EventHandler( AnimatedSlider_LayoutUpdated );
_areaWidth =
this
.ActualWidth - PreviousButton.ActualWidth - NextButton.ActualWidth;
RectangleGeometry visibleArea =
new
RectangleGeometry();
Rect clip =
new
Rect( 0, 0, _areaWidth,
this
.ActualHeight );
visibleArea.Rect = clip;
View.Clip = visibleArea;
}
The other way of scrolling is using mouse dragging. Both for the movement animation and the mouse dragging we’ve added limitations so that the view doesn’t go out of boundary.
void
RightButton_Click(
object
sender, RoutedEventArgs e )
{
double
dist = (
double
)ItemsPresenter.GetValue( Canvas.LeftProperty ) - _areaWidth - NextButton.ActualWidth;
if
( dist + 20 <= -( ItemsPresenter.ActualWidth ) )
{
ItemsPresenter.SetValue( Canvas.LeftProperty, _areaWidth - ( ItemsPresenter.ActualWidth ) );
}
else
if
( ToNextStoryboard !=
null
)
ToNextStoryboard.Begin();
}
private
void
Canvas_MouseMove(
object
sender, MouseEventArgs e )
{
if
( _IsMouseDrag )
{
Point toViewCoordinates = e.GetPosition( View );
double
delta = toViewCoordinates.X - _lastMousePos.X;
double
newLeft = (
double
)ItemsPresenter.GetValue( Canvas.LeftProperty ) + delta;
if
( newLeft <= 0 && newLeft >= -( ItemsPresenter.ActualWidth - _areaWidth ) )
ItemsPresenter.SetValue( Canvas.LeftProperty, newLeft );
_lastMousePos = toViewCoordinates;
}
}
In Part 3 we template the slider and connect it to the weather forecast web service.