Showing posts with label WinRT. Show all posts
Showing posts with label WinRT. Show all posts

Sunday, March 10, 2013

Displaying Status with Live Tiles

Brain Dead Simple Windows RT App Part 4

In Windows Phone, Microsoft introduced the idea of Tiles that could provide the use with updates. Your tile is important because it is the one thing that is always there whispering to the user “run me” day and night. There are four types of updates, Local, Scheduled, Periodic and Push. Because this is a basic level intro article, I’m going to ignore Scheduled, Periodic and Push and work on Local (in the wild, you will probably need to use one of the other three).

Windows 8 offers a total of 46 tile templates that are enumerated in TileTemplateType. To update a tile you:

  1. Get the Template with TileUpdateManager.GetTemplateContent
  2. Create a notification or a new TileNotification object with a reference to the template you retrieved on Step 1 and set any ExpirationTime
  3. Set notification text on the Template XML you retrieved on Step 1
  4. Update Live Tile Create a TileUpdater and call its Update with a the TileNotification created in Step 1

Back to the App

I’m lazy, so I don’t even want to open my app to know how excited I am. I can use the apps tile to remind me. Now I only need to open the app to use the more advanced features like changing my excitement level.

NOTE: In this app, the settings roam but the Live Tile does not. Since this is a really simple demo, I’m OK with that, I would never ship an app like this.

public static void SetLiveTile(string title, string body, int? seconds = null)
{
    // Get tile template.
    var tileTemplate = TileTemplateType.TileSquareBlock;
    XmlDocument tileXml = TileUpdateManager.GetTemplateContent(tileTemplate);

    // Create notification.
    var notification = new TileNotification(tileXml);
    if (seconds.HasValue)
    {
        notification.ExpirationTime = DateTime.Now + 
        TimeSpan.FromSeconds(seconds.Value);
    }

    // Set notification text.
    XmlNodeList nodes =    tileXml.GetElementsByTagName("text");
    nodes[0].InnerText = title;
    nodes[1].InnerText = body;

    // Update Live Tile.
    var upd = TileUpdateManager.CreateTileUpdaterForApplication();
    upd.Update(notification);
}

And I add the following line to the end of saveSettings()

SetLiveTile(string.Format("{0}!", _exlamationPointCount), HelloMessage, 3600);

Complete Code


