In the last post, I introduced a command button for use in Media Center add-ons. In doing so, I demonstrated the idea of input focus, Command objects and rules to dictate behaviour and data-binding. All of these concepts are transferrable to this instalment’s topic:

A List Control (with Selected Item Tracking)

A simple way of visualising a data source, for the purpose of performing some action on one of its items, is to display the items as a series of buttons. The advantage of using buttons is that they already provide a mechanism for selection (i.e. mouse/keyboard focus) and action (i.e. clicking and invoking a command). With this in mind, the basic goals for a list control are:

  • Dynamically create a button for each item in the list (using the list item to provide text for the button) – for this, we will use a <Repeater> element
  • Arrange the buttons using some logical layout – i’ll introduce the <GridLayout> for this
  • Keep track of which list item is selected – in this regard, it is useful to know both the item and its index in the list. These require a holding mechanism which we will define in code.
  • Regardless of which item is selected, invoke only a single command in response to an action on the list – this will utilise <InvokeCommand>.

Definition

Code

public class ListSelection : ModelItem {

    private int _index;
    private object _item;

    public int Index {
        get {
            return _index;
        }
        set {
            _index = value;
            FirePropertyChanged("Index");
        }
    }

    public object Item {
        get {
            return _item;
        }
        set {
            _item = value;
            FirePropertyChanged("Item");
        }
    }
}

Markup

List.mcml

Usage

<UI Name="ListTest">
  <Locals>
    <ArrayListDataSet Name="[Items]">
      <Source>
        <cor:String String="First item" />
        <cor:String String="Second item" />
        ...
      </Source>
    </ArrayListDataSet>
    <Command Name="ListItemCommand" />
  </Locals>
  <Content>
    <me:List Name="List" Model="[Items]" Command="[ListItemCommand]" />
  </Content>
</UI>

In the above example, the data source is declared inline in MCML markup; normally, the source would be external, perhaps passed to the UI as a property. The ArrayListDataSet class is simply a collection that implements the Media Center SDK interface IPropertyObject, which provides change detection. It is often desirable to bind an IList or IList<T> first to an ArrayListDataSet and then to a UI element, so that changes to the collection are reflected automatically in the UI.

The command declared in the <Locals> section above is not being used for anything; it is merely provided to demonstrate how to associate a command with the list control.

In the <Rules> section of the UI, we could bind to List.SelectedIndex or List.SelectedItem in order to determine which list item was selected. In practice, these properties would be examined when handling the invocation of the list’s command.

Explanation

ListSelection class

In order to track the item which is selected in the list, we need to pass an object to the list item which it can update when it receives focus (it’s not like WinForms where we can just handle an event on the child from within the parent). This item has to be passed by reference, not by value, otherwise changes will not be reflected – so we can’t just use cor:Int32. Additionally, we want to support change notifications – this way, we can use the selection in binding expressions – the best way to achieve this under the Media Center SDK is to extend the ModelItem class and call its FirePropertyChanged() method whenever the item or index are set.

Note that you can simply implement the IPropertyObject interface if you do not want to extend from ModelItem, however only the latter provides a cross-thread compatible mechanism for raising change notifications. Since my network browsing add-on will use worker threads (and raise events on these), this is essential.

List – Properties

The list needs a data source of some sort which implements System.Collections.IList – the data-binding mechanisms in the Media Center SDK do not seem to play nicely with generics (with that said, List<T> implements IList anyway, so that is an acceptable data source).

Since we will be essentially aggregating the Command objects from the list items, we need to require a single command to represent an action performed on the list.

Although ListSelection is being used internally to track the selected item/index, we will expose SelectedIndex and SelectedItem as primitive-type properties for convenience.

List – Content

This example introduces the <Repeater> element, which is the UI primitive used to emit particular UI content for each item in a data source. It will re-emit its content whenever the data source or its content changes.

Like any other container, the repeater can specify a layout for its content. So far i’ve demonstrated Anchor, Dock and VerticalFlow layouts. To make the most of the available space on the screen, particularly when the number of items is unknown (and dynamic), a flow layout can be wasteful. The <GridLayout> will automatically arrange its content into a grid of uniformly-sized cells, filling downward (or sideward) before wrapping to the next column (or row).

The actual list items are defined in a separate <UI> tag – i’ll explain these later. What is important is that we pass several pieces of data to the list items when defining the repeated content: Two reserved object paths, [RepeatedItem] and [RepeatedItemIndex] (which are only valid inside repeater elements), are assigned to each list item so that they have an awareness of a) the item that led to their creation and b) its index in the data source. We must also pass the local instance of ListSelection for the aforementioned reasons.

For the command that must be specified for each list item, we are using an <InvokeCommand>. This is a specialised version of Command which invokes a method, and allows this method to be specified inline. The description comes from the item in the data source (in this case, we just call the ToString() method), and the target of the invocation is the command associated with the whole list. (This performs the aggregation I mentioned.)

List Item – Properties

We cannot simply use the Button element to represent a list item, as we need to extend it to perform selection tracking. This is done via the BaseUI attribute. We need only override the properties and rules – the content and base behaviour are already defined. As stated above, the list item requires the data source item that created it, its index in the data source and the selection holder from the owning list.

List Item – Rules

When the button receives key focus (recalling that key focus can be caused by the keyboard, mouse or remote control), we need to update the owning list’s selection holder so that the selected item and index match the item and index for the list item. This is achieved using a simple <Condition> rule with <Set> actions. The owning list will see changes to these properties (through change notification) and update its properties accordingly.

Next Time

In the next article, i’d like to take a break from MCML and introduce my network browsing and copying add-on. I’ll demonstrate how the add-on is structured, the separation of logic between the download manager service and the interactive portion, and how it achieves the principal tasks of browsing the network, retrieving the paths to media libraries and, finally, copying files.

3 thoughts on “Developing with the Media Center SDK, Part 4

  1. vote

    hi

    i find it nice that you share your mcml skills with us
    an little addition for your List control

    in properties have i added

    under rules have i added this

    the content shows so

    with all this changes have you now this options to set: rows, columns, orientation for scroller and repeater, spacing, size of the list

    Reply
  2. Pingback: Developing with the Media Center SDK, Part 5 | Brad Smith's Coding Blog

Leave a reply to Kay Cancel reply

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> 

required