Click here to Skip to main content
Click here to Skip to main content

WPFSpark: 7 of n: FluidStatusBar

By , 19 Jan 2012
Rate this:
Please Sign up or sign in to vote.

Introduction

This is the seventh article in the WPFSpark series. Till now I have covered five controls in WPFSpark - SprocketControl, ToggleSwitch, FluidWrapPanel, SparkWindow, FluidPivotPanel, and FluidProgressBar.

The previous articles in the WPFSpark series can be accessed here:

  1. WPFSpark: 1 of n: SprocketControl
  2. WPFSpark: 2 of n: ToggleSwitch
  3. WPFSpark: 3 of n: FluidWrapPanel
  4. WPFSpark: 4 of n: SparkWindow
  5. WPFSpark: 5 of n: FluidPivotPanel
  6. WPFSpark: 6 of n: FluidProgressBar

In this article, I describe in detail the seventh control in this library - the FluidStatusBar control.

Inspiration

The need for FluidStatusBar arose when I was creating a small application for use at my workplace. In this tool, processing of data took some time, so instead of making the user stare at an indeterminate progress bar (SprocketControl in this case!) for a long time, I thought of providing short messages to the user at various stages of the processing. So I added a TextBlock in my application to display the processing status message. Once this was accomplished, I felt that just updating the text of the TextBlock seemed so static and boring! There is no indication to the user that the status message is updating and the text change happens in the blink of the eye. So, in order to make the status updates more dynamic and exciting, the idea of FluidStatusBar was born!

In FluidStatusBar, whenever the status is updated, the previous status message slides out and fades out. At the same time, the new message fades in. This has a better visual appeal when compared to the static update of the TextBlock. FluidStatusBar can also be made to update the status without any animations.

FluidStatusBar Demystified

StatusDirection

StatusDirection is an enum which defines the various directions in which the old status message can slide out when a new status message arrives.

/// <summary>
/// Defines the various direction the old message can slide out
/// </summary>
public enum StatusDirection
{
    Left = 0,
    Right = 1,
    Up = 2,
    Down = 3
}

StatusMessage

This class encapsulates the message that needs to be displayed in the FluidStatusBar.

/// <summary>
/// Class encapsulating the status message that has to be
/// displayed in the FluidStatusBar
/// </summary>
public class StatusMessage
{
    #region Properties

    /// <summary>
    /// The message to be displayed in the FluidStatusBar
    /// </summary>
    public string Message { get; set; }
    /// <summary>
    /// Flag to indicate whether to animate the fade out of
    /// the currently displayed message before showing the 
    /// new message.
    /// </summary>
    public bool IsAnimated { get; set; }

    #endregion

    #region Construction / Initialization

    /// <summary>
    /// Ctor
    /// </summary>
    public StatusMessage()
    {
    }

    /// <summary>
    /// Ctor
    /// </summary>
    /// <param name="message">The message to be displayed in the FluidStatusBar</param>
    /// <param name="isAnimated">Flag to indicate whether to animate the fade out of 
    /// the currently displayed message before showing the new message.</param>
    public StatusMessage(string message, bool isAnimated = false)
    {
        Message = message;
        IsAnimated = isAnimated;
    }

    #endregion
}

StatusMessage Properties

Dependency PropertyTypeDescriptionDefault Value
MessageStringGets or sets the status message that needs to be displayed in the FluidStatusBar.String.Empty
IsAnimatedBooleanGets or sets the flag which indicates whether the old status message must slide out when the new status message arrives.false

FluidStatusBar

The FluidStatusBar consists of two overlapped TextBlocks - FadeOutTextBlock and FadeInTextBlock. Both have the same properties except the Text property. Also, initially both FadeOutTextBlock and FadeInTextBlock have their Visibility set to Collapsed. FadeOutTextBlock is located on top of the FadeInTextBlock.