using System;
using System.ComponentModel;
using Windows.Data.Xml.Dom;
using Windows.Storage;
using Windows.UI.Notifications;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace HelloWindows
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private const string BASE_MESSAGE = "Hello Windows";

        public MainPage()
        {
            this.InitializeComponent();

            // Load Settings here:
            loadSettings();
            HelloMessage = setMessge(_exlamationPointCount);
            this.DataContext = this;
        }


        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new  PropertyChangedEventArgs(propertyName));
            }
        }

        public string HelloMessage { get; set; }


        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  
        /// The Parameter property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private int _exlamationPointCount = 0;

        private string setMessge(int exlamationPointCount)
        {
            if (exlamationPointCount > 0)
            {
                return _baseMessage + new string('!', exlamationPointCount);
            }
            else
            {
                return _baseMessage;
            }
        }

        private void addPoint_Click(object  sender, RoutedEventArgs e)
        {
            HelloMessage = setMessge(++_exlamationPointCount);
            RaisePropertyChanged("HelloMessage");
            RaisePropertyChanged("IsRemovePossible");
            // This is overkill
            saveSettings();
        }

        private void removePoint_Click(object sender, RoutedEventArgs e)
        {
            if (_exlamationPointCount >= 1)
            {
                HelloMessage = setMessge(--_exlamationPointCount);
                RaisePropertyChanged("HelloMessage");
                RaisePropertyChanged("IsRemovePossible");
                // This is overkill
                saveSettings();
            }
        }

        // Make it possible to disable Remove Point button when there
        // is nothing left to remove
        public bool IsRemovePossible
        {
            get { return _exlamationPointCount > 0; }
        }
        // I replace most refrence to BASE_MESSAGE to _baseMessage
        private string _baseMessage = BASE_MESSAGE;

        // Ensure that RoamingSettings has been loaded
        private static ApplicationDataContainer _roamingSettings;
        public static ApplicationDataContainer RoamingSettings
        {
            get
            {
              if (_roamingSettings == null)
              {
                  // If I don't want roaming, use 
                  // ApplicationData.Current.LocalSettings;
                  _roamingSettings = ApplicationData.Current.RoamingSettings;
              }
              return _roamingSettings;
            }
        }

        // I know that saving every time the value is changed,
        // in real life I'd save the data in App.OnSuspending()
        private void saveSettings()
        {
            // I combine both values into a ApplicationDataCompositeValue
            // and save them both to "HighPriority"
            var composite = new ApplicationDataCompositeValue();
            composite["baseMessage"] = _baseMessage;
            composite["exlamationPointCount"] = _exlamationPointCount;
            RoamingSettings.Values["HighPriority"] = composite;

           SetLiveTile(string.Format("{0}!" , _exlamationPointCount),
           HelloMessage, 3600);
        }
        // To be called from the constructor
        private void loadSettings()
        {
            // I get "HighPriority" cast it to ApplicationDataCompositeValue
            // and extract the values.
            var composite = (ApplicationDataCompositeValue)
            RoamingSettings.Values["HighPriority"];
            if (composite != null) // the first time it will be null
            {
                _baseMessage = (string)composite["baseMessage"];
                _exlamationPointCount = (int)composite["exlamationPointCount"];
          }
      }


      public static void SetLiveTile(string     title, string body, int? seconds = null)
      {
          // Get tile template.
          var tileTemplate = TileTemplateType.TileSquareBlock;
          XmlDocument tileXml = TileUpdateManager.GetTemplateContent(tileTemplate);

          // Create notification.
          var notification = new TileNotification(tileXml);
          if (seconds.HasValue)
          {
              notification.ExpirationTime = DateTime.Now +
              TimeSpan.FromSeconds(seconds.Value);
          }

          // Set notification text.
          XmlNodeList nodes = tileXml.GetElementsByTagName("text");
          nodes[0].InnerText = title;
          nodes[1].InnerText = body;

          // Update Live Tile.
          var upd = TileUpdateManager.CreateTileUpdaterForApplication();
          upd.Update(notification);
      }
    }
}

My Brain Dead Simple Windows RT App: The Series

Thursday, March 07, 2013

Roaming data with ApplicationData

Brain Dead Simple Windows RT App Part 3

In Window 8, you should be able to move between your devices and your apps should follow you. In the Microsoft documentation, there is a lot of talk about some office worker who likes to start some task on her office computer (possibly when her boss’s back is turned) and finish them on the bus with her tablet. The feature that they are pushing with these examples is Roaming.

So by the time that Emily (we can call her that) turns off her computer at work, the app needs to save some state data to the cloud, when she opens the same app on her tablet, it goes out and gets the that data from the cloud and uses it to remember where she left off.

All the stuff you need to do this is in Windows.Storage.ApplicationData. You have a choice of where you want to save it (Roaming or Local) and how (ApplicationDataContainer or StorageFolder).

Roaming data is first stored locally and then pushed to Microsoft servers on the cloud; there is a limit to how much data can be roamed, you can use RoamingStorageQuota to find out how much (note that if you exceed the quota, it will just fail without any errors at all).

Local stores the data locally so no roaming occurs but you have more space to work with and can be used for things that aren’t appropriate to roam.

ApplicationDataContainer (used for RoamingSettings & LocalSettings) contains a property called Values which is a dictionary that contains the settings. Each value in the dictionary is handled separately, so one value may be roamed before the other. You can use ApplicationDataCompositeValue to stick values together and they will be treated as a single unit. Oh, yea, there is a special value in the dictionary called “HighPriority” that will be roamed before everything else.

