Tag Archives: winrt

[WinRT] Toggle selection of a list item on long press

As you probably know, the standard way to select or deselect an item in a WinRT list control is to slide it up or down a little. Although I rather like this gesture, it’s not very intuitive for users unfamiliar with Modern UI. And it gets even more confusing, because my previous statement wasn’t perfectly accurate: in fact, you have to slide the item perpendicularly to the panning direction. In a GridView, which (by default) pans horizontally, that means up or down; but in a ListView, which pans vertically, you have to slide the item left or right. If an application uses both kinds of lists, it becomes very confusing for the user.

Sure, in the default style, there is visual hint (a discrete “slide down” animation with a gray tick symbol) when the user presses and holds an item, but it’s not always enough for everyone to understand. Many people (e.g. Android users) are used to do a “long press” gesture (known as “Hold” in Modern UI terminology) to select items. So, in order to make your app easier to use for a larger number of people, you might want to enable selection by long press.

A simple way to do it is to create an attached property which, when set to true, subscribes to the Holding event of an item, and toggles the IsSelected property when the event occurs. Here’s a possible implementation:

using Windows.UI.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Input;

namespace TestSelectOnHold
{
    public static class SelectorItemEx
    {
        public static bool GetToggleSelectedOnHold(SelectorItem item)
        {
            return (bool)item.GetValue(ToggleSelectedOnHoldProperty);
        }

        public static void SetToggleSelectedOnHold(SelectorItem item, bool value)
        {
            item.SetValue(ToggleSelectedOnHoldProperty, value);
        }

        public static readonly DependencyProperty ToggleSelectedOnHoldProperty =
            DependencyProperty.RegisterAttached(
              "ToggleSelectedOnHold",
              typeof(bool),
              typeof(SelectorItemEx),
              new PropertyMetadata(
                false,
                ToggleSelectedOnHoldChanged));

        private static void ToggleSelectedOnHoldChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            var item = o as SelectorItem;
            if (item == null)
                return;

            var oldValue = (bool)e.OldValue;
            var newValue = (bool)e.NewValue;

            if (oldValue && !newValue)
            {
                item.Holding -= Item_Holding;
            }
            else if (newValue && !oldValue)
            {
                item.Holding += Item_Holding;
            }
        }

        private static void Item_Holding(object sender, HoldingRoutedEventArgs e)
        {
            var item = sender as SelectorItem;
            if (item == null)
                return;

            if (e.HoldingState == HoldingState.Started)
                item.IsSelected = !item.IsSelected;
        }
    }
}

You can then set this property in the ItemContainerStyle of the list control

<GridView.ItemContainerStyle>
    <Style TargetType="GridViewItem">
        ...
        <Setter Property="local:SelectorItemEx.ToggleSelectedOnHold" Value="False" />
    </Style>
</GridView.ItemContainerStyle>

And you’re done : the user can now select items by holding them. The standard gesture still works, of course, so users who know it can still use it.

Note that this feature could also have been implemented as a full-fledged Behavior. There are two reasons why I didn’t choose this approach:

  • Behaviors are not natively supported in WinRT (though they can be added as a Nuget package)
  • Behaviors don’t play well with styles, because Interaction.Behaviors is a collection, and you can’t add items to a collection from a style. A possible workaround would be to create an IsEnabled attached property that would add the behavior to the item when set to true, but then we would end up with a solution almost identical to the one described above, only more complex…

Strongly typed helper for toast notifications

Windows 8 provides an API for showing toast notifications. Unfortunately, it’s very cumbersome: to define the content of a notification, you must use a predefined template that is provided in the form of an XmlDocument, and set the value for each field in the XML. There is nothing in the API to let you know which fields the template defines, you need to check the toast template catalog in the documentation. It would be much more convenient to have a strongly typed API…

So I created a simple wrapper around the standard toast API. It can be used like this:

var content = new ToastContent.ImageAndText02
{
    Image = "ms-appx:///Images/dotnet.png",
    Title = "Hello world!",
    Text = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
};
var notifier = ToastNotificationManager.CreateToastNotifier();
notifier.Show(content.CreateNotification());