FluidStatusBar maintains a Queue of StatusMessages (called messageQueue) so that they are processed on a first-come-first-serve basis. Whenever a new StatusMessage arrives, either by setting the Message property of the FluidStatusBar or by calling the SetStatus() method, the StatusMessage object gets added to the messageQueue. Then the ProcessAnimationQueue() method is called to process the items available in the messageQueue.

In the ProcessAnimationQueue() method, the first StatusMessage is inspected. If the IsAnimated property is set to true, then the old status message which was set in FadeInTextBlock's Text property is now set to FadeOutTextBlock's Text property. FadeInTextBlock's Text property is then set to the new status message. Then the Storyboards responsible for animating the sliding out of the FadeOutTextBlock and the fading in of the FadeInTextBlock begin. If the IsAnimated property is set to false, the FadeInTextBlock's Text property is simply set to the new status message. When the animation completes, the first StatusMessage in the messageQueue is dequeued and the whole process repeats.

/// <summary>
/// Interaction logic for FluidStatusBar.xaml
/// </summary>
public partial class FluidStatusBar : UserControl
{
    #region Fields

    Storyboard fadeInOutSB = null;
    Storyboard fadeInSB = null;

    Storyboard fadeOutLeftSB = null;
    Storyboard fadeOutRightSB = null;
    Storyboard fadeOutUpSB = null;
    Storyboard fadeOutDownSB = null;

    Queue<StatusMessage> messageQueue = null;
    bool isAnimationInProgress = false;

    #endregion

    #region DependencyProperties

    ...
    
    #endregion

    #region Construction / Initialization

    public FluidStatusBar()
    {
        InitializeComponent();

        messageQueue = new Queue<StatusMessage>();

        fadeOutLeftSB = (Storyboard)this.Resources["FadeInOutLeftStoryboard"];
        if (fadeOutLeftSB != null)
        {
            fadeOutLeftSB.Completed += new EventHandler(OnFadeOutAnimationCompleted);
        }

        fadeOutRightSB = (Storyboard)this.Resources["FadeInOutRightStoryboard"];
        if (fadeOutRightSB != null)
        {
            fadeOutRightSB.Completed += new EventHandler(OnFadeOutAnimationCompleted);
        }

        fadeOutUpSB = (Storyboard)this.Resources["FadeInOutUpStoryboard"];
        if (fadeOutUpSB != null)
        {
            fadeOutUpSB.Completed += new EventHandler(OnFadeOutAnimationCompleted);
        }

        fadeOutDownSB = (Storyboard)this.Resources["FadeInOutDownStoryboard"];
        if (fadeOutDownSB != null)
        {
            fadeOutDownSB.Completed += new EventHandler(OnFadeOutAnimationCompleted);
        }

        fadeInSB = (Storyboard)this.Resources["FadeInStoryboard"];
        if (fadeInSB != null)
        {
            fadeInSB.Completed += new EventHandler(OnFadeOutAnimationCompleted);
        }

        fadeInOutSB = fadeOutLeftSB;

        isAnimationInProgress = false;
    }

    #endregion

    #region APIs

    /// <summary>
    /// Sets the new status message in the status bar.
    /// </summary>
    /// <param name="statusMsg">New Status Message</param>
    public void SetStatus(StatusMessage statusMsg)
    {
        if (statusMsg == null)
            return;

        lock (messageQueue)
        {
            messageQueue.Enqueue(statusMsg);
        }

        ProcessAnimationQueue();
   }

    /// <summary>
    /// Sets the new status message in the status bar.
    /// </summary>
    /// <param name="message">New message to be displayed</param>
    /// <param name="isAnimated">Flag to indicate whether the old status message 
    /// should be animated when it fades out</param>
    public void SetStatus(string message, bool isAnimated)
    {
        lock (messageQueue)
        {
            messageQueue.Enqueue(new StatusMessage(message, isAnimated));
        }
        ProcessAnimationQueue();
    }

    #endregion

    #region Helpers

    private void UpdateFadeOutDistance(Storyboard sb, Thickness thickness)
    {
        if (sb != null)
        {
            foreach (Timeline timeline in sb.Children)
            {
                ThicknessAnimation anim = timeline as ThicknessAnimation;
                if (anim != null)
                {
                    anim.SetValue(ThicknessAnimation.ToProperty, thickness);
                }
            }
        }
    }

