WPFでアプリ画面をキャプチャーしてJpegで保存する(exif付き)
なにでググったらよかったのか分からなかったので自分で書きます(主にexifのところが)
ソース
とりあえず動くものを置きました。自由に使ってください
以下はその説明みたいな感じ
UIElementを画像にする
WPFなのでSystem.Drawingは使いません(たまに使うけど)
var width = (int)CaptureTarget.ActualWidth; var height = (int)CaptureTarget.ActualHeight; // UIElement to RenderTargetBitmap var renderTargetBitmap = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32); var visual = new DrawingVisual(); using (var context = visual.RenderOpen()) { var brush = new VisualBrush(CaptureTarget); context.DrawRectangle(brush, null, new Rect(0, 0, width, height)); } renderTargetBitmap.Render(visual);
RenderTargetBitmap
とDrawingVisual
でうまいこと作りましょう
exifを付ける
BitmapMetadata
ってのを作ってその中にいろいろ入れちゃいます
var encoder = new JpegBitmapEncoder(); // Add exif data var metadata = new BitmapMetadata("jpg"); metadata.ApplicationName = Assembly.GetExecutingAssembly().GetName().Name; metadata.Comment = Assembly.GetExecutingAssembly().GetName().Version.ToString(); metadata.DateTaken = DateTime.UtcNow.ToString(DateTimeFormatInfo.InvariantInfo); //Custom metadata //metadata.SetQuery("", ""); encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap, null, metadata, null));
一般的なのはプロパティとしてすでに生えているのでそこに代入
今回はアプリ名・コメント(バージョン)・作成日時を入れてみました
プロパティが無い奴はSetQuery
メソッドで追加できるっぽいです
あとは保存して完成
できました
ファイルの変更をRxで監視する
前人の知恵がググると簡単にでてきます
まぁこれでホボ完成なんですが私はコンソールアプリで読み込むだけだったので、スケジューラー指定してとかそういうところを割愛
するとファイルの変更イベントが2回上がるようになりました
ほぼ同時なので2回目のイベントでのファイル読み込みは1回目の読み込みの影響でだいたい失敗します
これまたググると
メモ帳の場合ファイルの保存で1回、属性の変更で1回ファイルを更新するよ、そしてこの動作は他のアプリでも同じだよ、的なことが書いてあるような気がします
今回は変更があったらファイルを読むとかその程度の動きができればいいのでThrottleで2回連続を1回にまとめました
private FileSystemWatcher _watcher; private IDisposable _disposable; public FileWatcher(string fileName) { var file = new FileInfo(fileName); _watcher = new FileSystemWatcher(); _watcher.Path = file.Directory?.FullName; _watcher.Filter = file.Name; _watcher.NotifyFilter = NotifyFilters.LastWrite; _disposable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>( h => this._watcher.Changed += h, h => this._watcher.Changed -= h) .Throttle(TimeSpan.FromSeconds(.1)) .Subscribe(e => this.ReadFile(e.EventArgs.FullPath)); _watcher.EnableRaisingEvents = true; } public void Stop() { _disposable?.Dispose(); _watcher?.Dispose(); } private void ReadFile(string file) { try { using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read)) { } } catch (Exception exp) { System.Diagnostics.Debug.WriteLine("read error."); } }
まぁ100msも待てば十分でしょう
Microsoft.Maps.MapControl.WPF の残念なうごき
Microsoft.Maps.MapControl.WPFはNuGetで入れることができるWPF用の地図コントロールです
ベースとなる地図がBing MapsになっているだけのよくあるTMS(?)のコントロールで最新版は1.0.0.3(2015/02/19)となっています
このコントロールはMapCore.BoundingRectangle
プロパティを参照することで現在表示している領域の東西南北が取得できます
ということで地図が移動した時の西と東の度を画面上に表示するだけのアプリを作ってみました(わかると思いますが西を左側、東を右側にだしてます)
東が180°を超えた瞬間に西と東が入れ替わります
BoundingRectangleプロパティが返すLocationRect
クラスがWestとEastをイイ感じ(?)に入れ替えてくれるのが原因なんですが、どう見てもバグです、すごく困ります
LocationRectクラスはどうやら
- West < East
- 緯度は±180の中にある
を保証しようとしているためにこんなことになっているようです (これだと経度180度線が入った領域は表現できませんよね)
Microsoft.Maps.MapControl.WPFの中ではLocationRectを使う箇所がいくつかあるんですが、上の値チェックはクラスに側が保証していることを前提にできていたりするので、ちょっと修正とかできないんだろうなーとは思うんですがどうなんでしょう
そもそもバグ報告をどこにしていいのかも分からないし、とっくにサポートおわってんじゃないかとか思っていたり
ちなみにUWP版にはこのプロパティはありません