C#
WPF
WriteableBitmap

【C#/WPF】WriteableBitmapで点を打って、テレビの放送終了後の砂嵐をつくる(Taskで処理)


タイマーでやる場合

こちらを参照。


Taskで処理を行うパターン

上の例では、UIとバインドしているプロパティへのアクセス'(MyWBitmap.WritePixels()の部分)'はUIスレッドでしか実施できないため、別スレッドで実施されるTaskを使用せず、タイマで処理をしていたが、

Taskを使った場合でも、UI周りの処理の時だけUIスレッドで処理してもらうということができる。

下記が、そのサンプル。

Dispatcherを使用し、UIスレッドに処理を渡している。

using System;

using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace WpfApp1
{
class ViewModel : BindingBase
{
public WriteableBitmap MyWBitmap { get; set; } = new WriteableBitmap(1280, 720, 96, 96, PixelFormats.Pbgra32, null);

Dispatcher MainViewDispatcher = null;

public ViewModel(Dispatcher dispatcher)
{
MainViewDispatcher = dispatcher;

Task.Run(() =>
{
while (true)
{
int width = (int)1280;
int height = (int)720;

// 計算用のバイト列の準備
int pixelsSize = (int)(width * height * 4);
byte[] pixels = new byte[pixelsSize];

Random rnd = new System.Random(); // インスタンスを生成
int rndMax = 256; // 0~256の乱数を取得

// バイト列に色情報を入れる
for (int i = 0; i < width * height; i++)
{
pixels[4 * i] = (byte)rnd.Next(rndMax); // blue;
pixels[4 * i + 1] = (byte)rnd.Next(rndMax); // green;
pixels[4 * i + 2] = (byte)rnd.Next(rndMax); // red;
pixels[4 * i + 3] = (byte)255; // alpha
}

// バイト列をBitmapImageに変換する
int stride = width * 4;

// TaskはUIスレッドと別のスレッドのため、ビットマップにバインドしたプロパティの操作はUIスレッドでやってもらう
// そのために、Dispatcherを使う
MainViewDispatcher.BeginInvoke(new Action(() =>
{
MyWBitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, stride, 0, 0);
}));

Thread.Sleep(33);
}
});
}
}
}

今回は、砂嵐の画面データをつくる処理がそれほど重い処理ではないので問題ないが、なにか重い処理をするような場合は、タイマーでやっているとUIが固まったように見えてしまうことが考えられる。

その場合、上記のように、TaskとDispatcherで、重い処理の部分を別スレッドで実施すればよい。