【发布时间】: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