F#でWPF --- UI画像キャプチャ --- キャプチャ時VM変更
下記記事にてUI画像をキャプチャする方法を記述しました。
F#でWPF --- UI画像キャプチャ - 何でもプログラミング
今回は、キャプチャ時のみに表示を変更したい場合に対応します。
実現方法
キャプチャ実行時にViewModelが公開しているキャプチャ用のViewModelをDataContextに設定します。(保存後は元のDataContextに戻します。)
今回は関数にして、生成を遅延させています。(Viewのパラメータも渡せるので、関数の形が良いと思います。)
SaveImageAction
前回と比べ、VMを生成する関数のプロパティを追加し、キャプチャ時にDataContextを入れ替えています。
open System.IO open System.Windows open System.Windows.Media open System.Windows.Media.Imaging open System.Windows.Interactivity open Microsoft.Win32 open Microsoft.FSharp.Linq.NullableOperators type SaveImageAction() = inherit TriggerAction<FrameworkElement>() static member val TargetProperty = DependencyProperty.Register("Target", typeof<FrameworkElement>, typeof<SaveImageAction>) member this.Target with get() = this.GetValue(SaveImageAction.TargetProperty) :?> FrameworkElement and set(x:FrameworkElement) = this.SetValue(SaveImageAction.TargetProperty, x) static member val VMForCapturingProperty = DependencyProperty.Register("VMForCapturing", typeof<unit -> obj>, typeof<SaveImageAction>) member this.VMForCapturing with get() = this.GetValue(SaveImageAction.VMForCapturingProperty) :?> unit -> obj and set(x:unit -> obj) = this.SetValue(SaveImageAction.VMForCapturingProperty, x) override this.Invoke _ = let dialog = SaveFileDialog(Filter = "PNGファイル|*.png") if this.Target <> null && dialog.ShowDialog() ?= true then let oldVM = this.Target.DataContext this.Target.DataContext <- this.VMForCapturing() let width = this.Target.ActualWidth let height = this.Target.ActualHeight let bmp = RenderTargetBitmap(int width, int height, 96.0, 96.0, PixelFormats.Pbgra32) this.Target.Measure(Size(width, height)) this.Target.Arrange(Rect(0.0, 0.0, width, height)) this.Target.UpdateLayout() bmp.Render(this.Target) this.Target.DataContext <- oldVM let pngEncoder = PngBitmapEncoder() pngEncoder.Frames.Add(BitmapFrame.Create(bmp)) use writer = new StreamWriter(dialog.FileName) pngEncoder.Save(writer.BaseStream)
アプリケーションコード
アプリケーション名はSaveWpfImage.exeで、SaveImageActionはnamespace Actionsに定義されています。
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:Actions;assembly=SaveWpfImage" Title="MainWindow" Height="100" Width="200"> <Grid x:Name="grid" Background="CornflowerBlue"> <Button Content="キャプチャ" HorizontalAlignment="Left" Margin="100,35,0,0" VerticalAlignment="Top" Width="75"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <local:SaveImageAction Target="{Binding ElementName=grid}" VMForCapturing="{Binding VMForCapturing}" /> </i:EventTrigger> </i:Interaction.Triggers> </Button> <TextBlock Text="{Binding Text}" Foreground="White" HorizontalAlignment="Left" Margin="35,15,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/> </Grid> </Window>
F#
open System open System.Windows type ViewModel = { Text : string } member this.VMForCapturing = fun () -> { Text = "World" } :> obj [<STAThread>] [<EntryPoint>] let main argv = let window = Application.LoadComponent(Uri("MainWindow.xaml", UriKind.Relative)) :?> Window window.DataContext <- { Text = "Hello" } Application().Run(window) |> ignore 0