0

我正在尝试使包含图像的 WPF 窗口连续显示 USB 相机捕获的帧。

在我的代码中,ViewModel 实例化了一个作为参数CameraServiceClass传递的字段。然后,当事件被触发时,该字段被设置,但我不知道如何通知属性更改,因为事件是在内部触发和处理的。_framerefNewFrameCameraViewModel.Frame_camera_service

问题是:

  1. 我应该使用这样的ref参数吗?
  2. CameraServiceClass向 中添加一个事件,在类中收听它CameraViewModel,并通过提高Frame属性更改来处理它是一个好主意吗?如果是,我会怎么做?
  3. 本身是否应该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);         
    }
}

和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;
    }
}
4

3 回答 3

1

我会在您的 CameraServiceClass 中创建一个事件,并在捕获新帧时引发该事件。然后在您的视图模型中简单地收听该事件并对其做出反应。

然后,当事件触发时,您可以将视图模型中的属性设置为模型中的框架,这将调用 PropertyChanged 并更新 UI。

而不是 ref 参数,您当前正在使用

对于你的第三个问题,我不会在论点中传递框架,而是让听众自己得到它。

于 2013-10-24T18:10:04.813 回答
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; }
    }
}

视图模型:

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;
    }

}
于 2013-10-24T18:52:30.953 回答
0

在您的视图类中,您将图像源绑定到属性框架,但我在您的视图模型类中看不到该属性。它在那里并且没有显示在您的代码段中吗?您应该有一个属性,并且您的视图模型应该实现 PropertyChanged。然后当你得到事件时,改变属性,视图就会接受改变

于 2013-10-24T17:45:11.390 回答