我有我的xaml:
<Window x:Class="Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Height="768" Width="1024" WindowStartupLocation="CenterScreen"
Title="{Binding Path=DisplayName}">
<xctk:BusyIndicator x:Name="BusyIndicator" IsBusy="{Binding IsBusy, UpdateSourceTrigger=PropertyChanged}" >
<TreeView Style="{StaticResource TableSchemaTreeViewStyle}" ItemContainerStyle="{StaticResource SchemaTreeViewStyle}" Margin="0,15,0,0"
x:Name="TreeViewSchema"
TreeViewItem.Expanded="TreeViewSchema_OnExpanded"
TreeViewItem.Selected="TreeViewSchema_OnSelected"
Grid.Row="2"
ItemsSource="{Binding CurrentProject.Tables, Converter={StaticResource TreeViewSortingConverter}, ConverterParameter=FullTableName}">
</TreeView>
</xctk:BusyIndicator>
</Window>
假设我在代码隐藏中运行的任务很长,它在UI线程上执行(对treeview进行长时间过滤,它可以有超过1000个表,每个表中有超过100列)。
让我说我迭代并为每个项目设置tableTreeViewItem.Visibility = Visibility.Collapsed;
。
我想要的:通过在此操作之前将其设置为true来显示BusyIndicator:
BusyIndicator.IsBusy = true;
。
问题:UI线程和绑定上的两个操作都无法按预期工作。我尝试了几件事:
BusyIndicator.IsBusy = true;
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
tableTreeViewItem.Visibility = Visibility.Collapsed;
}, CancellationToken.None, TaskCreationOptions.None, uiScheduler).ContinueWith(task => Dispatcher.Invoke(() =>
{
BusyIndicator.IsBusy = false;
}));
使用调度员:
BusyIndicator.IsBusy = true;
//long-running UI task
tableTreeViewItem.Visibility = Visibility.Collapsed;
BusyIndicator.IsBusy = false;
但它不起作用,任何想法如何解决它?
PSS
我做了一些更新,我决定抓取所有数据并存储哪个树视图项应该可见或隐藏。
所以我有用于存储过滤方法
的表,可见性和可见列的类 class TreeViewItemVisibilityTableContainer
{
private TreeViewItem _treeViewItem;
private TableModel _table;
private Visibility _visibility;
private List<ColumnModel> _visibleColumns;
public TableModel Table
{
get { return _table; }
set { _table = value; }
}
public TreeViewItem TreeViewItem
{
get { return _treeViewItem; }
set { _treeViewItem = value; }
}
public Visibility Visibility
{
get { return _visibility; }
set { _visibility = value; }
}
public List<ColumnModel> VisibleColumns
{
get { return _visibleColumns; }
set { _visibleColumns = value; }
}
}
现在我可以直接在UI线程上过滤所有这些工作人员:
System.Action filterTreeViewItemsVisibility = () => Dispatcher.Invoke(() =>
{
foreach (var item in itemsToFilter)
{
item.TreeViewItem.Visibility = item.Visibility;
var capturedItemForClosure = item;
if (item.Visibility == Visibility.Visible)
{
if (item.VisibleColumns.Any())
{
item.TreeViewItem.Items.Filter = item.TreeViewItem.Items.Filter =
treeViewItem =>
capturedItemForClosure.VisibleColumns.Any(
columnModel => columnModel.Equals(treeViewItem));
}
else
{
item.TreeViewItem.Visibility = Visibility.Collapsed;
}
}
}
});
IoC.Get<IBusyIndicatorHelper>().PerformLongrunningAction(filterTreeViewItemsVisibility, IoC.Get<IShellViewModel>());
但它仍然超级慢
答案 0 :(得分:4)
这是忙碌指标的解决方案。
显示解决方案的组件很少
BusyIndicator 用户控件
BusyIndicator.xaml - 非常简单
<UserControl x:Class="BusyIndicatorExample.BusyInidicator"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Visibility="Collapsed">
<Grid Background="#BFFFFFFF" >
<TextBlock Text="Loading data..." HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="#FF2C2C2C" FontSize="16" FontWeight="Bold" />
</Grid>
</UserControl>
<强> BusyIndicator.xaml.cs 强>
using System;
using System.Windows.Controls;
namespace BusyIndicatorExample
{
/// <summary>
/// Interaction logic for BusyInidcator.xaml
/// </summary>
public partial class BusyInidicator : UserControl
{
public BusyInidicator()
{
InitializeComponent();
}
显示指标的方法
public void Start()
{
this.Dispatcher.Invoke(new Action(delegate()
{
this.Visibility = System.Windows.Visibility.Visible;
}), System.Windows.Threading.DispatcherPriority.Normal);
}
隐藏指标的方法
public void Stop()
{
this.Dispatcher.Invoke(new Action(delegate()
{
this.Visibility = System.Windows.Visibility.Collapsed;
}), System.Windows.Threading.DispatcherPriority.Normal);
}
}
}
用于模拟非UI任务的AbortableBackgroundWorker
using System;
using System.ComponentModel;
using System.Threading;
namespace BusyIndicatorExample
{
/// <summary>
/// Abortable background worker
/// </summary>
public class AbortableBackgroundWorker : BackgroundWorker
{
//Internal Thread
private Thread workerThread;
protected override void OnDoWork(DoWorkEventArgs e)
{
try
{
base.OnDoWork(e);
}
catch (ThreadAbortException)
{
e.Cancel = true; //We must set Cancel property to true!
Thread.ResetAbort(); //Prevents ThreadAbortException propagation
}
}
public void Abort()
{
if (workerThread != null)
{
workerThread.Abort();
workerThread = null;
}
}
}
}
最后,模拟过程的 MainWindow
<强> MainWindow.xaml 强>
<Window x:Class="BusyIndicatorExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BusyIndicatorExample"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Start Data Loading" HorizontalAlignment="Left" Margin="63,42,0,0" VerticalAlignment="Top" Width="125" Height="28" Click="Button_Click"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="63,87,0,0" TextWrapping="Wrap" Text="{Binding DataString}" VerticalAlignment="Top" Width="412"/>
<local:BusyInidicator x:Name="busyIndicator" HorizontalAlignment="Left" Height="100" Margin="177,140,0,0" VerticalAlignment="Top" Width="300"/>
</Grid>
</Window>
MainWindow.xaml.cs - 这是应用程序代码
using System.ComponentModel;
using System.Windows;
namespace BusyIndicatorExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private AbortableBackgroundWorker _worker;
绑定到文本框的构造函数和公共属性
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private string _dataString = "No Data";
public string DataString
{
get { return _dataString; }
set {
if (_dataString != value)
{
_dataString = value;
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("DataString"));
}
}
}
按钮点击事件 - 初始化BackgroundWorker并启动它
private void Button_Click(object sender, RoutedEventArgs e)
{
if(_worker == null)
{
_worker = new AbortableBackgroundWorker();
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += _worker_DoWork;
_worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
}
if (!_worker.IsBusy)
_worker.RunWorkerAsync();
}
BackgroundWorker事件处理程序
RunWorkerCompleted更新数据字符串并隐藏指标。
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
DataString = "Data has been loaded";
busyIndicator.Stop();
}
DoWork显示指示符并进入睡眠线程5秒。
void _worker_DoWork(object sender, DoWorkEventArgs e)
{
DataString = "No Data";
busyIndicator.Start();
System.Threading.Thread.Sleep(5000);
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
希望这会有所帮助。根据需要修改代码以适应您的方案 可以下载完整的示例项目代码here