Avirall Time Suite

462x120_WPS_Download_cyan

Windows Phone: Progress Indicator Plus

In Windows Phone, progress indicator on System Tray is a quick and easy way to indicate that some activity is running in the background. It is all right if you want to indicate progress from a single thread. What if you have multiple threads running and all need to use progress indicator?

I created a ProgressBarManager wrapper class to handle multiple thread progress indication scenario. This class can work in both scenarios – indeterminate or determinate, but in case of multiple background processes the preferable way is indeterminate. The accompanied progress text from different processes can show progress value in the text. Following is example screen shot of ProgressBarManager in action.
MultipleMessage

Usage Example

At class level define ProgressBarManager:

        ProgressBarManager ProgressBarManager = new ProgressBarManager();

In constructor of the form where you wish to show progress bar with multiple clients, instantiate the class like so:

        SystemTray.SetProgressIndicator(this, new ProgressIndicator());
        ProgressBarManager.Hook(SystemTray.ProgressIndicator);

Update ProgressBar from different processes with the ProgressBarManager.Show(). In this example I have a button which starts two background threads which update progress bar asynchronously. The button’s click method is as follows:

       private void button1_Click(object sender, RoutedEventArgs e)
        {
            BackgroundWorker _bw = new BackgroundWorker();
            _bw.DoWork += new DoWorkEventHandler(
                (object caller, DoWorkEventArgs workArgs) =>
                {
                    for (int i = 0; i <= 10; i++)
                    {
                        Deployment.Current.Dispatcher.BeginInvoke(() =>
                        {
                            double _val = (double)i / 10d;
                            double _percent = _val * 100d;
                            ProgressBarManager.Show(1, string.Format("2: Progress-{0}%", _percent), false, _val);
                        });
                        Thread.CurrentThread.Join(230);
                    }
                    ProgressBarManager.Hide(1);
                }
                );

            BackgroundWorker _bw2 = new BackgroundWorker();
            _bw2.DoWork += new DoWorkEventHandler(
                (object caller, DoWorkEventArgs workArgs) =>
                {
                    for (int i = 0; i <= 10; i++)
                    {
                        Deployment.Current.Dispatcher.BeginInvoke(() =>
                        {
                            double _val = (double)i / 10d;
                            double _percent = _val * 100d;
                            ProgressBarManager.Show(2, string.Format("2: Progress-{0}%", _percent), false, _val);
                        });
                        Thread.CurrentThread.Join(500);
                    }
                    ProgressBarManager.Hide(2);
                }
                );
            _bw.RunWorkerAsync();
            _bw2.RunWorkerAsync();
        }

ProgressBarManager Class

Following is full code of ProgressBarManager class:

using System;
using System.ComponentModel;
using System.Collections.Generic;
using Microsoft.Phone.Shell;
using System.Windows.Data;
using System.Windows;
namespace YourCompany.UI.Controls
{
    public class ProgressBarManager : INotifyPropertyChanged
    {
        private Dictionary<int, string> _clients = new Dictionary<int, string>();
        private bool _show;
        private bool _inDeterminant;
        private string _text;
        private double _value;
        public bool ShowProgress { get { return _show; } }
        public bool ProgressInDeterminant { get { return _inDeterminant; } }
        public string ProgressText { get { return _text; } }
        public Double Value { get { return _value; } }

        public event PropertyChangedEventHandler PropertyChanged;

        public void Show(int ClientID, string Text, bool IsDeterminant, double Value)
        {
            if (!_clients.ContainsKey(ClientID))
            {
                _clients.Add(ClientID, Text);
            }
            else
            {
                _clients[ClientID] = Text;
            }
            _value = Value;
            _inDeterminant = !IsDeterminant;
            updateProperties();
        }
        public void Show(int ClientID, string Text)
        {
            if (!_clients.ContainsKey(ClientID))
            {
                _clients.Add(ClientID, Text);
            }
            else
            {
                _clients[ClientID] = Text;
            }
            updateProperties();
        }
        public void Hide(int ClientId)
        {
            if (_clients.ContainsKey(ClientId))
            {
                _clients.Remove(ClientId);
            }
            updateProperties();
        }
        public void Hook(ProgressIndicator FormProgressIndicator)
        {
            #region setup ProgressIndicator
            Binding _binding;
            _binding = new Binding("ShowProgress");
            _binding.Source = this;
            _binding.Mode = BindingMode.TwoWay;
            BindingOperations.SetBinding(FormProgressIndicator, ProgressIndicator.IsVisibleProperty, _binding);
            _binding = new Binding("ProgressInDeterminant");
            _binding.Source = this;
            _binding.Mode = BindingMode.TwoWay;
            BindingOperations.SetBinding(FormProgressIndicator, ProgressIndicator.IsIndeterminateProperty, _binding);
            _binding = new Binding("ProgressText");
            _binding.Source = this;
            _binding.Mode = BindingMode.TwoWay;
            BindingOperations.SetBinding(FormProgressIndicator, ProgressIndicator.TextProperty, _binding);
            _binding = new Binding("Value");
            _binding.Source = this;
            _binding.Mode = BindingMode.TwoWay;
            BindingOperations.SetBinding(FormProgressIndicator, ProgressIndicator.ValueProperty, _binding);
            #endregion
        }
        private void updateProperties()
        {
            if (_clients.Count == 0)
            {
                _show = false;
                _text = "";
            }
            else
            {
                _show = true;
                string _m_seperator = "";
                _text = "";
                foreach (KeyValuePair<int, string> _keyvaluePair in _clients)
                {
                    _text = _text + _m_seperator + _keyvaluePair.Value;
                    _m_seperator = ", ";
                }
            }
            onPropertyChanged("ShowProgress");
            onPropertyChanged("ProgressInDeterminant");
            onPropertyChanged("ProgressText");
            onPropertyChanged("Value");
        }
        private void onPropertyChanged(string PropertyName)
        {
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
                }
            });
        }
    }
}

