這今說的這個所謂進階篇,是相對而言的,就是比上一節說的內容稍稍靈活了一些,不過我想我們在做應用的時候,也很少這麼進階去搞,你要說專業的拍攝程式,那壓根用不著你做,人家裝置供應商就已經開發了,就像我買的Dell的產品,人家就已經提供了一個很強大的拍攝程式了。
不過呢,瞭解一下,研究一下還是有意義的。這個“進階”內容便是和MediaCapture類有關,主要就是它,你看它的人生閱曆不淺,N多個方法,當然,這些方法我們總的來說就可能用到三個,即預覽的,拍照片的,錄製視頻的,是啊你想攝像就這幾個用途而已。可能有人說,那視訊交談呢?視訊交談不就動態錄製視頻唄。
所以,很多事情,我們不必須弄得太複雜,就像今天這例子一樣,很簡單,不過也許是可以說明其應用價值的。
那麼,我們今天玩什麼呢?弄一個定時拍攝好不?這還有點意思的。
1、建立項目,不會的回家複習。
2、XAML頁面不用多東西,既然要拍東西,那得能夠預覽網路攝影機中的內容,這樣方便拍攝,所以,先弄一個CaptureElement,這是專門在UI上顯晃網路攝影機預覽用的;另外,我們要顯示定時拍到的照片,所以,要一個ListView控制項;要進行相關操作,需要扔幾個按鈕。
<Page x:Class="App1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App1" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <CaptureElement x:Name="ce" Grid.Row="0" Margin="13"/> <Grid Grid.Row="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="auto"/> </Grid.ColumnDefinitions> <ListView x:Name="lv" Grid.Column="0" Margin="3,2,3,3" SelectionMode="None" Height="150"> <ListView.ItemTemplate> <DataTemplate> <Image Width="120" Height="120" Stretch="Uniform" Source="{Binding}"/> </DataTemplate> </ListView.ItemTemplate> <ListView.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </ListView.ItemsPanel> </ListView> <StackPanel Grid.Column="1" Margin="10"> <Button Content="開始" Click="onStart" x:Name="btnStart"/> <Button Content="停止" Click="onStop" x:Name="btnStop" IsEnabled="False"/> </StackPanel> </Grid> </Grid></Page>
3、要定時拍攝,自然要有個計時器,這個有,在Windows.System.Threading命名空間下,其名曰:ThreadPoolTimer,我們將用它來計時。
先引入以下命名空間:
using Windows.System.Threading;using System.Collections.ObjectModel;using Windows.UI.Xaml.Media.Imaging;using Windows.Media.Capture;using Windows.Media.MediaProperties;using Windows.Storage.Streams;
在MainPage類中定義以下成員:
public sealed partial class MainPage : Page { ObservableCollection<BitmapImage> images = null; MediaCapture myCapture = null; ThreadPoolTimer myTimer = null;
在MainPage的建構函式裡面幫它們化妝一下。
public MainPage() { this.InitializeComponent(); this.myCapture = new MediaCapture(); this.images = new ObservableCollection<BitmapImage>(); this.lv.ItemsSource = images; }
我們希望在導航到頁面時就開啟網路攝影機並開始預覽,所以,在OnNavigatedTo方法中要做點手腳。
protected override async void OnNavigatedTo(NavigationEventArgs e) { await this.myCapture.InitializeAsync(); this.ce.Source = myCapture; await this.myCapture.StartPreviewAsync(); }
ThreadPoolTimer類是通過調用一個靜態方法CreatePeriodicTimer來建立計時器的,而我們看到這個方法的兩個重載中,參數都需要一個委託,這個委託要綁定一個方法,當計時器到了執行任務的時間,就會調用這個委託,從而調用指定的方法。
所以我們要先定義好這個處理方法:
private async void timerHandler(ThreadPoolTimer t) { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => { IRandomAccessStream stream = new InMemoryRandomAccessStream(); await myCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreatePng(), stream); // 在向流寫入資料時,已經把指標移到結尾處 // 所以在初始化映像是要先把指標移回開始位置 stream.Seek(0); BitmapImage bmpimg = new BitmapImage(); bmpimg.SetSource(stream); this.images.Add(bmpimg); }); }
注意,在調用CreatePeriodicTimer方法成功返回後就馬上開始計時了,如果要取消計時,就調用Cancel方法。
4、這時候,我們需要的組件代碼基本可以了,下面就處理那兩個控制開始拍攝和停止拍攝的按鈕的Click事件。
private void onStop(object sender, RoutedEventArgs e) { if (this.myTimer != null) { this.myTimer.Cancel(); //取消 } btnStop.IsEnabled = false; btnStart.IsEnabled = true; } private void onStart(object sender, RoutedEventArgs e) { this.images.Clear(); // 開始計時 this.myTimer = ThreadPoolTimer.CreatePeriodicTimer(timerHandler, TimeSpan.FromSeconds(5)); btnStart.IsEnabled = false; btnStop.IsEnabled = true; }
完整的代碼如下:
using System;using System.Collections.Generic;using System.IO;using System.Linq;using Windows.Foundation;using Windows.Foundation.Collections;using Windows.UI.Xaml;using Windows.UI.Xaml.Controls;using Windows.UI.Xaml.Controls.Primitives;using Windows.UI.Xaml.Data;using Windows.UI.Xaml.Input;using Windows.UI.Xaml.Media;using Windows.UI.Xaml.Navigation;using Windows.System.Threading;using System.Collections.ObjectModel;using Windows.UI.Xaml.Media.Imaging;using Windows.Media.Capture;using Windows.Media.MediaProperties;using Windows.Storage.Streams;// “空白頁”項目範本在 http://go.microsoft.com/fwlink/?LinkId=234238 上有介紹namespace App1{ /// <summary> /// 可用於自身或導航至 Frame 內部的空白頁。 /// </summary> public sealed partial class MainPage : Page { ObservableCollection<BitmapImage> images = null; MediaCapture myCapture = null; ThreadPoolTimer myTimer = null; public MainPage() { this.InitializeComponent(); this.myCapture = new MediaCapture(); this.images = new ObservableCollection<BitmapImage>(); this.lv.ItemsSource = images; } /// <summary> /// 在此頁將要在 Frame 中顯示時進行調用。 /// </summary> /// <param name="e">描述如何訪問此頁的事件數目據。Parameter /// 屬性通常用於配置頁。</param> protected override async void OnNavigatedTo(NavigationEventArgs e) { await this.myCapture.InitializeAsync(); this.ce.Source = myCapture; await this.myCapture.StartPreviewAsync(); } private async void timerHandler(ThreadPoolTimer t) { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => { IRandomAccessStream stream = new InMemoryRandomAccessStream(); await myCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreatePng(), stream); // 在向流寫入資料時,已經把指標移到結尾處 // 所以在初始化映像是要先把指標移回開始位置 stream.Seek(0); BitmapImage bmpimg = new BitmapImage(); bmpimg.SetSource(stream); this.images.Add(bmpimg); }); } private void onStop(object sender, RoutedEventArgs e) { if (this.myTimer != null) { this.myTimer.Cancel(); //取消 } btnStop.IsEnabled = false; btnStart.IsEnabled = true; } private void onStart(object sender, RoutedEventArgs e) { this.images.Clear(); // 開始計時 this.myTimer = ThreadPoolTimer.CreatePeriodicTimer(timerHandler, TimeSpan.FromSeconds(5)); btnStart.IsEnabled = false; btnStop.IsEnabled = true; } }}
5、開啟資訊清單檔,切換到“功能”選項卡,同時勾選“麥克風”和“網路攝像機”,並儲存。
6、運行應用程式,等預覽開始後,點擊[開始] 按鈕,程式就會每隔5秒鐘(因為代碼中指定的是秒)拍攝一張照片,並顯示在ListView中。