【问题标题】:ReportProgressChanged is not updating UIReportProgressChanged 未更新 UI
【发布时间】:2014-03-18 22:20:06
【问题描述】:

我正在尝试使用多线程更新可写位图,但是我无法让 ProgressChanged 事件触发 UI 的任何更改。它虽然在边缘,因为一旦 RunWorkerCompleted 事件被称为 UI 更新。我是否错过了了解 ProgressChanged 事件的目的或发生了什么?

progresschanged的代码如下:

    private void BgWorkerMap_ProgressChanged_Handler(object sender, ProgressChangedEventArgs progressChangedArgs)
    {
        TestBitmap[7].Wb.AddDirtyRect(new Int32Rect(args[7].BgWorkerProperties.ProccessedX, args[7].BgWorkerProperties.ProccessedY, 256, 256));
        TestBitmap[7].Wb.Unlock();
        Src = TestBitmap[7].Wb;
        TestBitmap[7].Wb.Lock();
    }

这是我使用的完整代码。如果您尝试一下,您将在 3 秒后看到 UI 将被更新(添加了一个线程睡眠 1000 毫秒/循环)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;

namespace Testmultithredabitmap
{


public partial class MainWindow : INotifyPropertyChanged
{
    private readonly BackgroundWorker worker;
    private readonly Random random = new Random();
    private WriteableBitmap src;
    public WriteableBitmap Src
    {
        get { return src; }
        set
        {
            if (Equals(value, src)) return;
            src = value;
            NotifyPropertyChanged("Src");
        }
    }

    private class WorkerArgs
    {
        public int Width { get; set; }
        public int Height { get; set; }
        public IntPtr BackBuffer { get; set; }
    }

    WorldMapFloor[] TestBitmap = new WorldMapFloor[14];

    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        Src = new WriteableBitmap(256, 256, 96, 96, PixelFormats.Bgr32, null);

        for (int i = 0; i < TestBitmap.Length; i++)
        {
            TestBitmap[i] = new WorldMapFloor(i);

            TestBitmap[i].Wb.Lock();
        }

        worker = new BackgroundWorker();

        worker.WorkerReportsProgress = true;

        worker.DoWork += WorkerOnDoWork;
        worker.ProgressChanged += BgWorkerMap_ProgressChanged_Handler;
        worker.RunWorkerCompleted += WorkerOnRunWorkerCompleted;
        worker.RunWorkerAsync(TestBitmap);

    }

    private void WorkerOnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs runWorkerCompletedEventArgs)
    {
        TestBitmap[7].Wb.AddDirtyRect(new Int32Rect(0, 0, 256 * 3, 256));
        TestBitmap[7].Wb.Unlock();
        Src = TestBitmap[7].Wb;
        //TestBitmap[7].Wb.Lock();
    }

    private void WorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
    {
        WorldMapFloor[] args = (WorldMapFloor[])doWorkEventArgs.Argument;

        var width = args[7].BgWorkerProperties.Stride;

        int index = 0;

        while (true)
        {
            int startXCord = (index) * 256;
            int startYCord = (0) * 256;

            int filrStartIndexInBuffer = startXCord + startYCord * 256;

            args[7].BgWorkerProperties.ProccessedX = startXCord;
            args[7].BgWorkerProperties.ProccessedY = startYCord;

            Thread.Sleep(1000);

            unsafe
            {
                var buffer = (int*)args[7].BgWorkerProperties.pBackBuffer;
                for (var x = 0; x < 256; ++x)
                {
                    for (var y = 0; y < 256; ++y)
                    {
                        buffer[filrStartIndexInBuffer + x + y * (width / 4)] = random.Next();
                    }
                }
            }
            worker.ReportProgress(0, args);
            ++index;
            if (index == 3) break;
        }
    }

    private void BgWorkerMap_ProgressChanged_Handler(object sender, ProgressChangedEventArgs progressChangedArgs)
    {
        TestBitmap[7].Wb.AddDirtyRect(new Int32Rect(args[7].BgWorkerProperties.ProccessedX, args[7].BgWorkerProperties.ProccessedY, 256, 256));
        TestBitmap[7].Wb.Unlock();
        Src = TestBitmap[7].Wb;
        TestBitmap[7].Wb.Lock();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class BgWorkerMapProperties
    {
        public int ProccessedX;
        public int ProccessedY;

        public IntPtr pBackBuffer;
        public int Stride;


        public BgWorkerMapProperties(WriteableBitmap wb, int floorZ)
        {
            // Get a pointer to the back buffer. 
            pBackBuffer = wb.BackBuffer;
            Stride = wb.BackBufferStride;


        }
    }

    public class WorldMapFloor
    {
        public WriteableBitmap Wb = new WriteableBitmap(256 * 8, 256 * 8, 96, 96, PixelFormats.Bgr32, null);

        public BgWorkerMapProperties BgWorkerProperties;

        public string[] Files = null;

        public WorldMapFloor(int floorZ)
        {
            BgWorkerProperties = new BgWorkerMapProperties(Wb, floorZ);
        }

    }
}
}

在此先感谢一位绝望的 WPF 程序员。

【问题讨论】:

  • 从第一次读取开始,Src 指向一个立即再次锁定的位图,防止屏幕更新。我认为您将无法避免copying the pixels
  • 完美无瑕!我现在开始工作了!非常感谢!

标签: c# wpf backgroundworker


【解决方案1】:

通过 Henk Holterman 的评论,我设法让它正确更新,整个原因似乎是你应该只更新 1 个单个位图,但是因为我想将不同的位图存储为一个 chache 并快速交换它们。我所要做的就是将后台缓冲区数据复制到“主位图”

progresschange 事件应该是这样的:

private void BgWorkerMap_ProgressChanged_Handler(object sender, ProgressChangedEventArgs progressChangedArgs)
{
        Src.Lock();

        int size = Src.BackBufferStride * Src.PixelHeight;

        // Copy the bitmap's data directly to the on-screen buffers
        // Method is DLL imported from kernel32.dll
        CopyMemory(Src.BackBuffer, TestBitmap[7].Wb.BackBuffer, size);

        Src.AddDirtyRect(new Int32Rect(0, 0, 256 * 3, 256));

        Src.Unlock();
}

在我的例子中,你甚至可以移除 workercomplete 事件,因为在 UI 上呈现位图数据之后什么都没有做。

【讨论】:

    【解决方案2】:

    在我看来,除了 0 之外,您似乎从未报告任何其他进度百分比。当工作人员完成其工作时,它将报告 100%。

    您必须在流程的不同点调用 worker.ReportProgress(percentage)。 You can read more here

    【讨论】:

    • 他不关心完成的百分比,只关心 ProgressChanged 处理程序触发。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-27
    • 1970-01-01
    • 1970-01-01
    • 2019-02-23
    • 1970-01-01
    相关资源
    最近更新 更多