WPF 超入門 〜番外編「とある動画の同期再生」
先日、MSDN のフォーラムで
1つの Form に4つの MediaPlayer のコントロールを貼り付けている。1つの動画を同時再生させたいが微妙に再生がずれてしまう。4つの MediaPlayer コントロールで同期を取って再生する方法はないか?」(取意)
という質問がありました。
ここ最近エッセンシャルWPF 読んでたせいで、この質問見て真っ先に思い浮かんだのが MediaElement と VisualBrush を使う方法です。
この VisualBrush というのはかなり強力で、対象となるコントロールに描画されたグラフィックをそのまま描画するという代物。しかも静止画に留まらず、動画も同期を取って再生しちゃいます。
例えばこんな具合。左上が MediaElement コントロールで、残り三つは Rectangle の背景を VisualBrush を使って塗り潰してます。
では実例で見てみましょう。
サンプルプロジェクト
VisualStudio 2010 を起動し、C# で WPF アプリケーションのプロジェクトを作成します。プロジェクト名は「VisualBrushSample」としました。まぁベタな名前ですが良しとしましょう。
まず MediaElement と Button のみ。以下 XAML の定義です。Button は「Play(再生)」ボタンと「Pause(一時停止)」ボタン、「Stop(停止)」ボタンを用意しました。ここはメンドくさい説明を省きます。MainWindow.xaml に以下の XAML をコピペしてください。
MainWindow.xaml
<Window x:Class="VisualBrushSample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="380" Width="700"> <Grid> <MediaElement Height="160" Width="280" HorizontalAlignment="Left" Name="MediaElement1" VerticalAlignment="Top" LoadedBehavior="Manual" UnloadedBehavior="Manual" Stretch="Fill" /> <Button Content="Play" Height="30" HorizontalAlignment="Left" Margin="580,11,0,0" Name="Button1" VerticalAlignment="Top" Width="90" Click="Button1_Click" /> <Button Content="Pause" Height="30" HorizontalAlignment="Left" Margin="580,55,0,0" Name="Button2" VerticalAlignment="Top" Width="90" Click="Button2_Click" /> <Button Content="Stop" Height="30" HorizontalAlignment="Left" Margin="580,103,0,0" Name="Button3" VerticalAlignment="Top" Width="90" Click="Button3_Click" /> </Grid> </Window>
次は C# のコードです。MainWindow.xaml.cs に以下のコードを上書きします。
MediaElement1.Source に設定してる動画のファイル名がかなり怪しいですが、まぁここは気にしないことにしましょうw
using System.Windows; namespace VisualBrushSample { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button1_Click(object sender, RoutedEventArgs e) { MediaElement1.Source = new System.Uri("C:\\projects\\k-on_oped.mp4"); MediaElement1.Play(); } private void Button2_Click(object sender, RoutedEventArgs e) { if (MediaElement1.Source == null) return; MediaElement1.Pause(); } private void Button3_Click(object sender, RoutedEventArgs e) { if (MediaElement1.Source == null) return; MediaElement1.Stop(); MediaElement1.Source = null; } } }
実行するとこうなります。
VisualBrush
お次は VisualBrush の例を見ていきます。Rectangle を一つ用意して、MediaElement の描画内容を転送して描画します。
Rectangle を VisualBrush で塗りつぶすわけですが、Visual="{Binding ElementName=MediaElement1}" という具合に、バインディング対象となるコントロールに MediaElement を指定します。
MainWindow.xaml
<Window x:Class="VisualBrushSample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="380" Width="700"> <Grid> <MediaElement Height="160" Width="280" HorizontalAlignment="Left" Name="MediaElement1" VerticalAlignment="Top" LoadedBehavior="Manual" UnloadedBehavior="Manual" Stretch="Fill" /> <Button Content="Play" Height="30" HorizontalAlignment="Left" Margin="580,11,0,0" Name="Button1" VerticalAlignment="Top" Width="90" Click="Button1_Click" /> <Button Content="Pause" Height="30" HorizontalAlignment="Left" Margin="580,55,0,0" Name="Button2" VerticalAlignment="Top" Width="90" Click="Button2_Click" /> <Button Content="Stop" Height="30" HorizontalAlignment="Left" Margin="580,103,0,0" Name="Button3" VerticalAlignment="Top" Width="90" Click="Button3_Click" /> <Rectangle HorizontalAlignment="Left" Margin="0,172,0,0" Name="Rectangle1" Stroke="Black" VerticalAlignment="Top" Height="160" Width="280" Stretch="Fill"> <Rectangle.Fill> <VisualBrush TileMode="None" Visual="{Binding ElementName=MediaElement1}" /> </Rectangle.Fill> </Rectangle> </Grid> </Window>
実行すると、こんな感じになります。上が MediaElement、下が Rectangle で VisualBrush を使って MediaElement の表示内容を描画しています。
Rectangle を三つにするとこうなる・・・
MainWindow.xaml
<Window x:Class="VisualBrushSample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="380" Width="700"> <Grid> <MediaElement Height="160" Width="280" HorizontalAlignment="Left" Name="MediaElement1" VerticalAlignment="Top" LoadedBehavior="Manual" UnloadedBehavior="Manual" Stretch="Fill" /> <Button Content="Play" Height="30" HorizontalAlignment="Left" Margin="580,11,0,0" Name="Button1" VerticalAlignment="Top" Width="90" Click="Button1_Click" /> <Button Content="Pause" Height="30" HorizontalAlignment="Left" Margin="580,55,0,0" Name="Button2" VerticalAlignment="Top" Width="90" Click="Button2_Click" /> <Button Content="Stop" Height="30" HorizontalAlignment="Left" Margin="580,103,0,0" Name="Button3" VerticalAlignment="Top" Width="90" Click="Button3_Click" /> <Rectangle HorizontalAlignment="Left" Margin="0,172,0,0" Name="Rectangle1" Stroke="Black" VerticalAlignment="Top" Height="160" Width="280" Stretch="Fill"> <Rectangle.Fill> <VisualBrush TileMode="None" Visual="{Binding ElementName=MediaElement1}" /> </Rectangle.Fill> </Rectangle> <Rectangle HorizontalAlignment="Left" Margin="290,0,0,0" Name="Rectangle2" Stroke="Black" VerticalAlignment="Top" Height="160" Width="280" Stretch="Fill"> <Rectangle.Fill> <VisualBrush TileMode="None" Visual="{Binding ElementName=MediaElement1}" /> </Rectangle.Fill> </Rectangle> <Rectangle HorizontalAlignment="Left" Margin="290,172,0,0" Name="Rectangle3" Stroke="Black" VerticalAlignment="Top" Height="160" Width="280" Stretch="Fill"> <Rectangle.Fill> <VisualBrush TileMode="None" Visual="{Binding ElementName=MediaElement1}" /> </Rectangle.Fill> </Rectangle> </Grid> </Window>
F5 キーで実行するとこうなりました。PAUSE ボタンで一時停止してみましたが、同期してるのが判ると思います。
次のネタは 「WindowsForms 開発者のための WPF 超早わかりQ&A」 です。