C#|.NET Query String in Uri

C#|.NET Query String in Uri

In a not-so-basic-application you might have pages which are used for multiple similar purposes. You pass query string with many fields with multiple values between such pages (or web pages). In database driven apps, parameter values could be user generated and stored in the back-end and you pull more info from database on the basis of the value in query parameter. This is not the scenario of back-end driven app. This is more about “Field Names” and Values, which are part of the design and known to you while coding, and you want to manage them effectively and make the code more readable.

Let’s take an example:

You have a page in your app which loads different lists (ex: city, state, pin, salutation, etc.) and lets user select an item from the list. At different places in your app you pop this page up with required parameters to load appropriate items. The call to page looks something like this:

this.NavigationService.Navigate("/ListPicker.xaml?ListType=city",UriKind.Relative)

Let’s assume your page also has the ability for editing and you want to activate appropriate functionality (select only || edit). You would add one more parameter to your query, like so:

this.NavigationService.Navigate("/ListPicker.xaml?ListType=city&FormType=select",UriKind.Relative)

If you have many such pages, each have multiple fields and their multiple values, and you make calls to these pages from different places in your code, soon it will be very difficult to manage hard-coded query strings in Uri’s.

Here comes enum based solution:

We will have enums for fields and their values. For the purpose of this example we will keep single enum for fields and multiple enums for values for different fields. Let’s code
First define enums for fields and their values:

        internal enum QueryFields { ListPicker_FormType, ListPicker_ListType };
        internal enum ListTypes { City, States, Zip, Salutation };
        internal enum FormTypes { Select, Edit };

If you do not wish to be more detailed, you could simply build your Uri’s like so:

This.NavigationService.Navigate("/ListPicker.xaml?{0}={1}",QueryFields.ListPicker_FormType.ToString(), FormTypes.Select.ToString());
//The resultant uri - /ListPicer.xaml?ListPicker_FormType=Select

We will see below how you could parse query parameters in the called page and retrieve values in enum types.

Creating Uri as above still has string formatting which is not easy to maintain in multiple uses. To make things manageable and less error prone, let’s create a new enum for pages in the app and shift Uri building code in a single method which could be called from anywhere in the app with different field and values.

        internal enum AppPages {ListPicker, Setting, Main, etc };
        internal static Uri GetUri(AppPages appPage, params KeyValuePair<string, string>[] args)
        {
            string uriString = "";
            switch (appPage)
            {
                case AppPages.ListPicker:
                    uriString = "/Views/ListPicker.xaml";
                    break;
                case AppPages.Setting:
                    uriString = "/Views/Settings.xaml";
                    break;
                case AppPages.Main:
                    uriString = "/Main.xaml";
                    break;
                default:
                    uriString = "/Main.xaml";
                    break;
            }
            int counter = 0;
            string seperator = "?";
            foreach(KeyValuePair<string, string> query in args)
            {
                if (counter > 0) seperator = "&";
                uriString = String.Format("{0}{1}{2}={3}", uriString, seperator, query.Key, query.Value);
            }
            return new Uri(uriString, UriKind.Relative);
        }

With GetUri, you could create page navigation Uri with enums only instead of hard-coded strings:

            KeyValuePair<string, string> query_1 = new KeyValuePair<string,string>(QueryFields.ListPicker_FormType.ToString(), FormTypes.Select.ToString());
            KeyValuePair<string, string> query_2 = new KeyValuePair<string,string>(QueryFields.ListPicker_ListType.ToString(), ListTypes.City.ToString());
            This.NavigationService.Navigate(GetUri(AppPages.ListPicker, query_1, query_2));

Once you are navigated to your page, you need to parse query strings and extract enums which you could use in the page to decide page’s functionality.

