WPF loading animation on a separate UI thread? (C#)

我只是一个虾纸丫 提交于 2019-11-27 09:30:32

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

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.

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.

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

I wrote a little test program which shows the use of the Dispatcher class. It just requires a WPF-Window and a ListBox with Name "listBox". Should be easy to apply this solution to your problem.

    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();
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!