Home > C#, MVVM, Windows Phone > Behaviors to handle StatusBar and ProgressIndicator in Windows Phone 8.1 apps

Behaviors to handle StatusBar and ProgressIndicator in Windows Phone 8.1 apps

11/09/2014

The new StatusBar object in Windows Phone apps replaces the SystemTray from Windows Phone Silverlight apps. Unlike the SystemTray, the StausBar can only be accessed via code.

We can, however, define a behavior that allows to handle the StatusBar in XAML. After adding to the project the reference to the Behavior SDK, we can create the following class:

public class StatusBarBehavior : DependencyObject, IBehavior
{
    private const string IS_VISIBLE = "IsVisible";
    private const string FOREGROUND_COLOR = "ForegroundColor";
    private const string BACKGROUND_COLOR = "BackgroundColor";
    private const string BACKGROUND_OPACITY = "BackgroundOpacity";

    public DependencyObject AssociatedObject { get; private set; }

    public void Attach(DependencyObject associatedObject) { }

    public void Detach() { }

    public bool IsVisible
    {
        get { return (bool)GetValue(IsVisibleProperty); }
        set { SetValue(IsVisibleProperty, value); }
    }

    public static readonly DependencyProperty IsVisibleProperty =
        DependencyProperty.Register(IS_VISIBLE,
        typeof(bool),
        typeof(StatusBarBehavior),
        new PropertyMetadata(true, OnIsVisibleChanged));

    private static async void OnIsVisibleChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var statusBar = StatusBar.GetForCurrentView();
        if ((bool)e.NewValue)
            await statusBar.ShowAsync();
        else
            await statusBar.HideAsync();
    }

    public Color ForegroundColor
    {
        get { return (Color)GetValue(ForegroundColorProperty); }
        set { SetValue(ForegroundColorProperty, value); }
    }

    public static readonly DependencyProperty ForegroundColorProperty =
        DependencyProperty.Register(FOREGROUND_COLOR,
        typeof(Color),
        typeof(StatusBarBehavior),
        new PropertyMetadata(null, OnForegroundColorChanged));

    private static void OnForegroundColorChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        StatusBar.GetForCurrentView().ForegroundColor = (Color)e.NewValue;
    }

    public Color BackgroundColor
    {
        get { return (Color)GetValue(BackgroundColorProperty); }
        set { SetValue(BackgroundColorProperty, value); }
    }

    public static readonly DependencyProperty BackgroundColorProperty =
        DependencyProperty.Register(BACKGROUND_COLOR,
        typeof(Color),
        typeof(StatusBarBehavior),
        new PropertyMetadata(null, OnBackgroundColorChanged));

    private static void OnBackgroundColorChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var behavior = d as StatusBarBehavior;
        if (behavior.BackgroundOpacity == 0)
            behavior.BackgroundOpacity = 1;

        StatusBar.GetForCurrentView().BackgroundColor = behavior.BackgroundColor;
    }

    public double BackgroundOpacity
    {
        get { return (double)GetValue(BackgroundOpacityProperty); }
        set { SetValue(BackgroundOpacityProperty, value); }
    }

    public static readonly DependencyProperty BackgroundOpacityProperty =
        DependencyProperty.Register(BACKGROUND_OPACITY,
        typeof(double),
        typeof(StatusBarBehavior),
        new PropertyMetadata(null, OnBackgroundOpacityChanged));

    private static void OnBackgroundOpacityChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var behavior = d as StatusBarBehavior;
        StatusBar.GetForCurrentView().BackgroundOpacity = behavior.BackgroundOpacity;
    }
}

The new StatusBar is exposed via the static StatusBar.GetForCurrentView() method, that gets the status bar for the current window (app view). So, it is impossible to directly access it in XAML. This behavior provides 4 dependency properties to workaround this limitation. As the StatusBar is exposed via a static property, we don’t need to explicitly handle the Attach method.

For example, the OnIsVisibleChanged method (lines 26-33), invoked when the IsVisible property changes, calls the ShowAsync or HideAsync method. The same approach is followed by the other properties.

Similarly, the StatusBar ProgressIndicator can be accessed only via code, because it is exposed by the StatusBar.GetForCurrentView().ProgressIndicator property. So, also in this case we can define a behavior to use it in XAML:

