mvvm

How to implement a bindable progress indicator (loading dots) for MVVM Windows (8.1) Universal apps

Screenshot (23)

Now that I am on a good way to understand and use the MVVM pattern, I am also finding that there are some times rather simple solutions for every day problems. One of these problems is that we don’t have a global progress indicator for Windows Universal apps. That is a little bit annoying, and so I wrote my own solution. I don’t know if this is good or bad practice, but my solution is making it globally available in a Windows Universal app. The best thing is, you just need to bind to a Boolean property to use it. No Behaviors, just the one base implementation and Binding (Yes, I am a bit excited about it). For your convenience, I attached a demo project at the end of this post.

To get the main work for this done, we are implementing our own class, inherited from the Page class. The latter one is available for Windows as well as Windows Phone, so we can define it in the shared project of our Universal app. To do so, add a new class in the shared project. I named it PageBase (as it is quite common for this scenario, as I found out).

First, we need to inherit our class from the Page class:

public abstract class PageBase : Page

Now that we have done this, we need a global available property that we can bind to. We are using a DependencyProperty to achieve this goal. To make the property reflect our changes also to the UI, we also need to hook into a PropertyChanged callback on it:

        //this DepenedencyProperty is our Binding target to get all the action done!
        public static readonly DependencyProperty IsProgressIndicatorNeededProperty = DependencyProperty.Register(
            "IsProgressIndicatorNeeded", typeof (bool), typeof (PageBase), new PropertyMetadata((bool)false, OnIsProgressIndicatorNeededChanged));

        public static void OnIsProgressIndicatorNeededChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {

        }

        //get and send the value of our Binding target
        public bool IsProgressIndicatorNeeded
        {
            get { return (bool) GetValue(IsProgressIndicatorNeededProperty); }
            set { SetValue(IsProgressIndicatorNeededProperty, value); }
        }

The next step we need to do is to find the UIElement we want the progress indicator to be in. To do so, we are going through the VisualTree and pick our desired element. This helper method (taken from the MSDN documentation) will enable us to find this element:

        //helper method to find children in the visual tree (taken from MSDN documentation)
        private static void FindChildren<T>(List<T> results, DependencyObject startNode)
          where T : DependencyObject
        {
            int count = VisualTreeHelper.GetChildrenCount(startNode);
            for (int i = 0; i < count; i++)
            {
                var current = VisualTreeHelper.GetChild(startNode, i);
                if ((current.GetType()) == typeof(T) || (current.GetType().GetTypeInfo().IsSubclassOf(typeof(T))))
                {
                    T asType = (T)current;
                    results.Add(asType);
                }
                FindChildren<T>(results, current);
            }
        }

The method goes through the VisualTree, starting at the point we are throwing in as DependecyObject and gives us a List<T> with all specified Elements. From this List we are going to pick our UIElement that will hold the progress indicator for us. Let’s create a new method that will do all the work for us:

        private void CheckIfProgressIndicatorIsNeeded(DependencyObject currentObject)
        {
        }

Notice the DependencyObject parameter? This makes it easier for us to use the method in different places (which we will, more on that later). Let’s get our list of DependencyObjects from our parameter and pick the first Grid as our desired UIElement to hold the progress indicator:

            if (currentObject == null) return;

            //getting a list of all DependencyObjects in the visual tree
            var children = new List<DependencyObject>();
            FindChildren(children, currentObject);
            if (children.Count == 0) return;

            //getting a reference to the first Grid in the visual tree
            //this can be any other UIElement you define
            var rootGrid = (Grid)children.FirstOrDefault(i => i.GetType() == typeof(Grid));

Now that we have this, we are already at the point where we need to create our progress indicator object.  I declared a class member of type ProgressBar (which needs to be instantiated in the constructor then). This is how I set it up:

            //setting up the ProgressIndicator
            //you can also create a more complex object for this, like a StackPanel with a TextBlock and the ProgressIndicator in it
            _progressIndicator.IsIndeterminate = IsProgressIndicatorNeeded;
            _progressIndicator.Height = 20;
            _progressIndicator.VerticalAlignment = VerticalAlignment.Top;

