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

前端 未结 2 1995
孤街浪徒
孤街浪徒 2020-12-02 00:11

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

相关标签:
2条回答
  • 2020-12-02 00:52

    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();
        }
    
    0 讨论(0)
  • 2020-12-02 01:11

    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();
    }
    
    0 讨论(0)
提交回复
热议问题