在我的TabControl
包装器控件中,我想提供以下功能:
void AddTab(Func<object> tabContentGenerator)
该函数应添加一个带有“Please wait”内容的新TabItem
,然后调用tabContentGenerator
函数以使对象显示并用返回的对象替换该TabItem的内容。
我尝试在tabContentGenerator
中实施对BackgrounWorker
的调用。但是,tabContentGenerator
函数通常会创建一个UserControl
作为内容,这会在BackgroundWorker
中调用时导致异常。您是否有另一个想法,如何实现“请等待”标签项所需的行为,该标签项后来被替换为真实内容(需要在STA线程中生成)?
答案 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是什么,你几乎可以肯定。