The final step in the PageBase class is to check if there is already a chikd of type ProgressBar, if not adding it to the Grid and setting it’s Visibility property to Visible if our above attached DependencyProperty has the value ‘true’:

            //showing the ProgressIndicator
            if (IsProgressIndicatorNeeded)
            {
                //only add the ProgressIndicator if there isn't already one in the rootGrid
                if (!rootGrid.Children.Contains(_progressIndicator))
                {
                    rootGrid.Children.Add(_progressIndicator);
                }
                _progressIndicator.Visibility = Visibility.Visible;
            }

If the value is ‘false’, we are setting the Visibility back to collapsed:

            //hiding the ProgressIndicator
            else
            {
                if (rootGrid.Children.Contains(_progressIndicator))
                {
                    _progressIndicator.Visibility = Visibility.Collapsed;
                }
            }

Now that we have this method in place, let’s go back to our callback method we have been hooking into earlier. To reflect the changes that we are throwing into our DependencyProperty,  we are calling our method within the PropertyChanged callback. To do so, we are getting a reference to the PageBase class, which is needed because we are in a static method. Once we have this reference, we are calling our method to show/hide the progress indicator:

        public static void OnIsProgressIndicatorNeededChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //resolving d as PageBase to enable us calling our helper method
            var currentObject = d as PageBase;

            //avoid NullReferenceException
            if (currentObject == null)
            {
                return;
            }
            //call our helper method
            currentObject.CheckIfProgressIndicatorIsNeeded(d);
        }

That’s all, we are already able to use the global progress indicator. To use this class, you need to do a few things. First, go to the code-behind part of your page. Make the class inherit from the PageBase class:

    public sealed partial class MainPage : PageBase

Now, let’s go to the XAML part and add a reference to your class:

    xmlns:common="using:MvvmUniversalProgressIndicator.Common"

Once you have done this, replace the ‘Page’ element with the PageBase class:

<common:PageBase>
//
</common:PageBase>

After you have build the project, you should be able to set the Binding to the IsProgressIndicatorNeeded property:

    IsProgressIndicatorNeeded="{Binding IsProgressIndicatorVisible}">

If you now add two buttons to the mix, binding their Commands to change the value of the Boolean property, you will see that you can switch the loading dots on and off like you wish. That makes it pretty easy to use it in a MVVM driven application.

But what if we need to show the progress indicator as soon as we are coming to the page? No worries, we are already prepared and need only a little more code for that. In the PageBase class constructor, register for the Loaded event:

            Loaded += PageBase_Loaded;

In the Loaded event, we are calling again our main method to show the progress indicator, but this time we use the current window content as reference to start with:

        void PageBase_Loaded(object sender, RoutedEventArgs e)
        {
            //using the DispatcherHelper of MvvmLight to get it running on the UI
            DispatcherHelper.CheckBeginInvokeOnUI(() =>
            {
                //Window.Current.Content is our visual root and contains all UIElements of a page
                var visualRoot = Window.Current.Content as DependencyObject;
                CheckIfProgressIndicatorIsNeeded(visualRoot);
            });

        }

As we need to reflect changes on the UI thread, I am using the DispatcherHelper of the MvvmLight Toolkit. You can use your own preferred method as well for that. That’s all, If you now test it with setting the IsProgressIndicatorNeeded property in your page directly to ‘True’ in XAML, you will see the loading dots right from the start.

Screenshot (21)

Like always, I hope this is helpful for some of you.

Happy coding!

Download Sample project

Posted by msicc in Archive, 1 comment

Helper class to easily display local toast notifications (Windows Universal app)

Often , we need to display a confirmation that some action in our app has been finished (like some data has been updated etc.). There are several ways of doing this, like displaying a MessageBox or MessageDialog. This however breaks the user interaction, and a lot of users will start complaining on that if your app keeps doing so. There needs to be a better way.