Icons In Accent Color

You have about 50 different icons in your app. You want to show them in dynamic color (determined by user/device at runtime) in different sizes at different places. You want them to be lightweight and swift to render. If you go by traditional PNG way, you could somewhat achieve (no scalability) it with about 2000 different files of no less than 5MB. Here is my proven solution to achieve this with a 15KB, yes a single 15KB file.

If you have used my app Avirall Time Suite, you must have noticed that throughout the app, where required, icons adopt accent color. Some elements in the app are also shown in dynamic contrast color of the accent color. I will write another post about how to get contrast color of the accent color. Following are screen shots of the app with different accent colors and different size icons. Notice New, Recent, and Options sections:
Avirall

I had to meet following requirements to achieve accent color icons in the app:

    • There are about 50 different icons in the app to be shown in current theme accent on the device.
    • At different places same icons have different sizes.
    • No JPG because of lack of alpha channel.
    • No PNG because though it had alpha channel, color and size scalability is an issue. 50 icons X 4 sizes X 12 accent color would have gotten to unmanageable 2400 icon files, and still it would support only rigid 12 accent colors.
    • So, no raster images.
    • No paths – managing complex paths would not be easy, plus i was not sure about the performance compared to the solution I was thinking.

So I decided to develop and design my own custom dingbat TTF font. Having fonts for icons has following pros:

  • Very light file containing all 50+ icons. Just 15KB!
  • System renders the fonts as text, so no performance issue.
  • Easy Color and size scalability.
  • Low maintenance.

Following are the cons:

  • No gradient. (Though it’s possible but I have not experimented)
  • Font creation is a pain, initially.

You could also think of using fonts for icons, if you need color/size variations in your app for a large number of icons.

I have released the font I created for Avirall, under OPEN FONT LICENSE. Read this post for details.

Happy coding!

Avirall Time Suite : Update 1.2.8544.0

Find more help here.

With simple timer to professional task tracking, Avirall is a Windows Phone exclusive and unique suite of 5 innovative millisecond precise professional grade time keeping tools. Avirall has Quick Stopwatch, Stopwatch Pro for sports people, and students, Timer Pro, Activity Logger and Project tracker to keep track of your projects, tasks, money, profits, and payments. Best tool for freelancers. You can create and maintain multiple timekeepers with each of the tools and keep them running for as long as you need them. Timekeepers in Avirall maintain themselves even if you shut your device off completely. Professional class clocks have days, hours, minutes, seconds, and milliseconds. You can edit, remove, or keep timekeepers on your device. Some unique features of Avirall are:
– Tasks & profiles
– Mail sharing
– Multiple projects with multiple tasks, rates, earning, payments and summaries
– Alarm and notifications for timers
– Photo sharing
– Pin TaskTimer to Start screen for instant access
– Define millisecond precision for individual timekeepers
– Running timekeepers adjust with current time zone of the device
– Lots of settings to control app’s behavior
– Image attachments in every timekeeper

462x120_WPS_Download_cyan

===
CHANGE LOG

Oct 2013 [Ver 1.2.8544.0]

-3 new time styles
-Project report image design update
-Now supports 6 languages – en, fr, de, nl, ch, ru
-Peek view re-designed
-Quick stopwatch digital area redesigned
-Large tile for 7.8 and 8.0
-Bug Fixes


Sep 2013 [Ver 1.1.8338.0]

-Bug Fixes
-Imagery change for more contemporary design
-Comprehensive title – Avirall Time Suite
-Help button on Quick Stopwatch
-Tool names changed
-New timekeeper’s default name will be “New [hh:mm:ss]


Aug 2013 [Ver 1.0.8112.0 ]

[First published]