Неправильно определяется ширина панели

На одной из вкладок TabControlа есть панель. У панели включены Anchorы. По-этому, всё ресайзится автоматически. В этой панели нужно застекать массив моих любимых UserControlов.


            // 
            // tabPageStreams
            // 
            this.tabPageStreams.Controls.Add(this.panelStreams);
            this.tabPageStreams.Controls.Add(this.scrollBarStreams);
            this.tabPageStreams.Location = new System.Drawing.Point(4, 22);
            this.tabPageStreams.Name = "tabPageStreams";
            this.tabPageStreams.Padding = new System.Windows.Forms.Padding(3);
            this.tabPageStreams.Size = new System.Drawing.Size(818, 482);
            this.tabPageStreams.TabIndex = 4;
            this.tabPageStreams.Text = "Стримы";
            this.tabPageStreams.UseVisualStyleBackColor = true;
            // 
            // panelStreams
            // 
            this.panelStreams.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.panelStreams.BackColor = System.Drawing.Color.Black;
            this.panelStreams.Location = new System.Drawing.Point(0, 0);
            this.panelStreams.Name = "panelStreams";
            this.panelStreams.Size = new System.Drawing.Size(795, 482);
            this.panelStreams.TabIndex = 2;
            // 
            // scrollBarStreams
            // 
            this.scrollBarStreams.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.scrollBarStreams.Location = new System.Drawing.Point(798, 0);
            this.scrollBarStreams.Name = "scrollBarStreams";
            this.scrollBarStreams.Size = new System.Drawing.Size(17, 482);
            this.scrollBarStreams.TabIndex = 1;
            this.scrollBarStreams.Scroll += new System.Windows.Forms.ScrollEventHandler(this.scrollBarStreams_Scroll);

        private void Form1_Resize(object sender, EventArgs e)
        {
            if (tabControlMain.SelectedTab == tabPageStreams)
            {
                StackFramesStream();
            }
            else if (tabControlMain.SelectedTab == tabPageDownload)
            {
                StackFramesDownload();
            }
        }

        private void tabControlMain_Selected(object sender, TabControlEventArgs e)
        {
            if (e.TabPage == tabPageStreams)
            {
                StackFramesStream();
            }
            else if (e.TabPage == tabPageDownload)
            {
                StackFramesDownload();
            }
        }

        private int StackFramesStream()
        {
            if (framesStream.Count > 0)
            {
                int w = framesStream[0].Width;
                int h = framesStream[0].Height;
                int gap = 4;
                int rowsCount;
                int perRow = panelStreams.Width / (w + gap);
                if (perRow == 0)
                {
                    perRow = 1;
                }
                if (framesStream.Count % perRow == 0)
                {
                    rowsCount = framesStream.Count / perRow;
                }
                else
                {
                    rowsCount = framesStream.Count / perRow + 1;
                }
                int xStart = ((panelStreams.Width - scrollBarStreams.Width) / 2) - ((w + gap) * perRow / 2);
                int x = xStart;
                int y = -h - gap;
                for (int i = 0; i < framesStream.Count; i++)
                {
                    if (i % perRow == 0)
                    {
                        y += h + gap;
                        x = xStart;
                    }
                    framesStream[i].Location = new Point(x, y - scrollBarStreams.Value);
                    x += w + gap;
                }

                int j = (h + gap) * rowsCount;
                if (j > panelStreams.Height)
                {
                    scrollBarStreams.Maximum = j;
                    scrollBarStreams.LargeChange = panelStreams.Height;
                    scrollBarStreams.SmallChange = 10;
                    scrollBarStreams.Enabled = true;
                }
                else
                {
                    scrollBarStreams.Enabled = false;
                }
                return rowsCount;
            }
            else
            {
                scrollBarStreams.Enabled = false;
            }
            return 0;
        }

Если ресайзить форму при активной вкладке, то всё нормально.
А если ресайзнуть окно при неактивной вкладке, а потом переключиться на нужную, то ширина панели определяется неправильно и стековка выглядит вот так:

если пощёлкать вкладки туда-сюда, то становится нормально.

FlowLayoutPanel с WrapContents вроде делает это.

Есть ситуации, при которых это не подойдёт. Например, если ширина UserControlов должна зависеть от ширины панели.
Мне надо как здесь
WindowsFormsApp1.7z (21.2 КБ)
только чтобы вместо панели был UserControl.

Тут же просто панель на всю ширину :thinking:

image

Да, он не умеет автоматически ресайзить контролы внутри, такие сложности — это в WPF.

Но менять размер по событию ресайза проще же, чем еще и расcтавлять их )

Если всегда один столбец как выше, то вообще всё просто.

Если несколько, то что-нибудь такое (в зависимости от ширины + ограничение мин и макс размеров)

image image

        private void Form1_Load(object sender, EventArgs e)
        {
            UpdateFlowChildrenSize();
        }

        private void flowLayoutPanel1_Resize(object sender, EventArgs e)
        {
            UpdateFlowChildrenSize();
        }

        private void UpdateFlowChildrenSize()
        {
            int totalWidth = flowLayoutPanel1.Width - SystemInformation.VerticalScrollBarWidth;
            var children = flowLayoutPanel1.Controls;
            foreach (Control child in children)
            {
                int autoWidth = totalWidth / 2;
                child.Width = Math.Min(350, Math.Max(150, autoWidth)) - flowLayoutPanel1.Margin.Horizontal;
                child.Height = (int) (child.Width * 0.6);
            }
        }

