(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Tip: Easy/Reusable selection with ISelectable and SelectionManager

(0 votes)
Denislav Savkov
>
Denislav Savkov
Joined Feb 11, 2008
Articles:   14
Comments:   6
More Articles
2 comments   /   posted on Aug 01, 2008
Tags:   denislav-savkov
Categories:   General

This article is compatible with the latest version of Silverlight.

Introduction

The need of selection is a pretty common thing. That made us think of a way to generalize the task and create a reusable class that can be used in more than one scenario. We put together a simple helper class that lets you handle selection easily.  The responsibilities of the class are:

  • to keep only one item selected;
  • to give you the index of the selected item;
  • possibly to return a handle to the selected item

You can use the SelectionManager on any ObservableCollection of ISelectable objects. In fact you could use it on non-UI objects if you need to have one object in one state and the rest in another. ISelecatble is a simple interface that has three parts:

  • Selected event that should be raised when an object transits to the Selected state
  • Select/Deselect methods that should set the state of the object to selected or deselected
public interface ISelectable
{
    event EventHandler Selected;
    void Select();
    void Deselect();
}

After you define these three parts in your class you only need to pass the collection of objects to the SelectionManager and hook to the SelectionChange event.

Items are selected on MouseOver.

 Download source code.

Implementation

The SelectionManager keeps a reference to an ObservableCollection outside the class and it hooks to the Selected event of each item provided by ISelectable.

private ObservableCollection items;
 
public void HookToObservableCollection( ObservableCollection items )
{   ...
    this.items = items;
    items.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(
                   CollectionChanged );
    foreach ( ISelectable current in this.items )
    {
        current.Selected += new EventHandler( OnItemSelected );
    }
}

The SelectionManager handles changes in the collection automatically.

void CollectionChanged( object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e )
{
    if ( e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Replace )
    {
        foreach ( ISelectable newItem in e.OldItems )
        {
            newItem.Selected -= new EventHandler( OnItemSelected );
        }
    }
    if ( e.Action == NotifyCollectionChangedAction.Add || e.Action == NotifyCollectionChangedAction.Replace )
        foreach ( ISelectable newItem in e.NewItems )
        {
            newItem.Selected += new EventHandler( OnItemSelected );
        }
}

When an item is selected the SelectionManager deselects the last selected item. and fires the SelectionChange event.

public void OnItemSelected( object sender, EventArgs e )
{
    _next = this.items.IndexOf( sender as ISelectable );
    if ( _lastSelected != _next )
        this.items[ _lastSelected ].Deselect();
    _lastSelected = _next;
    if ( this.SelectionChange != null )
        this.SelectionChange( sender, new SelectionChangedEventArgs( _lastSelected ) );
}

To deliver the index of the selected item SelectionChange uses SelectionChangedEventArgs that contain an int.

public class SelectionChangedEventArgs : EventArgs
{
    public SelectionChangedEventArgs( int indexOfSelectedItem )
    {
        this.selectedItemIndex = indexOfSelectedItem;
    }
    public int selectedItemIndex;
}

Conclusion

In our example we demonstrate the selection in two ways. Inside the ISelectable control with the blue border and outside in the application with the grey "x"-box. Check out our source for more details.


Subscribe

Comments

  • -_-

    RE: Tip: Easy/Reusable selection with ISelectable and SelectionManager


    posted by Justin-Josef Angel [MSFT] on Aug 26, 2008 10:46

    Very nice article.

    One thing though, if we take this sample towards a real world application, we'd probably want something a bit less intrusive.
    Implementing interfaces on controls provided in the framework is hardly a good option since it requires us to subset all controls in the framework.
    Sub-setting doesn't work as well as we'd think it does because of stuff like missing blend support (the controls icons in blend will always create framework controls and not our subset controls). 

    IMO, A far less intrusive mechanism would have been to catch all user interactions (hookup to all keyboard and mouse events on the Application.RootVisual) and than query the FocusManager to get the currently focused element. 

     

     

  • -_-

    RE: Tip: Easy/Reusable selection with ISelectable and SelectionManager


    posted by Denislav Savkov on Aug 28, 2008 08:16

    Hi Justin and thank you for your comment,

    a less intrusive SelectionManager is possible if we hookup to some of the FrameworkElement events. However this won't work  very well for all FrameworkElements. If we hookup to MouseButtonDown for instance Buttons won't be selectable. If we hookup to GotFocus some elements that don't receive focus like Canvas won't be selectable.

    If it is possible would you explain in more detail your idea with the FocusManager. 

Add Comment

Login to comment:
  *      *