WPF:如何显示“请等待”选项卡

时间:2011-03-01 09:25:37

标签: wpf multithreading tabcontrol

在我的TabControl包装器控件中,我想提供以下功能:

void AddTab(Func<object> tabContentGenerator)

该函数应添加一个带有“Please wait”内容的新TabItem,然后调用tabContentGenerator函数以使对象显示并用返回的对象替换该TabItem的内容。

我尝试在tabContentGenerator中实施对BackgrounWorker的调用。但是,tabContentGenerator函数通常会创建一个UserControl作为内容,这会在BackgroundWorker中调用时导致异常。您是否有另一个想法,如何实现“请等待”标签项所需的行为,该标签项后来被替换为真实内容(需要在STA线程中生成)?

3 个答案:

答案 0 :(得分:1)

您可以查看http://wpftoolkit.codeplex.com/wikipage?title=Extended%20WPF%20Toolkit%20Controls这显示了一个无限期的进度条。

答案 1 :(得分:1)

您可能已经想到需要在UI线程上构建框架对象,而不是在后台工作程序中构建框架对象。我认为这是问题的核心。

这看起来像某种插件框架,其中注入了tabContentGenerator。如果是这样,我将使用两个动作,一个执行长时间运行的工作,另一个执行创建控件。您的展开式TabControl将在DoWork中运行第一个,在WorkerCompleted中运行第二个。

例如(伪代码):

public void AddTab(Action backgroundAction, Func<FrameworkElement> constructUiAction)
{
    var tab = ...

    var worker = new BackgroundWorker();

    worker.DoWork += (sender, e) => { backgroundAction(); };

    worker.RunWorkerCompleted += (sender, e) => 
        { 
            var ui = constructUiAction(); 
            if (ui != null) tab.Content = ui;
        };


    worker.RunWorkerAsync();
}

另一个选项是让操作返回FrameworkElementFactory,然后用它通过ControlTemplate在UI线程上实例化GUI。 FrameworkElementFactory不是DispatcherObject,可以在非GUI线程上创建。从工厂创建UI更加困难,但如果客户端在XAML中的资源中指定控件模板,则可以从其可视树中获取FrameworkFactoryElement(例如((ControlTemplate)FindResource("MyTemplate")).VisualTree)。

public void AddTab(Func<FrameworkElementFactory> tabContentGenerator)
{
    var tab = ...

    var worker = new BackgroundWorker();

    FrameworkElementFactory uiFactory = null;

    worker.DoWork += (sender, e) => { uiFactory = tabContentGenerator(); }

    worker.RunWorkerCompleted += (sender, e) => 
        { 
            if (uiFactory != null)
            {
                var template = new ControlTemplate();
                template.VisualTree = uiFactory;
                template.Seal();
                tab.Content = template.LoadContent();
            };
        }

    worker.RunWorkerAsync();
}

答案 2 :(得分:-1)

执行此操作的一种简单方法是向视图模型添加Waiting属性,创建在标签等待时使用的UI元素,并在内容上添加样式,例如:

<Grid>
  <DockPanel>
     <DockPanel.Style>
        <Style TargetType="DockPanel">
           <Setter Property="Visibility" Value="Visible"/>
           <DataTrigger Binding="{Binding Waiting}" Value="True">
              <Setter Property="Visibility" Value="Collapsed"/>
           </DataTrigger>
        </Style>
        <! -- normal content goes here -->
  </DockPanel>
  <DockPanel>
     <DockPanel.Style>
        <Style TargetType="DockPanel">
           <Setter Property="Visibility" Value="Collapsed"/>
           <DataTrigger Binding="{Binding Waiting}" Value="True">
              <Setter Property="Visibility" Value="Visible"/>
           </DataTrigger>
        </Style>
        <! -- waiting content goes here -->
  </DockPanel>
</Grid>

在调用BackgroundWorker之前,启动Waiting的命令应将PropertyChanged更新为true(并使对象引发DoWork)。 BackgroundWorker的{​​{1}}事件处理程序将RunWorkerCompleted设置为false。

您会注意到我没有使用任何Warning方法来创建我的标签。在大多数情况下,您永远不应该编写直接创建WPF对象的代码。以声明方式执行它会更有效率。当我学习WPF时,我发现自己说了很多,“我需要在代码中创建它,因为我不能在XAML中创建X.”对此的正确答案是学习如何在XAML中执行X,因为无论X是什么,你几乎可以肯定。