将图像绘制到 Window 中并更新它

Drawing image into a Window and update it

我有一个不安全的 class,它生成一个转换为 ToImageSource 的位图,以便绘制到 Window。位图本身包含一个经常更新的正弦文本,我希望它从左到右 "move"(选取框样式?)。无论如何,它在 WinForm 中工作得很好,但我坚持使用 WPF Window。

以下是一些代码示例:

public AboutWindow()
{
    InheritanceBehavior = InheritanceBehavior.SkipAllNow;
    InitializeComponent();
    Initialize();
}

protected override void OnRender(DrawingContext drawingContext)
{
    base.OnRender(drawingContext);

    drawingContext.DrawImage(bitmapRender.WindowBitmap, drawingArea);

    if (!worker.IsBusy) 
        worker.RunWorkerAsync(); // BackgroundWorker in charge of updating the bitmap
}

void DispatcherTimerRender_Tick(object sender, EventArgs e) => InvalidateVisual();

我的问题是:Window 上没有显示任何内容,调用 InvalidateVisual() 的 DispatchedTimer 导致此异常:

System.InvalidOperationException: 'Cannot use a DependencyObject that belongs to a different thread than its parent Freezable.'

我看过其他线程,我知道 WPF 是一个保留的绘图系统,但我还是很想实现它。

关于实现此目的的"best"方法有什么建议吗?

任何有用的 explanation/link 将不胜感激。

[编辑]

<Window x:Class="CustomerManagement.View.AboutWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
       mc:Ignorable="d" Height="450" WindowStartupLocation="CenterScreen" Width="800" ResizeMode="NoResize" AllowsTransparency="True" WindowStyle="None">
    <Grid KeyDown="Grid_KeyDown">
        <Image Width="800" Height="450" Source="{Binding 'Image'}" />
    </Grid>
</Window>

您应该使用一个 Image 元素,其 Source 属性 绑定到视图模型中的 ImageSource 属性。这是基于 MVVM 架构模式的 "standard" 方式,因此也是 "best" 方式 - 在我看来。

<Image Source="{Binding Image}"/>

视图模型可能如下所示:

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private ImageSource image;
    public ImageSource Image
    {
        get { return image; }
        set
        {
            image = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Image)));
        }
    }
}

并且它的一个实例将被分配给 window:

的 DataContext
public AboutWindow()
{
    InitializeComponent();

    var vm = new ViewModel();
    DataContext = vm;
}

为了测试它,下面的代码对目录中的图像文件进行幻灯片放映。您也可以分配任何其他 ImageSource - 例如一个 DrawingImage - 到图像 属性.

var imageFiles = Directory.GetFiles(..., "*.jpg");
var index = -1;
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };

timer.Tick += (s, e) =>
{
    if (++index < imageFiles.Length)
    {
        vm.Image = new BitmapImage(new Uri(imageFiles[index]));
    }
    else
    {
        timer.Stop();
    }
};

timer.Start();