WPF 在单独的 UI 线程上加载动画?(C#) [英] WPF loading animation on a separate UI thread? (C#)

查看:47
本文介绍了WPF 在单独的 UI 线程上加载动画?(C#)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,我有一个加载动画,它在填充一个大的 DataTable 时运行,让用户知道程序没有冻结.我的动画工作正常,但是在 DataTable 更新时它也冻结了.有没有办法让多个 UI 线程在 DataTable 加载信息时继续运行动画?

Okay, I have a loading animation that runs while a large DataTable is populated to let the user know that the program has not frozen. I have the animation working fine, but it freezes while the DataTable is updatingv as well. Is there some way to have multiple UI threads, so that the animation will continue to run while the DataTable is loading information?

当前代码如下.

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() 方法,但我没有任何运气.

I have tried using the BackgroundWorker and calling the UpdateFileList() method inside it, but I'm not having any luck.

下面是我的 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();
}

我在调用 InitializeBackgroundWorker() 和 UpdateFileList() 的 TreeView 上有一个 SelectionChanged 事件.列表仍在加载,我没有收到任何错误,但加载动画永远不会变得可见.

I have a SelectionChanged event on a TreeView that calls InitializeBackgroundWorker() and UpdateFileList(). The list loads still, and I get no errors, but the loading animation never becomes visible.

任何帮助将不胜感激.

谢谢.

推荐答案

只有一个 UI 线程.您需要做的是在不同的线程上加载 DataTable 中的数据.

There is only one UI thread. What you need to do is to load the data in the DataTable on a different thread.

如果您想在加载过程中显示 DataTable 的加载进度(直接,或者通过 ProgressBar 或其他一些机制),BackgroundWorker 是一种相当直接的方法.

If you want to show progress to the DataTable loading along the way (either directly, or through a ProgressBar or some other mechanism), the BackgroundWorker is a fairly straight-forward way to do that.

更新:非常简单的后台工作器示例
这是一个相当简单的例子.它将 100 个随机数添加到集合中,在每个数之间暂停线程一小段时间以模拟长时间的加载过程.您可以简单地将其剪切并粘贴到您自己的测试项目中,以查看它的效果.

UPDATE: Very Simple Background Worker example
Here is a fairly simple example. It adds 100 random numbers to a collection, pausing the thread for a short time between each to simulate a long loading process. You can simply cut and paste this into a test project of your own to see it work.

需要注意的是,繁重的工作(需要一段时间的东西)是在 DoWork 中完成的,而所有 UI 更新都是在 ProgressChanged 和 RunWorkerCompleted 中完成的.实际上,在DoWork handler中创建了一个单独的列表(numbers),因为全局mNumbers集合在UI线程上,无法在DoWork handler中交互.

The thing to notice is that the heavy lifting (the stuff that takes a while) is done in the DoWork, while all UI updates are done in ProgressChanged and RunWorkerCompleted. In fact, a separate list (numbers) is created in the DoWork handler because the global mNumbers collection is on the UI thread, and can't interact in the DoWork handler.

XAML

<Button x:Name="btnGenerateNumbers"
        Grid.Row="1"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Content="Generate Numbers" />

C# 代码隐藏

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

这篇关于WPF 在单独的 UI 线程上加载动画?(C#)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