12 August 2015

Gotcha: custom Microsoft Band page shows only data in last added UI element

Today I finally nailed a very odd issue in the Microsoft Band SDK - or actually, my understanding thereof. For my current research project, I am building a custom UI on my Microsoft Band. I should contain two panels: one with a text, an icon and a button, and a second panel containing two texts. Details of how to setup up something like this, can be found in the band samples.

Building a Band UI is not for the faint hearted, it includes building the UI from code without any kind of designer. Filling those UI elements with a value then is a separate step, which contains quite a big gotcha that you cannot derive from the samples.

My Band UI creation code is like this:

private IEnumerable<PageLayout> BuildTileUi()
{
  var bandUi = new List<PageLayout>();
  var page1Elements = new List<PageElement>
  {
	new Icon {ElementId = IconId, Rect = new PageRect(60,10,24,24)},
	new TextBlock  {ElementId = TextTemperatureId, Rect = new PageRect(90, 10, 50, 40)},
	new TextButton {ElementId = ButtonToggleFanId, Rect = new PageRect(10, 50, 220, 40), 
	    HorizontalAlignment = HorizontalAlignment.Center}
  };
  var firstPanel = new FilledPanel(page1Elements) { Rect = new PageRect(0, 0, 240, 150) };

  var page2Elements = new List<PageElement>
  {
	new TextBlock {ElementId = TextTimeId, Rect = new PageRect(10, 10, 220, 40)},
	new TextBlock {ElementId = TextDateId, Rect = new PageRect(10, 58, 220, 40)}
  };
  var secondPanel = new FilledPanel(page2Elements) { Rect = new PageRect(0, 0, 240, 150) };

  bandUi.Add(new PageLayout(firstPanel));
  bandUi.Add(new PageLayout(secondPanel));

  return bandUi;
}

That is quite a handful, right? Then for setting values to those UI element, I had this little piece of code:

private List<PageData> BuildTileData1(string timeText,
                                      string dateText,
                                      string temperature,
                                      string buttonText)
{
  var result = new List<PageData>
  {
    new PageData(Page2Id, 1,
      new TextBlockData(TextTimeId, timeText)),
    new PageData(Page2Id, 1,
      new TextBlockData(TextDateId, dateText)),
    new PageData(Page1Id, 0, new IconData(IconId, 1)),
    new PageData(Page1Id, 0,
      new TextBlockData(TextTemperatureId, temperature)),
    new PageData(Page1Id, 0, new TextButtonData(ButtonToggleFanId, buttonText))
  };

  return result;
}

And this is set to my tile using bandClient.TileManager.SetPagesAsync. Which gave me a quite puzzling result: only the last element on each page actually had a value. So that means only the datetext and the button actually showed text. As the sample in the Band SDK samples only uses one UI element, I was like, repeat adding PageData elements, right?

Wrong!

It turns out you can only add values to a PageData element once per SetPagesAsync call. So what you need to do, actually, is:

private List<PageData> BuildTileData(string timeText, 
                                     string dateText, 
                                     string temperature, 
                                     string buttonText)
{
  var result = new List<PageData>
  {
    new PageData(Page2Id, 1,
      new TextBlockData(TextTimeId, timeText),
      new TextBlockData(TextDateId, dateText)),
    new PageData(Page1Id, 0, 
      new IconData(IconId, 1), 
      new TextButtonData(ButtonToggleFanId, buttonText), 
      new TextBlockData(TextTemperatureId, temperature)),
  };

  return result;
}

See the difference? Only add 2 PageData elements, one for every Page, not one for every page element data object. Maybe elementary, but I overlooked it. For quite some time. The constructors of PageData give a bit of a hint:

public PageData(Guid pageId, int pageLayoutIndex, IEnumerable<PageElementData> values);
public PageData(Guid pageId, int pageLayoutIndex, params PageElementData[] values);

as the third parameter is not a page element data object but a list of it. The thing is, I created the UI on demand (when I tapped the tile) and then it worked as expected - but not the seconds time around.

It took me quite some time to figure this one out, so I thought it best to blog about it right away. Maybe I am giving help here, maybe I am just documenting my own stupidity, but nevertheless ;)

I skip the demo sample this time - if you run into this problem, you are quite far with the Band UI already, and the demo I am working on contains this code anyway and will - hopefully - arrive online soon anyway. Attentive readers will have some inkling of what that demo exactly comprises.

Happy Band coding!

No comments: