Okay, so we have created a (really simple) menu control, the next step is to wrap it all in a reusable control that you could use in all your pages / in another project. Doing so will require only 2 things :
- manage keyboard and mouse input
- move all the mcml code to a new UI
Moving the control to its own UI element is not really difficult, all we need to remember is to add properties for the needed data.
<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>
<me:MyMenu MenuItems="[TheCommands]" />
</Children>
</ColorFill>
</Content>
</UI>
<UI Name="MyMenu">
<Properties>
<Choice Name="MenuItems" Choice="$Required" />
</Properties>
<Content>
<Repeater Layout="VerticalFlow" Source="[MenuItems.Options]">
<Content>
<me:MyMenuItem
Title="[RepeatedItem!cor:String]"
MyIndex="[RepeatedItemIndex]"
Commands="[MenuItems]"/>
</Content>
</Repeater>
</Content>
</UI>
...
</Mcml>
Nothing difficult here : just cut & paste the Repeater into a new UI, add a property of type Choice and add a "reference" to the newly created control. Now let's add a little interactivity, shall we ?
As I have hinted in the previous post, you just add keyboard and mouse interaction by adding rules to your object. One of the fundamental rules is :
<Default Target="[Input.KeyInteractive]" Value="true" />
which enables a UI to be keyboard-focusable. The actual process of finding which control will be the next one to gain focus when you press a key is left to MediaCenter itself, so don't bother with it. Whenever an item gains focus, the KeyFocus property of Input is changed from false to true, so you can easily add a rule for event-handling.
<Condition Source="[Input.KeyFocus]" SourceValue="true">
<Actions>
<Set Target="[Commands.ChosenIndex]" Value="[MyIndex.Value]" />
<Set Target="[CommandLabel.Font]" Value="Segoe UI, 24, Bold" />
<Set Target="[CommandLabel.Alpha]" Value="1" />
</Actions>
</Condition>
For the MyMenuItem class, we just change the selected item (the chosen index from our list of commands) and the formatting of our label. This code is a little bit different from the one I'd shown in my previous post, but I won't explain here what makes this code easier to read but not as powerful as the other one.
If you look at the SDK documentation, you'll see that the InputElement as quite a few properties, some of them beeing really interesting :
- KeyFocusOnMouseEnter can be set to false if you want the focus not to be automatically taken by a control on mouse-over
- MouseFocus is set to true when the mouse is over an element. (combined with the previous element, it allows you to create a hover effect like an html A/A:hover css rule)
- KeyDeepFocus is set to true if the element or one of its children have the keyboard-focus
- MouseDeepFocus is the same but for mouse-focus
Next thing to do : add a click effect to our menu-item (the process of actually doing something will be covered in a future post). This is a little bit more difficult, as there's no EnterPressed or MouseClicked property on the Input element... For this basic interactivity, we'll use a local ClickHandler object, which as its name suggest, just reacts to a "click" (left mouse button, space and enter) and add rules to its Clicking property.
<UI Name="MyMenuItem">
...
<Locals>
<ClickHandler Name="ClickHandler"/>
</Locals>
<Rules>
...
<Condition Source="[ClickHandler.Clicking]" SourceValue="true">
<Actions>
<Set Target="[CommandLabel.Color]" Value="Red" />
</Actions>
</Condition>
</Rules>
...
</UI>
Now, we'll make it really re-usable by creating a new mcml file, and moving the two controls in it :
MyMenu.mcml
<Mcml
xmlns="http://schemas.microsoft.com/2006/mcml"
xmlns:me="Me"
xmlns:cor="assembly://MScorLib/System">
<UI Name="MyMenu">
<Properties>
<Choice Name="MenuItems" Choice="$Required" />
</Properties>
<Content>
<Repeater Layout="VerticalFlow" Source="[MenuItems.Options]">
<Content>
<me:MyMenuItem
Commands="[MenuItems]"
Title="[RepeatedItem!cor:String]"
MyIndex="[RepeatedItemIndex]"/>
</Content>
</Repeater>
</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>
<Locals>
<ClickHandler Name="ClickHandler"/>
</Locals>
<Rules>
<Default Target="[Input.KeyInteractive]" Value="true" />
<Default Target="[Input.KeyFocusOnMouseEnter]" Value="false" />
<Condition Source="[ClickHandler.Clicking]" SourceValue="true">
<Actions>
<Set Target="[CommandLabel.Color]" Value="Red" />
</Actions>
</Condition>
<Condition Source="[Input.MouseFocus]" SourceValue="true">
<Actions>
<Set Target="[CommandLabel.Alpha]" Value="0.75" />
</Actions>
</Condition>
<Condition Source="[Input.KeyFocus]" SourceValue="true">
<Actions>
<Set Target="[Commands.ChosenIndex]" Value="[MyIndex.Value]" />
<Set Target="[CommandLabel.Font]" Value="Segoe UI, 24, Bold" />
<Set Target="[CommandLabel.Alpha]" Value="1" />
</Actions>
</Condition>
</Rules>
<Content>
<Text Alpha="0.50"
Name = "CommandLabel"
Content="[Title]"
Color="White"
Font="Segoe UI, 20">
</Text>
</Content>
</UI>
</Mcml>
In order to reuse this control (in the same project or another one), you'll need to :
- add this file to your project,
- add it in the resources.resx file
- add the following line in any mcml file in which you want to use those controls (replacing MyAddin/MyDefaultNamespace )
<Aggregate Source="resx://MyAddIn/MyDefaultNamespace.Resources/MyMenu" />
In the next few posts, we'll try to make this menu a full fledged control by adding scrolling, commands, accessibility...