• MCML : First Contact

    For the last view days, I've take some of my spare time to dwell into the new generation of Media Center Extensibility technologies. I've already blogged (and I will do it again with a little more details) about my "Lazy boy server monitoring" tool, but the subject for today is MCML (for Media Center Markup Language).

    MCML doesn't really have much in common with XAML (for WPF or WPF/e), which is sad. Having to learn one more description language and its associated bag of trick is pretty difficult, but the learning curve for MCML is not really long or even hard. In this post, I'll try to explain how to create a simple menu control with a few options (read without any need to scroll).

    After creating a new Media Center Presentation Layer Application, the project have a Default.mcml file that contains a UI element with some content. The UI element is really important in MCML for it's the root container ofevery graphical element you'll build - like a UserControl in asp.net but with the small difference that pages are also an UI element. Clearing the content from this element will bring you with a good starting point for the menu.

    <Mcml 
       xmlns="http://schemas.microsoft.com/2006/mcml"
               xmlns:me="Me">
       <UI Name="MenuPage">
          
       </UI>
    </Mcml>

    We'll start by adding a ColorFill in the MenuPage. This control is similar to a simple Border object in WPF and by default use all the available space in its container but have on advantage : it serves as a layout manager too (meaning that you can arrange its children in different ways : center them, make them flow vertically etc.).

    <Mcml 
       xmlns="http://schemas.microsoft.com/2006/mcml"
        xmlns:me="Me">
       <UI Name="MenuPage">
          <Content>
             <ColorFill Content="LightGray" Layout="Center">
                <Children>
                   
                </Children>
             </ColorFill>
          </Content>
       </UI>
    </Mcml>

    In MCML, there's nothing like a ListView control or any high-end controls. You'll have to go on with a simple Repeater control and a few tricks, but as you will see, there's nothing really complicated.

    MCML is as much a model/view paradigm as XAML is, so to get a list of menu items to display, we'll need three things :

    • a control to represent a menu-item,
    • a control to host those menu items
    • … and a list of menu-items

    For the last requirement, we'll keep it simple : MCML contains a special collection of objects (the Choice class), that handles most of the thing we'll need : a list of object, a property to know which one is currently selected and events when one of its property changes. As nothing can be that simple and easy, there's a catch in using this Class : by default, MCML does not know what a string (for example) is… Like WPF, you'll need to add a xml-namespace to reference the assembly which contains those types.

    As the Choice class is not a visual element, you won't be able to add it to a Content or Children property, but, that's not a big deal : the UI allows you to define local "variables".

    <Mcml 
       xmlns="http://schemas.microsoft.com/2006/mcml"
        xmlns:me="Me"
       xmlns:cor="assembly://MScorLib/System">
       <UI Name="MenuPage">
          <Locals>
             <Choice Name="TheCommands">
                <Options>
                   <cor:String String="Command1" />
                   <cor:String String="Command2" />
                   <cor:String String="Command3" />
                </Options>
             </Choice>
          </Locals>
          <Content>
             <ColorFill Content="LightGray" Layout="Center">
                <Children>
                   <Repeater Layout="VerticalFlow" Source="[TheCommands.Options]">
                      <Content>
                         <Text Content="[RepeatedItem!cor:String]" />
                      </Content>
                   </Repeater>
                </Children>
             </ColorFill>
          </Content>
       </UI>
    </Mcml>

    As you have certainly guessed, the [Something] notation allows to get the value of a variable (the !-notation is used to do some type casting). So we just created a RepeaterItems which displays the options from the local variable TheCommands, and for each value, displays a text. If you take this mcml snippet and run it into MCML Sampler, you should get something like :

    That's not yet amazing, but it will be ;).

    What we need to do next is to replace the simple Text element with something a little bit more complex (what I want to do is to make items become white when selected and red during a mouse-click/enter-key-press). The easiest way to create such a modern - or even futuristic interface - (:p) is to make a user-control (an new UI element) to display the menu item.

    <Mcml 
       xmlns="http://schemas.microsoft.com/2006/mcml"
        xmlns:me="Me"
       xmlns:cor="assembly://MScorLib/System">
       <UI Name="MenuPage">
          ...
       </UI>
       <UI Name="MyMenuItem">
           <Content>
             <Text Content="-not defined-" />
          </Content>
       </UI>
    </Mcml>
    

    We'll need a way to pass the data from the repeater-item to this control, and that's just what the Properties collection is for. These properties are used in the same way that local variables but can be set be the "caller" (parent) control.

    <Mcml 
       xmlns="http://schemas.microsoft.com/2006/mcml"
        xmlns:me="Me"
       xmlns:cor="assembly://MScorLib/System">
       <UI Name="MyMenuItem">
          <Properties>
             <Choice Name="Commands" Choice="$Required" />
             <Index Name="MyIndex" Index="$Required"/>
             <cor:String Name="Title" String="$Required"/>
             <Color Name="ClickedColor" Color="Red"/>
          </Properties>
          <Content>
             <Text Alpha="0.75" 
                Name = "CommandLabel" 
                Content="[Title]" 
                Color="White" 
                Font="Segoe UI, 20">
             </Text>
          </Content>
       </UI>
    </Mcml>
    

    The $Required notation specifies that there is no default value for this property and that the parent should always send a value. Now we just have to change the repeater control :

    ...
    <Repeater Layout="VerticalFlow" Source="[TheCommands.Options]">
       <Content>
          <me:MyMenuItem 
             Title="[RepeatedItem!cor:String]"
             MyIndex="[RepeatedItemIndex]"
             Commands="[TheCommands]"/>
       </Content>
    </Repeater>
    ...

    Okay, we're almost done. In order to get the selected item to be different from the others, we'll need to write some rules. Those rules are almost the equivalent of Triggers in WPF : you can specify a test (of the value of a property/variable) and if this test is true, have MCML apply some changes.

    The rule for displaying the selected item in a different way is :

    <UI Name="MyMenuItem">
       <Rules>
          <Condition Source="[Commands.ChosenIndex]" SourceValue="[MyIndex.Value]">
             <Actions>
                <Set Target="[CommandLabel.Font]" Value="Segoe UI, 24, Bold" />
                <Set Target="[CommandLabel.Alpha]" Value="1" />
             </Actions>
          </Condition>
       </Rules>
       ...
    </UI>

    It's rather simple : we just check that the ChosenIndex (from the Choice object) is equal to the Index of the current item, and if it's true, we set two properties to different values. If it's false, the default formatting will stay. If you run the mcml file now, the first element will stand-out, but you won't be able to change the selected element. For doing that, you'll need to add the following rule (we'll see more details about mouse/keyboard interaction in another post) :

    <UI Name="MyMenuItem">
       <Rules>
          <Default Target="[Input.KeyInteractive]" Value="true" />
          <Condition Source="[Input.KeyFocus]" SourceValue="true">
             <Actions>
                <Set Target="[Commands.ChosenIndex]" Value="[MyIndex.Value]" />
             </Actions>
          </Condition>
          ...
       </Rules>
       ...
    </UI>
    

    It basically tell the run-time that you control is focusable and that you want to change the ChosenIndex (to set it to the item's index) when an item as the focus. You can now run the sample and look at the amazing menu we have done :

    In the next post, we'll look at the way MediaCenter applications can handle keyboard and mouse input.

    Complete source

    <Mcml 
       xmlns="http://schemas.microsoft.com/2006/mcml"
        xmlns:me="Me"
       xmlns:cor="assembly://MScorLib/System">
       <UI Name="MenuPage">
          <Locals>
             <Choice Name="TheCommands">
                <Options>
                   <cor:String String="Command1" />
                   <cor:String String="Command2" />
                   <cor:String String="Command3" />
                </Options>
             </Choice>
          </Locals>
          <Content>
             <ColorFill Content="LightGray" Layout="Center">
                <Children>
                   <Repeater Layout="VerticalFlow" 
    Source="[TheCommands.Options]"> <Content> <me:MyMenuItem Title="[RepeatedItem!cor:String]" MyIndex="[RepeatedItemIndex]" Commands="[TheCommands]"/> </Content> </Repeater> </Children> </ColorFill> </Content> </UI> <UI Name="MyMenuItem"> <Properties> <Choice Name="Commands" Choice="$Required" /> <Index Name="MyIndex" Index="$Required"/> <cor:String Name="Title" String="$Required"/> <Color Name="ClickedColor" Color="Red"/> </Properties> <Rules> <Default Target="[Input.KeyInteractive]"
    Value="true" /> <Condition Source="[Input.KeyFocus]"
    SourceValue="true"> <Actions> <Set Target="[Commands.ChosenIndex]"
    Value="[MyIndex.Value]" /> </Actions> </Condition> <Condition Source="[Commands.ChosenIndex]"
    SourceValue="[MyIndex.Value]"> <Actions> <Set Target="[CommandLabel.Font]"
    Value="Segoe UI, 24, Bold" /> <Set Target="[CommandLabel.Alpha]"
    Value="1" /> </Actions> </Condition> </Rules> <Content> <Text Alpha="0.75" Name = "CommandLabel" Content="[Title]" Color="White" Font="Segoe UI, 20"> </Text> </Content> </UI> </Mcml>