我正在尝试制作一个包含图像的WPF窗口,连续显示USB摄像头拍摄的帧。
在我的代码中,ViewModel实例化CameraServiceClass
将_frame
字段作为ref
参数传递。然后,当触发NewFrame
事件时,该字段已设置,但我不知道如何通知CameraViewModel.Frame
属性更改,因为事件在_camera_service
内触发并处理。
问题是:
ref
参数吗?CameraServiceClass
,在CameraViewModel
类中收听,并通过提升Frame
属性来处理它是不是一个好主意?如果是的话,我该怎么做?CameraServiceClass
本身是否应该通知自定义FrameReceived事件并在事件args中传递Bitmap?如果是的话,我该怎么做?我的课程是:
<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>
Camera ViewModel:
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);
}
}
和CameraServiceClass:
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;
}
}
答案 0 :(得分:1)
我会在CameraServiceClass中创建一个事件,并在捕获新帧时提高该事件。然后在你的viewmodel中只需监听该事件并对其做出反应。
然后,当触发事件时,您可以在viewmodel中将属性设置为模型中的框架,该框架将调用PropertyChanged并更新UI。
您正在使用
,而不是参考参数对于你的第三个问题,我不会在参数中传递帧,而是让听众自己得到它。
答案 1 :(得分:0)
在您的视图类中,您将图像源绑定到属性Frame,但我没有在视图模型类中看到该属性。它在那里,只是没有显示在你的代码片段中吗?您应该有一个属性,您的视图模型应该实现PropertyChanged。然后,当您收到事件时,更改属性,视图将获取更改
答案 2 :(得分:0)
按照MSDN中的“如何:在类中实现事件”教程后,按照第二个过程(使用自定义数据实现事件),我完成了这项工作。
原则是创建一个包含数据的自定义EventArgs,并创建一个合适的处理程序(委托)。我之前没有与事件,代表和处理程序合作,这也是事情没有在这里发展的原因之一......
另一种方法是使用Peter Davidsen建议在服务类中拥有一个公共属性并让监听器自己获取它,但我认为这个细节是一个偏好的问题(最有可能更容易实现,使用第一个链接的MSDN教程的过程。)
新工作代码:
MainWindow.xaml(现在代码隐藏中有一个valueconverter(未显示)):
<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; }
}
}
ViewModel:
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;
}
}