At page level you need to have required enum type fields. For this example we will have two fields, one FormTypes type and other ListTypes type. A private processQuery method which accepts a dictionary sets these two fields appropriately.

        FormTypes formType;
        ListTypes listType;
        private void processQuery(Dictionary<string, string> query)
        {
            string _formTypeName = "";
            string _listTypeName = "";
            query.TryGetValue(QueryFields.ListPicker_FormType.ToString(), out _formTypeName);
            query.TryGetValue(QueryFields.ListPicker_ListType.ToString(), out _listTypeName);
            if (_formTypeName.Length != 0) formType = (FormTypes)(Convert.ToInt32(_formTypeName));
            if (_listTypeName.Length != 0) listType = (ListTypes)(Convert.ToInt32(_listTypeName));
        }

You would call processQuery method from OnNavigatedTo method of your page.

        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            Dictionary<string, string> _params = new Dictionary<string, string>(NavigationContext.QueryString);
            processQuery(new Dictionary<string, string>());
        }

If you have 100’s of case statements (to choose a page) in GetUri, and you are concerned about performance, refactore the method to accept page name with path as string. With page path name directly in string, you would not need case statements. By the way, in my case with about 80+ cases to get PageName, the query creation does not take more than 50MS, which is negligible for me. More so, navigation calls are not recursive ones.

Transparent Live Tile For Windows Phone 8.1

Transparent Live Tile For Windows Phone 8.1

Windows Phone 8.1 Dev Preview

Windows Phone 8.1 developer preview is totally awesome. So what is one of the most important things app devs can do right away, without much fuss, to make their apps look great with Windows Phone 8.1? Revisit your app’s Live Tile images. Why? In 8.1, Start screen is mesmerizing with the parallax effect and Live Tile with background overlay. Dev preview users are going crazy with new Start experience, and I am sure, with public release, end users are going to love to have background image as well. Take a look at this awesome Nokia Lumia 925 Start screen:

wp_ss_20140416_0015[1]

When users have a background image, they might not like to have solid Live Tiles block their beautiful background. So, revisit your app’s Live Tile images – all of them! Check if they are transparent or not? If they are like Cortana, Avirall Time Suite, etc. in the image above, you are all set. You can safely ignore this article and find some other awesome way to improve your app for WP8.1 (think Cortana). If they are not, I suggest to update ‘em ASAP. And it’s easy, too. Let’s see how you can create transparent Live Tile images with Open Source Inkscape. You could choose to use Paint.NET (not MS Paint), Photoshop, GIMP, or any other tool which allows you to export image with transparency. Though I prefer GIMP for image editing but I chose Inkscape for this demo. Let’s go.

Step 1: Setup the page

InkScape01

  1. Open Inkscape and create a new document and Open Document Properties from file menu.
  2. In most cases app icons are solid white, if yours is one too, change the background color of the page, temporarily, to light yellow. This is while working only so that you can see white color. Before exporting we will reset this to transparent (this is very important).
  3. This example is for 336 X 336 medium tile. So set the custom size to 336 X 336.
  4. Make sure Units is “px”.

Step 2: Setup the drawing

InkScape02

Windows Phone 8 Asset Template Guide suggests a 336 X 336 tile to have 110px margin on all sides. Create 110px margin guides on all the sides. To create margin guides click and drag from the horizontal ruler on the top and from the vertical ruler on the left.

Step 3: Create Live Tile image of your app

InkScape03

The center part between the margins is the space where your tile image will go. Create your image here. For this example I simply dragged the box from tool box and made a hole in it 😀

Step 4: Set background to transparent

InkScape03a

To create a transparent Live Tile image we would like to have a transparent background. Go back in document properties and set Background to transparent by resetting “A” to 0. If your image is solid white, you might not see anything after making the background transparent. Don’t worry, your image is there (maybe somebody can tell how working space (not document background) in Inkscape can be made a different color than white).

Step 5: Export

InkScape04

  1. Open Export dialog from File > Export Bitmap…
  2. Set X0, X1, Y0, Y1 as shown in the image above.
  3. Browse for the image location, and make sure you give “PNG” as the extension of the image, NOT JPG or BMP.
  4. Export. (Alternatively, as suggested by Austin Andrews, you could export as SVG, and use his awesome service!)

Step 6: Check Final Image

WindowsPhotoViewer05

