Every developer has found him or herself in the situation where a control is missing the one feature that would be perfect in a specific situation or they would love to have a slightly different behavior of an existing feature. This is where attached properties come in handy.
Attached properties could be used to extend the functionality of a control with very little implementation on our side. In this blog post I will demonstrate how to implement some basic attached properties in specific scenarios to extend the functionality of a WPF / Silverlight control.
The next examples will demonstrate how to extend the functionality of RadListBox and RadDocking controls but you could use this approach in every WPF or Silverlight control.
The first example will focus on how to extend the functionality of RadListBox control. A very common scenario when using a ListBox control is when you would like to bind a collection of objects to the control and use it as a list of selected items. Accomplishing this task with the help of attached properties is a fast and simple task.
First we will need to create a helper class and in it create the attached property as a dependency property by defining a public static readonly field of type DependencyProperty. After that we will need to create a set and get methods that will write and return the data of the attached property. Next we will need to create the registered OnPropertyChanged method with the custom logic that will add each of the objects in the created dependency property List to the SelectedItems of the RadListBox. The next code snippet shows how this helper class should look like:
public
class
SelectedItemsHelper
{
public
static
readonly
DependencyProperty SelectedItemsProperty =
DependencyProperty.RegisterAttached(
"SelectedItems"
,
typeof
(IList),
typeof
(SelectedItemsHelper),
new
FrameworkPropertyMetadata((IList)
null
,
new
PropertyChangedCallback(OnSelectedItemsChanged)));
public
static
IList GetSelectedItems(DependencyObject d)
{
return
(IList)d.GetValue(SelectedItemsProperty);
}
public
static
void
SetSelectedItems(DependencyObject d, IList value)
{
d.SetValue(SelectedItemsProperty, value);
}
private
static
void
OnSelectedItemsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
...
}
}
The full code can be found in the downloadable project.
All that is left is to declare your custom attached property in the xaml of the RadListBox and bind a collection of objects to it. The next code snippet shows how this should look like:
<
telerik:RadListBox
ItemsSource
=
"{Binding Countries}"
DisplayMemberPath
=
"Name"
local:SelectedItemsHelper.SelectedItems
=
"{Binding SelectedCountries}"
SelectionMode
=
"Multiple"
/>
The Countries collection in the ViewModel and the SelectedCountries collection are sample ObservableCollections of custom Country objects that have Name property and the local namespace is the namespace of the custom helper class.
Now let us see how we can benefit from the use of attached properties in the RadDocking control. One common scenario with RadDocking is when you find yourself with the need to implement a MVVM pattern and set a collection of Panes as ItemsSource of a specific PaneGroup. Again achieving this with the help of attached properties is a simple task.
public
class
PaneModel
{
public
string
Header {
get
;
set
; }
public
string
Content {
get
;
set
; }
}
Next we will need to create an extension class for the RadPaneGroups (the PaneGroupExtensions class). In this class we will define the attached properties for the PaneGroupExtension and the ItemsSource.
Define a Group field of type RadPaneGroup and Panes field of type Dictionary<object, RadPane>. This fields will store the RadPaneGroup and RadPanes:
private
RadPaneGroup Group {
get
;
set
; }
private
Dictionary<
object
, RadPane> Panes {
get
;
set
; }
private
static
void
OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var group = d
as
RadPaneGroup;
var oldValue = e.OldValue
as
IEnumerable;
var newValue = e.NewValue
as
IEnumerable;
var oldValueObservableCollection = e.OldValue
as
INotifyCollectionChanged;
var newValueObservableCollection = e.NewValue
as
INotifyCollectionChanged;
if
(group !=
null
)
{
...
}
}
After that all that is left to do is define a field of type ObservableCollection<PaneModel> in the code behind and populate it with some sample data.
private
ObservableCollection<PaneModel> panes;
this
.panes =
new
ObservableCollection<PaneModel>()
{
new
PaneModel
{
Header =
"Pane 1"
,
Content =
"Sample Content 1"
},
new
PaneModel
{
Header =
"Pane 2"
,
Content =
"Sample Content 2"
},
};
this
.PaneGroup.DataContext =
this
.panes;
The xaml of the Docking control should look like this:
<
telerik:RadDocking
x:Name
=
"dock"
>
<
telerik:RadSplitContainer
>
<
telerik:RadPaneGroup
x:Name
=
"PaneGroup"
local:PaneGroupExtensions.ItemsSource
=
"{Binding}"
/>
</
telerik:RadSplitContainer
>
</
telerik:RadDocking
>
The previous examples (SelectedItems and PaneGroupItemsSource) are available in our SDK repository located at GitHub. You can download the entire repository as a zip file via this link.
Vladimir Amiorkov is a Software Developer at Progress. He is currently working with Web, iOS and Android technologies and is a part of the NativeScript team. In his spare time, he enjoys playing computer games such as Diablo and StarCraft.