Desperately Seeking Love of Sophie

Archive for May 24th, 2007

Part 3: Search Application (Progress UI)

with one comment

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.

Written by Vivek

May 24, 2007 at 12:23 am

Posted in Professional, Software

Tagged with