Pusatkan beberapa baris kontrol dalam FlowLayoutPanel

Feb 06 2019

Saya mencoba membuat panel yang akan menjadi tuan rumah kontrol yang ditambahkan secara dinamis. Ada dua peringatan:

  • Akan ada banyak kontrol, jadi panel harus membungkus elemen menjadi baris baru saat mencapai batas lebarnya dan menggulir secara vertikal.
  • Kontrol dapat berubah ukuran, yang akan mengubah jumlah elemen
    yang dapat ditampung dalam satu baris.

Saya telah melihat beberapa solusi yang diusulkan untuk memusatkan kontrol dinamis dalam Formulir dan menolaknya karena alasan berikut:

  • TableLayoutPanel - masalah utama yang saya miliki dengan menggunakan ini adalah peristiwa ketika elemen tumbuh dan harus bergeser dari kisi 3-2 ke 2-4, karena TableLayoutPanel tampaknya tidak menangani hal itu dengan baik.
  • AutoSize FlowLayoutPanel yang dapat tumbuh dan menyusut di dalam TableLayoutControl - masalah utama saya dengan solusi ini adalah solusi ini hanya memusatkan satu baris di dalam Formulir, setelah membungkus ke baris baru, elemen mulai sejajar ke sisi kanan. Saya kira saya dapat secara dinamis menambahkan FlowLayoutPanels baru ke baris baru dari TableLayoutControl, tetapi kemudian saya memiliki masalah yang sama seperti skenario pertama di mana saya perlu mendistribusikan ulang elemen secara manual di antara baris jika ukurannya bertambah / menyusut.

Saya ingin tahu apakah saya kehilangan beberapa fungsionalitas yang dapat membantu saya menangani acara tumbuh / menyusut tanpa membuat variasi TableLayoutPanel saya sendiri?

Edit:
Di bawah ini adalah draf fungsionalitas:

  • A - Dua elemen berpusat di panel
  • B - Elemen ketiga ditambahkan, ketiganya berada di tengah
  • C - Elemen Keempat ditambahkan, dibungkus ke baris baru dan di tengah
  • D - Elemen diperbesar, sekarang membungkus elemen kedua, di tengah

Jawaban

7 Jimi Feb 07 2019 at 08:31

Berikut adalah contoh yang mereproduksi perilaku yang Anda jelaskan.
Itu menggunakan TableLayoutPanel yang menghosting beberapa FlowLayoutPanels.

Satu detail penting adalah penahan dari anak FlowLayoutPanels: mereka harus ditambatkan ke Atas-Bawah : ini menyebabkan panel diposisikan di tengah Baris TableLayoutPanel.

Perhatikan bahwa, dalam konstruktor Formulir, salah satu dari yang RowStylesdihapus. Ini juga sangat penting: the TLP(yang cukup eksentrik), meskipun Anda hanya memiliki satu Baris (atau satu Kolom, hal yang sama), akan menyimpan 2 RowStyles. Gaya kedua akan diterapkan ke Baris pertama yang Anda tambahkan; hanya untuk yang pertama, bukan yang lain: ini dapat mengacaukan tata letak.

Anomali lain, tidak menyediakan metode untuk menghapus Row, jadi saya telah membuatnya. Ini fungsional tetapi sederhana dan perlu diperpanjang, termasuk validasi lebih lanjut.

Lihat contoh grafik tentang fungsionalitas saat ini. Jika Anda membutuhkan bantuan dalam mengimplementasikan sesuatu yang lain, tinggalkan komentar.


Untuk membangun ini tambahkan kontrol berikut ke Formulir (di sini, disebut FLPTest1):

  1. Tambahkan satu Panel, atur Dock.Bottom. Klik kanan dan SendToBack(),
  2. Tambahkan TableLayoutPanel(di sini, disebut tlp1), setel:
    • AutoScroll = true, AutoSize = true,
    • AutoSizeMode = GrowAndShrink, Dock.Fill
    • Pertahankan 1 Kolom, setel ke UkuranOtomatis dan satu Baris, setel ke UkuranOtomatis
  3. Tambahkan FlowLayoutPanel(di sini, disebut flp1), diposisikan di dalam TableLayoutPanel. Sebenarnya tidak perlu, hanya untuk kode contoh ini
    • Atur Anchor-nya ke Top, Bottom <=ini !important, tata letak tidak akan berfungsi dengan benar tanpanya: ini memungkinkan untuk memusatkan bagian FLPdalam TLPRow,
    • AutoSize = true, AutoSizeMode = GrowAndShrink
  4. Tambahkan Tombol (disebut btnAddControl)
  5. Tambahkan Tombol kedua (disebut btnRemoveControl)
  6. Tambahkan Kotak Centang (disebut chkRandom)
  7. Tempel kode di sini di dalam file kode Formulir