public class ProgressIndicatorBehavior : DependencyObject, IBehavior
{
    private const string IS_VISIBLE = "IsVisible";
    private const string TEXT = "Text";
    private const string VALUE = "Value";
    private const string IS_INDETERMINATE = "IsIndeterminate";

    public DependencyObject AssociatedObject { get; private set; }

    public void Attach(DependencyObject associatedObject) { }

    public void Detach() { }

    public bool IsVisible
    {
        get { return (bool)GetValue(IsVisibleProperty); }
        set { SetValue(IsVisibleProperty, value); }
    }

    public static readonly DependencyProperty IsVisibleProperty =
        DependencyProperty.Register(IS_VISIBLE,
        typeof(bool),
        typeof(ProgressIndicatorBehavior),
        new PropertyMetadata(false, OnIsVisibleChanged));

    private static async void OnIsVisibleChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var progressIndicator = StatusBar.GetForCurrentView().ProgressIndicator;
        if ((bool)e.NewValue)
        {
            progressIndicator.ProgressValue = 0;
            await progressIndicator.ShowAsync();
        }
        else
        {
            await progressIndicator.HideAsync();
        }
    }

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register(TEXT,
        typeof(string),
        typeof(ProgressIndicatorBehavior),
        new PropertyMetadata(null, OnTextChanged));

    private static void OnTextChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        ProgressIndicatorBehavior behavior = (ProgressIndicatorBehavior)d;
        StatusBar.GetForCurrentView().ProgressIndicator.Text = behavior.Text;
    }

    public object Value
    {
        get { return (double?)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    public static readonly DependencyProperty ValueProperty =
         DependencyProperty.Register(VALUE,
         typeof(object),
         typeof(ProgressIndicatorBehavior),
         new PropertyMetadata(null, OnValueChanged));

    private static void OnValueChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        double? val = null;
        if (e.NewValue != null)
            val = (double?)Convert.ToDouble(e.NewValue);

        StatusBar.GetForCurrentView().ProgressIndicator.ProgressValue = val;
    }

    public bool IsIndeterminate
    {
        get { return (bool)GetValue(IsIndeterminateProperty); }
        set { SetValue(IsIndeterminateProperty, value); }
    }

    public static readonly DependencyProperty IsIndeterminateProperty =
         DependencyProperty.Register(IS_INDETERMINATE,
         typeof(bool),
         typeof(ProgressIndicatorBehavior),
         new PropertyMetadata(false, OnIsIndeterminateChanged));

    private static void OnIsIndeterminateChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var progressIndicator = StatusBar.GetForCurrentView().ProgressIndicator;
        if ((bool)e.NewValue)
            progressIndicator.ProgressValue = null;
        else
            progressIndicator.ProgressValue = 0;
    }
}

The behavior allows to set the text and the value of the indicator in XAML, accessing the underlying properties of the ProgressBarStatusIndicator object. Note that the Value property must be between 0 and 1 (it is a percentage). We can also use IsIndeterminate to show continuous progress feedback.

Using these behaviors is straightforward. Let’s see an example:

<Page
    x:Class="StatusBarDemo.MainPage"
    ...
    xmlns:i="using:Microsoft.Xaml.Interactivity"
    xmlns:behaviors="using:StatusBarDemo.Behaviors"
    >

    <i:Interaction.Behaviors>
        <behaviors:StatusBarBehavior IsVisible="True"
                                     ForegroundColor="White"
                                     BackgroundColor="Green"
                                     BackgroundOpacity="0.9" />
        <behaviors:ProgressIndicatorBehavior IsVisible="True"
                                             Text="StatusBar Demo"
                                             Value="0.6" />
    </i:Interaction.Behaviors>

This XAML produces the following results:

StatusBar in Windows Phone apps

StatusBar in Windows Phone apps

Moreover, because we have defined all dependency properties, we can use binding. Suppose we have a ViewModel and we want to set the ProgressIndicator to indeterminate state when, say, the IsBusy property is true:

<i:Interaction.Behaviors>
    ...
    <behaviors:ProgressIndicatorBehavior IsVisible="True" 
                                         Text="StatusBar Demo"
                                         IsIndeterminate="{Binding IsBusy}" />
</i:Interaction.Behaviors>

You can download a working example using the link below:

Behaviors to handle StatusBar and ProgressIndicator in Windows Phone 8.1 apps

Categories: C#, MVVM, Windows Phone
%d bloggers like this: