我正在开发一个自定义的WPF框架元素,该元素写入WriteableBitmap,然后在元素Onender()中显示该位图.
由于写入WriteableBitmap可能会有点慢(由于我当时正在计算的算法),而且我需要显示其中36个元素,因此我想在后台线程上完成更新WriteableBitmap的工作.
所以我想出了以下方法,大大提高了性能.问题是,如果我只创建8个或更少的这些元素,它可以正常工作,但如果我创建更多,例如请求的36个,当您调整窗口大小时,前8个之后的所有元素都会闪烁?
你知道是什么导致了这种情况吗?
渲染器元素:
public class Renderer : FrameworkElement
{
private WriteableBitmap? _bitmap, _previousBitmap;
private long _pBackBuffer = 0;
private int _backBufferStride = 0, _backBufferWidth = 0, _backBufferHeight = 0;
private SemaphoreSlim _semaphore = new(1, 1);
private void ResizeBitmap()
{
int width = (int)ActualWidth;
int height = (int)ActualHeight;
if (width <= 0 || height <= 0)
return;
if (_bitmap == null || width != _bitmap.PixelWidth || height != _bitmap.PixelHeight)
{
_previousBitmap = _bitmap;
_bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Pbgra32, null);
_pBackBuffer = _bitmap.BackBuffer;
_backBufferStride = _bitmap.BackBufferStride;
_backBufferWidth = width;
_backBufferHeight = height;
// fill with blue for debugging purposes
byte[] data = new byte[_bitmap.BackBufferStride * _bitmap.PixelHeight];
for (int i = 0; i < _bitmap.PixelHeight; ++i)
{
for (int j = 0; j < _bitmap.PixelWidth; ++j)
{
data[i * _bitmap.BackBufferStride + j * sizeof(int) + 0] = 255;
data[i * _bitmap.BackBufferStride + j * sizeof(int) + 1] = 0;
data[i * _bitmap.BackBufferStride + j * sizeof(int) + 2] = 0;
data[i * _bitmap.BackBufferStride + j * sizeof(int) + 3] = 255;
}
}
_bitmap.WritePixels(new Int32Rect(0, 0, _bitmap.PixelWidth, _bitmap.PixelHeight), data, _bitmap.BackBufferStride, 0);
}
}
public void InvalidateRender() => WriteToBitmapInWorkerThread();
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
if (_bitmap == null)
return;
_semaphore.Wait();
_bitmap.Lock();
_bitmap.AddDirtyRect(new Int32Rect(0, 0, (int)_bitmap.Width, (int)_bitmap.Height));
_bitmap.Unlock();
drawingContext.DrawImage(_bitmap, new Rect(0, 0, ActualWidth, ActualHeight));
_semaphore.Release();
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
_semaphore.Wait();
ResizeBitmap();
_semaphore.Release();
WriteToBitmapInWorkerThread();
}
private void WriteToBitmapInWorkerThread()
{
// do some writing to the bitmap that is slow (so run on a worker thread)
Task.Run(() =>
{
_semaphore.Wait();
unsafe
{
int* pPointer = (int*)_pBackBuffer;
int stride = _backBufferStride / 4;
// simulate slowness
Thread.Sleep(10);
// render a gradient for demo purposes
for (int i = 0; i < _backBufferHeight; ++i)
{
byte x = (byte)(255d / _backBufferHeight * i);
for (int j = 0; j < _backBufferWidth; ++j)
{
pPointer[i * stride + j] = 255 << 24 | x << 16 | x << 8 | 255;
}
}
}
_semaphore.Release();
Dispatcher.BeginInvoke(DispatcherPriority.Render, () => InvalidateVisual());
});
}
}
主窗口 :
<Grid Background="Orange">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="2"
Padding="2"
BorderThickness="1"
BorderBrush="Red">
<local:Renderer />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
public partial class MainWindow : Window
{
public List<string> Items { get; } = Enumerable.Range(0, 36).Select(e => e.ToString()).ToList();
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}