Professional, Software

Part 3: Search Application (Progress UI)

Since webservice call can take time to return, I needed a progress UI. This progress UI is different than typical progress bar since I don’t know how long actually it is going to take for webservice to return the data. So I needed “infinite” progress where user knows something is going on.

I created something simple control, there are 5 circles on the screen that get highlighted one after other while search application is waiting for web service to return.  

 Again, I know it is not the coolest progress UI that can be created using XAML but that is my limitation :). This control exposes two methods, StartProgress and StopProgress which basically start/stop the animation that creates the highlights. Other than that control is pretty basic. So if somebody wants to update this control to look different they will have to do following things…

  1. Change the Xaml to create different visual look (in XAML)
  2. Modify the “Progress” storyboard to animate new look created in step 1 (in XAML)
  3. Update “UpdateLayout” method so that new visual look gets layout behavior.

ProgressUI.Xaml looks like follows….

<Canvas xmlns="http://schemas.microsoft.com/client/2007"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Width="160"
        Height="40"
        Background="Black"
        >

    <Canvas.Resources>
        <Storyboard x:Name="Progress" RepeatBehavior="Forever">
            <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)">
                <SplineColorKeyFrame KeyTime="00:00:00.5000000" Value="#FFFFFFFF"/>
                <SplineColorKeyFrame KeyTime="00:00:00.7000000" Value="#FFE5E5E5"/>
            </ColorAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
                <SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
                <SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">
                <SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
                <SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
            <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
                <SplineColorKeyFrame KeyTime="00:00:00.5000000" Value="#FF2E9C33"/>
                <SplineColorKeyFrame KeyTime="00:00:00.7000000" Value="#FF000000"/>
            </ColorAnimationUsingKeyFrames>
            <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse1" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
                <SplineColorKeyFrame KeyTime="00:00:01" Value="#FF2E9C33"/>
                <SplineColorKeyFrame KeyTime="00:00:01.2000000" Value="#FF000000"/>
            </ColorAnimationUsingKeyFrames>
            <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse2" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
                <SplineColorKeyFrame KeyTime="00:00:01.3000000" Value="#FF2E9C33"/>
                <SplineColorKeyFrame KeyTime="00:00:01.5000000" Value="#FF000000"/>
            </ColorAnimationUsingKeyFrames>
            <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse3" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
                <SplineColorKeyFrame KeyTime="00:00:01.6000000" Value="#FF2E9C33"/>
                <SplineColorKeyFrame KeyTime="00:00:01.8000000" Value="#FF000000"/>
            </ColorAnimationUsingKeyFrames>
            <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse4" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
                <SplineColorKeyFrame KeyTime="00:00:01.8000000" Value="#FF2E9C33"/>
                <SplineColorKeyFrame KeyTime="00:00:02" Value="#FF000000"/>
            </ColorAnimationUsingKeyFrames>
        </Storyboard>
    </Canvas.Resources>

    <Ellipse Stroke="#FF000000" Width="24" Height="24" Canvas.Left="8" Canvas.Top="8" RenderTransformOrigin="0.5,0.5" x:Name="ellipse">
        <Ellipse.Fill>
            <RadialGradientBrush>
                <GradientStop Color="#FF868181" Offset="0.394"/>
                <GradientStop Color="#FF000000" Offset="1"/>
                <GradientStop Color="#FFE5E5E5" Offset="0"/>
            </RadialGradientBrush>
        </Ellipse.Fill>
        <Ellipse.RenderTransform>
            <TransformGroup>
                <ScaleTransform ScaleX="1" ScaleY="1"/>
                <SkewTransform AngleX="0" AngleY="0"/>
                <RotateTransform Angle="0"/>
                <TranslateTransform X="0" Y="0"/>
            </TransformGroup>
        </Ellipse.RenderTransform>
    </Ellipse>

    <Ellipse Stroke="#FF000000" Width="24" Height="24" Canvas.Left="120" Canvas.Top="8" x:Name="ellipse4">
        <Ellipse.Fill>
            <RadialGradientBrush>
                <GradientStop Color="#FF868181" Offset="0.394"/>
                <GradientStop Color="#FF000000" Offset="1"/>
                <GradientStop Color="#FFE5E5E5" Offset="0"/>
            </RadialGradientBrush>
        </Ellipse.Fill>
    </Ellipse>

    <Ellipse Stroke="#FF000000" Width="24" Height="24" Canvas.Left="92" Canvas.Top="8" x:Name="ellipse3">
        <Ellipse.Fill>
            <RadialGradientBrush>
                <GradientStop Color="#FF868181" Offset="0.394"/>
                <GradientStop Color="#FF000000" Offset="1"/>
                <GradientStop Color="#FFE5E5E5" Offset="0"/>
            </RadialGradientBrush>
        </Ellipse.Fill>
    </Ellipse>

    <Ellipse Stroke="#FF000000" Width="24" Height="24" Canvas.Left="64" Canvas.Top="8" x:Name="ellipse2">
        <Ellipse.Fill>
            <RadialGradientBrush>
                <GradientStop Color="#FF868181" Offset="0.394"/>
                <GradientStop Color="#FF000000" Offset="1"/>
                <GradientStop Color="#FFE5E5E5" Offset="0"/>
            </RadialGradientBrush>
        </Ellipse.Fill>
    </Ellipse>

    <Ellipse Stroke="#FF000000" Width="24" Height="24" Canvas.Left="36" Canvas.Top="8" x:Name="ellipse1">
        <Ellipse.Fill>
            <RadialGradientBrush>
                <GradientStop Color="#FF868181" Offset="0.394"/>
                <GradientStop Color="#FF000000" Offset="1"/>
                <GradientStop Color="#FFE5E5E5" Offset="0"/>
            </RadialGradientBrush>
        </Ellipse.Fill>
    </Ellipse>