Note that I kept the original names from the toast template catalog, because sufficiently descriptive names would have been too long. I included XML documentation comments on each class to make it easier to choose the correct template.

If you want more flexibility than a strongly typed template can provide, but don’t want to manipulate the template’s XML, you can use the ToastContent class directly:

var content = new ToastContent(ToastTemplateType.ToastImageAndText02);
content.SetImage(1, "ms-appx:///Images/dotnet.png");
content.SetText(1, "Hello world!");
content.SetText(2, "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
var notifier = ToastNotificationManager.CreateToastNotifier();
notifier.Show(content.CreateNotification());

The code is available on GitHub, along with a demo app. A NuGet package is also available.

A point of interest is how I created the template classes: I could have done it manually, but it would have been quite tedious. So instead I extracted the toast templates to an XML file, I added some extra information (property names, description for XML doc comments) in the XML, and created a T4 template to generate the classes automatically from the XML file.

Showing result suggestions in a WinRT SearchBox: bug regarding the image

Today I ran into a strange problem that made me waste an hour or two, so I thought I’d write about it in case someone else faces the same issue.

The SearchBox control was introduced in Windows 8.1 to enable search scenarios from within a Windows Store app. One of its features is that it can show suggestions based on user input. There are three kinds of suggestions:

  • History suggestions are search queries previously entered by the user. This is handled automatically, so you don’t need to write any code for it to work.
  • Search suggestions allow you to provide search terms based on user input; if the user selects one, the current query text will be replaced with the text of the suggestion, and submitting the query will start the search.
  • Result suggestions are suggestions for exact results. The user can select one of these results directly without actually starting a full search.

To provide suggestions, you need to handle the SuggestionsRequested event of the SearchBox, and add suggestions using the AppendQuerySuggestion and AppendResultSuggestion methods. Let’s focus on result suggestions.

The AppendResultSuggestion method takes several parameters, and one of them is the image to display for the suggestion. It is mandatory (passing null will throw an exception), and the parameter is of type IRandomAccessStreamReference, i.e. something that can provide a stream. I find this a little peculiar, since it would be more natural to pass an ImageSource, but that’s the way it is… So I looked for a class that implements the IRandomAccessStreamReference interface, and the first obvious candidate I found was StorageFile, which represents a file. So I wrote the following code:

private async void SearchBox_SuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{
    var deferral = args.Request.GetDeferral();
    try
    {
        var imageUri = new Uri("ms-appx:///test.png");
        var imageRef = await StorageFile.GetFileFromApplicationUriAsync(imageUri);
        args.Request.SearchSuggestionCollection.AppendQuerySuggestion("test");
        args.Request.SearchSuggestionCollection.AppendSearchSeparator("Foo Bar");
        args.Request.SearchSuggestionCollection.AppendResultSuggestion("foo", "Details", "foo", imageRef, "Result");
        args.Request.SearchSuggestionCollection.AppendResultSuggestion("bar", "Details", "bar", imageRef, "Result");
        args.Request.SearchSuggestionCollection.AppendResultSuggestion("baz", "Details", "baz", imageRef, "Result");
    }
    finally
    {
        deferral.Complete();
    }
}

This code runs without any error, and the suggestions are displayed… but the image is not shown!

https://i2.wp.com/i.stack.imgur.com/BiF0g.png?w=474

I spent a long time double-checking everything, making lots of minor changes to try and locate the issue, I even wrote a custom implementation of IRandomAccessStreamReference… to no avail.

I eventually submitted the problem to Stack Overflow, and someone kindly provided the solution, which was very simple: instead of StorageFile, use RandomAccessStreamReference (seems pretty obvious once you know that it exists). The code now becomes :

private void SearchBox_SuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{
    var imageUri = new Uri("ms-appx:///test.png");
    var imageRef = RandomAccessStreamReference.CreateFromUri(imageUri);
    args.Request.SearchSuggestionCollection.AppendQuerySuggestion("test");
    args.Request.SearchSuggestionCollection.AppendSearchSeparator("Foo Bar");
    args.Request.SearchSuggestionCollection.AppendResultSuggestion("foo", "Details", "foo", imageRef, "Result");
    args.Request.SearchSuggestionCollection.AppendResultSuggestion("bar", "Details", "bar", imageRef, "Result");
    args.Request.SearchSuggestionCollection.AppendResultSuggestion("baz", "Details", "baz", imageRef, "Result");
}

(Note that the method is not asynchronous anymore, so there is no need to use the deferral object).

The suggestions are now displayed as expected, with the image:

https://i1.wp.com/i.imgur.com/cjmogKp.png?w=474

So, the lesson of this story is that even though the image parameter is of type IRandomAccessStreamReference, it doesn’t seem to accept anything other than an instance of the RandomAccessStreamReference class. If you pass any other implementation of the interface, it just fails silently and the image is not shown. This is obviously a bug: if the parameter type in the method signature is an interface, it should accept any implementation of that interface, not just a specific implementation; if it doesn’t, it should be declared of the concrete type. I submitted the bug to Connect, hopefully it will be fixed in a future version.

I hope this helps someone!

Detecting dependency property changes in WinRT

Today I’d like to share a trick I used while developing my first Windows Store application. I’m very new to this technology and it’s my first article about it, so I hope I won’t make a fool of myself…

It’s often useful to be notified when the value of a dependency property changes; many controls expose events for that purpose, but it’s not always the case. For instance, recently I was trying to detect when the Content property of a ContentControl changed. In WPF, I would have used the DependencyPropertyDescriptor class, but it’s not available in WinRT.

Fortunately, there is a mechanism which is available on all XAML platforms, and can solve this problem: binding. So, the solution is just to create a class with a dummy property that is bound to the property we want to watch, and call a handler when the value of the dummy property changes. To make it cleaner and hide the actual implementation, I wrapped it as an extension method that returns an IDisposable:

    public static class DependencyObjectExtensions
    {
        public static IDisposable WatchProperty(this DependencyObject target,
                                                string propertyPath,
                                                DependencyPropertyChangedEventHandler handler)
        {
            return new DependencyPropertyWatcher(target, propertyPath, handler);
        }

        class DependencyPropertyWatcher : DependencyObject, IDisposable
        {
            private DependencyPropertyChangedEventHandler _handler;

            public DependencyPropertyWatcher(DependencyObject target,
                                             string propertyPath,
                                             DependencyPropertyChangedEventHandler handler)
            {
                if (target == null) throw new ArgumentNullException("target");
                if (propertyPath == null) throw new ArgumentNullException("propertyPath");
                if (handler == null) throw new ArgumentNullException("handler");

                _handler = handler;

                var binding = new Binding
                {
                    Source = target,
                    Path = new PropertyPath(propertyPath),
                    Mode = BindingMode.OneWay
                };
                BindingOperations.SetBinding(this, ValueProperty, binding);
            }

            private static readonly DependencyProperty ValueProperty =
                DependencyProperty.Register(
                    "Value",
                    typeof(object),
                    typeof(DependencyPropertyWatcher),
                    new PropertyMetadata(null, ValuePropertyChanged));

            private static void ValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var watcher = d as DependencyPropertyWatcher;
                if (watcher == null)
                    return;

                watcher.OnValueChanged(e);
            }

            private void OnValueChanged(DependencyPropertyChangedEventArgs e)
            {
                var handler = _handler;
                if (handler != null)
                    handler(this, e);
            }

            public void Dispose()
            {
                _handler = null;
                // There is no ClearBinding method, so set a dummy binding instead
                BindingOperations.SetBinding(this, ValueProperty, new Binding());
            }
        }
    }

It can be used like this:

// Subscribe
watcher = myControl.WatchProperty("Content", myControl_ContentChanged);

// Unsubscribe
watcher.Dispose();

I hope you will find this useful!