WPF - 渲染"实时"最佳实践

era*_*zap 5 c# wpf multithreading rendering

实施说明:

在每个CompositionTarget.Rendering事件中,我使用Writablebitmap绘制4条相邻的线条(这是"实时"线图的绘图仪).

问题:

这很有用,直到UI-Thread看起来很忙,然后下一个CompositionTarget.Rendering 事件需要更长时间才能触发.

题 :

是否有一些机制来保持一个恒定的渲染间隔,优先于任何其他UI操作?

可能的灵魂:

我正在考虑创建一个HostVisual以及如何为它分配一个DispatcherTimer,
这种方法会起作用吗?

想到的任何其他方法?

提前致谢 .

Zar*_*rat 4

如果您已经渲染成 WritableBitmap,为什么不在 ThreadPool 上分拆一个异步任务呢?WritableBitmap 支持来自不同线程的更新,例如检查此问题或类文档备注上的信息。或者,如果您不喜欢线程池,则可以运行您自己的线程。这样您就可以控制时间,并且除了同步图像之外不需要依赖 UI 线程执行其他任何操作。

这是我刚刚编写的一些粗略的示例代码,应该可以让您了解我的意思:

public partial class MainWindow : Window
{
    private WriteableBitmap mImage;
    private bool mShutdown;
    private object mUpdateLock = new object();
    private IntPtr mBuffer;
    private int mStride;

    public MainWindow()
    {
        InitializeComponent();

        mImage = new WriteableBitmap(10, 2, 96, 96, PixelFormats.Bgr32, null);
        wImage.Source = mImage;

        Closed += delegate {
            CompositionTarget.Rendering -= CompositionTarget_Rendering;
            lock (mUpdateLock)
            {
                mShutdown = true;
                mImage.Unlock();
            }
        };

        mImage.Lock();
        mBuffer = mImage.BackBuffer;
        mStride = mImage.BackBufferStride;
        CompositionTarget.Rendering += CompositionTarget_Rendering;

        UpdateAsync();
    }

    private void CompositionTarget_Rendering(object sender, EventArgs e)
    {
        lock (mUpdateLock)
        {
            // for a large image you can optimize that by only updating the parts that changed,
            // collect dirty-areas in a list or something (while under the lock of course!)
            mImage.AddDirtyRect(new Int32Rect(0, 0, 10, 2));
            mImage.Unlock();
            mImage.Lock();

            // I don't know if these can changes, but docs say to acquire them after locking ...
            mBuffer = mImage.BackBuffer;
            mStride = mImage.BackBufferStride;
        }
    }

    private async void UpdateAsync()
    {
        int x = 0;
        int y = 0;
        int color = 0xFF0000;

        for (; ;)
        {
            lock (mUpdateLock)
            {
                if (mShutdown)
                    return;

                // feel free to do 'unsafe' code here if you know what you're doing
                Marshal.WriteInt32(new IntPtr(mBuffer.ToInt64() + x * 4 + y * mStride), color);
            }

            if (++x == 10)
            {
                x = 0;
                if (++y == 2)
                {
                    y = 0;
                    color ^= 0xFF00FF;
                }
            }

            await Task.Delay(500).ConfigureAwait(false);
        }
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Thread.Sleep(2000);
    }
}
Run Code Online (Sandbox Code Playgroud)

以及对应的XAML

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Content="Slow" HorizontalAlignment="Left" VerticalAlignment="Top" Click="Button_Click"/>
        <Image x:Name="wImage"/>
    </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)