안녕하세요, WPF MVVM 패턴을 열심히 익히고 있는 개발자입니다.
이제 대략 개념적인것들은 잡힌것 같은데 궁금한게 하나 있어서 질문드립니다.
AForge란 라이브러리를 사용해서 webcam preview 테스트를 진행하는 중에 MVVM 패턴으로 적용하는 방식을 검색해보니 UserControl을 만들어서 VideoSource를 플레이하는 Sample code가 있었습니다.
Sample code에서는 .cs파일 내에 Camera Device객체를 선언하고 DependencyProperty를 이용해서 주고받아야할 데이터를 처리하는 듯 합니다.
그러다보니 단순 image 뿐 아니라 여러가지 camera setting 값들을 MainViewModel에서 Binding 해야 하다보니 Property들이 계속해서 추가 할 수 밖에 없었는데요.
제 짧은 생각으로는 애초에 Camera Device객체를 MainViewModel에서 선언하고 UserControl은 Preview image만 전달받아 처리할 수 있는게 아닌가 하는 생각이 있어서 질문드립니다.
굳이 view단에서 처리하지 않아도 될 로직들이 .cs파일에 있어도 되는건가? 라는 생각이 들었구요. 또 검색해보니 UserControl은 따로 Viewmodel을 두지 않는다 라는 내용을 봐서 MainViewModel에다가 객체를 두어 활용해야하지 않나 라는 생각이 들기도 해서 질문드립니다.
어찌어찌 배워가면서 Property도 추가해서 연결하고 바인딩하고 해서 구동상엔 문제가 없습니다만 그래도 이런 개념들은 확실히 하고 가야 다음에 프로젝트할 때 헷갈리지 않을 것 같아서요!
아래는 코드비하인드에서 객체로 사용하는 Sample code입니다.
[VideoWindow.xaml]
<UserControl x:Class="MVVMVideoControl.Video.VideoWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MVVMVideoControl.Video"
xmlns:controls="clr-namespace:AForge.Controls;assembly=AForge.Controls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Loaded="OnLoaded"
Unloaded="OnUnloaded">
<Grid>
<Grid x:Name="NoVideoSourceGrid" Background="LightGray">
<Border BorderBrush="DimGray"
BorderThickness="1">
<TextBlock x:Name="NoVideoSourceMessage"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap"
FontSize="20"
FontWeight="Bold" />
</Border>
</Grid>
<WindowsFormsHost x:Name="VideoSourceWindowsFormsHost"
Background="Transparent">
<controls:VideoSourcePlayer x:Name="VideoSourcePlayer" />
</WindowsFormsHost>
</Grid>
</UserControl>
[VideoWindow.xaml.cs]
public partial class VideoWindow : UserControl
{
public static readonly DependencyProperty VideoBitmapProperty = DependencyProperty.Register
("VideoBitmap", typeof(ImageSource), typeof(VideoWindow), new PropertyMetadata(VideoBitmapPropertyChangedCallback));
private VideoCaptureDevice videoCaptureDevice;
public ImageSource VideoBitmap
{
get
{
return (ImageSource)this.GetValue(VideoBitmapProperty);
}
set
{
this.SetValue(VideoBitmapProperty, value);
}
}
[Capture Device 초기화 부분]
if (!GetVideoDevices.Any(item => item.UsbId.Equals(videoDeviceSourceId)))
{
return;
}
this.videoCaptureDevice = new VideoCaptureDevice(videoDeviceSourceId);
this.videoCapabilities = this.videoCaptureDevice.VideoCapabilities;
this.snapshotCapabilities = this.videoCaptureDevice.SnapshotCapabilities;
//Snapshot 활성화
this.videoCaptureDevice.ProvideSnapshots = true;
//이벤트 초기화
this.videoCaptureDevice.SnapshotFrame += new NewFrameEventHandler(NewFrameSnapshot);
this.videoCaptureDevice.NewFrame += new NewFrameEventHandler(NewFrameVideo);
//VideoFilterRange 초기화
InitializedVideoProcAmpRange();
//VideoCameraControlRange 초기화
InitializedVideoControlRange();
//messenger 테스트
var videoMessage = new VideoMessage()
{
mediaCapabilitiesList = GetVideoCapabilities,
mediaStillPinCapabilitiesList = GetSnapshotCapabilities
};
Messenger.Default.Send(videoMessage);
private void NewFrameVideo(object sender, NewFrameEventArgs eventArgs)
{
BitmapImage bi;
using (var bitmap = (Bitmap)eventArgs.Frame.Clone())
{
bi = new BitmapImage();
bi.BeginInit();
MemoryStream ms = new MemoryStream();
bitmap.Save(ms, ImageFormat.Bmp);
bi.StreamSource = ms;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.EndInit();
}
bi.Freeze();
Dispatcher.BeginInvoke(new ThreadStart(delegate { this.VideoBitmap = bi; }));
}
}
[MainWindow.xaml]
<video:VideoWindow
x:Name="CameraVideoDeviceControl"
VideoPreviewWidth="Auto"
VideoPreviewHeight="Auto"
SnapshotSize="{Binding SelectedStillPinSize, Converter={StaticResource MediaCapabilitiesConverter}}"
VideoSize="{Binding SelectedVideoSize, Converter={StaticResource MediaCapabilitiesConverter}}"
VideoSourceId="{Binding SelectedVideoDevice, Converter={StaticResource MediaInformationConverter}}"
VideoProcAmpRange="{Binding VideoProcAmpRange, Mode=OneWayToSource}"
VideoAmp="{Binding VideoAmp}"
VideoControlRange="{Binding VideoControlRange, Mode=OneWayToSource}"
VideoControl="{Binding VideoControl}"
SnapshotBitmap="{Binding SnapshotFrame, Mode=TwoWay}"
VideoBitmap="{Binding VideoFrame, Mode=TwoWay}">
</video:VideoWindow>