StorageFolder (used for RoamingFolder, LocalFolder & TemporaryFolder) gives you a handle to a folder in the file system that you can use to read and write files. Files in RoamingFolder will be synced with the magic Microsoft server (if the total size doesn’t exceed RoamingStorageQuota. Files in LocalFolder are not synced but will stay around for a while; the files in TemporaryFolder don’t.

Back to the App

When I run my Hello app, I can press the Exclamation Point Button 42 times but when I close it down and open it up again, if forgets. So, when I open the app again, there are NO exclamation points and that dampens my enthusiasm; I want to keep my excitement so the exclamation points need to stay. We can solve this problem with Roaming!

 

// I replace most refrence to BASE_MESSAGE to _baseMessage
private string _baseMessage = BASE_MESSAGE;

// Ensure that RoamingSettings has been loaded
private static ApplicationDataContainer _roamingSettings;
public static ApplicationDataContainer RoamingSettings
{
    get
    {
        if (_roamingSettings == null)
        {
            //If I don't want roaming, use ApplicationData.Current.LocalSettings;
            _roamingSettings = ApplicationData.Current.RoamingSettings;
        }
        return _roamingSettings;
    }
}

// I know that saving every time the value is changed,
// in real life I'd save the data in App.OnSuspending()
private void saveSettings()
{
    // I combine both values into a ApplicationDataCompositeValue
    // and save them both to "HighPriority"
    var composite = new ApplicationDataCompositeValue();
    composite["baseMessage"] = _baseMessage;
    composite["exlamationPointCount"] = _exlamationPointCount;
    RoamingSettings.Values["HighPriority"] = composite;
}
// To be called from the constructor
private void loadSettings()
{
    // I get "HighPriority" cast it to ApplicationDataCompositeValue
    // and extract the values.
    var composite = (ApplicationDataCompositeValue)
    RoamingSettings.Values["HighPriority"];
    if (composite != null) // the first time it will be null
    {
        _baseMessage = (string)composite["baseMessage"];
        _exlamationPointCount = (int)composite["exlamationPointCount"];
    }
}

And I change the constructor to look like:

public MainPage()
{
    this.InitializeComponent();

    // Load Settings here:
    loadSettings();
    HelloMessage = setMessge(_exlamationPointCount);
    this.DataContext = this;
}

And the setMessage() to the field instead of the constant:

private string setMessge(int exlamationPointCount)
{
    if (exlamationPointCount > 0)
    {
        return _baseMessage + new string('!', exlamationPointCount);
    }
    else
    {
        return _baseMessage;
    }
}

Since I haven’t changed the markup, I won’t repost that part (see my previous post), but I will repost the code behind (MainPage.xaml.cs):

using System.ComponentModel;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace HelloWindows
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private const string BASE_MESSAGE = "Hello Windows";

        public MainPage()
        {
            this.InitializeComponent();

            // Load Settings here:
            loadSettings();
            HelloMessage = setMessge(_exlamationPointCount);
            this.DataContext = this;
        }


        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new  PropertyChangedEventArgs(propertyName));
            }
        }

        public string HelloMessage { get; set; }


        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  
        /// The Parameter property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private int _exlamationPointCount = 0;

        private string setMessge(int  exlamationPointCount)
        {
            if (exlamationPointCount > 0)
            {
                return _baseMessage + new string('!', exlamationPointCount);
            }
            else
            {
                return _baseMessage;
            }
        }

        private void addPoint_Click(object      sender, RoutedEventArgs e)
        {
            HelloMessage = setMessge(++_exlamationPointCount);
            RaisePropertyChanged("HelloMessage");
            RaisePropertyChanged("IsRemovePossible");
            // This is overkill
            saveSettings();
        }

        private void removePoint_Click(object sender, RoutedEventArgs e)
        {
            if (_exlamationPointCount >= 1)
            {
                HelloMessage = setMessge(--_exlamationPointCount);
                RaisePropertyChanged("HelloMessage");
                RaisePropertyChanged("IsRemovePossible");
                // This is overkill
                saveSettings();
            }
        }

        // Make it possible to disable Remove Point button when there
        // is nothing left to remove
        public bool IsRemovePossible
        {
            get { return _exlamationPointCount > 0; }
        }
        // I replace most refrence to BASE_MESSAGE to _baseMessage
        private string _baseMessage = BASE_MESSAGE;

        // Ensure that RoamingSettings has been loaded
        private static ApplicationDataContainer _roamingSettings;
        public static ApplicationDataContainer RoamingSettings
        {
            get
            {
                if (_roamingSettings == null)
                {
                    // If I don't want roaming, use 
                    // ApplicationData.Current.LocalSettings;
                    _roamingSettings = ApplicationData.Current.RoamingSettings;
                }
                return _roamingSettings;
            }
        }

        // I know that saving every time the value is changed,
        // in real life I'd save the data in App.OnSuspending()
        private void saveSettings()
        {
            // I combine both values into a ApplicationDataCompositeValue
            // and save them both to "HighPriority"
            var composite = new  ApplicationDataCompositeValue();
            composite["baseMessage"] = _baseMessage;
            composite["exlamationPointCount"] = _exlamationPointCount;
            RoamingSettings.Values["HighPriority"] = composite;
        }
        // To be called from the constructor
        private void loadSettings()
        {
            // I get "HighPriority" cast it to ApplicationDataCompositeValue
            // and extract the values.
            var composite = (ApplicationDataCompositeValue)
            RoamingSettings.Values["HighPriority"];
            if (composite != null           ) // the first time it will be null
            {
                _baseMessage = (string)composite["baseMessage"];
                _exlamationPointCount = (int)composite["exlamationPointCount"];
            }
        }
    }
}

