将图像绘制到 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();
我有一个不安全的 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:
的 DataContextpublic 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();