или как-нибудь по ширине определять желаемое кол-во столбцов

            int totalWidth = flowLayoutPanel1.Width - SystemInformation.VerticalScrollBarWidth;
            int columnsCount = Math.Max(1, totalWidth / 300);
            var children = flowLayoutPanel1.Controls;
            foreach (Control child in children)
            {
                child.Width = totalWidth / columnsCount - flowLayoutPanel1.Margin.Horizontal;
                child.Height = (int) (child.Width * 0.6);
            }

И да, проблем из-за активности вкладок не увидел.

Нижняя панель - на всю ширину. А верхняя - больше, чем на всю ширину. Вы видите только часть панели. Её можно двигать влево-вправо.
Более понятно здесь:

Не понимаю, как можно писать на WPF, если нет события Paint и нельзя сделать вот так:

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            if (image != null)
            {
                if (!miZoomToolStripMenuItem.Checked)
                {
                    scaledImageRect = miFitToScreenToolStripMenuItem.Checked ?
                        ResizeRect(originalImageRect, pictureBox1.Size) : originalImageRect;

                    if (miTiledViewToolStripMenuItem.Checked)
                    {
                        for (int x = 0; x < pictureBox1.Width; x += scaledImageRect.Width)
                        {
                            for (int y = 0; y < pictureBox1.Height; y += scaledImageRect.Height)
                            {
                                e.Graphics.DrawImage(image, x, y, scaledImageRect.Width, scaledImageRect.Height);
                            }
                        }
                    }
                    else
                    {
                        Rectangle r = CenterRect(scaledImageRect, pictureBox1.ClientRectangle);
                        e.Graphics.DrawImage(image, r.X, r.Y, r.Width, r.Height);
                    }
                }
                else
                {
                    e.Graphics.DrawImage(image, scaledImageRect.X, scaledImageRect.Y, scaledImageRect.Width, scaledImageRect.Height);
                }
            }
        }

Это потому что у вас нет панелей во вкладках. Вы стекуете сразу во вкладке, а надо в панели, которая должна лежать во вкладке.

Если бы ещё события приходили вовремя, а не так как у Майкрософт, то даже костыли не были бы нужны.

А что тут происходит?)

Там есть OnRender, но обычно он не нужен.

А зачем они тут?) Но и с панелью нет проблем.

Стекаться должно только в пределах определённой области. Для этого удобнее всего использовать панель. А за её пределами могут быть кнопки и т.д.

Как нет, если панель в неактивных вкладках попросту не ресайзится? :thinking:

DrawImage() и т.п. всё-равно ведь нет :man_shrugging:

Там происходит “рисуем что хотим где хотим как хотим”. На WPF так нельзя.

А это что? DrawingContext.DrawImage Method (System.Windows.Media) | Microsoft Docs

Ну и можно просто добавлять фигуры и любые другие объекты (хоть кнопки) на канвас.

И так тоже всё ок.
Даже если использовать событие ресайза формы (по идее лучше событие панели).

Была только проблема, что при добавлении первого объекта после ресайза (вкладки не причем) оно немного доресайзивалось. Решилось добавлением UpdateFlowChildrenSize в событие Layout флоу панели. Точнее с ним даже не нужны события ресайза и загрузки.

А вы попробуйте обычную панель. Даже без стэковки. Просто будет она ресайзиться или нет :thinking:

В событии ресайза панели работает, событие формы да, отстает.

        private void panel8_Resize(object sender, EventArgs e)
        {
            button1.Width = panel8.Width - 20;
        }

Поэтому и говорю


В Моно на Линуксе оба не отстают.

И еще, как вы и мечтали, ресайз срабатывает при загрузке.

И нет странного мерцания первой панели при добавлении панелей как выше. (впрочем возможно это просто глюк графики виртуальной машины)

Я, вроде, понял. Раньше я анкоры не юзал, по-этому ресайз панели не работал (или как-то не так работал). Точно не помню.
И еще плохо что нельзя узнать размер клиентской области неактивной вкладки. Кажется, у меня из-за этого все проблемы с ресайзом :thinking:
Наверное, надо просто брать размер активной вкладки. Они же все одинаковые :man_facepalming:
Но тогда в класс UserControlа надо передавать TabControl, в котором он находится.

А, не, я нифига не понял. Если с анкорами панель сама ресайзится вместе с формой, то почему в событии

        private void tabControlMain_Selected(object sender, TabControlEventArgs e)

она определяется такой, какой была до ресайза? :thinking:
То есть, если делать вот так


        private void tabControlMain_Selected(object sender, TabControlEventArgs e)
        {
            /*if (e.TabPage == tabPageStreams)
            {
                StackFramesStream();
            }
            else if (e.TabPage == tabPageDownload)
            {
                StackFramesDownload();
            }*/
        }

        private void panelStreams_Resize(object sender, EventArgs e)
        {
            if (tabControlMain.SelectedTab == tabPageStreams)
            {
                StackFramesStream();
            }
        }

то всё работает даже при неактивной вкладке (хотя не должно!). А если раскомментировать строчки, то будет так как в первом посте.