My Brain Dead Simple Windows RT App: The Series

Monday, March 04, 2013

Using App Bars to Hide Chrome

Brain Dead Simple Windows RT App Part 2

In Microsoft’s UX Guidelines for Windows Store apps, they talk about reducing “chrome” and hiding it in well known locations (to anyone who uses Windows 8). A couple of those well known locations are the top and bottom App Bars. Microsoft has very specific recommendations on where commands belong on these things. I will violate those standards and put one button on the top and one on the bottom.

App Bars are hidden UI elements that can be used to hide commands that are too chromey for the main canvas. They appear when you:

  • swipe from the top or bottom of the screen
  • right click your mouse
  • press WindowKey + Z

The Changes

The app fails to express my level of enthusiasm, which can very as time goes on. So, I propose that I add the ability to add and remove exclamation points to the greeting as my mood changes.

I will put a button to add an exclamation point to the Top App Bar (in clear violation of the UX guidelines) and a button to remove exclamation point to the Bottom App Bar.

<Page.TopAppBar>
    <AppBar>
        <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
            <Button Style="{StaticResource AddAppBarButtonStyle}" 
                Click="addPoint_Click"  />
        </StackPanel>
    </AppBar>
</Page.TopAppBar>
<Page.BottomAppBar>
    <AppBar>
        <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
            <Button Style="{StaticResource RemoveAppBarButtonStyle}" 
                Click="removePoint_Click" 
                IsEnabled="{Binding Path=IsRemovePossible}"  />
        </StackPanel>
    </AppBar>
</Page.BottomAppBar>

The styles are available in \Common\StandardStyles.xaml, you need to find them and uncomment them or copy them to MainPage.xaml’s Page.Resources (like I did):

<!-- These styles along with many other can be found in \Common\StandardStyles.xaml -->
<Style x:Key="AddAppBarButtonStyle" TargetType="ButtonBase" 
    BasedOn="{StaticResource AppBarButtonStyle}">
        <Setter Property="AutomationProperties.AutomationId" 
            Value="AddAppBarButton" />
        <Setter Property="AutomationProperties.Name" Value="Add" />
        <Setter Property="Content"                 Value="&#xE109;" />
    </Style>    
    <Style x:Key="RemoveAppBarButtonStyle" TargetType="ButtonBase" 
            BasedOn="{StaticResource AppBarButtonStyle}">
        <Setter Property="AutomationProperties.AutomationId" 
            Value="RemoveAppBarButton" />
        <Setter Property="AutomationProperties.Name" Value="Remove" />
        <Setter Property="Content" Value="&#xE108;" />
    </Style>        

So here’s the additional code (I put it at the bottom of my MainPage.xaml.cs), what is does really isn’t important for this post, it just needs to do something to prove that everything is working.

private int _exlamationPointCount = 0;

private static string setMessge(int exlamationPointCount)
{
    if (exlamationPointCount > 0)
    {
        return BASE_MESSAGE + new string('!', exlamationPointCount);
    }
    else
    {
        return BASE_MESSAGE;
    }
}

private void addPoint_Click(object sender, RoutedEventArgs e)
{
    HelloMessage = setMessge(++_exlamationPointCount);
    RaisePropertyChanged("HelloMessage");
    RaisePropertyChanged("IsRemovePossible");
}

private void removePoint_Click(object sender, RoutedEventArgs e)
{
    if (_exlamationPointCount >= 1)
    {
        HelloMessage = setMessge(--_exlamationPointCount);
        RaisePropertyChanged("HelloMessage");
        RaisePropertyChanged("IsRemovePossible");
    }
}

// Make it possible to disable Remove Point button when there
// is nothing left to remove
public bool IsRemovePossible
{
    get { return _exlamationPointCount > 0; }
}

Basically, what I’m doing is keeping track of exclamation point count in _exlamationPointCount. When the buttons are click I increment or decrement the count, make a new message with setMessge() and notify that the properties have been changed.

Complete MainPage Code Listings


MainPage.xaml

<Page
        x:Class="HelloWindows.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:HelloWindows"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">

    <Page.Resources>
    <!-- These styles along with many other can be found in 
        \Common\StandardStyles.xaml -->
        <Style x:Key="AddAppBarButtonStyle" TargetType="ButtonBase" 
                BasedOn="{StaticResource AppBarButtonStyle}">
            <Setter Property="AutomationProperties.AutomationId" 
                Value="AddAppBarButton" />
            <Setter Property="AutomationProperties.Name" Value="Add '!'" />
            <Setter Property="Content" Value="&#xE109;" />
    </Style>
    <Style x:Key="RemoveAppBarButtonStyle" TargetType="ButtonBase" 
                BasedOn="{StaticResource AppBarButtonStyle}">
            <Setter Property="AutomationProperties.AutomationId" 
                Value="RemoveAppBarButton" />
            <Setter Property="AutomationProperties.Name" Value="Remove '!'" />
            <Setter Property="Content" Value="&#xE108;" />
        </Style>        
    </Page.Resources>
    <Page.TopAppBar>
        <AppBar>
            <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
                <Button Style="{StaticResource AddAppBarButtonStyle}" 
                    Click="addPoint_Click"  />
            </StackPanel>
        </AppBar>
    </Page.TopAppBar>
    <Page.BottomAppBar>
        <AppBar>
            <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
                <Button Style="{StaticResource RemoveAppBarButtonStyle}" 
                    Click="removePoint_Click" 
                IsEnabled="{Binding  Path=IsRemovePossible}"  />
            </StackPanel>
        </AppBar>
    </Page.BottomAppBar>
    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="{Binding Path=HelloMessage}" 
            Style="{StaticResource    HeaderTextStyle}" 
            HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</Page>

MainPage.xaml.cs

using System.ComponentModel; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace HelloWindows { /// <summary> /// An empty page that can be used on its own or navigated to within a Frame. /// </summary> public sealed partial class MainPage : Page, INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private const string BASE_MESSAGE = "Hello Windows"; public MainPage() { this.InitializeComponent(); HelloMessage = setMessge(_exlamationPointCount); this.DataContext = this; } private void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public string HelloMessage { get; set ; } /// <summary> /// Invoked when this page is about to be displayed in a Frame. /// </summary> /// <param name="e">Event data that describes how this page was reached. /// The Parameter /// property is typically used to configure the page.</param> protected override void OnNavigatedTo(NavigationEventArgs e) { } private int _exlamationPointCount = 0; private static string setMessge(int exlamationPointCount) { if (exlamationPointCount > 0) { return BASE_MESSAGE + new string('!', exlamationPointCount); } else { return BASE_MESSAGE; } } private void addPoint_Click(object sender, RoutedEventArgs e) { HelloMessage = setMessge(++_exlamationPointCount); RaisePropertyChanged("HelloMessage"); RaisePropertyChanged("IsRemovePossible"); } private void removePoint_Click(object sender, RoutedEventArgs e) { if (_exlamationPointCount >= 1) { HelloMessage = setMessge(--_exlamationPointCount); RaisePropertyChanged("HelloMessage"); RaisePropertyChanged("IsRemovePossible"); } } // Make it possible to disable Remove Point button when there // is nothing left to remove public bool IsRemovePossible { get { return _exlamationPointCount > 0; } } } }

My Brain Dead Simple Windows RT App: The Series

Sunday, March 03, 2013

Brain Dead Simple Windows RT App

Brain Dead Simple Windows RT App Part 1

This article creates a really simple base app that I will use as a starting point for at least 1 other blog post (I’m holding this one until the first one is done).

In this series I will site design standards and then violate them. My intention is to justify why you need to learn how to use things like App Bars and Charms. At the same time, I don’t want to spend too many words trying to make everything UX correct. Microsoft has committed thousands of bits to the internet about Windows 8 UX standards on MSDN and various blogs.

Build the Brain Dead Simple App

  1. Create a new Visual C# Windows Store “Blank App (XAML)”
  2. Call it “HelloWindows”
  3. Open MainPage.xaml
  4. Within the bottom most Grid, add a text block with the Text of “Hello Windows”



I added a little style and now the grid XAML looks like this:

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <TextBlock Text="Hello Windows" 
        Style="{StaticResource HeaderTextStyle}"
        HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>

Add Binding for Future Proofiness

I know that I will need to make my app a little more interactive. With this in mind, I’m going to add some really simple binding code and XAML plumbing so I can separate this from the later posts.

public sealed partial class MainPage : Page, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private const string BASE_MESSAGE = "Hello Windows";
    public MainPage()
    {
        this.InitializeComponent();

        HelloMessage = setMessge(ExlamationPointCount);
        this.DataContext = this;
    }

    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
AND XAML
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <TextBlock Text="{Binding Path=HelloMessage}" 
        Style="{StaticResource HeaderTextStyle}" />
</Grid>

My Brain Dead Simple Windows RT App: The Series

Tuesday, February 26, 2013

I want Half View in Windows 8

Windows 8 has this mode that they call "Snap View" where an app takes 10% of the screen and another one takes the other 90%. This is cool because it allows me to work with two apps at the same time (sort of). However I sometimes like to compare things where one app isn’t that much more important than the other. For this I would request half screen where each app would get 50%.

This may be an edge case, but I like to compare the WinRT version of common apps like Kindle and Evernote with their Classic Windows versions. With two monitors I could do this, but if with just my laptop, no way. I would also like to compare two WinRT apps or even compare the data of two different apps.

Monday, February 11, 2013

My New Surface RT

This weekend I finally got my Windows RT tablet. It is a 64 GB Surface. I am using it right now to write this very blog entry.

ARM vs Intel

Originally, I was going to hold out for a Surface Pro (and I nearly made it), but I began to think about: 1) I need to be able to test my stuff on an ARM device. 2) I need to think in a Metro way (since I don’t work for the Empire, I can still use that term), I need to find a Metro app to solve whatever problem I need to solve. 3) Most of the desktop stuff I need to do require more screen area; 10 inches is not enough space for Visual Studio. 4) Besides, I’m a cheapskate.

I am running Windows 8 on my elderly HP laptop. I plan on getting a new laptop with touch much later this year; it’ll probably be cheaper than this Surface RT.

Office

I know that Window 8 is somewhat experimental. Still, I’m disappointed that there is no Metro, er, Modern App version of Office. I know that would be a big task and that Microsoft will eventually get around to it, it would be cool to see how such a big and important app migrated to the Modern World. Is there an Office worthy app out there that belongs on the Tablet.

Tuesday, January 29, 2013

WinRT CS vs JS: Grids

C# XAML

In XAML, a grid is pretty straight forward. Within the Grid tag, you define the rows and column in RowDefinitions and ColumnDefinitions tags. Below the definitions, you place the controls that will reside in the Grid, you assign them to cells and control cell spans with Row. attributes.

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="1*"/>
        <RowDefinition Height="1*"/>
        <RowDefinition Height="1*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*"/>
        <ColumnDefinition Width="1*"/>
        <ColumnDefinition Width="1*"/>
    </Grid.ColumnDefinitions>
    <TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3">Title</TextBlock>
    <TextBlock Grid.Row="1" Grid.Column="0">2, 1</TextBlock>
    <TextBlock Grid.Row="1" Grid.Column="1">2, 2</TextBlock>
    <TextBlock Grid.Row="1" Grid.Column="2">2, 3</TextBlock>
    <TextBlock Grid.Row="2" Grid.Column="0">3, 1</TextBlock>
    <TextBlock Grid.Row="2" Grid.Column="1">3, 2</TextBlock>
    <TextBlock Grid.Row="2" Grid.Column="2">3, 3</TextBlock>
</Grid>

This is straight forward; the WinJS examples will attempt to match this layout.

JavaScript HTML


In WinJS, a grid is achieved using an IE 10 specific version of the CSS Grid Layout. Since this is CSS, you can select HTML elements using id or class selectors, you can even use inline styles. In this example, I will mix in all three. Basically you:

Create a style for the grid, in that style you need to tell it to display as a grid with a display attribute with the value -ms-grid.

Define the columns with a -ms-grid-columns property (“1fr” means 1 fraction value, behaves like 1* in XAML, see table below).

Define the rows with a -ms-grid-rows property.

You can assign an HTML element to a row with a -ms-grid-row and to a column with a -ms-grid-column. I have been playing with the idea of creating a class for each row and column/

With this in mind, I created the following CSS (put in css/default.css, if you want to do this yourself):

#MainGrid {
    display: -ms-grid;
    -ms-grid-columns: 1fr 1fr 1fr;
    -ms-grid-rows: 1fr 1fr 1fr;
    height: 100%; /* make it big, so we can see it */
}
.Col01 {
    -ms-grid-column: 1;
}
.Col02 {
    -ms-grid-column: 2;
}
.Col03 {
    -ms-grid-column: 3;
}
.Row01 {
    -ms-grid-row: 1;
}
.Row02 {
    -ms-grid-row: 2;
}
.Row03 {
    -ms-grid-row: 3;
}

OK, so you have some styles, but you can’t see styles. You need some HTML markup to receive the styles.

At this early stage in my WInJS development, it makes sense to me to style the main grid using an ID selector, so I’m calling the main grid div “MainGrid”. Each grid is going to be unique.

For row and column assignment I am using the ColXX and RowXX classes. To me is as easy to read as XAML’s Grid.Row=”0” and you can avoid creating ids for every cell in every cell in your application.

And I’m using the inline style on the first row to force it to span the 3 columns. I was debating using a class called “ColSpan03” but this gave me the opportunity to demonstrate an inline style.

With all this in mind, here’s the HTML for my grid (put in the body of default.html, if you are playing along at home):

<div id="MainGrid" >
    <div class="Col01 Row01" style="-ms-grid-column-span: 3">Title</div>
    <div class="Col01 Row02">2, 1</div>
    <div class="Col02 Row02">2, 2</div>
    <div class="Col03 Row02">2, 3</div>
    <div class="Col01 Row03">3, 1</div>
    <div class="Col02 Row03">3, 2</div>
    <div class="Col03 Row03">3, 3</div>
</div>

Comparing units in Row and Column Definitions


Here is a list of the various units that can be used to define the sized of rows and columns.


Purpose XAML WinJS Notes
Fractional Units * so 1 fractional unit would be “1*”, 2 would be 2* and so on fr so 1 fractional unit would be “1fr”, 2 would be “2fr” and so on  
Auto Sizing “auto” “auto” Although they are the same keyword, they behave slightly differently,
WinJS is more willing to wrap text and XAML is more likely to scroll
Percent Not supported % so fifty percent would be “50%” For XAML, you could use Fractional Units that add up to 100
Pixels Just the number, so 20 pixels would be “20” px, so 20 pixels would be 20px  
Inches Not supported in  
Centimeters Not supported cm  
Millimeters Not supported mm  
Points Not supported pt  

Saturday, January 26, 2013

WinRT CS vs JS: Events

Without events, it is imposable for the user to interact with the program. So, for this comparison I am going to wire up a simple click event (_btnUpdateText_Click()) that changes the text on a label (_txtHelloText) with the value of an integer that counts clicks (_clickNo). The sample is mind numbly simple to make the differences as stark as possible. I am using the same names where possible in both C# and JavaScript. So here goes:

Javascript HTML

  1. Create a JavaScript Windows Store Blank App, I’m calling mine “simpleEventJS”
  2. Open up default.html and paste the following on top of the existing body tag:
    <body>
    <h1>Simple Events</h1>
    <div id="_txtHelloText">Button is still Unclicked</div>
    <button id="_btnUpdateText">Click Me</button>
    </body>
    

  3. Open js/default.js
  4. Insert the following code just above the call to app.start();:
    // Just for proofiness, I will keep track of clicks and display in the event
    var _clickNo = 0;
    
    function _btnUpdateText_Click() {
    "use strict";
    var control = document.getElementById("_txtHelloText");
    control.innerText = "Click #" + (++_clickNo);
    }
    

  5. Replace args.setPromise(WinJS.UI.processAll()); with the following code:
    args.setPromise(WinJS.UI.processAll().done(function () {
    var button1 = document.getElementById("_btnUpdateText");
    button1.addEventListener("click", _btnUpdateText_Click, false);
    })
    );
    

NOTE: On wiring the event, my first instinct would to wire it in the markup (onclick="_btnUpdateText_Click"). Experts recommend that you don’t do that, besides, if you put the event handler in js/default.js, it would be out of scope anyway. I suppose you could imbed the JavaScript in the markup, but that stinks too.

C# XAML

  1. Create a Visual Studio C# Windows Store Blank App (XAML), I’m calling mine “simpleEventCS”
  2. Open MainPage.xaml and paste the following on top of the existing Grid tag (I am using the StackPanel to get it to be more similar to the JS sample above):
    <StackPanel Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <TextBlock>Simple Events</TextBlock>
    <TextBlock Name="_txtHelloText">Button is still Unclicked</TextBlock>
    <Button Name="_btnUpdateText" Click="_btnUpdateText_Click">Click Me</Button>
    </StackPanel>
    

  3. Double click on “_btnUpdateText_Click” in the Button tag
  4. Right click on the highlighted text and select Navigate to Event Handler
  5. Visual Studio will open MainPage.xaml.cs and create an empty even handler named _btnUpdateText_Click, replace that handler (all the way to the first closing bracket ‘}’) with:
    // Just for proofiness, I will keep track of clicks and display in the event
    private int _clickNo = 0;
    
    private void _btnUpdateText_Click(object sender, RoutedEventArgs e)
    {
    _txtHelloText.Text = "Click #" + (++_clickNo).ToString();
    }
    

NOTE: Yes, the UI for this sample is ugly compared the JavaScript sample above; this is what I get with no styling. It would violate my brain dead simple rule.