Notice that your final image does not have a white background. Windows Photo Viewers’s light blue background is your image’s background.

You are done. You would want to change all Live Tile images of your app in the same way. A transparent Live Tile image will encourage users to have your app pinned to Start screen without obstructing their beautiful background images.

Respect your users!

Download Lumia 925 Emulator Skin

Have you seen this feature demo video of Avirall? (jump to 00:50)

Do you like the emulator? This is Visual Studio Windows Phone Nokia Lumia 925 emulator skin, i created :D.

Nokia925_UP

You can also create your own app demo video with this skin! Download this skin from here.

Instructions about how to apply skin to your emulator are here.

Happy skinning, oops!

XAML|C# Step By Step : UserControl – Wait Spinner

Waiting by moonux, on Flickr
Waiting, a photo by moonux on Flickr.

Something is cooking in the background? Do not forget to inform your user. A modern, flat, light weight, wait spinner is an animated and prominent way to inform user about running background processes (you would not have long running processes on the UI thread, right? See how to effectively free up UI). You would choose to show this control in that area of the screen to which the information, related to which the BG work is taking place – loading/processing etc, belongs. You may have multiple information points on screen for which background work is running.

For this step-by-step we will create this example:

A practical example of implementation of this spinner is this panorama app there are two wait spinners being used, one in Quick Stopwatch pano, and other in Recent pano. When data related to Quick stopwatch is loading, the wait spinner shows beside the title “Quick”, and when data related to Recent is loading, the wait spinner shows near the pano title “Recent”. This way user is informed about which section of the app is doing something in the background.

WaitSpinnerExample

Don’t have this app on your Windows Phone? Visit here

Let’s go through step-by-step of creating a WaitSpinner UserControl and using it in a page.

Step 1 : Create Solution and Project

  1. Create a project in VS with the name “WaitSpinner”. The solution gets created automatically.
  2. Right click on the solution in Solution Explorer, and choose “Add”>”New Project”.
  3. Select Class Library type project and name it “XAMLControls”.

Step 2 : Create UserControl

  1. Right click on XAMLControls and select “Add”>”New item”.
  2. Choose User Control and name it “UCWaitSpinner”.
  3. A new UCWaitSpinner.xaml and its code behind is created.

Step 3 : Open in Blend

Right click on UCWaitSpinner.xaml and choose to open in Blend. Depending on the version of Visual Studio the view you get might be a little different, but most of the UI will be similar. You will get something like this:
01_OpenUCInBlend

Step 4 : Change the type of Layout control

Change the type of Layout from Grid to ViewBox.
02_ChangeLayoutType

Step 5 : Add a new Grid to Layout ViewBox

  1. Choose Grid from control and right click and drag in XAML design view area.
  2. Update Layout properties; Height and Width to 50, HorizontalAlignment and VerticalAlignment to Stretch.

03_AddGridToLayoutRoot

Step 6 : Add Outer border

  1. Choose Ellipse tool from Toolbox and create a circle in the design area. Don’t worry about size and fill at this point.
  2. Make sure Ellipse is selected.
  3. In Properties.Brushes;
    • Set Fill to “No Brush”.
    • Set Stroke to “PhoneForegroundBrush”.
  4. In Layout section;
    • Set HorizontalAlignment to stretch.
    • Set VerticalAlignment to stretch.
    • Set all margins to 0.

04_CreateACircle

Step 7 : Add axis for hands

  1. Choose Ellipse tool from Toolbox again and create a circle in the middle of XAML design. Don’t bother about size at this point.
  2. Make sure this ellipse is selected.
  3. In Properties.Brushes;
    • Set Fill to “PhoneForegroundBrush”.
    • Set Stroke to “No Brush”.
  4. In Layout section;
    • Set Width and Height to 5.
    • Set HorizontalAlignment to center.
    • Set VerticalAlignment to center.
    • Set all margins to 0.

05_CreateAxisForHands

