WPF学習中「2枚の画像を左右に並べて表示 – その2」

C# コンピュータ
C#

Windowサイズに合わせて画像が拡大縮小して画像全体を表示させたいのですが、ImageコントロールのStretchプロパティの動作が今一つ理解できず、いろいろ試行錯誤してみました。

プロジェクトの作成

mkdir プロジェクト名
cd プロジェクト名
dotnet new wpf
code .

ソースコード

ファイル名:MainWindow.xaml

<Window x:Class="Sample55CopyPixels.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Sample55CopyPixels"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel
            Background="Black"
            SizeChanged="StackPanel_SizeChanged">
            <Canvas>
                <Image Name="Image1">
                </Image>
            </Canvas>
        </StackPanel>
    </Grid>
</Window>

ファイル名:MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.IO;
using System.Diagnostics;

namespace Sample55CopyPixels
{
    /// 
    /// Interaction logic for MainWindow.xaml
    /// 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            // 左側画像の読み込み
            var leftPath = @"C:\Users\asagao\Pictures\202108070814.png";
            var lb = new BitmapImage();
            using (var fs = new FileStream(leftPath, FileMode.Open, FileAccess.Read))
            {
                lb.BeginInit();
                lb.CacheOption = BitmapCacheOption.OnLoad;
                lb.StreamSource = fs;
                lb.EndInit();
            }
            lb.Freeze();

            // 左側画像をbyte配列にコピー
            int lb_stride = lb.PixelWidth * 4;
            byte[] lb_datas = new byte[lb_stride * lb.PixelHeight];
            lb.CopyPixels(new Int32Rect(0, 0, lb.PixelWidth, lb.PixelHeight), lb_datas, lb_stride, 0);

            // 右側画像の読み込み
            var rightPath = @"C:\Users\asagao\Pictures\202108070821.png";
            var rb = new BitmapImage();
            using (var fs = new FileStream(rightPath, FileMode.Open, FileAccess.Read))
            {
                rb.BeginInit();
                rb.CacheOption = BitmapCacheOption.OnLoad;
                rb.StreamSource = fs;
                rb.EndInit();
            }
            rb.Freeze();
            
            // 右側画像をbyte配列にコピー
            int rb_stride = rb.PixelWidth * 4;
            byte[] rb_datas = new byte[rb_stride * rb.PixelHeight];
            rb.CopyPixels(new Int32Rect(0, 0, rb.PixelWidth, rb.PixelHeight), rb_datas, rb_stride, 0);

            // 表示画像の作成
            //int height = lb.PixelHeight;
            int height = (lb.PixelHeight > rb.PixelHeight) ? lb.PixelHeight : rb.PixelHeight;
            //int width = (int)((height/9)*16);
            int width = lb.PixelWidth + rb.PixelWidth;
            var wb = new WriteableBitmap(
                width,
                height,
                lb.DpiX,
                lb.DpiY,
                lb.Format,
                null);
            
            // 貼り付け先のx座標を計算
            //int x = (int)((width - (lb.PixelWidth + rb.PixelWidth))/2);
            int x = 0;
            
            // 画像をコピー
            wb.WritePixels(
                new Int32Rect(x, 0, lb.PixelWidth, lb.PixelHeight),
                lb_datas,
                lb_stride,
                0);
            wb.WritePixels(
                new Int32Rect(x+lb.PixelWidth, 0, rb.PixelWidth, rb.PixelHeight),
                rb_datas,
                rb_stride,
                0);
            wb.Freeze();



            // イメージコントロールのソースにビットマップイメージを割り当て
            Image1.Source = wb;
        }
        private void StackPanel_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Debug.Print("W:{0} H:{1}", (int)e.NewSize.Width, (int)e.NewSize.Height);
            Image1.Height = (int)e.NewSize.Height;
            Image1.Width = (int)e.NewSize.Width;
        }
    }
}

感想

成功すると画像が表示されます。

MicrosoftのサイトでImage.Stretchプロパティの規定値のUniformの説明を見ると

割り当てられた広さに収まるようにコンテンツのサイズを変更しますが、元の縦横比が維持されます。

との記述があります。”割り当てられた広さ”をImageコントロールのサイズだと当たりをつけて考えてみます。
StackPanelにImageコントロールを配置するとStackPanelのサイズ変更に連動しImageコントロールのサイズの幅は変更されているようですが、高さが連動していないように見受けられます。
サイズの自動調整では、うまくいかないようなのでStackPanelのサイズ変更のイベントを捕まえてImageコントロールのサイズを強制的に変更してみます。
その際、サイズが自動調整されないようにStackPanelとImageコントロールの間にCanvasを挟んでみました。

また、前回は画像を16:9になるように加工していましたが、今回は単純に2枚並べる加工にしています。

縦横比を維持しながら画像全体が表示されるようになり、望み通りの動作をするようになりました。

コメント