Расположите элементы управления FlowLayoutPanel так, чтобы они занимали минимум места.

Я использую элемент управления FlowLayoutPanel, и мне интересно, можно ли как-нибудь выполнить автоматическую компоновку элементов управления, чтобы они занимали как можно меньше места? Например, добавьте 1 элемент управления шириной 100 пикселей, три элемента управления шириной 500 пикселей и снова 1 элемент управления шириной 100 пикселей.

Это должно выглядеть так:

##Control1##
##############Control2##########################
##############Control3##########################
##############Control4##########################
##Control5##

Как вы видите, некоторое пространство тратится впустую, можно ли как-то оптимизировать его расположение, чтобы добиться чего-то подобного без ручного перемещения?

##Control1## ##Control5##
##############Control2##########################
##############Control3##########################
##############Control4##########################

person Marduk    schedule 14.10.2014    source источник
comment
msdn.microsoft .com/en-us/library/   -  person aybe    schedule 15.10.2014
comment
к сожалению, но это не решает мою проблему   -  person Marduk    schedule 15.10.2014
comment
Установлено ли для FlowBreak свойств элементов внутри Panel значение False?   -  person aybe    schedule 15.10.2014
comment
Да, все элементы управления имеют свойство разрыва потока при значении false   -  person Marduk    schedule 15.10.2014


Ответы (1)


Как расположить элементы управления так, чтобы они занимали минимум места:

(вверху: исходный макет, внизу: итоговый макет)

введите здесь описание изображения

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

    private void Form1_Load(object sender, EventArgs e) {
        var flowLayoutPanel = flowLayoutPanel1;
        ReorganizeFlowLayoutPanel(flowLayoutPanel);
    }

    private static void ReorganizeFlowLayoutPanel(FlowLayoutPanel flowLayoutPanel) {
        var width = flowLayoutPanel.Width;
        var controls = flowLayoutPanel.Controls.OfType<Control>().ToList();
        var ascending = new List<Control>(controls.OrderBy(s => s.Width));
        var descending = new List<Control>(controls.OrderByDescending(s => s.Width));

        var list = new List<Control>();
        while (ascending.Count > 0) {
            Control smallest = ascending[0];
            ascending.RemoveAt(0);
            if (ascending.Count == 0) {
                list.Add(smallest);
                break;
            }
            foreach (var largest in descending) {
                if (smallest.Width + largest.Width < width) {
                    list.Add(smallest);
                    list.Add(largest);
                    ascending.Remove(largest);
                    descending.Remove(largest);
                    descending.Remove(smallest);
                    break;
                }
            }
        }
        var i = 0;
        foreach (var control in list) {
            flowLayoutPanel.Controls.SetChildIndex(control, i++);
        }
    }
}

Теперь вы можете столкнуться с проблемой взаимодействия с пользователем при его использовании, как будто вы ожидаете, что некоторые из них будут расположены в определенном порядке. Чтобы решить эту проблему, вы можете установить тег Control.Tag на некоторую строку, такую ​​как "Priority=1", "Priority=2" и т. д.

Затем замените определение ascending на:

var ascending = new List<Control>(controls.OrderBy(s => s.Width).ThenBy(s=>(string)s.Tag) );

Упорядочить без тегов приоритета:

введите здесь описание изображения

Упорядочить с помощью тегов приоритета:

введите здесь описание изображения

Пример, который я показал, очень прост, вы можете использовать свою собственную логику, чтобы лучше различать элементы управления.

Примечание: это действительно наивный подход, если вы ищете более сложный алгоритм, вам следует вдохновиться созданием текстур листа спрайтов, таким как http://spritesheetpacker.codeplex.com/.


Наоборот: (оригинальный ответ)

Вы можете каким-то образом реорганизовать элементы управления, используя этот метод:

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

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1() {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e) {
            var flowLayoutPanel = flowLayoutPanel1;
            ReorganizeFlowLayoutPanel(flowLayoutPanel);
        }

        private static void ReorganizeFlowLayoutPanel(FlowLayoutPanel flowLayoutPanel) {
            var controls = flowLayoutPanel.Controls.OfType<Control>().OrderBy(s => s.Width);
            var index = 0;
            foreach (var tuple in controls) {
                flowLayoutPanel.Controls.SetChildIndex(tuple, index++);
            }
        }
    }
}

Исходный макет:

введите здесь описание изображения

Новый макет:

введите здесь описание изображения

Примечание: они упорядочены в порядке их обнаружения, т. е. если вы не хотите. 3 после нет. 4 вам придется вручную поместить его после нет. 4 перед реорганизацией панели.

person aybe    schedule 15.10.2014
comment
хм, мне нужно что-то противоположное тому, что вы показали, мне нужно перейти от вашего нового макета к исходному макету, чтобы иметь как можно меньше неиспользуемого пространства. - person Marduk; 17.10.2014
comment
Извини ! :D Проверьте мое редактирование наоборот, должно быть легко понять, как я это сделал. Я также пробовал добавить несколько элементов управления (и нечетное их количество), он не взорвался, так что, думаю, все в порядке;) - person aybe; 18.10.2014
comment
Отлично, кажется, это именно то, что мне нужно :) Я изучу ваш код, чтобы внедрить это в свой проект. Спасибо - person Marduk; 18.10.2014