With the Coding4fun Toolkit floating around, you can mimic a toast notification – sadly only on Windows Phone (at least for the moment, but Dave told me he will work on implementing it for Windows, too). Also, Toastinet library is floating around, which is also able to mimic the toast notification behavior (although for Windows Universal app, the implementation is not that intuitive as for Windows  Phone). Both are fantastic libraries that I used in the past, but I wanted a solution that is implemented easily and works with my Universal app. So I did some searching in the Web and the MSDN docs, and found out that is pretty easy to use the system toast notifications on both platforms locally.

There are 8 possible ways to format toast notifications (as you can see here in the toast template catalog). This gives us pretty much options on how a notification can be styled. However, most options just work on Windows 8.1, while Windows Phone 8.1 apps will only show the notification in the way “app logo”  “bold text”  “normal text”. However, the notification system takes care of that, so you can specify some other type on Windows 8.1, while knowing that it gets converted on Windows Phone automatically. This allows us to write a helper class that implements all possible options without any headache.

the code parts for the notification

Let’s have a look at the code parts for the notification. First, you need to add two Namespaces to the class:

using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;

After that, we can start writing our code. Toast notifications are formatted using Xml. Because of this, we need to get a reference to the underlying Xml template for the system toast notification:

ToastTemplateType xmlForToast= ToastTemplateType.ToastImageAndText01; 
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(xmlForToast);

System toast notifications can hold Text (and an Image on Windows 8.1). So we need to declare the elements of the toast notification. We are using the Xml methods of the DOM namespace to get the text elements of the chosen template first:

XmlNodeList toastTextElements = xmlForToast.GetElementsByTagName("text");
toastTextElements[0].AppendChild(xmlForToast.CreateTextNode("text1"));
//additional texts, depending on the template:
//toastTextElements[1].AppendChild(xmlForToast.CreateTextNode("text2"));
//toastTextElements[2].AppendChild(xmlForToast.CreateTextNode("text3"));

This is how the image element is implemented:

XmlNodeList toastImageElement = xmlForToast.GetElementsByTagName("image");
//setting the image source uri:
if (toastImageElement != null) ((XmlElement) toastImageElement[0]).SetAttribute("src", imageSourceUri);
//setting optional alternative text for the image
if (toastImageElement != null)  ((XmlElement) toastImageElement[0]).SetAttribute("alt", imageSourceAlternativeText);

You can attach local or remote images to the toast notification, but remember this works only on Windows, not on Windows Phone.

The next part we are able to set is the duration. The duration options are long (25 seconds) and short (7 seconds). The default is short, which should be ok for most scenarios. Microsoft recommends to use long only when a personal interaction of the user is needed (like in a chat). This is how we do it:

IXmlNode toastRoot = xmlForToast.SelectSingleNode("/toast");
((XmlElement) toastRoot).SetAttribute("duration", "short");

What we are doing here is to get the root element of the template’s Xml and add a new element for the duration. Now that we finally have set all options, we are able to create our toast notification and display it to the user:

ToastNotification notification = new ToastNotification(xmlForToast);
ToastNotificationManager.CreateToastNotifier().Show(notification);

the helper class