    private void UpdateMoveDuration(Storyboard sb, Duration duration)
    {
        if (sb != null)
        {
            foreach (Timeline timeline in sb.Children)
            {
                ThicknessAnimation anim = timeline as ThicknessAnimation;
                if (anim != null)
                {
                    anim.SetValue(ThicknessAnimation.DurationProperty, duration);
                }
            }
        }
    }

    private void UpdateFadeOutDuration(Storyboard sb, Duration duration)
    {
        if (sb != null)
        {
            foreach (Timeline timeline in sb.Children)
            {
                DoubleAnimation anim = timeline as DoubleAnimation;
                if (anim != null)
                {
                    anim.SetValue(DoubleAnimation.DurationProperty, duration);
                }
            }
        }
    }

    private void ProcessAnimationQueue()
    {
        if (isAnimationInProgress)
            return;

        lock (messageQueue)
        {
            if (messageQueue.Count > 0)
            {
                Dispatcher.BeginInvoke(new Action(() =>
                    {
                        if (messageQueue.Count > 0)
                        {
                            isAnimationInProgress = true;
                            StatusMessage msg = messageQueue.Peek() as StatusMessage;
                            if (msg != null)
                            {
                                if (msg.IsAnimated)
                                {
                                    // Copy the text to begin fade out
                                    FadeOutTextBlock.Text = FadeInTextBlock.Text;
                                    FadeInTextBlock.Text = msg.Message;

                                    if (fadeInOutSB != null)
                                        fadeInOutSB.Begin();
                                }
                                else
                                {
                                    FadeInTextBlock.Text = msg.Message;
                                    if (fadeInSB != null)
                                        fadeInSB.Begin();
                                }
                            }
                        }
                    }));
            }
        }
    }

    #endregion

    #region Event Handlers

    void OnFadeOutAnimationCompleted(object sender, EventArgs e)
    {
        isAnimationInProgress = false;

        lock (messageQueue)
        {
            if (messageQueue.Count > 0)
                messageQueue.Dequeue();
        }

        ProcessAnimationQueue();
    }

    #endregion
}

FluidStatusBar Properties

Dependency PropertyTypeDescriptionDefault Value
FadeOutDirectionWPFSpark.StatusDirectionGets or sets the direction in which the old status message should slide out when the new status message arrives.WPFSpark.StatusDirection.Left
FadeOutDistanceDoubleGets or sets the distance to which the old status message should slide out when the new status message arrives.0.0
FadeOutDurationDurationGets or sets the duration for the fading out animation.0
MoveDurationDurationGets or sets the duration for the sliding out animation.0
TextHorizontalAlignmentHorizontalAlignmentGets or sets the HorizontalAlignment of the TextBlocks displaying the status message.HorizontalAlignment.Center
TextVerticalAlignmentVerticalAlignmentGets or sets the VerticalAlignment of the TextBlocks displaying the status message.VerticalAlignment.Center
MessageWPFSpark.StatusMessageGets or sets the StatusMessage that has to be displayed.null

History

  • December 21, 2011: WPFSpark v1.0 released.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

About the Author

Ratish Philip
Software Developer NA
India India
Ratish Philip is a software developer with 8 years of experience. He loves programming in C#, WPF & Silverlight.
 
He is currently exploring the depths of Windows 8 programming.
 
Creating enriched user experiences is what appeals to him the most.
 
Occasionally expresses his creativity through pencil sketching too!
 
Ratish's personal blog: wpfspark.wordpress.com

Comments and Discussions

 
QuestionCan I use WPF application against Silverlight? PinmemberChanghan Kim23-Jul-12 15:01 
AnswerRe: Can I use WPF application against Silverlight? PinmemberRatish Philip1-Aug-12 0:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 20 Jan 2012
Article Copyright 2011 by Ratish Philip
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid