好的,我有一个加载动画,在填充大型DataTable时运行,让用户知道该程序尚未冻结。我有动画工作正常,但它也冻结了DataTable也在更新。有没有办法拥有多个UI线程,以便在DataTable加载信息时动画将继续运行?
编辑:当前代码如下。
private void CreateFileTable()
{
file_data = new DataSet();
data_table = new DataTable();
file_data.Tables.Add(data_table);
DataColumn tempCol = new DataColumn("File Name", typeof(string));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Ext", typeof(string));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Size", typeof(string));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Created", typeof(Label));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Modified", typeof(Label));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Accessed", typeof(Label));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Location", typeof(string));
data_table.Columns.Add(tempCol);
File_List.ItemsSource = file_data.Tables[0].DefaultView;
}
private void PopulateDirectories(string[] directories)
{
for (int i = 0; i < directories.Length; i++)
{
DirectoryInfo tempDirInfo = new DirectoryInfo(directories[i]);
bool isSystem = ((tempDirInfo.Attributes & FileAttributes.System) == FileAttributes.System);
if (!isSystem)
{
DataRow tempRow = data_table.NewRow();
tempRow["File Name"] = tempDirInfo.Name;
tempRow["Ext"] = "";
tempRow["Size"] = "";
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempDirInfo.CreationTime.ToLongDateString() + ", " + tempDirInfo.CreationTime.ToLongTimeString();
tempRow["Created"] = tempLabel;
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempDirInfo.LastWriteTime.ToLongDateString() + ", " + tempDirInfo.LastWriteTime.ToLongTimeString();
tempRow["Modified"] = tempLabel;
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempDirInfo.LastAccessTime.ToLongDateString() + ", " + tempDirInfo.LastAccessTime.ToLongTimeString();
tempRow["Accessed"] = tempLabel;
tempRow["Location"] = tempDirInfo.FullName;
data_table.Rows.Add(tempRow);
}
}
}
private void PopulateFiles(string[] files)
{
for (int i = 0; i < files.Length; i++)
{
FileInfo tempFileInfo = new FileInfo(files[i]);
bool isSystem = ((File.GetAttributes(files[i]) & FileAttributes.System) == FileAttributes.System);
if (!isSystem)
{
DataRow tempRow = data_table.NewRow();
tempRow["File Name"] = tempFileInfo.Name;
tempRow["Ext"] = tempFileInfo.Extension;
int fileSize = (int)tempFileInfo.Length;
if (fileSize > 1048576)
{
tempRow["Size"] = "" + fileSize / 1048576 + " MB";
}
else if (fileSize > 1024)
{
tempRow["Size"] = "" + fileSize / 1024 + " KB";
}
else
{
tempRow["Size"] = "" + fileSize + " B";
}
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempFileInfo.CreationTime.ToLongDateString() + ", " + tempFileInfo.CreationTime.ToLongTimeString();
tempRow["Created"] = tempLabel;
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempFileInfo.LastWriteTime.ToLongDateString() + ", " + tempFileInfo.LastWriteTime.ToLongTimeString();
tempRow["Modified"] = tempLabel;
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempFileInfo.LastAccessTime.ToLongDateString() + ", " + tempFileInfo.LastAccessTime.ToLongTimeString();
tempRow["Accessed"] = tempLabel;
tempRow["Location"] = tempFileInfo.DirectoryName;
data_table.Rows.Add(tempRow);
}
}
}
private string GetSelectedPath(TreeViewItem selectedNode)
{
return selectedNode.Tag as string;
}
private void PopulateFileList()
{
PopulateDirectories(Directory.GetDirectories(GetSelectedPath((TreeViewItem)Dir_Tree.SelectedItem)));
PopulateFiles(Directory.GetFiles(GetSelectedPath((TreeViewItem)Dir_Tree.SelectedItem)));
}
private void UpdateFileList()
{
LoadingWheel.Visibility = System.Windows.Visibility.Visible;
CreateFileTable();
PopulateFileList();
TxtFoundCount.Text = "Files/Folders Found: " + File_List.Items.Count;
LoadingWheel.Visibility = System.Windows.Visibility.Hidden;
}
我尝试过使用BackgroundWorker并在其中调用UpdateFileList()方法,但我没有运气。
编辑:以下是我的BackgroundWorker代码。
private BackgroundWorker bgWorker1;
private void InitializeBackgroundWorker()
{
bgWorker1 = new BackgroundWorker();
bgWorker1.DoWork += new DoWorkEventHandler(bgWorker1_DoWork);
bgWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker1_RunWorkerCompleted);
}
private void bgWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (Dispatcher.CheckAccess())
{
PopulateFileList();
}
else
{
Dispatcher.Invoke(new Action(() => PopulateFileList()));
}
}
private void bgWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
TxtFoundCount.Text = "Files/Folders Found: " + File_List.Items.Count;
LoadingWheel.Visibility = System.Windows.Visibility.Hidden;
}
private void UpdateFileList()
{
LoadingWheel.Visibility = System.Windows.Visibility.Visible;
CreateFileTable();
bgWorker1.RunWorkerAsync();
}
我在TreeView上有一个SelectionChanged事件,它调用InitializeBackgroundWorker()和UpdateFileList()。列表仍然加载,我没有错误,但加载动画永远不会显示。
非常感谢任何帮助。
感谢。
答案 0 :(得分:10)
只有一个UI线程。您需要做的是在不同的线程上加载DataTable中的数据。
如果要在整个过程中(直接或通过ProgressBar或其他某种机制)显示DataTable加载的进度,BackgroundWorker是一种相当直接的方法。
更新:非常简单的后台工作者示例
这是一个相当简单的例子。它为一个集合添加了100个随机数,在每个集合之间暂停一段时间以模拟一个长时间加载过程。您可以将其剪切并粘贴到您自己的测试项目中,以确保其正常工作。
需要注意的是,繁重的工作(需要一段时间的工作)是在DoWork中完成的,而所有UI更新都在ProgressChanged和RunWorkerCompleted中完成。实际上,在DoWork处理程序中创建了一个单独的列表(数字),因为全局mNumbers集合位于UI线程上,并且无法在DoWork处理程序中进行交互。
<强> XAML 强>
<Button x:Name="btnGenerateNumbers"
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="Generate Numbers" />
C#Code-Behind
BackgroundWorker bgWorker = new BackgroundWorker();
ObservableCollection<int> mNumbers = new ObservableCollection<int>();
public Window1()
{
InitializeComponent();
bgWorker.DoWork +=
new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.ProgressChanged +=
new ProgressChangedEventHandler(bgWorker_ProgressChanged);
bgWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
bgWorker.WorkerReportsProgress = true;
btnGenerateNumbers.Click += (s, e) => UpdateNumbers();
this.DataContext = this;
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progress.Visibility = Visibility.Collapsed;
lstItems.Opacity = 1d;
btnGenerateNumbers.IsEnabled = true;
}
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
List<int> numbers = (List<int>)e.UserState;
foreach (int number in numbers)
{
mNumbers.Add(number);
}
progress.Value = e.ProgressPercentage;
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
Random rnd = new Random();
List<int> numbers = new List<int>(10);
for (int i = 1; i <= 100; i++)
{
// Add a random number
numbers.Add(rnd.Next());
// Sleep from 1/8 of a second to 1 second
Thread.Sleep(rnd.Next(125, 1000));
// Every 10 iterations, report progress
if ((i % 10) == 0)
{
bgWorker.ReportProgress(i, numbers.ToList<int>());
numbers.Clear();
}
}
}
public ObservableCollection<int> NumberItems
{
get { return mNumbers; }
}
private void UpdateNumbers()
{
btnGenerateNumbers.IsEnabled = false;
mNumbers.Clear();
progress.Value = 0;
progress.Visibility = Visibility.Visible;
lstItems.Opacity = 0.5;
bgWorker.RunWorkerAsync();
}
答案 1 :(得分:3)
我写了一个小测试程序,它显示了Dispatcher类的用法。它只需要一个WPF-Window和一个名为“listBox”的ListBox。应该很容易将此解决方案应用于您的问题。
public void Populate() {
// for comparison, freezing the ui thread
for (int i = 0; i < 1000000; i++) {
listBox.Items.Add(i);
}
}
private delegate void AddItemDelegate(int item);
public void PopulateAsync() {
// create a new thread which is iterating the elements
new System.Threading.Thread(new System.Threading.ThreadStart(delegate() {
// inside the new thread: iterate the elements
for (int i = 0; i < 1000000; i++) {
// use the dispatcher to "queue" the insertion of elements into the UI-Thread
// DispatcherPriority.Background ensures Animations have a higher Priority and the UI does not freeze
// possible enhancement: group the "jobs" to small units to enhance the performance
listBox.Dispatcher.Invoke(new AddItemDelegate(delegate(int item) {
listBox.Items.Add(item);
}), System.Windows.Threading.DispatcherPriority.Background, i);
}
})).Start();
}