Step 8 : Add minute and hour hands

  1. Choose Rectangle tool from Toolbox and create a rectangle in the design area. Don’t worry about size and placement at this point.
  2. Rename the [Rectangle] to “MinuteHand”.
  3. Make sure you have MinuteHand selected.
  4. In Properties.Brushes;
    • Set Fill to “PhoneForegroundBrush”.
    • Set Stroke to “No Brush”.
  5. In Layout section;
    • Set Width to 2 and Height to 20.
    • Set HorizontalAlignment to Center.
    • Set VerticalAlignment to Bottom.
    • Set Bottom margin to 25 and all others to 0.
  6. In Properties.Transform;
    • Select Center Point tab.
    • Set X to 0.5.
    • Set Y to 1.
  7. Copy and paste “MinuteHand” control and name it “HourHand”. Keep everything same and change only the Properties.Layout.Height to 13.

06_CreateMinuteHand

Step 9 : Create Storyboard for moving hands

  1. In Objects and Timeline, click on “+” sign to add a new storyboard.
  2. Name the storyboard as “MoveHands”.

07_CreateStoryBoard

Step 10 : Create animation for minute hand

  1. In timeline window slide the marker to 3 second.
  2. Select MinuteHand control.
  3. In Properties.Transform select Rotate tab and set Angle property to 1440.

07b_CreateStoryBoard

Step 11 : Create animation for hour hand

  1. Select HourHand control.
  2. In Properties.Transform select Rotate tab and set Angle property to 360.
  3. c. In timeline windows click on play button to see hands are rotating properly (Hour hand completes 1 rotation and minute hand completes 4 rotations in 3 seconds).

07c_CreateStoryBoard

Step 11B : Follow me on Twitter 😀

Step 12 : Review XAML code

Close Blend and go back to XAML view of UCWaitSpinner in VS. You should see following XAML code:

<UserControl x:Class="XAMLControls.UCWaitSpinner"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    d:DesignHeight="480" d:DesignWidth="480">
	<UserControl.Resources>
		<Storyboard x:Name="MoveHands">
			<DoubleAnimation Duration="0:0:3" To="360" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="HourHand" d:IsOptimized="True"/>
			<DoubleAnimation Duration="0:0:3" To="1440" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="MinuteHand" d:IsOptimized="True"/>
		</Storyboard>
    </UserControl.Resources>

    <Viewbox x:Name="LayoutRoot">
    	<Grid Width="50" Height="50">
    		<Ellipse Margin="0">
    			<Ellipse.Stroke>
    				<SolidColorBrush Color="{StaticResource PhoneForegroundColor}"/>
    			</Ellipse.Stroke>
    		</Ellipse>
    		<Ellipse Margin="0" Width="4" Height="4" HorizontalAlignment="Center" VerticalAlignment="Center">
    			<Ellipse.Fill>
    				<SolidColorBrush Color="{StaticResource PhoneForegroundColor}"/>
    			</Ellipse.Fill>
    		</Ellipse>
    		<Rectangle x:Name="MinuteHand" Height="20" Margin="0,0,0,25" Width="2" StrokeThickness="0" HorizontalAlignment="Center" VerticalAlignment="Bottom" RenderTransformOrigin="0.5,1">
    			<Rectangle.RenderTransform>
    				<CompositeTransform/>
    			</Rectangle.RenderTransform>
    			<Rectangle.Fill>
    				<SolidColorBrush Color="{StaticResource PhoneContrastBackgroundColor}"/>
    			</Rectangle.Fill>
    		</Rectangle>
    		<Rectangle x:Name="HourHand" Height="13" Margin="0,0,0,25" Width="2" StrokeThickness="0" HorizontalAlignment="Center" VerticalAlignment="Bottom" RenderTransformOrigin="0.5,1">
    			<Rectangle.RenderTransform>
    				<CompositeTransform/>
    			</Rectangle.RenderTransform>
    			<Rectangle.Fill>
    				<SolidColorBrush Color="{StaticResource PhoneContrastBackgroundColor}"/>
    			</Rectangle.Fill>
    		</Rectangle>
    	</Grid>
    </Viewbox>
</UserControl>

Step 13 : Add appear disappear animations

