C#のWinFormsでScrollBar付PictureBoxをUserControlで作成してみた。

C# コンピュータ
C#

PanelのAutoScrollとPictureBoxのSizeModeプロパティをAutoSizeにした状態と同じような振る舞いになるようなUserControlで作ることが目標です。

ソースコード

ファイル名:CustomControl.cs

using System.Diagnostics;

class CustomControl : UserControl
{
    HScrollBar _hScrollBar;
    VScrollBar _vScrollBar;
    PictureBox _pictureBox;
    public CustomControl()
    {
        _hScrollBar = new()
        {
            Dock = DockStyle.Bottom,
        };
        _vScrollBar = new()
        {
            Dock = DockStyle.Right,
        };
        _pictureBox = new()
        {
            Dock = DockStyle.Fill,
        };
        this.Controls.Add(this._hScrollBar);
        this.Controls.Add(this._vScrollBar);
        this.Controls.Add(this._pictureBox);

        this._vScrollBar.Scroll += VScrollBar_Scroll;
        this._hScrollBar.Scroll += HScrollBar_Scroll;
        this._pictureBox.Paint += PictureBox_Paint;
        this.Resize += CustomControl_Resize;
    }
    Bitmap? _bitmap = null;
    public Bitmap? Bitmap
    {
        get { return _bitmap; }
        set
        {
            if (value == _bitmap) return;

            _bitmap?.Dispose();
            _bitmap = value;
            ShowBitmap();
        }
    }
    void ResetScrollBar()
    {
        if (Bitmap is null) return;

        _hScrollBar.Minimum = 0;
        _hScrollBar.Maximum = 0;

        int width = _pictureBox.Width - _hScrollBar.Height;
        int height = _pictureBox.Height - _vScrollBar.Width;
        if(Bitmap.Width - width > 0)
        {
            float ratio = (float)width / (float)Bitmap.Width; 
            int largeChange = (int)((float)width * ratio);
            _hScrollBar.LargeChange = largeChange;
            _hScrollBar.Maximum = Bitmap.Width - width + largeChange;
            _hScrollBar.Visible = true;
        }
        else
        {
            _hScrollBar.Visible = false;
        }

        _vScrollBar.Minimum = 0;
        _vScrollBar.Maximum = 0;

        if(Bitmap.Height - height > 0)
        {
            float ratio = (float)height / (float)Bitmap.Height; 
            int largeChange = (int)((float)height * ratio);
            _vScrollBar.LargeChange = largeChange;
            _vScrollBar.Maximum = Bitmap.Height - height + largeChange;
            _vScrollBar.Visible = true;
        }
        else
        {
            _vScrollBar.Visible = false;
        }

    }
    void ShowBitmap()
    {
        if (Bitmap is null) return;

        ResetScrollBar();
        _pictureBox.Invalidate();
    }
    void PictureBox_Paint(Object? s, PaintEventArgs e)
    {
        if (Bitmap is null) return;

        RectangleF rectf = new (
            _hScrollBar.Value, _vScrollBar.Value,
            _pictureBox.ClientSize.Width, _pictureBox.ClientSize.Height);
        
        e.Graphics.DrawImage(Bitmap, 0, 0, rectf, GraphicsUnit.Pixel);
    }
    void VScrollBar_Scroll(Object? s, ScrollEventArgs e)
    {
        _pictureBox.Invalidate();
    }
    void HScrollBar_Scroll(Object? s, ScrollEventArgs e)
    {
        _pictureBox.Invalidate();
    }
    void CustomControl_Resize(Object? s, EventArgs e)
    {
        if (Bitmap is null) return;

        if (_pictureBox.Width == 0) return;

        ResetScrollBar();
        _pictureBox.Invalidate();
    }
}

ファイル名:Form1.cs

namespace UserControl01;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        CustomControl obj = new ()
        {
            Parent = this,
            Dock = DockStyle.Fill,
        };
        Bitmap bmp = new (@"H:\csharp\dotnet8\winforms\UserControl01\202406041040.png");
        obj.Bitmap = bmp;
    }
}

実行

起動した状態

右下へスクロール

ウィンドウサイズを縮小

ウィンドウサイズを拡大し画像がウィンドウ内におさまるサイズになるとスクロールバーが消える。

感想

作成当初スクロールバーの幅(高さ)分、画像が表示されない領域が発生してしまい試行錯誤しました。
まだ動きが怪しい部分がありますが、とりあえず思った通りに動くようになりました。

元画像からピクチャボックスの表示領域分画像を切り出して表示しています。
スクロールバーのスクロール位置が切り出し開始の座標となり、スクロールするたびに再描画するようになっています。

次に拡大(表示)機能を考えてみたいと思います。

コメント