Tag Archives: windows8.1

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!).