Add AppearClock and DisappearClock animations just below MoveHands inside UserControl.Resource, like so:

        <Storyboard x:Name="AppearClock">
            <DoubleAnimation Duration="0:0:0.3" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="LayoutRoot" d:IsOptimized="True"/>
        </Storyboard>
        <Storyboard x:Name="DisappearClock">
            <DoubleAnimation Duration="0:0:2" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="LayoutRoot" d:IsOptimized="True"/>
        </Storyboard>

Step 14 : Update code behind

Open code behind UCWaitSpinner.xaml.cs and add following lines to its constructor:

        public UCWaitSpinner()
        {
            InitializeComponent();
            MoveHands.RepeatBehavior = RepeatBehavior.Forever;
            LayoutRoot.Opacity = 0d;
            DisappearClock.Completed += (object sender, EventArgs e) => { MoveHands.Stop(); };
        }

Step 15 : Add start functionality

Add a public Start method to the class:

        public void Start()
        {
            MoveHands.Stop();
            AppearClock.Begin();
            MoveHands.Begin();
        }

Step 16 : Add stop functionality

Add a public Stop methods to the class:

        public void Stop()
        {
            DisappearClock.Begin();
        }

Step 17 : Build

Build XAMLControls project. If your project builds properly you should see UCWaitSpinner listed in your Toolbox in XAML Controls.

Step 18 : Add spinner to form

Open MainPage.xaml from the main project WaitSpinner and drag UCWaitSpinner from the Toolbox to the page. Open MainPage.xaml in code view and your will find the newly added control in ContentPanel:

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
                <my:UCWaitSpinner/>
        </Grid>

Step 19 : Edit control properties

In MainPage.xaml name UCWaitSpinner control as “waitSpinner”, add Height and Width properties with a value of 120 in both, and wrap the control in a StackPanel:

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <StackPanel>
                <my:UCWaitSpinner x:Name="waitSpinner" Height="120" Width="120" />
            </StackPanel>
        </Grid>

Step 20 : Add buttons to control test

Add two buttons, just below WaitSpinner, Start and Stop with Click handlers to control WaitSpinner:

        <my:UCWaitSpinner x:Name="waitSpinner" Height="120" Width="120" />
        <Button x:Name="buttonStart" Content="Start" Click="buttonStart_Click"/>
        <Button x:Name="buttonStop" Content="Stop"  Click="buttonStop_Click"/>

Step 21 : Write code in click handlers

Open MainPage.xaml.cs and call Start and Stop of WaitSpinner in buttons’ click handlers:

        private void buttonStart_Click(object sender, RoutedEventArgs e)
        {
            waitSpinner.Start();
        }

        private void buttonStop_Click(object sender, RoutedEventArgs e)
        {
            waitSpinner.Stop();
        }

You can run your code and click on Start button to start spinner. Click on Stop button and spinner will slowly fade out.

Let’s see the example code to integrate the control with background processes.

Make following changes to MainPage.xaml’s ContentPanel control:

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <StackPanel>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Column="0" x:Name="textBlockSeconds" Text="..."/>
                    <my:UCWaitSpinner Grid.Column="1" x:Name="waitSpinner2" Height="90" Width="90" />
                </Grid>
                <my:UCWaitSpinner x:Name="waitSpinner" Height="120" Width="120" />
                <Button x:Name="buttonStart" Content="Start" Click="buttonStart_Click"/>
                <Button x:Name="buttonStop" Content="Stop"  Click="buttonStop_Click"/>
                <Button x:Name="buttonBackground" Content="Background" Click="buttonBackground_Click"/>
            </StackPanel>
        </Grid>

