Background
This is just one of many ways to implement JIL capabilities into your scroller. I don't know if it would be considered really good, or even really bad - it works for me though, and figured I'd share how I did it (as searching the internet high and low brought back to good matches for me).
Thanks to IgnoranceIsBliss and AndyC for initial ideas on getting this to work.
Right, so let's get going. Jump-in-List, or JIL as it's more commonly refered to as, is the ability to jump around in a Scroller (at least it is in this example) based on keyboard or remote button input. For example, you have a list of 200 TV Shows and want to quickly navigate to Prison Break - press PRI on your keyboard or [using triple-tap] 7-777-444 on your remote. Something like this;

This was one of those must-have features for myTV, but I really dreaded starting work on it because the whole concept seemed like it would require a lot of behind-the scenes logic.
Anyway, needing a break from WiX installer and SQL Database upgrade script issues yesterday I started looking at it, and whaddayaknow. In five or so minutes I had a basic working model up and running within the Main Menu (both Default and Bannerview modes) of myTV. Which means this will finally be included in the next release (which by the way is still coming - Finally have the WiX and SQL issues under control now I think).
What do you need to get this working?
Again, this is my interpretation of this feature. There may be others, more or less effective, better or less better suited to your needs.
Obviously you need a Scroller with focusable items. The easiest way of achieving this is something along the lines of
<
Scroller Name="ShowScroller" ScrollingData="[ShowScrollingData]" Orientation="Vertical" Margins="10,0,10,0" Navigation="RememberFocus, Column" FadeAmount="1" FadeSize="75">
<Children>
<Repeater Name="ShowRepeater" Source="[Choice.Options]" DiscardOffscreenVisuals="true" Navigation="RememberFocus">
<Layout>
<GridLayout Orientation="Vertical" ReferenceSize="0,50" AllowWrap="true"/>
</Layout>
<Content>
<me:RepeatedShowItem Name="ShowItem" Choice="[Choice]" Option="[RepeatedItem!a:TVShow]" OptionIndex="[RepeatedItemIndex]" Selection="[Selection]" Rotate="[Rotate]"/>
</Content>
</Repeater>
</Children>
</Scroller>
The above is taken (pretty much) directly from the Default myTV main menu. There are obviously animations and other effects missing, but it gives you an idea of what's needed. So, in the snippet above I create a Scroller, naming it ShowScroller and feeding it the [ShowScrollingData] object as it's ScrollingData. I also set Navigation to RememberFocus - this means if you leave the scroller (for instance, moving to the Show Overview or command buttons in myTV) and then move back into it, it's supposed to remember what item was previously selected.
Right, inside the scroller we have a Repeater. Pretty much a bog standard one. I have set DiscardOffscreenVisuals to false above, but I cannot remember why or when I did this - it doesn't seem to have any effect on the speed at all.
Finally, the Repeater is set to repeat me:RepeatedShowItem. You probably do not need all the parameters I'm sending down the pipe, but this is how I started out doing things in MCML and well, I don't really see a need to start changing it now :) Most of the properties are self-explanatory, the Choice is, well, the C# Choice object containing all the TVShows from the database. Option is the current Show, OptionIndex is the index of said show, Selection is a Command which when invoked moves you along to the Season (or episode) selection screen. Last but not least, Rotate is a Boolean value which if true makes the ShowBanners rotate.
Ok, that's the Scroller taken care of. Obviously you're going to need a few other elements as well. These are, in non specific order, a TypingHandler, an EditableText object, a Text element (to display your 'search' criteria on-screen), a couple rules and finally an Int32 (or Index if you prefer) which tells the scroller where to move to.
Let's start off with the EditableText and Int32. Personally I've created these in my Application C# class (available to all MCML screens via the App. property) - but you could, with minimal changes have this defined within your MCML code for a webapp or similar. My implementation of this looks like this;
private EditableText _JILtext;
private Int32 _JILindex = new Int32();
[MarkupVisible]
internal EditableText JILtext
{
get { return _JILtext; }
set { if (_JILtext != value) { _JILtext = value; base.FirePropertyChanged("JILtext"); } }
}
[MarkupVisible]
internal Int32 JILindex
{
get { return _JILindex; }
set { if (_JILindex != value) { _JILindex = value; base.FirePropertyChanged("JILindex"); } }
}
then in my public Application(HistoryOrientedPageSession session, AddInHost host) I do
_JILtext =
new EditableText(this.Owner, "JIL");
JILtext.Value = "";
JILtext.Submitted += new EventHandler(JILtext_Activity);
which in effect, creates the EditableText _JILtext. Sets it's .Value to "" (I kept getting NullRefExceptions if I did NOT do this) and finally adds an EventHandler (JILtext_Activity) which fires when the text is submitted. We'll return to this EventHandler later on, but for now that's it for the C# part of things.
Ok, let's add the TypingHandler. In the UI where you want the search to be available from, add a
<
TypingHandler Name="Typer" HandlerStage="Bubbled" MaxLength="3" TypingPolicy="TripleTap"/>
in your <Local> element. MaxLength, for some reason, only seems to be active when the TypingPolicy is set to TripleTap. But really, as we want to allow for remote usage of this, it doesn't matter.
Now, you need some way to attach your EditableText to this TypingHandler. If you (like I have) create the EditableText object in C# you can't simply add a EditableText="[App.JILtext]" - it won't find it. So what we do instead is, Bind the property [App.JILtext] to [Typer.EditableText][ like this (in your Rules section)
<Binding Source="[App.JILtext]" Target="[Typer.EditableText]"/>
Next, you'll need to create a Timer. So back in your Local's, add this
<Timer Name="JILtimer" AutoRepeat="false" Interval="1500" />
so what does this do? Well, it's a Timer which will Submit our EditableText.Value (remember the EventHandler in C#?) 1.5 seconds after it's started.
So next we create a few rules. These looks like
<
Changed Source="[App.JILtext.Value]">
<Conditions>
<Equality Source="[App.JILtext.Value]" ConditionOp="NotEquals" Value=""/>
</Conditions>
<Actions>
<Set Target="[JILtimer.Enabled]" Value="true"/>
</Actions>
</Changed>
<Changed Source="[JILtimer.Tick]">
<Actions>
<Invoke Target="[App.JILtext.Submit]"/>
</Actions>
</Changed>
<Changed Source="[App.JILindex]">
<Actions>
<Invoke Target="[ScrollingData.Scroll]" amount="[App.JILindex]"/>
</Actions>
</Changed>
Ok. Rule by Rule, these do the following. The first one will Start the Timer mentioned above, when JILtext.Value has changed (ie. because you have typed something), but only if JILtext.Value is not "" (this will become clear in the EventHandler further down).
Second rule is fired when the Timer ticks - ie. 1.5 seconds after it was started. What it does is simply to submit the EditableText.Value (which in turn fires the EventHandler in C#).
Finally, the last rule actually takes care of moving the Scroller to the correct location. It does this whenever App.JILindex changes.
And at last it's time for the EventHandler which actually does all (not much really, but never mind) the work. This will obviously need to be customized for your needs, but it shows you the basics at least.
void JILtext_Activity(Object sender, EventArgs e)
{
int jump = 0;
int current = TVShows.ChosenIndex;
//throw new NotImplementedException();
foreach (TVShow tvs in TVShows.Options)
{
if (tvs.SortName.ToLower().StartsWith(JILtext.Value.ToString().ToLower()))
{
jump = TVShows.Options.IndexOf(tvs);
JILindex = jump - current;
break;
}
}
JILtext.Value = "";
}
so, step-by-step. We create a couple Int's. One called jump (which we'll assign a value to later) and one called current, which is the
currently chosen item from our Scroller. Next, and this is where you'll have to customize, we step through each and every item in TVShows.Options (which is the Choice object passed into the Repeater inside the Scroller we're jumping around in). For each item we check to see if SortName begins with JILtext.Value (which is whatever you typed on-screen) and if it does, JILindex is set to the
first match in your Choice, it then breaks the foreach loop.
Finally, JILtext.Value is reset to "" which removes the text from being displayed on screen. The reason for setting this outside the foreach loop is so that it is 'emptied' even if there were no matching entries in your Choice.
That's it pretty much. There are obviously room for improvement here, but it does what I intended it to do, and it was way easier than I had imagined.
What I'm still working on
Having the JIL move the scroller to the nearest matching item if no direct matches exist. For example, you enter PRA but the closest match is Prison Break, it'll still move the scroller to the vincinity of what you're looking for. Currently it'll just sit there, doing nothing.
Starting the 1.5 second timer after you've entered your text. I.e it should re-start when you enter character 2 and/or 3. I tried doing this with a Timer, but instead of restarting it seems to simply fire two more Timers - in effect, not working.
And finally, have the text be displayed on-screen instantly. For some reason, it currently takes about a second after you've pressed a key for the character to appear on screen and 'matching' begin. Removing TripleTap from the TypingHandler fixes this, but obviously then you cannot navigate the list effectively with your remote.
Right, that's it for now. Enjoy.
What I forgot to include above
The all important Text element. You can obviously place this anywhere you want, but mine looks like this
<Text Name="JILdisplay" Content="[App.JILtext.Value]" Font="font://comm:JILdisplay" Color="color://comm:OffWhite" Alpha=".75">
<LayoutInput>
<FormLayoutInput Right="Parent,1" Bottom="Parent,1"/>
</LayoutInput>
</Text>
which displays it as shown in the screenshot above. The Font and Color values are taken from my aggregate file, namespaced as comm.
Posted
Thu, Nov 22 2007 16:56
by
admin