如何使模型层中的事件通知视图模型中的属性已更改
本文关键字:模型 属性 视图 事件 何使 通知 | 更新日期: 2023-09-27 18:36:42
我正在尝试使包含图像的WPF窗口连续显示USB相机捕获的帧。
在我的代码中,ViewModel 实例化CameraServiceClass
将其_frame
字段作为ref
参数传递。然后,当触发NewFrame
事件时,设置了该字段,但我不知道如何通知CameraViewModel.Frame
属性更改,因为该事件是在_camera_service
内部触发和处理的。
问题是:
- 我应该使用这样的
ref
参数吗?
将 - 事件添加到
CameraServiceClass
,在CameraViewModel
类中侦听它,并通过提高属性更改来处理它Frame
是一个好主意吗?如果是,我该怎么做? CameraServiceClass
本身是否应该通知自定义 FrameReceived 事件并在事件参数中传递位图本身?如果是,我该怎么做?
我的课程是:
<Window x:Class="CameraGUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ap="clr-namespace:CameraGUI"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<ap:CameraViewModel/>
</Window.DataContext>
<Grid>
<Viewbox Stretch="Uniform">
<Image Source="{Binding Frame, Mode=OneWay}" />
</Viewbox>
</Grid>
</Window>
相机视图型号:
class CameraViewModel : ViewModelBase {
System.Drawing.Bitmap _frame_camera;
public System.Windows.Media.Imaging.BitmapImage Frame {
get {
if (_frame_camera != null) {
using(MemoryStream ms = new MemoryStream())
{
_frame_camera.Save(ms, ImageFormat.Bmp);
ms.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = ms;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
} else return null;
}
}
CameraServiceClass _camera_service;
// CONSTRUTOR
public CameraViewModel() {
_camera_service = new CameraServiceClass(ref _frame_camera);
}
}
和相机服务类:
public class CameraServiceClass
{
System.Drawing.Bitmap _frame;
VideoCaptureDevice videoSource;
// CONSTRUTOR
public CameraServiceClass(ref System.Drawing.Bitmap bitmap) {
_frame = bitmap;
var videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString);
videoSource.NewFrame += new NewFrameEventHandler(video_NewFrame);
videoSource.Start();
}
private void video_NewFrame (object sender, NewFrameEventArgs eventArgs) {
System.Drawing.Bitmap bmp = (System.Drawing.Bitmap)eventArgs.Frame.Clone();
_frame = bmp;
}
}
我会在你的 CameraServiceClass 中创建一个事件,并在捕获新帧时引发它。然后在您的视图中,只需侦听该事件并对其进行响应即可。
然后,当触发事件时,可以将视图模型中的属性设置为模型中的帧,这将调用 PropertyChanged 并更新 UI。
而不是引用参数,您当前正在使用
对于你的第三个问题,我不会在论证中传递框架,而是让听众自己理解。
在您的视图类中,您将图像源绑定到属性 Frame,但我在您的视图模型类中看不到该属性。它在那里并且没有显示在您的代码段中吗? 你应该有一个属性,你的视图模型应该实现属性更改。然后,当您获取事件时,更改属性,视图将选取更改
按照 MSDN 中的"如何:在类中实现事件"教程进行操作后,按照第二个过程(使用自定义数据实现事件),我开始工作。
原则是创建一个包含数据的自定义 EventArgs,并创建一个适当的处理程序(委托)。我以前没有与事件、代表和处理程序合作过,这就是这里没有进展的原因之一......
另一种方法是使用 Peter Davidsen 的建议,即在服务类中拥有一个公共属性,并让侦听器自己获取它,但我认为这个细节是一个偏好问题(使用链接的 MSDN 教程的第一个过程,很可能更容易实现)。
新的工作代码:
MainWindow.xaml(现在在代码隐藏中有一个值转换器(未显示)):
<Window x:Class="CameraGUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ap="clr-namespace:CameraGUI"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<ap:CameraViewModel/>
</Window.DataContext>
<Window.Resources>
<ap:BitmapToSource x:Key="BitmapToSource"/>
</Window.Resources>
<Grid>
<Viewbox Stretch="Uniform">
<Image Source="{Binding Frame, Mode=OneWay, Converter={StaticResource BitmapToSource}}" />
</Viewbox>
</Grid>
</Window>
CameraService.cs 文件(现在带有委托、处理程序和自定义"EventArgs"类)
public class CameraServiceClass
{
VideoCaptureDevice videoSource;
// CONSTRUTOR
public void Start() {
var videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString);
videoSource.NewFrame += new NewFrameEventHandler(video_NewFrame);
videoSource.Start();
}
private void video_NewFrame (object sender, NewFrameEventArgs eventArgs) {
System.Drawing.Bitmap bmp = (System.Drawing.Bitmap)eventArgs.Frame.Clone();
OnNovoFrame(new NovoFrameArgs(bmp));
}
public event NovoFrameEventHandler NovoFrame;
protected void OnNovoFrame(NovoFrameArgs e) {
if (NovoFrame != null)
NovoFrame(this, e);
}
}
public delegate void NovoFrameEventHandler(object sender, NovoFrameArgs e);
public class NovoFrameArgs : EventArgs {
System.Drawing.Bitmap _frame;
public NovoFrameArgs(System.Drawing.Bitmap fr) {
this._frame = fr;
}
public System.Drawing.Bitmap Frame {
get { return _frame; }
set { _frame = value; }
}
}
视图模型:
class CameraViewModel : ViewModelBase {
CameraServiceClass _camera_service;
public System.Drawing.Bitmap Frame {
get { return _frame; }
set {
_frame = value;
RaisePropertyChanged(() => Frame);
}
}
System.Drawing.Bitmap _frame;
// CONSTRUTOR
public CameraViewModel() {
_camera_service = new CameraServiceClass();
_camera_service.NovoFrame += new NovoFrameEventHandler(_camera_service_NovoFrame);
_camera_service.Start();
}
void _camera_service_NovoFrame(object sender, NovoFrameArgs e) {
Frame = e.Frame;
}
}