using System.Drawing;
using System.Linq;
using System.Windows.Forms;


public partial class TLPTest1 : Form
{
    public TLPTest1()
    {
        InitializeComponent();
        this.tlp1.RowStyles.RemoveAt(1);
    }

    private void TLPTest1_Load(object sender, EventArgs e)
    {
        PictureBox pBox = new PictureBox() {
            Anchor = AnchorStyles.None,
            BackColor = Color.Orange,
            MinimumSize = new Size(125, 125),
            Size = new Size(125, 125),
        };
        this.flp1.Controls.Add(pBox);
        this.tlp1.Controls.Add(flp1);
    }

    Random rnd = new Random();
    Size[] sizes = new Size[] { new Size(75, 75), new Size(100, 100), new Size(125, 125)};
    Color[] colors = new Color[] { Color.Red, Color.LightGreen, Color.YellowGreen, Color.SteelBlue };
    Control selectedObject = null;

    private void btnAddControl_Click(object sender, EventArgs e)
    {
        Size size = new Size(125, 125);
        if (this.chkRandom.Checked)
            size = sizes[rnd.Next(sizes.Length)];

        PictureBox pBox = new PictureBox() {
            Anchor = AnchorStyles.None,
            BackColor = colors[rnd.Next(colors.Length)],
            MinimumSize = size,
            Size = size
        };

        bool drawborder = false;
        pBox.MouseEnter += (s, evt) => { drawborder = true;  pBox.Invalidate(); };
        pBox.MouseLeave += (s, evt) => { drawborder = false; pBox.Invalidate(); };
        pBox.MouseDown += (s, evt) => { selectedObject = pBox;  pBox.Invalidate(); };
        pBox.Paint += (s, evt) => { if (drawborder) {
                ControlPaint.DrawBorder(evt.Graphics, pBox.ClientRectangle, 
                                        Color.White, ButtonBorderStyle.Solid);
            }
        };

        var ctl = this.tlp1.GetControlFromPosition(0, this.tlp1.RowCount - 1);
        int overallWith = ctl.Controls.OfType<Control>().Sum(c => c.Width + c.Margin.Left + c.Margin.Right);
        overallWith += (ctl.Margin.Right + ctl.Margin.Left);
        if ((overallWith + pBox.Size.Width + pBox.Margin.Left + pBox.Margin.Right) >= this.tlp1.Width)
        {
            var flp = new FlowLayoutPanel() {
                Anchor = AnchorStyles.Top | AnchorStyles.Bottom,
                AutoSize = true,
                AutoSizeMode = AutoSizeMode.GrowAndShrink,
            };
            flp.Controls.Add(pBox);

            this.tlp1.SuspendLayout();
            this.tlp1.RowCount += 1;
            this.tlp1.Controls.Add(flp, 0, this.tlp1.RowCount - 1);
            this.tlp1.ResumeLayout(true);
        }
        else
        {
            ctl.Controls.Add(pBox);
        }
    }

    private void btnRemoveControl_Click(object sender, EventArgs e)
    {
        if (selectedObject is null) return;
        Control parent = selectedObject.Parent;
        selectedObject.Dispose();
        if (parent?.Controls.Count == 0)
        {
            TLPRemoveRow(this.tlp1, parent);
            parent.Dispose();
        }
    }

    private void TLPRemoveRow(TableLayoutPanel tlp, Control control)
    {
        int ctlPosition = this.tlp1.GetRow(control);
        if (ctlPosition < this.tlp1.RowCount - 1)
        {
            for (int i = ctlPosition; i < this.tlp1.RowCount - 1; i++)
            {
                tlp.SetRow(tlp.GetControlFromPosition(0, i + 1), i);
            }
        }
        tlp.RowCount -= 1;
    }
}