Страницы

Поиск по вопросам

четверг, 25 октября 2018 г.

Как заставить FlowLayoutPanel нормально работать?

Пытаюсь присобачить FlowLayoutPanel с выравнивание элементов снизу вверх. Либо я совсем не умею ей пользоваться, либо она вообще работает как-то не так…
Что это за -6, составляющие разницу между шириной панели и контролов в ней? При уменьшении ширины элементов ширина панели не уменьшается. Нижний Padding в 2 раза больше чем должен быть. Нет горизонтального скролла, если переполнение происходит из-за паддинга. Те части, которые не помещаются из-за паддинга, оказываются вне области прокрутки. В том числе сверху.
Скринвидео для пунктов 1 - 4, 121 МБ Скринвидео для пункта 5, 12 МБ


Ответ

Что это за -6, составляющие разницу между шириной панели и контролов в ней?
Эта разница из-за Margin-ов вложенных контролов. Значения по умолчанию - 3px со всех сторон.
При уменьшении ширины элементов ширина панели не уменьшается.
Для того чтобы ширина панели могла менять размер, нужно выставить свойство AutoSize = true, а если хотите чтобы она умела не только расширяться, но и уменьшаться, то свойство AutoSizeMode = AutoSizeMode.GrowAndShrink
Нижний Padding в 2 раза больше чем должен быть. Нет горизонтального скролла, если переполнение происходит из-за паддинга. Те части, которые не помещаются из-за паддинга, оказываются вне области прокрутки. В том числе сверху.
С падингами эта панель нормально работает только при включенном AutoSize и отключенном AutoScroll, подробности ниже.
Наконец то добрался написать обновление к ответу. Итак:
Особенности FlowLayoutPanel
А если чуть точнее, то всех контролов-контейнеров, унаследованных от ScrollableControl. Для желающих самостоятельно все проверить и любителей копаться в коде исходники тут. Исследования проводились на родных .NET-овских формах. В Mono поведение может отличаться.
В данном вопросе нас интересуют следующие свойства панели: AutoSize, AutoSizeMode, AutoScroll, Padding, MaximumSize, Dock и их взаимное влияние друг на друга, влияние на внешний вид и поведение панели. На первый взгляд все просто, но есть некоторое количество тараканов, способных отнять массу времени и, возможно, нервов.
Начнем с AutoSize - согласно документации, если включить данное свойство, то размер контрола будет подогнан под размер его содержимого. Этим поведением можно в известной степени управлять используя свойства MaximumSize и MinimumSize, думаю тут пояснения не требуются. А также свойством AutoSizeMode, которое может принимать одно из двух значений: GrowOnly и GrowAndShrink. В первом случае панель будет увеличиваться чтобы отобразить на себе весь контент, но не будет сжиматься если контент меньше, во втором изменение размера происходит в обе стороны. Если кроме этого задействовать свойство Padding, то размер элемента будет подогнан с учетом отступов. Добавляем Dock, любой кроме Fill и опять получаем то, что ожидаем. Отступы учтены, контент отображается весь, если умещается в родительский котрол нашей панели.
Если все ожидаемо - зачем столько текста? А вот зачем, попробуйте добавить сюда AutoScrol. Если свойство MaximumSize не выставлено, и включен AutoSize, то ничего не произойдет. Вообще. Оно вполне логично, зачем включать скрол если все умещается и так. Теперь выставим MaximumSize так, чтобы контент перестал умещаться, например по высоте. Появляются скрол бары и начинаются чудеса. Сколбары появляются сразу оба, даже если контент умещается по ширине, т.к. вертикальный скролбар при включении уменьшает ширину отображаемой части панели и контент перестает умещаться по ширине. А как же AutoSize? а ни как, он ничего не знает про скролбар и продолжает вычислять ширину панели по ширине ее контента.
Далее все чудесатее. Кажется, что ситуацию можно исправить с помощью свойства Padding, о нем то AutoSize знает и учитывает. Да можно, но только если использовать, в нашем случае с вертикальным скролом, правый отступ. AutoSize пересчитает размер с его учетом и скролбар наложится не на контент, провоцируя появление второго скролбара, а на отступ, если размер отступа достаточный. Добавим левый отступ - все хорошо, размер опять пересчитался правильно. Добавим нижний отступ - ничего не произошло, как будто мы его и не добавляли. Ладно, не страшно, ну пропал отступ и черт с ним, контент то виден и скролится. Добавим верхний отступ - "Шеф! все пропало!" отступ то виден, но ровно на размер этого отступа контент съехал вниз и скрол не позволяет сместить контент так, что бы он стал виден весь. Соответственно если у нас активен горизонтальный скролбар, то такой же эффект будет от применения левого отступа. Еще интересный момент, если указать только левый отступ, причем размера больше чем ширина скролбара, то отступ будет виден, вертикальный скролбар наложится на контент, НО горизонтальный скролбар не появится, т.к. ширины достаточно для размещения контента.
Причина такого безобразия, как мне удалось выяснить из исходников (тык) кроется в том, что, при пересчете размеров клиентской области, учитываются только нижний и правый отступы.
Возможно я перечислил не все варианты странного поведения отступов на панелях, но общую суть проблемы постарался раскрыть полностью.
Что делать?
Для себя, я в свое время нашел способ получить желаемую картинку, может пригодится и вам.
Для FlowLayoutPanel выставить AutoSize = true и AutoSizeMode = AutoSizeMode.GrowAndShrink, выключить AutoScroll и убрать любые ограничения на размер. Остальное по вкусу, включая Padding Разместить FlowLayoutPanel внутри обычной Panel с включенным AutoScroll и выключенным AutoSize Подписаться на событие FlowLayoutPanel.SizeChanged и менять размер Panel с учетом наличия или отсутствия скролбаров на ней.
Почему понадобилась внешняя панель? тут все просто, если использовать ручной подбор размера FlowLayoutPanel, то придется реагировать на изменение любого из вложенных элементов и потенциальное добавление или удаление элементов. Для внешней же панели, нужно следить только за изменением размера FlowLayoutPanel

Комментариев нет:

Отправить комментарий