Visually Located

XAML and GIS

Using a behavior to control the ProgressIndicator in Windows Phone 8.1 XAML Apps

In the last post I talked about showing a ProgressIndicator in a Windows Phone 8.1 XAML App. I mentioned that with the new API you can only show the ProgressIndicator through code. This means that you cannot show the progress indicator using binding, you cannot set a Loading property, or something similar to show/hide the indicator. I’m not a fan of this solution. I like using XAML and keeping my view models simple. I saw three possible solutions to overcome this and show/hide/modify the progress indicator in xaml. The first is to create a control, use a third party progress indicator, or to use a behavior. I didn’t fully like the first or second solutions as it would require you to put the control inside the content of a page. This just didn’t seem right.

<Page>
    <Grid>
        <custom:ProgressIndicator/>
        <!-- content for page -->
    </Grid>
</Page>

Creating a behavior allows you to place the progress indicator in the same place as you had it when building Windows Phone apps with Silverlight.

<Page>
    <i:Interaction.Behaviors>
        <local:ProgressBehavior IsVisible="{Binding Loading}" Text="Loading"/>
    </i:Interaction.Behaviors>
    <Grid>
        <!-- content for page -->
    </Grid>
</Page>

We’ll build a Behavior that will allow us to set properties in XAML, just like we had in Silverlight apps. To use behaviors in your project, you must add a reference to the Behaviors SDK (XAML).

image

Add a new class to the project called ProgressBehavior and implement the IBehavior interface.

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

The interface is simple, and for this behavior we will not need to do anything with the methods or property. Instead we’ll add a few dependency properties. I don’t want to completely replicate the Silverlight ProgressIndicator with this behavior so we won’t be implementing all four properties that it had. We will provide a way to set the value, show and hide the indicator and set the text. First we’ll add a dependency property for the text. When creating dependency properties, it’s best to use the ‘propdp’ built-in shortcut. Type propdp and then the tab button. Your property will be stubbed out and allow you to fill in the needed values.

public int MyProperty
{
    get { return (int)GetValue(MyPropertyProperty); }
    set { SetValue(MyPropertyProperty, value); }
}
 
// Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
    DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));

First type the type of the property, the Text property will be a string. Then hit tab and give the property a name, hit tab and fill out the rest of the properties (ownerclass will be ProgressBehavior).

public string Text
{
    get { return (string)GetValue(TextProperty); }
    set { SetValue(TextProperty, value); }
}
 
public static readonly DependencyProperty TextProperty =
    DependencyProperty.Register("Text",
    typeof(string),
    typeof(ProgressBehavior),
    new PropertyMetadata(null, OnTextChanged));

Notice on the last line, I added OnTextChanged to the constructor to PropertyMetadata. This specifies a method to call when the property changes. When the text changes we’ll set the text of the progress indicator.

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

Next we’ll create a dependency property to toggle the visibility. I like the IsVisible property from the Silverlight Phone SDK because there is no need for converters like a BooleanToVisibilityConverter. We will again need to interact with the progress indicator when the IsVisible value changes.

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

The last dependency property will control the ProgressValue of the indicator as well as if the indicator is indeterminate. This property is not as straight forward as you would think. Following the pattern from before, we would declare the property as such.

public double? Value
{
    get { return (double?)GetValue(ValueProperty); }
    set { SetValue(ValueProperty, value); }
}
 
public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register("Value",
    typeof(double?),
    typeof(ProgressBehavior),
    new PropertyMetadata(null, OnValueChanged));
 
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    double? val = (double?)e.NewValue;
    StatusBar.GetForCurrentView().ProgressIndicator.ProgressValue = val;
}

Now it would seem that our behavior is complete. We’ve declared the Text, IsVisible, and Value properties. The behavior will modify the StatusBarProgressIndicator when values change. Yup, we’re all done. However, when we try to change the value of the indicator, nothing happens. Well, not nothing, we do get the following error in the output window while debugging

Error: Converter failed to convert value of type 'Double' to type 'IReference`1<Double>'; BindingExpression: Path='LoadingValue' DataItem='ProgressBehaviorSample.ViewModel, ProgressBehaviorSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'; target element is 'ProgressBehaviorSample.ProgressBehavior' (Name='null'); target property is 'Value' (type 'IReference`1<Double>').

I could go into detail about this, but Dan Rigby already did that. The short of it the nullable double in .NET does not convert nicely to IReference in Windows Runtime. We can fix that by changing the second parameter of the DependencyProperty.Register in our Value dependency property from double? to object. Our property still remains double?.

public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register("Value",
    typeof(object),
    typeof(ProgressBehavior),
    new PropertyMetadata(null, OnValueChanged));

Now that we have that fixed the property, our behavior is complete and can be used like such.

<i:Interaction.Behaviors>
    <local:ProgressBehavior IsVisible="{Binding Loading}"
                            Text="Loading"/>
         <!-- feel free to put a value in as well :) -->
</i:Interaction.Behaviors>

Take this behavior for a spin with a complete working sample or just download the behavior. This sample is a little weird because it uses a viewmodel that has properties that you would normally not use. But they are there for the purpose of being able to change the indicator values in the app.

blog comments powered by Disqus