如何在WPF中的控件顶部显示进度条

10
在一个WPF UserControl中,我需要调用一个Web服务。我在单独的线程上进行此调用,但是我想通知用户调用可能需要一些时间。
WebMethod返回一个对象集合,并将其绑定到UC中的ListBox上。到目前为止,一切都很好...... 这部分工作得非常好。然而,我想在调用期间显示进度条(或任何其他类型的动画...)。该动画将位于ListBox控件的顶部并居中显示。
我尝试使用Adorner,它部分地起作用。但是,我必须在protected override void OnRender(DrawingContext drawingContext)中绘制所有控件... 我只想添加一个控件几秒钟...
有人有办法实现这个吗?
谢谢!
2个回答

10

不要使用adorner - 我的做法是使用两个独立的容器控件(通常是网格),它们占据屏幕上的同一区域。一个是我的“进度”控件,另一个是我的“内容”控件。默认情况下,我将进度控件的可见性设置为Collapsed,将内容控件的可见性设置为Visible。

如果您已经按照这种方式设置,当您启动与webservice的异步调用时,可以使进度控件可见,并将内容控件折叠起来。当webservice完成后,请使用Dispatcher.BeginInvoke更新UI,此时将进度控件切换回折叠状态,并将内容控件切换回可见状态。

我通常将进度控件设置为indeterminate。以下是一个示例;在这个示例中,我有一个名为ProgressGrid的独立UserControl,其中包含我的进度条。

    <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>

而在代码后台,只需像这样简单的东西:

    private void SignInCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        contentGrid.Visibility = Visibility.Collapsed;
        progressGrid.Visibility = Visibility.Visible;
    }

哇!太棒了!不过,使用你的解决方案,我该如何将 ProgressBar 控件居中放置在 ListBox 的中心位置?Canvas? - Martin
嗯 - 你可以让进度面板成为ListBox的子元素,并将列表框中的所有其他元素的可见性设置为折叠状态,当你不需要进度面板时,将其可见性设置为折叠状态。 - Rob
或者您可以将ListBox设置为另一个容器(比如一个Grid)的子级,进度面板则成为ListBox的同级元素——这样,您只需将进度面板的水平和垂直对齐方式设置为“居中”,在显示时它就会自动居中。 - Rob
我正在尝试实现您的想法,但是它们中没有一个完全符合我的需求。由于ListBox已经有项目,我无法添加子项... :( - Martin
1
@DaveInCaz,遗憾的是我不记得为什么没有使用装饰器(我简直不敢相信这个答案已经快10年了!)。我的猜测是,自那时以来,装饰器可能已经有所进步,现在可能值得再试一次。 - Rob
显示剩余2条评论

1

有一个技巧可以使用零高度的Canvas,可能会起作用。Chris Anderson的WPF书籍详细介绍了这一点以及它的工作原理,但大致如下:

  • 创建一个StackPanel
  • 将高度为“0”且具有较高z-index的Canvas添加到堆栈面板中
  • 将用户控件添加到堆栈面板中。

当您想要显示进度条时,请将其添加到零高度画布中。它将允许您将其定位在用户控件上方。Canvas允许您超出其边界。居中进度条只需要查看用户控件的尺寸并相应地设置进度条在Canvas上的位置即可。完成后从画布中删除进度条。

这里是一个简单的示例,使用了一个TextBox。它不完美,但展示了这个想法。单击按钮会在InkCanvas上方显示TextBox。

<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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接