Looking at the world of media: from music to RIA.

VivMedia Code: Selection Controller

July 3rd, 2008 Posted in ActionScript, Flash Player, Flex Development, Rich Internet Applications

rune_flash.gifWith the previous release of version 0.02 of the code base I added a new class called the SelectionController that enables you to treat items that implement the ISelectable interface as a selection group. As promised, this is the tutorial/indepth examples of the SelectionController and how to use it within your applications.

Overview
The SelectionController allows you to group items similarly to how a radio group works in both HTML and Flex. The general principle with a radio group is that when one radio button is selected, any of the other radio buttons in the group are then deselected. This kind of selection occurs in Lists, DataGrids, etc.

The challenge is that in a highly stylized Flash/Flex applications we often need to treat custom components as a group. One example is my Wordpress PhotoSlider. When a user selects a thumbnail, the selected thumbnail is highlighted and the previously highlighted thumbnail is deselected.

A typical solution is to make the parent container smart enough to understand when an item is selected and then let the parent change the highlight state. This approach can get tricky when you have items that are deeply nested and don’t share an easily accessible common parent or if you have complex selection/deselection requirements that may span across multiple containers or even have selected children that are currently off the display stack.

To solve the easiest issues to the most complex selection challenges I developed a SelectionController that is a Singleton instance that manages selection of content for you. Items first need to implement the ISelectable interface and then they can be added to a selection group. When you want to select or deselect a group you can either pass in the group ID or you can pass in one of the items in the group to handle selection.

Examples
First, lets look at our item that we want to be selectable and then we will look at how you can select/deselect the item.

package com.vivisectingmedia.tutorial
{
	import com.vivisectingmedia.framework.controllers.SelectionController;
	import com.vivisectingmedia.framework.controllers.interfaces.ISelectable;
 
	import flash.events.MouseEvent;
 
	import mx.controls.Button;
 
	public class MySelectionButton extends Button implements ISelectable
	{
		// constructor
		public function MySelectionButton()
		{
			super();
		}
 
		/**
		 * The default method for when a Button is clicked.
		 * We override this method so that when a user clicks
		 * on this Button we select this item and deselect
		 * all the other Buttons in the group.
		 * 
		 * @param event Click event.
		 * 
		 */
		override protected function clickHandler(event:MouseEvent):void
		{
			super.clickHandler(event);
 
			// this item is now selected
			SelectionController.selectItem(this);
		}
 
		/**
		 * This setter is called when the SeletionController
		 * changes the Button's selection state.  Technically,
		 * we do not have to override this method because the Button
		 * Class already supports the selected getter/setter signature
		 * required by the ISelectable interface.
		 *  
		 * @param value True is selected, false is deselected.
		 * 
		 */		
		override public function set selected(value:Boolean):void
		{
			// store the private reference
			super.selected = value;
		}
 
		override public function get selected():Boolean
		{
			return super.selected;
		}
 
	}
}

A couple of notes about the above code. As I mentioned in the comments, we don’t really need to override the get/set method for selected. The Button Class already meets the ISelectable interface requirement, but you will still need to extend the Button class to add the ISelectable interface so that it works with the SelectionController. I overrode the code just to show you how you would need to implement this in a component that does not already support selection states. What I really want you to focus on is implements ISelectable and the clickHandler() method.

In the clickHandler() call we pass up the super.clickHandler() and then we call the SelectionController.selectItem() passing the component instance reference in as the argument. What this is telling the SelectionController to do is first, see if the passed item is in a selection group, if so, select the item by setting selected to true, and then set selected value to false on any other item in the selection group. The SelectionController is friendly enough that if the item passed in is not in a selection group, then the item is still selected and nothing else happens.

So how do you leverage the selection object? Let’s look at some examples:

// create some selectable items
var buttonOne:MySelectionButton = new MySelectionButton();
var buttonTwo:MySelectionButton = new MySelectionButton();
var buttonThree:MySelectionButton = new MySelectionButton();
 
// generate an group id to assign them to
var groupOne:int = SelectionController.generateNewId();
 
// assign the items to the group
SelectionController.addItem(buttonOne, groupOne);
SelectionController.addItem(buttonTwo, groupOne);
SelectionController.addItem(buttonThree, groupOne);

This is the basic use case for grouping items. First, we use a helper method to generate a new group ID. The ID is an int and it must be unique for each group. The generateNewId() returns the next ID number not in use. Once you have an ID you can then add items to the SelectionController using the addItem() method. This stores the reference of the item and pairs it with the group.

Because the MySelectionButton is built with the SelectionController in mind we don’t have to do anything else if we want these three buttons to behave as a group. When a user clicks one of the buttons in the group, the clickHandler calls the SelectionController and the SelectionController will then select the clicked button and deselect the rest.

