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.

Extend RadListBox SelectedItems

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.

Extend Docking ItemsSource

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.

  • First we will need to create class PaneModel that will hold the details (Header, Content etc.) of each RadPane.
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; }
  • After that we will need to implement methods that will Add, Remove and Clear all Panes from the Group and Panes fields. You can find the code for the AddItem(), RemoveItem() and ClearItems() methods in the attached project.
  • Next we need to implement the OnItemsSourceChanged() method for the ItemsSource dependency property. The full code can be found in the downloadable project.
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.


VladimirAmiorkov3
About the Author

Vladimir Amiorkov

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.

Comments

Comments are disabled in preview mode.