That’s all we need to do for our local notification. You might see that always rewriting the same code just makes a lot of work. Because the code for the toast notification can be called nearly everywhere in an app (it does not matter if you are calling it from a ViewModel or code behind), I wrote this helper class that makes it even more easy to use the system toast notification locally:

    public class LocalToastHelper
    {
        public void ShowLocalToast(ToastTemplateType templateType, string toastText01, string toastText02 = null, string toastText03 = null, string imageSourceUri = null, string imageSourceAlternativeText = null, ToastDuration duration = ToastDuration.Short)
        {
            XmlDocument xmlForToast = ToastNotificationManager.GetTemplateContent(templateType);
            XmlNodeList toastTextElements = xmlForToast.GetElementsByTagName("text");

            switch (templateType)
            {
                case ToastTemplateType.ToastText01:
                case ToastTemplateType.ToastImageAndText01:
                    toastTextElements[0].AppendChild(xmlForToast.CreateTextNode(toastText01));
                    break;
                case ToastTemplateType.ToastText02:
                case ToastTemplateType.ToastImageAndText02:
                    toastTextElements[0].AppendChild(xmlForToast.CreateTextNode(toastText01));
                    if (toastText02 != null)
                    {
                        toastTextElements[1].AppendChild(xmlForToast.CreateTextNode(toastText02));
                    }
                    else
                    {
                        throw new ArgumentNullException("toastText02 must not be null when using this template type");
                        
                    }
                    ;
                    break;
                case ToastTemplateType.ToastText03:
                case ToastTemplateType.ToastImageAndText03:
                    toastTextElements[0].AppendChild(xmlForToast.CreateTextNode(toastText01));
                    if (toastText02 != null)
                    {
                        toastTextElements[1].AppendChild(xmlForToast.CreateTextNode(toastText02));
                    }
                    else
                    {
                        throw new ArgumentNullException("toastText02 must not be null when using this template type");
                    }
                    ;
                    break;
                case ToastTemplateType.ToastText04:
                case ToastTemplateType.ToastImageAndText04:
                    toastTextElements[0].AppendChild(xmlForToast.CreateTextNode(toastText01));
                    if (toastText02 != null)
                    {
                        toastTextElements[1].AppendChild(xmlForToast.CreateTextNode(toastText02));
                    }
                    else
                    {
                        throw new ArgumentNullException("toastText02 must not be null when using this template type");
                    }
                    ;
                    if (toastText03 != null)
                    {
                        toastTextElements[2].AppendChild(xmlForToast.CreateTextNode(toastText03));
                    }
                    else
                    {
                        throw new ArgumentNullException("toastText03 must not be null when using this template type");
                    }
                    ;
                    break;
            }

            switch (templateType)
            {
                case ToastTemplateType.ToastImageAndText01:
                case ToastTemplateType.ToastImageAndText02:
                case ToastTemplateType.ToastImageAndText03:
                case ToastTemplateType.ToastImageAndText04:
                    if (!string.IsNullOrEmpty(imageSourceUri))
                    {
                        XmlNodeList toastImageElement = xmlForToast.GetElementsByTagName("image");
                        if (toastImageElement != null)
                            ((XmlElement) toastImageElement[0]).SetAttribute("src", imageSourceUri);
                    }
                    else
                    {
                        throw new ArgumentNullException(
                            "imageSourceUri must not be null when using this template type");
                    }
                    if (!string.IsNullOrEmpty(imageSourceUri) && !string.IsNullOrEmpty(imageSourceAlternativeText))
                    {
                        XmlNodeList toastImageElement = xmlForToast.GetElementsByTagName("image");
                        if (toastImageElement != null)
                            ((XmlElement) toastImageElement[0]).SetAttribute("alt", imageSourceAlternativeText);
                    }
                    break;
                default:
                    break;
            }

            IXmlNode toastRoot = xmlForToast.SelectSingleNode("/toast");
            ((XmlElement) toastRoot).SetAttribute("duration", duration.ToString().ToLowerInvariant());

            ToastNotification notification = new ToastNotification(xmlForToast);
            ToastNotificationManager.CreateToastNotifier().Show(notification);
        }

        public enum ToastDuration
        {
            Short,
            Long
        }
    }

As you can see, you just need to provide the wanted parameters to the ShowLocalToast method, which will do the rest of the work for you.

One word to the second switch statement I am using. The image element needs to be set only when we are using the ToastImageAndTextXX templates. There are three ways to implement the integration: using an if with 4  “or” options, the switch statement I am using or a string comparison with String.Contains. The switch statement is the cleanest option for me, so I decided to go this way. Feel free to use any of the other ways in your implementation.

In my implementation, I added also some possible ArgumentNullExceptions to make it easy to find any usage errors.

For your convenience, I attached the source file. Just swap out the namespace with yours. Download

The usage of the class is pretty simple:

var _toastHelper = new LocalToastHelper();
_toastHelper.ShowLocalToast(ToastTemplateType.ToastText02, "This is text 1", "This is text 2");

audio options