And add following code to the code behind MainPage.xaml.cs:

    public partial class MainPage : PhoneApplicationPage
    {
        // Constructor
        Thread bgThread;
        public MainPage()
        {
            InitializeComponent();
            bgThread = new Thread(new ThreadStart(() => 
            {
                Dispatcher.BeginInvoke(new Action(() => { waitSpinner2.Start(); }));
                for (int cnt = 0; cnt < 5; cnt++)
                {
                    Dispatcher.BeginInvoke(new Action(() => { textBlockSeconds.Text = string.Format("Step - {0}/4", cnt); }));
                    Thread.CurrentThread.Join(2000);
                }
                Dispatcher.BeginInvoke(new Action(() => { waitSpinner2.Stop(); }));
            }));
        }

        private void buttonStart_Click(object sender, RoutedEventArgs e)
        {
            waitSpinner.Start();
        }

        private void buttonStop_Click(object sender, RoutedEventArgs e)
        {
            waitSpinner.Stop();
        }

        private void buttonBackground_Click(object sender, RoutedEventArgs e)
        {
            buttonBackground.IsEnabled = false;
            bgThread.Start();
        }
    }

F5. You should see something like this:

Download entire code here.

Talk About Your App

As an app developer you are supposed to reach out to your prospective users as much as you can to let them know about your app. Social networks – FB, Twitter, Reddit, etc. are some great tools to spread the word. I also think creating a small video about your app and publishing it to YouTube is an effective way to let your users quickly get to know about your app. These are some fantastic posts to talk more about how to promote your apps:
Making A Love Connection? Build Relationships To Promote Your Apps
The Simplest Things People Don’t Do To Market Their Apps (But should)

In this post I will share some points about creating video.

The simplest and very effective way of creating a video is capture the physical phone screen while you are using it. That’s it. You can upload it as it is with your commentary in it. If you want to be fancier, there could be many other pricey options – e.g. hiring an advertising agency. Here I would talk about a no-cost (but a little extra work) option for creating attractive videos showcasing your app. The two videos, listed below, are created with following tools and processes.

In this setup I chose not to go for a video on physical device screen because i wanted to keep focus on the functionality and information about the app, rather than telling about how to use the app. I used emulator instead. It’s easier with emulator and it also gives more control to capture the desired section on the screen.

Prepare Emulator

You could choose to use the default generic emulator image to show your app in. Following is the generic emulator skin which comes with the IDE:

WM7_Skin_Up

Or, to make your video more personalized/interesting, you could use a custom skin. You can customize skins at your heart content, for example, for one video I created this skin:

Nokia925_UP

More about the above skin here.

Once you have taken care of the device skin, have your app installed on the emulator. Don’t forget to prepare example data and load with the app, if your app is data heavy and you want to showcase the same for your users. Set up a nice background on your desktop (it could be your app’s or company’s branding). Background is required because in this case you would have the complete HD movie created along with the background. The least HD movie size is 720P, so apart from the device there would be a lot of background in the video.

Animations and 2D Graphics

Silverlight is an ultimate tool to create fantastic 2D animations. You need to come up with a story as to how you would want to present information to the audience. Think about dynamics of the information, their movements, exit, and entry. Plan 5-10 slides. Create animations and transitions in Silverlight desktop application. If your animations and graphics are showing on the same screen with the video, first capture the video and using VideoBrush add the video in the Silverlight application. Fine tune the timing of the video while test running the app. You can see in the videos below, one of the video is a completely Silverlight video captured on screen and other is a mix of Silverlight and pre-captured video. You can have your titles, credits, and subtitles in the app itself with your custom animations.

Raw Video Footage

I captured screen with Expression Encoder 4 (I hear that the studio has been discontinued, but there are other tools available to download with which you can effectively capture screen). Set the capture size to minimum 1280X720 to play well in HD. Run the app on emulator, or run your desktop Silverlight application and capture the video.

Voice Over

You might want to include commentary in your video. It might be your own voice or you can choose to use text to audio tools. In both the cased you will need a script to read. Prepare the script  by running and re-running the video and match your speed with the video speed. The best free tool to prepare audio is Audacity. You can add commentary, music, effects in a single with multiple tracks. You can adjust the speed of your audio and match it with your video output.

Fuse Video And Audio

Now remains the simple task of bringing Video and Audio together. In this step you would make least changes to video or audio so a simple video editing tool like Movie Maker is enough. Export the video in MP4 format with the source dimension locked.

Examples

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!