</Canvas>

 

ProgressUI.xaml.cs looks like follows…

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SearchApplication
{
    public class ProgressUI : Control
    {
        Storyboard progress;
        Canvas rootCanvas;
        Ellipse ellipse;
        Ellipse ellipse1;
        Ellipse ellipse2;
        Ellipse ellipse3;
        Ellipse ellipse4;

        public ProgressUI()
        {
            System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream("SearchApplication.ProgressUI.xaml");
            rootCanvas = (Canvas)this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd());
            progress = (Storyboard)rootCanvas.FindName("Progress");
            ellipse = (Ellipse)rootCanvas.FindName("ellipse");
            ellipse1 = (Ellipse)rootCanvas.FindName("ellipse1");
            ellipse2 = (Ellipse)rootCanvas.FindName("ellipse2");
            ellipse3 = (Ellipse)rootCanvas.FindName("ellipse3");
            ellipse4 = (Ellipse)rootCanvas.FindName("ellipse4");
        }

        public void StartProgress()
        {
            progress.Begin();
        }

        public void StopProgress()
        {
            progress.Stop();
        }

        public new double Height
        {
            get { return ((FrameworkElement)this).Height; }
            set
            {
                ((FrameworkElement)this).Height = value;
                UpdateLayout();
            }
        }

        public new double Width
        {
            get { return ((FrameworkElement)this).Width; }
            set
            {
                ((FrameworkElement)this).Width = value;
                UpdateLayout();
            }
        }

        private void UpdateLayout()
        {
            rootCanvas.Height = Height;
            rootCanvas.Width = Width;
            ellipse.Height = Height;
            ellipse.Width = Height;
            ellipse1.Height = Height;
            ellipse1.Width = Height;
            ellipse2.Height = Height;
            ellipse2.Width = Height;
            ellipse3.Height = Height;
            ellipse3.Width = Height;
            ellipse4.Height = Height;
            ellipse4.Width = Height;

            ellipse.SetValue(Canvas.LeftProperty, (0 * Width / 5) - Height);
            ellipse1.SetValue(Canvas.LeftProperty, (1*Width/5) - Height );
            ellipse2.SetValue(Canvas.LeftProperty, (2 * Width / 5) - Height);
            ellipse3.SetValue(Canvas.LeftProperty, (3 * Width / 5) - Height);
            ellipse4.SetValue(Canvas.LeftProperty, (4 * Width / 5) - Height);
        }
    }
}

In Application, I call webservice asynchronously, so when i make the call to webservice I call “StartProgress” which basically starts the animation.

[Scriptable]
//rememder it should be public
public void Search(string searchstring)
{
    progressui = new ProgressUI();
    progressui.Height = 60;
    progressui.Width = 300;
    this.Children.Add(progressui);
    UpdateLayout();

    progressui.StartProgress();
    SearchService.LiveWebService livecall = new SearchApplication.SearchService.LiveWebService();
    AsyncCallback searchcompleted = new AsyncCallback(SearchCompleted);
    livecall.BeginSearchImages(searchstring, this.SearchCompleted, livecall);
}

When web service returns, I call StopProgress which stops the animation.

void SearchCompleted(IAsyncResult asyncResult)
{
    SearchService.LiveWebService liveresult = asyncResult.AsyncState as SearchService.LiveWebService;
    searchresults = liveresult.EndSearchImages(asyncResult);
    progressui.StopProgress();
    this.Children.Remove(progressui);
    progressui = null;
}

This way you get animation (infinite progress UI) that tells user application is still working.

Advertisements
Standard

One thought on “Part 3: Search Application (Progress UI)

  1. Pingback: Recursive Reflection : Part 2: Search Application (Progress UI)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s