The system toasts have another option that can be set: the toast audio. This way, you can customize the appearance of the toast a bit more. I did not implement it yet, because there are some more options and things to remind, and I haven’t checked them out all together. Once I did, I will add a second post to this one with the new information.

As always, I hope this is helpful for some of you.

Happy coding!

Posted by msicc in Archive, 3 comments

How to easily check the pressed keyboard button with a converted event using MVVM (Windows Universal)

In case you missed it, I lately am deeply diving into MVVM. Earlier today, I wanted to implement the well loved feature that a search is performed by pressing the Enter button. Of course, this would be very easy to achieve in code behind using the KeyUpEvent (or the KeyDownEvent, if you prefer).

However, in MVVM, especially in a Universal app, this is a bit trickier. We need to route the event manually to our matching command. There are surely more ways to achieve it, but I decided to use the Behaviors SDK to achieve my goal. The first step is of course downloading the matching extension (if you haven’t done so before). To do so, click on TOOLS/Extensions and Updates in Visual Studio and install the Behaviors SDK from the list:image

The next step we need to do is to add a new Converter (I added it to the common folder, you may change this to your preferred place). As we are hooking up the KeyUpEventArgs, I called it KeyUpEventArgsConverter. After you created the class, implement the IValueConverter interface. You should now have a Convert and a ConvertBack method. We are just adding two lines of code to the Convert method:

            var args = (KeyRoutedEventArgs)value;
            return args;

That’s it for the Converter. Save the class and build the project. For the next step, we need to go to our View where the Converter should do its work. Before we can use it, we need to give our Converter a key to be identified by the Binding engine. You can do this app wide in App.xaml, or in your page:

<common:KeyUpEventArgsConverter x:Key="KeyUpEventArgsConverter"/>

Also, we need to add two more references to our View (besides the Common folder that holds our converter, that is):

    xmlns:i="using:Microsoft.Xaml.Interactivity" 
    xmlns:core="using:Microsoft.Xaml.Interactions.Core"

The next step is to implement the Behavior to our input control (a TextBox in my case):

<TextBox  Header="enter search terms" PlaceholderText="search terms" Text="{Binding KnowledgeBaseSearchTerms, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
    <i:Interaction.Behaviors>
         <core:EventTriggerBehavior EventName="KeyUp">
             <core:InvokeCommandAction
                   Command="{Binding SearchTermKeyEventArgsCommand}"
                   InputConverter="{StaticResource KeyUpEventArgsConverter}">
             </core:InvokeCommandAction>
          </core:EventTriggerBehavior>
     </i:Interaction.Behaviors>
</TextBox>

With the EventTriggerBehavior, we are able to hook into the desired event of a control. We then only need to bind to a Command in our ViewModel and tell the Behaviors SDK that it should route the “KeyUp” event using our Converter.

Let’s have a final look at the command that handles the event:

        public RelayCommand<KeyRoutedEventArgs> SearchTermKeyEventArgsCommand
        {
            get
            {
                return _searchTermKeyEventArgsCommand
                    ?? (_searchTermKeyEventArgsCommand = new RelayCommand<KeyRoutedEventArgs>(
                    p =>
                    {
                        if (p.Key == Windows.System.VirtualKey.Enter)
                        {
                            //your code here
                        }
                    }));
            }
        }

As you can see, we are using a Command that is able to take a Generic (in my case it comes from the MVVM Light Toolkit, but there are several other version floating around). Because of this, we are finally getting the KeyRoutedEventArgs into our ViewModel and are able to use its data and properties.

The VirtualKey Enumeration holds a reference to a lot of (if not all) keys and works for both hardware and virtual keyboards. This makes this code safe to use in an Universal app.

As I am quite new to MVVM, I am not entirely sure if this way is the “best” way, but it works as expected with little efforts. I hope this will be useful for some of you.

Useful links that helped me on my way to find this solution:

http://blog.galasoft.ch/posts/2014/01/using-the-eventargsconverter-in-mvvm-light-and-why-is-there-no-eventtocommand-in-the-windows-8-1-version/

https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh868246.aspx

Happy coding, everyone!

Posted by msicc in Archive, 0 comments