skysan's programming notebook

コーディングして思ったことなどを気ままに

PictureBoxを動的に整列する

久々の投稿です。

ここ最近はWindowsクライアントアプリ開発ばかりやってます。

今回のは自分用のメモです。

Panelの中にPictureBoxを整列させるだけですが、並べる数を可変にしています。 単純に自動配置するだけならFlowLayoutPanelクラスを使えばよさそうですが、 表示数を固定にしたかったので、自作しました。

環境

仕様

  • Panel内に縦横に指定した数のPictureBoxを動的に並べる
  • リサイズには対応しない

ソースコード

PictureBoxの個数と大きさから余白を計算して、描画する座標を計算しています。
余白計算ででた端数は両端の余白に追加しています。
そうしないと、左寄りになります。

/// <summary>
/// Panel内に指定した枚数を並べる
/// </summary>
/// <param name="columnNum">縦に並べる個数</param>
/// <param name="rowNum">横に並べる個数</param>
private void setPictures(int columnNum, int rowNum)
{
    // pictureboxのサイズ
    Size picSize = new Size(100, 80);

    // はみ出さないかチェック
    if (picSize.Width * rowNum > this.panel1.Width
    || picSize.Height * columnNum > this.panel1.Height)
    {
        return;
    }

    int offsetX, offsetY; // 調整用
    int paddingX = calcPadding(picSize.Width, this.panel1.Width, rowNum, out offsetX);
    int paddingY = calcPadding(picSize.Height, this.panel1.Height, columnNum, out offsetY);

    // 左上の始点
    Point startPos = new Point(paddingX + offsetX, paddingY + offsetY); 
    Point nextPos = startPos;

    int count = columnNum * rowNum;
    this.SuspendLayout();
    for (int i = 0; i < count; i++)
    {
        PictureBox pic = new PictureBox();
        pic.BackColor = Color.Coral;
        pic.Size = picSize;
        pic.Location = nextPos;
        this.panel1.Controls.Add(pic);

        nextPos = calcNextPostion(startPos, nextPos, picSize, this.panel1.Width, paddingX, paddingY);
    }
    this.ResumeLayout(false);
}
/// <summary>
/// 次の開始位置を計算
/// </summary>
/// <param name="startPos">行の開始位置</param>
/// <param name="lastPos">1つ前のPictureBocの座標</param>
/// <param name="picSize">PictureBoxのサイズ</param>
/// <param name="panelWidth">Panelの幅</param>
/// <param name="paddingX">X軸方向の余白</param>
/// <param name="paddingY">Y軸方向の余白</param>
/// <returns></returns>
private Point calcNextPostion(Point startPos, Point lastPos, Size picSize, int panelWidth, int paddingX, int paddingY)
{
    Point newPos = new Point
    {
        X = lastPos.X + picSize.Width + paddingX,
        Y = lastPos.Y
    };

    // 現在の行に収まるか
    bool isNextRow = panelWidth < newPos.X + picSize.Width + paddingX;
    if (isNextRow)
    {
        newPos.X = startPos.X;
        newPos.Y += picSize.Height + paddingY;
    }

    return newPos;
}
/// <summary>
/// 余白計算
/// </summary>
/// <param name="picSize">個体の長さ</param>
/// <param name="rowSize">列の長さ</param>
/// <param name="picNumInRow">1列に並べるPictureBoxの個数</param>
/// <param name="offset">微調整用補正値</param>
/// <returns></returns>
private int calcPadding(int picSize, int rowSize, int picNumInRow, out int offset)
{
    // 余白の合計
    int totalPadding = rowSize - picSize * picNumInRow;
    // PictureBox間の余白
    int padding = totalPadding / (picNumInRow + 1);
    // 端数の調整
    double amari = totalPadding % (picNumInRow + 1);
    offset = Convert.ToInt32(Math.Round(amari / 2));

    return padding;
}

結果

こんな感じになりました。
カウントアップ時にPanel内のコントロールをすべて削除してから、setPicturesメソッドを読んで再描画しています。
ちょっと気持ちいい。 f:id:skysan:20170722162517g:plain