Visually Located

XAML and GIS

Creating a behavior to control the new StatusBar (SystemTray) in Windows Phone 8.1 XAML apps

In a recent post I described creating a behavior to control the new StatusBarProgressIndicator. This is because the new progress indicator can only be accessed via code. A behavior allows us to control it via XAML. Just like the progress indicator, the new StatusBar (known as the SystemTray in Windows Phone Silverlight apps) can also only be accessed via code. I still prefer a behavior for getting this type of functionality into a page over using a custom control. It just makes sense. Before you begin, add a reference to the Behaviors SDK (XAML)

image

We’ll start off creating a new StatusBarBehavior class and inherits from DependencyObject and implements the IBehavior interface.

public class StatusBarBehavior : DependencyObject, IBehavior
{
    public void Attach(DependencyObject associatedObject)
    {        
    }
 
    public void Detach()
    {
    }
 
    public DependencyObject AssociatedObject { get; private set; }
}

For this behavior, we will not need to hook into the associated dependency object. However, it’s important to note that we could get and store the StatusBar object within the Attach method. We’ll want to add dependency properties to the behavior that will control the visibility, foreground and background colors and the opacity of the status bar. When creating a behavior to wrap existing functionality it is important that the default values of your properties match the default values of the functionality you are wrapping. This is because we want our PropertyChangedCallback methods to be called when the value is changed and function accordingly. Each of the properties we implement will need to account for these default values.

We’ll first look at changing the visibility. In the SystemTray from Windows Phone Silverlight apps, you would control visibility with the IsVisible property. We’ll keep that name as it’s familiar, and can easily be used with binding in a view model. The StatusBar defaults to being visible, so the default value needs to be true

public bool IsVisible
{
    get { return (bool)GetValue(IsVisibleProperty); }
    set { SetValue(IsVisibleProperty, value); }
}
 
public static readonly DependencyProperty IsVisibleProperty =
    DependencyProperty.Register("IsVisible",
    typeof(bool),
    typeof(StatusBarBehavior),
    new PropertyMetadata(true, OnIsVisibleChanged));
 
private static void OnIsVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    bool isvisible = (bool)e.NewValue;
    if (isvisible)
    {
        StatusBar.GetForCurrentView().ShowAsync();
    }
    else
    {
        StatusBar.GetForCurrentView().HideAsync();
    }
}

This should look very similar from the behavior to control the progress indicator. Next we’ll create a property for the opacity of the background. Unlike in Windows Phone Silverlight Apps, the opacity defaults to 0. Our property should also default to 0.

public double BackgroundOpacity
{
    get { return (double)GetValue(BackgroundOpacityProperty); }
    set { SetValue(BackgroundOpacityProperty, value); }
}
 
public static readonly DependencyProperty BackgroundOpacityProperty =
    DependencyProperty.Register("BackgroundOpacity",
    typeof(double), 
    typeof(StatusBarBehavior), 
    new PropertyMetadata(0d, OnOpacityChanged));
 
private static void OnOpacityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    StatusBar.GetForCurrentView().BackgroundOpacity = (double)e.NewValue;         
}
Alright, we’re cruising right along, this is easy. Next we’ll tackle the background and foreground colors. This is where it starts to get a little weird. The StatusBar implements the background and foreground with

Nullable<Color>. When we created the ProgressBehavior, we found that when we tried to create a dependency property in our behavior that it had some issues. We have issues again, but now they are different. Based on the last post, create the ForegroundColor property as you think you would with a nullable Color property.

public Color? ForegroundColor
{
    get { return (Color?)GetValue(ForegroundColorProperty); }
    set { SetValue(ForegroundColorProperty, value); }
}
 
public static readonly DependencyProperty ForegroundColorProperty =
    DependencyProperty.Register("ForegroundColor", 
    typeof(object), 
    typeof(StatusBarBehavior), 
    new PropertyMetadata(null, OnForegroundColorChanged));
 
private static void OnForegroundColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    StatusBar.GetForCurrentView().ForegroundColor = (Color) e.NewValue;
}

While this may work for binding, this fails in XAML. Trying this out as is:

<i:Interaction.Behaviors>
    <local:StatusBarBehavior ForegroundColor="Blue"/>
</i:Interaction.Behaviors>

Gives us the following error:

XamlCompiler error WMC0056: Cannot assign to nullable type on property ForegroundColor

So, we will not be able to use the Nullable<Color> for the foreground and background color. That’s fine because I doubt anyone would bind to a null value. Let’s correct that ForegroundColor property

public Color ForegroundColor
{
    get { return (Color)GetValue(ForegroundColorProperty); }
    set { SetValue(ForegroundColorProperty, value); }
}
 
public static readonly DependencyProperty ForegroundColorProperty =
    DependencyProperty.Register("ForegroundColor", 
    typeof(Color), 
    typeof(StatusBarBehavior), 
    new PropertyMetadata(null, OnForegroundColorChanged));

Notice that we can set the default value to null (weird). The BackgroundColor will require a little extra work. Because the BackgroundOpacity defaults to 0, setting the BackgroundColor will do nothing unless the opacity is changed as well. We could take two approaches with the behavior, either force the use of the opacity property, or set the opacity if the background is set. I like the latter option. If the background is set, then obviously they want to see it!

public Color BackgroundColor
{
    get { return (Color)GetValue(BackgroundColorProperty); }
    set { SetValue(BackgroundColorProperty, value); }
}
 
public static readonly DependencyProperty BackgroundColorProperty =
    DependencyProperty.Register("BackgroundColor", 
    typeof(Color), 
    typeof(StatusBarBehavior), 
    new PropertyMetadata(null, OnBackgroundChanged));
 
private static void OnBackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var behavior = (StatusBarBehavior) d;
    StatusBar.GetForCurrentView().BackgroundColor = behavior.BackgroundColor;
 
    // if they have not set the opacity, we need to so the new color is shown
    if (behavior.BackgroundOpacity == 0)
    {
        behavior.BackgroundOpacity = 1;
    }
}

Now we have a complete behavior that is very similar to the Windows Phone Silverlight SystemTray.

<Page
    ...
    xmlns:i="using:Microsoft.Xaml.Interactivity">
    <i:Interaction.Behaviors>
        <local:StatusBarBehavior IsVisible="True" 
                                 BackgroundColor="#FF0000"
                                 ForegroundColor="Blue"/>
    </i:Interaction.Behaviors>
    <Grid>
        <!-- Content -->
    <Grid>
</Page>

Download a sample to play with the behavior, or just download the behavior.

blog comments powered by Disqus