Tag Archives: Metro

An Evaluation of aVoice

aVoice is a Google Voice client for Windows 8. It makes use of some neat interaction paradigms:

The video doesn’t show pinch-to-zoom in, but that’s also possible. In the world of touch, these interaction paradigms are pretty new and they aren’t yet mainstream. I’ll tell you how they’re working for aVoice and some pitfalls to avoid.

Semantic Zoom

I’ve seen two uses for it over several platforms:

  • A jump list – Windows Phone’s app list is grouped alphabetically. The semantic zoom gets you to apps that start with Z quickly.
  • An information filter – Windows 8’s start screen zooms out to let you see a simpler big picture. Note that this is different from Android’s MIUI ROM launcher, which is simply optical zoom.

aVoice uses the information filter semantic zoom.

When zoomed in, a single message thread gets priority so that the user can see the conversation's history and respond accordingly.
When zoomed in, a single message thread gets priority so that the user can see the conversation’s history and respond.
When zoomed out, a message thread is reduced to a single box with a snippet of the most recent message. The color of the box indicates who sent the last message.
When zoomed out, a message thread is reduced to a single box with a snippet of the most recent message. The color of the box indicates who sent the last message.

A heuristic I think applies heavily to the information filter semantic zoom is recognition rather than recall (#6). The Windows 8 start screen does a great job of letting the user recognize what app they’d like in the zoomed out view by creating the illusion that the user is merely optically zooming out, but being a bit smarter. They disable live tiles and replace them with a small version of the app icon:

comparison2

 

Above, on the upper left is a screenshot of what the Finance app looks like in the zoomed out view (62px x 30px). On the right is what it would look like if the Finance app’s live tile was left on and simply shrunk. Obviously, the left retains meaning while the right does not.

This trick let’s the user recognize where they are spatially and recognize an app by its color and icon.

Unfortunately, aVoice doesn’t work the same way. When zoomed in, messages are in a horizontal stack. When zoomed out, they are in a grid, similar to the start screen. This breaks spatial recognition, and probably explains why I observed friends exclaiming “Wait, where was I?” when using aVoice. In a redesign, I would probably scrap the semantic zoom. It is ill suited for this case. So use caution when considering the information filter style. It could end up being more confusing than helpful.

Swipe to Select

Press-and-hold is the standard selection pattern for iOS (jiggle mode) and Android, but there’s plenty of debate about it.

Swipe to select takes a different approach, allowing users to flick and item down or up to select it. It’s significantly faster than press-and-hold (check out the video here, under Swipe to select, command, and move). However, swipe to select has some issues. Here are a few that I’ve seen with aVoice:

  • By virtue of being different, most people attempt to press-and-hold and are confused when it doesn’t work
  • Users attempting to quickly pan may accidentally select instead

Press-and-hold results in a hint that you should swipe down, but still doesn’t select the item. This goes against the consistency and standards (#4) of the popular market and ideally aVoice would allow both methods.

Error recovery (#9) is super important to keep a stress-free experience. The accidental selections while panning is worrisome, but fortunately recoverable via the Clear Selection button. If you use swipe to select, be careful about letting it do more than select because the action is frequently unintentional.

Introducing SortingObservableCollection

ObservableCollections are awesome. The two way binding lets your user seamlessly update your model and any programmatic changes to the model will be seamlessly reflected in the view.

I wanted one that kept it’s items in order, no matter what. It should maintain order as items are added and watch them via INotifyPropertyChanged to move them automatically. So here it is on CodePlex and NuGet. Sample usage is:

var collection = new SortingObservableCollection<MyViewModel, int>(Comparer<int>.Default, model => model.IntPropertyToSortOn);

collection.Add(new MyViewModel(3));
collection.Add(new MyViewModel(1));
collection.Add(new MyViewModel(2));

// At this point, the order is 1, 2, 3

collection[0].IntPropertyToSortOn = 4;
// As long as IntPropertyToSortOn uses INotifyPropertyChanged, this will cause the collection to resort correctly

The Hidden “gotcha” in Windows Store App Lifecycle

The application lifecycle diagram from MSDN:

Diagram

Pretty simple, but that Resuming bit is the “gotcha”. MSDN doesn’t really explain how this lifecycle relates to code as much as I’d like, so I’ll write it in layman’s terms:

  • For your Application class:
    • OnLaunched will be called any time your app is not in memory or if the app is being asked open something (see the ActivationKind enum)
    • The Suspending event will fire whenever your app leaves the view
    • The Resuming event will fire whenever your app re-enters the view, only if OnLaunched wouldn’t be called
  • For the Page class:
    • OnNavigatedTo will be called whenever someone calls Frame.Navigate with your page. This could be explicitly via a code-behind call, or implicitly via a call from the SuspensionManager helper class
    • OnNavigatedFrom will be called whenever your app leaves the view by the SuspensionManager (in sync with the Application’s Suspending event)

(NOTE: Visual Studio’s templates come with the NavigationHelper.cs class. You’re encouraged to ignore the OnNavigatedTo and OnNavigatedFrom overrides, instead using NavigationHelper’s LoadState and SaveState events)

You should see one basic thing missing from Page. It can’t know about the Resuming lifecycle event. This creates a bit of an awkward situation. Let’s say you’re in MainPage.xaml.cs and you need to register for some events on OnNavigatedTo with an object that you were passed as a navigation parameter. If you don’t de-register these events on OnNavigatedFrom, you’ll be sorry. It’s possible for another call to OnNavigatedTo to occur on the same instance of MainPage — you’ll register for the same event twice which is probably not what you expected.

So OK, de-register your events in OnNavigatedFrom. But wait, that doesn’t quite work either! Remember that OnNavigatedFrom is called every time your app leaves the view, but OnNavigatedTo might not be called when it returns. You’ll end up without event handlers if the user leaves the app then returns while it’s still in memory (extremely common case).

So what do you do?

Honestly, the Page class isn’t enough. We need to extend it. Try something like this:


/// <summary>
/// Use in place of the Page class for better application lifecycle support and better
/// code reuse.
/// </summary>
public class BasicPage : Page
{
    #region Public Properties

    /// <summary>
    /// Reference to the underlying NavigationHelper, which can be accessed by the Application.
    /// </summary>
    public NavigationHelper NavigationHelper { get; private set; }

    /// <summary>
    /// Use for the DataContext.
    /// </summary>
    public ObservableDictionary DefaultViewModel { get; private set; }

    #endregion

    #region Constructors

    /// <summary>
    /// Inheriting classes should call InitializeComponent().
    /// </summary>
    public BasicPage()
    {
        NavigationHelper = new NavigationHelper(this);
        NavigationHelper.LoadState += LoadState;
        NavigationHelper.SaveState += SaveState;
        NavigationHelper.ResumeState += ResumeState;

        DefaultViewModel = new ObservableDictionary();
    }

    #endregion

    #region Protected methods

    /// <summary>
    /// Called when this page should initialize it's data for the first time. Register event handlers.
    /// </summary>
    protected virtual void LoadState(object sender, LoadStateEventArgs e)
    {

    }

    /// <summary>
    /// Called when this page should save it's data to disk in preparation for leaving memory. Deregister
    /// event handlers.
    /// </summary>
    protected virtual void SaveState(object sender, SaveStateEventArgs e)
    {

    }

    /// <summary>
    /// Called when this page has returned to the view. Register event handlers if necessary or update the view.
    /// </summary>
    protected virtual void ResumeState(object sender)
    {

    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        NavigationHelper.OnNavigatedTo(e);
    }

    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        NavigationHelper.OnNavigatedFrom(e);
    }

    #endregion
}

The basic mechanics for how to hook up a BasicPage like this can be found in a similar blog post. The only real difference between the above example and the linked one is the ResumeState method. This requires a modification to NavigationHelper.cs as well. Add the following:

/// <summary>
/// Register this event on the current page to react to the app coming
/// back into view. This event and LoadState are mutually exclusive,
/// you will never get both.
///
/// You should only need to do view updates, as everything should still
/// be in-memory when this event is called.
/// </summary>
public event ResumeStateEventHandler ResumeState;

...

/// <summary>
/// Invoked when the app is resuming.
/// </summary>
public void OnResuming()
{
    if (this.ResumeState != null)
    {
        this.ResumeState(this);
    }
}

The final hook is in your App.xaml.cs code-behind, you need to register for the Resuming event and pass it to the page:

this.Resuming += OnResuming;

...

private void OnResuming(object sender, object e)
{
    Frame f = Window.Current.Content as Frame;
    BasicPage p = f.Content as BasicPage;

    if (p != null)
    {
        p.NavigationHelper.OnResuming();
    }
    else
    {
        throw new Exception("Every page must implement BasicPage!");
    }
}

Now you have the tools you need with minimal code reuse.

Not so Stupid Simple Logging for Windows Store Apps in C#/VB

The last time that I had to worry about logging was within Azure. My cloud solution was simple, and thus my logging solution was simple. Lately, the application I’ve been building for Windows 8.1 is not simple. It’s one of the most complex apps I’ve built to date.

It’s no secret that log4net won’t work in a Windows Store app, but that’s OK. There’s a different way to think about logging, and that’s with Event Tracing for Windows (here’s a complex, but good MSDN article). ETW logging is an obvious choice when developing on Windows as it guarantees:

  • Extremely fast performance (~16us per log event)
  • Application will not change behavior when logging is ON vs. OFF

The latter is very important. Imagine that you’re tracking down a race condition and you’re using a custom logging solution. You turn on logging, and it no longer reproduces! How could this happen? Your logging solution might cause the processor to schedule work differently when it’s ON vs. OFF.

First, if you’re looking for simple…

If you’re looking for a simple logging solution for Windows Store apps (regardless of language), look no further than Windows.Foundation.Diagnostics (new in Windows 8.1). Don’t be let astray by the first Google hit for “windows store” logging. While that solution uses the same methods I’ll expand on in this post, it will introduce more complexity that you’re looking for. It was also written before Windows.Foundation.Diagnostics became available in Windows 8.1.

The Windows.Foundation.Diagnostics namespace contains LoggingChannel and LoggingSession, which will likely fulfill your simple logging needs. Use LoggingChannel’s LogMessage(string) and LogValuePair(string, int) for simple string logging. There’s a wonderful Channel9 video on how to setup LoggingChannels with LoggingSessions.

If you’re OK with simple string logging and logs being saved to .ETL files, Windows.Foundation.Diagnostics is probably perfect for you.

But what if…?

I recently discovered that there’s more to life than simple string logging. That’s the “event” portion of ETW. I was raised to think of logging that looks like this:

Log.Verbose("Application starting");

In ETW-speak, this lets you log a Verbose event with a string parameter. Very powerful because that string can be anything. However, consider this:

Log.ApplicationStarting();

In a nutshell, that’s what ETW means by “event”. Both of the above log statement have the same purpose. They want to log that the application has started. The second statement is scale-able. The first one is not.

By scale-able, I mean that down the road, you might want to log more than just “Application starting” on boot. Maybe you want to know what platform and version of the code is starting. You could go change to:

Log.Verbose(string.Format("Application starting on {0} with version {1}", App.Platform.ToString(), App.Version.ToString()));

And that would be fine, except if you had multiple places in your app where you needed to log this event (OK, maybe app start is a bad example since you’ll probably only call it from one place, but you can imagine). Then you’d be copy & pasting log lines and that goes against the gospel of computer science.

So, you should centralize your events! That way, your event can really be as descriptive as you’d like. If it needs contextual data, you can just pass in the raw object instead of having to format it at the logging location.

Enter EventSource

.NET 4.5.1’s System.Diagnostics.Tracing namespace provides EventSource. It’s the simplest way to create ETW events in .NET. Check out the second example on the MSDN page for how to write it.

EventSource isn’t perfect though. You can only pass simple types like string and int to event methods. It cannot implement any interfaces. You’re probably going to want to wrap EventSource in another class like so:

public class LifecycleEventSourceWrapper
{
    public LifecycleEventSource EventSource {get; private set;}

    public LifecycleEventSourceWrapper()
    {
        EventSource = new LifecycleEventSource();
    }

    public void ApplicationStarting()
    {
        // Get app version
        var v = Package.Current.Id.Version;
        string version = string.Format("{0}.{1}.{2}.{3}", v.Major, v.Minor, v.Build, v.Revision);

        string platform = // get the platform

        EventSource.ApplicationStarting(version, platform);
    }
}

public class LifecycleEventSource : EventSource
{
    [Event(1, Message = "App starting on {0} with version {1}")]
    public void ApplicationStarting(string version, string platform)
    {
        if (this.IsEnabled()) this.WriteEvent(1, version, platform);
    }
}

Notice that I created LifecycleEventSource, and not just MyAppEventSource, implying that LifecycleEventSource should only be use for logging events related to application lifecycle. This is a powerful pattern because you can later filter your logs much more easily.

Check out MSDN for all the other awesome things that EventSource can do (like keywords and opcodes). You may not need them now, but they’re available for later use.

Cool, you’ve sold me on EventSource. So I’m done?

Not quite. Nobody is listening for these events. If you want to use another program like Windows Performance Analyzer to listen, then you actually are done, but you probably wanted to write to a file.

Assuming you want to do this all in one process, this is where the EventListener is used. There don’t appear to be any awesome examples that you can just drop in, so here’s an extremely basic one that keeps an in-memory cache of log lines, then writes them to a text file in your local storage folder every 5 seconds:

public class FileEventListener : EventListener
{
    private object syncObj = new object();
    private List<string> logLines;
    private StorageFile logFile;
    private ThreadPoolTimer periodicTimer;

    public FileEventListener()
    {
        logLines = new List<string>();
    }

    // Should be called right after the constructor (since constructors can't have async calls)
    public async Task InitializeAsync()
    {
        logFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("logs.txt", CreationCollisionOption.OpenIfExists);

        // We don't want to write to disk every single time a log event occurs, so let's schedule a
        // thread pool task
        periodicTimer = ThreadPoolTimer.CreatePeriodicTimer((source) =>
            {
                // We have to lock when writing to disk as well, otherwise the in memory cache could change
                // or we might try to write lines to disk more than once
                lock (syncObj)
                {
                    if (logLines.Count > 0)
                    {
                        // Write synchronously here. We'll never be called on a UI thread and you
                        // cannot make an async call within a lock statement
                        FileIO.WriteLinesAysnc(logFile, logLines).AsTask().Wait();
                        logLines = new List<string>();
                    }
                }
            }, TimeSpan.FromSeconds(5));
    }

    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        // This could be called from any thread, and we want our logs in order, so lock here
        lock (syncObj)
        {
            string payload = string.Format(eventData.Message, eventData.Payload.ToArray());
            logLines.Add(string.Format("{0}\t{1}", DateTime.Now.ToString(), payload));
        }
    }
}

You’ll need to hook EventSource to EventListener of course. I generally do this in the constructor of a class that exposes all of my EventSources.

Now what?

Now that you’ve got logs in a file form, you could ask users to send them to you when they report a bug, or you could upload them to cloud storage for the user (but be sure to discuss this in the privacy policy!).

4 Things to know about SuspensionManager

The default Visual C# app templates created by VS in Windows 8.1 make use of the SuspensionManager helper class (in the Common folder) to handle how your app can save it’s state when Windows puts it in the Suspended state. As you might expect, it serializes your data to disk, then deserializes it back for you when your app Resumes. How? DataContractSerializer. It’s a good way. Here’s what you need to know:

  1. DataContractSerializer (and thus SuspensionManager) needs to know about any custom types before it can work with your objects. Tell it in your App() constructor via:
    SuspensionManager.KnownTypes.Add(typeof(ObservableCollection<CarViewModel>));
    SuspensionManager.KnownTypes.Add(typeof(CarViewModel));
    
  2. Types serialized by DataContractSerializer must be decorated with the DataContractAttribute.
    [DataContract]
    public CarViewModel { ... }
    
  3. Member fields/properties of these types must be decorated with the DataMemberAttribute. By default, none of your objects members are serialized, you must use the attribute to include them. The cool thing is, you can apply it to a private member.
  4. When deserializing, the default constructor of your object will not be called. Weird, seemingly illegal, but it makes sense — say your object contains a state machine, and the default constructor sets the state machine to position 0. If you serialize your object with the state machine at position 3, you want it to deserialize in the same position.

If your types can deal with the constructor not being called, easy-as-pie, you are done! But, what if you need to run initialization logic on deserialization? For example, my CarViewModel has a private Car field. All of my public properties are simply meta-data based off of the Car. It’s more convenient for me to simply serialize only the Car, then re-initialize my meta-data once the Car is returned to me in deserialization.

Have no fear, just use the OnDeserializedAttribute above a method, like so:

[OnDeserialized]
internal void Initialize(StreamingContext context)
{
    // this.car is already in place, we've been deserialized
    InitializeDataFromCar(this.car);
}

Need more access? No problem, you’ve got an attribute for before serialization, after serialization, before deserialization, and after deserialization. You can find them all under the System.Runtime.Serialization namespace.