あさりのみそしるダイアリー

ココロおだやかに暮らしてます😌

C# WPF で Canvas に描いた内容を画像ファイルで保存したい

久々のプログラミングネタです。
XNAではないですけどね。XNAについてはとりあえず個人的にひと段落ついたので。また落ち着いたら何かやってもいいですけど、いつもの流れで言えば多分何もないんだろうな・・・と。

さて、ネタは表題の通り。どちらかというと私のメモみたいな感じになりますけど。

C#でとあるプログラムを作成しているのですが、WPFでフォームを作成していて、その中にCanvasっていうのがあって、これに描いた内容を画像ファイルとして残したいのですが・・・


なんか、直感的ではないというか、ちょっとむずかしかったかなぁ~って。
普通にCanvas.Imageみたいなのがあって、それをBitmapファイルだかで保存できて~みたいなのではないんですね。


で、「WPF Canvas 保存」とかで検索して、情報を収集したところ、コレかっていうのがありました。
でも、なぜか作成されるのは中身が空というか、たぶん透明なファイル。画像サイズは正しいんですけど。(320x240とか)

もちろん、参考にしたところのサンプルプログラムをそのまま記述してやると、サンプル通りの画像がちゃんと出力されるのですが、なんででしょ。

とりあえず、既存のCanvasの中身を画像ファイルに出力しようとすると、中身がなぜか透明なので、既存のCanvasの中身を新しいCanvas(というかサンプルプログラム通りにやれば中身が出力されるCanvas)にコピーして、それを出力してやればいいと思いまして・・・

--------------------------------------
        void makeCanvasImage(Canvas target)
        {
            // 新しいCanvasを作成(320x240で背景が黒いCanvas)
            Canvas canvas = new Canvas
            {
                Width = 320,
                Height = 240,
                Background = Brushes.Black
            };

            // targetに含まれる要素(UIElement)をcanvasにコピー
            // ただし、そのままコピーはできないので新しく作った要素について形状をtargetのものと同じものにしてcanvasに追加する
            // 私の場合はPolylineとLineしか使っていないので以下の2つについてそれぞれコピーの流れを書けばいい
            for (int i = 0; i < target.Children.Count; i++)
            {
                if (target.Children[i] is Polyline)
                {
                    Polyline source = target.Children[i] as Polyline;
                    Polyline polyline = new Polyline();
                    polyline.Points = source.Points;
                    polyline.Stroke = source.Stroke;
                    polyline.StrokeThickness = polyline.StrokeThickness;
                    canvas.Children.Add(polyline);
                }
                else if (target.Children[i] is Line)
                {
                    Line source = target.Children[i] as Line;
                    Line line = new Line();
                    line.X1 = source.X1;
                    line.X2 = source.X2;
                    line.Y1 = source.Y1;
                    line.Y2 = source.Y2;
                    line.Stroke = source.Stroke;
                    line.StrokeThickness = source.StrokeThickness;
                    canvas.Children.Add(line);
                }
            }

            // canvas内の要素(UIElement)をcanvasのサイズに合わせる
            canvas.Arrange(new Rect(0, 0, canvas.Width, canvas.Height));

            // bitmapを作成してcanvasをbitmapに描画する。たぶん透明じゃないはず!
            RenderTargetBitmap render = new RenderTargetBitmap*1;

            using (FileStream fs = new FileStream("test.png", FileMode.Create))
            {
                enc.Save(fs);    // 中身が透明じゃない画像が出力されるはず!!
                fs.Close();
            }
        }
--------------------------------------

これで、長いこと考えてなかなかできなかったCanvasに描いた内容の画像化ができました。
targetは画像出力すると中身が透明になってしまうCanvasです。

でも、なんで既存のCanvas(=target)からだとちゃんと描画されずに出力されてしまうのか、そこはわからないですけど・・・。

*1:Int32)canvas.Width, (Int32)canvas.Height, 96, 96, PixelFormats.Default);
            render.Render(canvas);

            // PNGフォーマットで画像を保存するので
            var enc = new PngBitmapEncoder();
            enc.Frames.Add(BitmapFrame.Create(render