What happens when you want to programatically select one item, or all of the items in the group or deselect all the items in the group? The SelectionController provides methods to handle these tasks:

// select one item
SelectionController.selectItem(buttonTwo);
 
// select all the items, first get the group if
// you don't know it
var currentGroup:int = SelectionController.getItemGroup(buttonOne);
 
// now select the group
SelectionController.selectAll(currentGroup);
 
// or you can deselect them all
SelectionController.deselectAll(currentGroup);

Considerations and Gotchas
As with all my tutorials, lets talk about things you should consider/know about the SelectionController that may cause you some trouble if you attempt to do certain things.

One group to an item. Selectable items can not be members of multiple groups and when a new group is applied the item is removed from the original group. For example:

// create a new group
var groupTwo:int = SelectionController.generateNewId();
 
// apply the group to ButtonTwo
SelectionController.addItem(buttonTwo, groupTwo);

Doing the above would remove ButtonTwo from groupOne and when you call selectAll() on groupOne only buttonOne and buttonThree would be selected.

Items are Strong referenced. When an item is added to a group it is strong referenced in memory. This means that if you want to delete the item so that Garbage Collection can be applied you need to remove the item form the SelectionController by calling removeItem(). This removes the item from the SelectionController and Garbage Collection can be applied when the next pass occurs.

Multi-Select is supported by not built in by default. If you want to create a multi-selectable grouping you can use the SelectionController for this kind of functionality, but you will need to add more logic to you selectable items. For example, we want our MySelectionButton’s to be multi-selectable when a user has the Control/Command key down. To do this in our example you would modify the clickHandler() like so:

override protected function clickHandler(event:MouseEvent):void
{
	super.clickHandler(event);
 
	if(event.ctrlKey)
	{
		// do not select using the controller
		this.selected = true;
	} else {
		// do select using the controller
		SelectionController.selectItem(this);
	}
}

The above example determines if the user is multi-selecting by checking the ctrlKey value in the MouseEvent. The MouseEvent passes true if the key is down and false if the key is up. In the case when the key is down we want the object selected but we don’t want the Controller to deselect the rest of the items in the group. Otherwise, we do want the rest of the items to be deselected, so use then use the Controller.

That is the basics of the SelectionController, play around with it and let me know if you need more examples, find any issues or have any questions with the Controller.

  1. 8 Responses to “VivMedia Code: Selection Controller”

  2. By webnesto on Jul 3, 2008

    This is a great pattern. It’s frustrating to me that with as long as web-tech has been around and as long as developers have been hacking form elements, using non-form elements as form elements, and otherwise completely thumbing our noses at the way the system(s) have been designed, they haven’t redesigned the system. You would think that this kind of functionality should be a class of behavior that can be assigned to any element rather than one defined as a “radio button”.

  3. By wootwoot on Jul 18, 2008

    Hey thanks for the code, ive been looking for something like this for a while
    I would really appreciate it if you could post the fla files for this example you just did , im getting errors when i run the code posted above.

    error: 1017 the definition of base class Button was not found and a few other

  4. By wootwoot on Jul 18, 2008

    Hey thanks for the code, ive been looking for something like this for a while
    I would really appreciate it if you could post the fla files for this example you just did , im getting errors when i run the code posted above.

    error: 1017 the definition of base class Button was not found and a few other

  5. By wootwoot on Jul 18, 2008

    Hey thanks for the code, ive been looking for something like this for a while
    I would really appreciate it if you could post the fla files for this example you just did , im getting errors when i run the code posted above.

    error: 1017 the definition of base class Button was not found and a few other

  6. By wootwoot on Jul 18, 2008

    Hey thanks for the code, ive been looking for something like this for a while
    I would really appreciate it if you could post the fla files for this example you just did , im getting errors when i run the code posted above.

    error: 1017 the definition of base class Button was not found and a few other

  7. By wootwoot on Jul 18, 2008

    Ok I cleared up most of the errors, but im confused. is this code for Flash CS3 or Flex?
    because it doesnt seem to work on flash CS3

  8. By wootwoot on Jul 18, 2008

    Ok I cleared up most of the errors, but im confused. is this code for Flash CS3 or Flex?
    because it doesnt seem to work on flash CS3

  9. By James on Jul 18, 2008

    The example code above is Flex but the SelectionController does work with CS3. I whipped up a quick example for you and posted it here:

    http://blog.vivisectingmedia.com/examples/Selec...

    I didn’t get a chance to comment the code since this is a quick and dirty example. What is going on is that you have a Document class that generates two SelectableItems, places the items on the stage and then adds them to the SelectionController.

    The items themselves are responsible for tracking their own click event and updating their selection. Check out the code and let me know if this helps explain how to use the SelectionController in CS3.

You must be logged in to post a comment.