views:

2385

answers:

2

Hello all,

In a WPF UserControl, I have to make to call to a WebService. I am making this call on a separate thread but I want to inform the user that the call may take some time.

The WebMethod returns me a collection of objects and I bind it to a ListBox in my UC. So far, so good... This part works really well. However, I want to display a progress bar (or an animation of any kind...) during the call. This animation would be on top and centered in the ListBox control.

I tried Adorner and it partially works. However, I have to draw all controls in protected override void OnRender(DrawingContext drawingContext)... I simply want to add a control for a couple of seconds...

Anybody has an idea of how I could achieve this?

Thanks!

+9  A: 

Don't go with the adorner - what I do is have two separate container controls (usually grids) that occupy the same area of the screen. One is my "progress" control, and the other is my "content" control. I set the visibility of the progress control to Collapsed and the visibility of the content control to Visible by default.

If you have it set up that way, when you start the asynchronous call to the webservice you can make the progress control visible and the content control collapsed. When the webservice finishes, have it use Dispatcher.BeginInvoke to update the UI, and at that point, switch the progress control back to collapsed and the content control back to visible.

I generally make the progress control indeterminate. Here is an example; in this, I have a separate UserControl called ProgressGrid that has my progress bar.

    <Grid x:Name="layoutRoot">
        <Grid x:Name="contentGrid" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Visible">
          <!-- snip -->
        </Grid>

        <controls:ProgressGrid x:Name="progressGrid" Text="Signing in, please wait..." Visibility="Collapsed"/>
    </Grid>

And in the code behind, just something simple like this:

    private void SignInCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        contentGrid.Visibility = Visibility.Collapsed;
        progressGrid.Visibility = Visibility.Visible;
    }
unforgiven3
Wow! Great answer! However, with your solution, how do I center the ProgressBar control in the center of the ListBox? Canvas?
Martin
Hmm - you could have the progress panel be a child of the ListBox, and set all the other elements within the list box's visibility to collapsed, and when you don't need the progress panel, set it's visibility to collapsed.
unforgiven3
unforgiven3
Thanks, though :-) One other note - I don't recommend a Canvas for positioning unless it's absolute positioning. WPF's rules of a alignment seem to break down within a Canvas. Like navigation in the Bermuda triangle, haha.
unforgiven3
I am trying to implement your ideas, but none of them exactly fit my needs. I am not able to add a child item to the ListBox since the ListBox already has items in it... :(
Martin
+1  A: 

There is a trick you can use with a zero height Canvas that might work. Chris Anderson's WPF book goes into detail on this and why it works, but it goes something like this.

  • create a StackPanel
  • add a Canvas with Height="0" and a high z-index to the stack panel
  • add your user control to the stack panel.

When you want to show the progress bar add it to the zero height canvas. It will allow you to position it over the user control. Canvas allows you to go beyond its borders. Centering the progress bar should just require looking at the dimensions of the user control and setting the position of the progress bar on the Canvas accordingly. Remove the progress bar from the canvas when you are done.

Here is a simple example that uses a TextBox. It's not perfect but it shows the idea. Clicking the button shows the TextBox on top of the InkCanvas

<DockPanel LastChildFill="True">
    <Button DockPanel.Dock="Top" Name="showButton" Click="showProgress">show</Button>
    <StackPanel DockPanel.Dock="Bottom">
        <Canvas Name="zeroHeight" Height="0"/>
        <InkCanvas Name="inky">
        </InkCanvas>
    </StackPanel>
</DockPanel>


private void showProgress(object sender, RoutedEventArgs e)
{
    TextBox box = new TextBox();
    box.Text = "on top";
    StackPanel.SetZIndex(zeroHeight, 8);
    zeroHeight.Children.Add(box);
    box.Width = 30;
    box.Height = 30;
    Canvas.SetLeft(box, 10);
    Canvas.SetTop(box, 10);
    Canvas.SetZIndex(box, 10);
}
Mike Two