Search code examples
silverlightsilverlight-toolkitchildwindowbusyindicator

Silverlight BusyIndicator : Higher Z-Index than all ChildWindows


Perhaps the title is worded incorrectly.

I have a "global" Busy Indicator that works great, as long as I don't try to use it when a ChildWindow is open.

I access the "global" Busy Indicator by using a static method in my App.xaml.cs:

BusyIndicator b = (BusyIndicator)App.Current.RootVisual;
if (b != null)
{
    b.BusyContent = busyText;
    b.IsBusy = true;
}

However, if a ChildWindow is open, the BusyIndicator is always behind it.

I thought that I could set b.Content = VisualTreeHelper.GetOpenPopups().First(), but that didn't work either.

Does anyone have any tips on having a BusyIndicator on top of open ChildWindows?

Thanks in advance.

UPDATE (SOLUTION)

Dave S sent me on the right track. It was more complicated than I had hoped, but here's my solution.

First, I had to make a full style for the ChildWindow, copying all of the Template style (left some out for post-size reasons):

<Style x:Key="MyChildWindowStyle" TargetType="gs:MyChildWindow">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="gs:MyChildWindow">
                <toolkit:BusyIndicator IsBusy="{TemplateBinding IsBusy}" BusyContent="{TemplateBinding BusyContent}" BusyContentTemplate="{StaticResource MyBusyIndicatorDataTemplate}">
                    <Grid>
                        <ContentPresenter x:Name="ContentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Grid>
                </toolkit:BusyIndicator>
</Style>

Then, I created my base class; notice the constructor sets the style. (Errors occurred if I tried to make it abstract.)

public class MyChildWindow : ChildWindow
{
    public static readonly DependencyProperty IsBusyProperty = DependencyProperty.Register("IsBusy", typeof(bool), typeof(MyChildWindow), null);
    public static readonly DependencyProperty BusyContentProperty = DependencyProperty.Register("BusyContent", typeof(object), typeof(MyChildWindow), null);

    public bool IsBusy
    {
        get { return (bool)GetValue(IsBusyProperty); }
        set { SetValue(IsBusyProperty, value); }
    }

    public object BusyContent
    {
        get { return GetValue(BusyContentProperty); }
        set { SetValue(BusyContentProperty, value); }
    }

    public MyChildWindow()
    {
        this.Style = Application.Current.Resources["MyChildWindowStyle"] as Style;
    }
}

Make sure to add a new ChildWindow, and change the <controls:ChildWindow to <gs:MyChildWindow (same with the code-behind).

Finally, update the static SetBusyIndicator method:

public static void SetBusyIndicator(string busyText, Uri busyImage)
{
    var op = VisualTreeHelper.GetOpenPopups().Where(o => o.Child is MyChildWindow);
    if (op.Any())
    {
        var bidc = new MyBusyIndicatorDataContext(busyText, busyImage);
        foreach (System.Windows.Controls.Primitives.Popup p in op)
        {
            var c = p.Child as MyChildWindow;
            c.BusyContent = bidc;
            c.IsBusy = true;
        }
    }
    else
    {
        BusyIndicator b = Current.RootVisual as BusyIndicator;
        if (b != null)
        {
            b.BusyContent = new MyBusyIndicatorDataContext(busyText, busyImage);
            b.IsBusy = true;
        }
    }
}

I'm not sure this is the most efficient, but it does seem to work nicely.


Solution

  • As you've got your application root visual set to the busy spinner you will not be able to change the Z-Index so it is higher than the ChildWindow. Your best bet would be to extend the ChildWindow control and add a busy spinner into it and then set IsBusy on the ChildWindow rather the root visual when you have windows open.

    Hope that helps.