Winforms: свойства местоположения, полученные во время события Layout FlowLayoutPanel, не отражают обновленный макет. Почему это?

Я использую неавтоматический элемент управления VScrollBar для панели FlowLayoutPanel, содержащей элементы управления UserControl.

Мне нужно выяснить, расположены ли какие-либо элементы управления в моей FlowLayoutPanel за пределами клиентской области. Я хочу использовать эту информацию, чтобы определить, виден ли VScrollBar или нет. Я поместил следующий код в метод обработчика событий FlowLayoutPanel Layout:

bool lookingForControl = true;
bool controlBelowClientArea = false;
int controlIndex = 0;
int controlBottomPos;
Control[] controlsTemp = new Control[leftFlowLayoutPanel.Controls.Count];
leftFlowLayoutPanel.Controls.CopyTo(controlsTemp, 0);
while (lookingForControl) {
    controlBottomPos = controlsTemp[controlIndex].Bottom +
        controlsTemp[controlIndex].Margin.Bottom;

    debugTextBox.AppendText("Bottom position of control " + controlIndex + 
        ": " + controlBottomPos + "\n");

    if (controlBottomPos > leftFlowLayoutPanel.ClientSize.Height) {
        controlBelowClientArea = true;
        lookingForControl = false;
    }
    controlIndex ++;
    if (controlIndex == leftFlowLayoutPanel.Controls.Count) {
        lookingForControl = false;
    }
}

Вот выходные данные для debugTextBox, полученные в результате добавления четырех последовательных элементов управления к элементу управления 2, для которого свойство AutoSize имеет значение true. Элемент управления 2 содержится в leftFlowLayoutPanel, и элементы управления добавляются к нему нажатием кнопки, содержащейся в нем:

  • Форма только что загружена:
  • Нижнее положение регулятора 0:3
  • Нижнее положение регулятора 1:128
  • Нижнее положение регулятора 2: 253
  • Добавлен первый элемент управления:
  • Нижнее положение регулятора 0:3
  • Нижнее положение регулятора 1:128
  • Нижнее положение регулятора 2: 253
  • Добавлен второй элемент управления:
  • Нижнее положение регулятора 0:3
  • Нижнее положение регулятора 1: 226
  • Нижнее положение регулятора 2: 351
  • Добавлен третий элемент управления:
  • Нижнее положение регулятора 0:3
  • Нижнее положение регулятора 1: 324
  • Нижнее положение регулятора 2: 449
  • Добавлен четвертый элемент управления:
  • Нижнее положение регулятора 0:3
  • Нижнее положение регулятора 1: 422
  • Нижнее положение регулятора 2: 547

После добавления четвертого элемента управления одно из нижних краев элемента управления превышает высоту ClientArea своего контейнера (458), и элементу управленияBelowClientArea присваивается значение true.

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

Единственный ответ, который пришел мне в голову, заключается в том, что событие Layout происходит до того, как все значения свойств будут правильно установлены. Возможно, он вызывается перед выполнением какой-либо встроенной логики компоновки. Если это так, есть ли более подходящее событие, которое я мог бы обработать?


person Simon Crowe    schedule 04.08.2012    source источник
comment
Это встроенная функция. Установите для свойства AutoScroll панели значение True.   -  person Hans Passant    schedule 04.08.2012
comment
Я должен был сделать это яснее: я попытался использовать встроенную функцию и обнаружил, что это вызвало слишком много проблем. Я хочу использовать только вертикальную полосу прокрутки, например.   -  person Simon Crowe    schedule 04.08.2012
comment
Что ж, если вы намеренно обрезаете элементы управления, просто убедитесь, что они никогда не шире, чем ClientSize.Width панели. По крайней мере, это не будет мерзко.   -  person Hans Passant    schedule 04.08.2012
comment
У меня есть для этого логика; ширина дочерних узлов leftFlowLayoutPanel определяется одним элементом управления guide, ширину которого я установил равной ширине клиентской области при соответствующих событиях. К сожалению, мне пришлось бы использовать код, аналогичный тому, что я пытаюсь сделать сейчас, чтобы изменить ширину первого дочернего элемента управления guide при отображении вертикальной полосы, в результате чего ширина клиентской области снижаться. Если мне не повезет с моим текущим подходом, я могу вернуться к автоматическим полосам прокрутки. У меня было что-то, что наполовину сработало...   -  person Simon Crowe    schedule 04.08.2012


Ответы (4)


Единственный ответ, который пришел мне в голову, заключается в том, что событие Layout происходит до того, как значения всех свойств установлены должным образом.

Если это действительно проблема, вы можете попробовать следующее в качестве обходного пути.

using System;
using System.Windows.Forms;

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

            // Sign up for the FlowLayoutPanel Layout event.
            // When that event occurs, run your layout logic
            // using BeginInvoke to give the control a chance
            // to "settle down".
            //

            this.flowLayoutPanel1.Layout += delegate { this.BeginInvoke( ( Action )this.DoYourWorkHere ); };
        }

        void DoYourWorkHere()
        {
            //TODO: do your custom layout logic here.
        }
    }
}
person ahazzah    schedule 04.08.2012
comment
Ваше предложение вызвало запуск кода, но не в требуемое время. Однако это привело меня к следующему решению: private void leftFlowLayoutPanel_Layout(object sender, LayoutEventArgs e) { if (this.IsHandleCreated) { this.BeginInvoke(( Action )this.leftFlowPanelScrollBarLogic); } } проверка IsHandleCreated используется, чтобы избежать исключения InvalidOperationException. Спасибо! - person Simon Crowe; 05.08.2012
comment
Если мой код помог вам найти ответ, как насчет голосования? - person ahazzah; 07.08.2012

я пробую простое приложение, в котором я проверяю, находится ли кнопка в области FlowLayoutPanel, если это правда, она показывает текстовое сообщение «out», оно работает:

 private void button2_Click(object sender, EventArgs e)
        {

                Button btn = new Button();
                flowLayoutPanel1.Controls.Add(btn);
            if((btn.Size.Height + btn.Location.Y) > (flowLayoutPanel1.Size.Height + flowLayoutPanel1.Location.Y))
            {
                textBox1.Text = "out";
            }
        }
person Hassan Boutougha    schedule 04.08.2012
comment
Боюсь, моя проблема связана с событием макета. Есть события Click, которые приводят к созданию новых элементов управления и, следовательно, к изменению размера их контейнеров в моем FlowLayoutPanel. К сожалению, нажатая кнопка не является членом того же класса, что и мой FlowLayoutControl или VScrollBar, поэтому я не могу использовать это событие. - person Simon Crowe; 04.08.2012
comment
но проблема только в том, что чтобы проверить, находится ли элемент управления в области flowlayoutControl, вам нужно сделать: ctl.Size.Height + ctl.Location.Y › FlowLayoutControl..Size.Height + FlowLayoutControl.Location.Y здесь я используйте кнопку, чтобы сделать простое приложение - person Hassan Boutougha; 04.08.2012
comment
Строка 8 в моем коде: controlTemp[controlIndex].Bottom ... Свойство bottom эквивалентно ctl.Location.Y + ctl.Size.Height. (msdn.microsoft.com/en-us /библиотека/) - person Simon Crowe; 04.08.2012
comment
вы правы, но leftFlowLayoutPanel.ClientSize.Height не эквивалентен FlowLayoutControl..Size.Height + FlowLayoutControl.Location.Y - person Hassan Boutougha; 04.08.2012
comment
Я изменил часть своего кода, начинающуюся со строки 8, на: Вверх; и часть строки 15 для: (controlBottomPos › (leftFlowLayoutPanel.Size.Height + leftFlowLayoutPanel.Location.Y)) Вывод был таким же, как и с моим исходным кодом, и ControlBelowClientArea не было установлено значение true в нужное время. Спасибо за вашу помощь, но у меня была эта проблема с каждым свойством, которое я пытался использовать из leftFlowLayoutPanel.Controls. - person Simon Crowe; 04.08.2012

попробуй это

 while (lookingForControl) {
    controlBottomPos = controlsTemp[controlIndex].Size.Height  +
    controlsTemp[controlIndex].Location.Y ;

    debugTextBox.AppendText("Bottom position of control " + controlIndex + 
        ": " + controlBottomPos + "\n");

    if (controlBottomPos > leftFlowLayoutPanel.Size.Height + leftFlowLayoutPanel.Location.Y) {
        controlBelowClientArea = true;
        lookingForControl = false;
    }
    controlIndex ++;
    if (controlIndex == leftFlowLayoutPanel.Controls.Count) {
        lookingForControl = false;
    }
}
person Hassan Boutougha    schedule 04.08.2012
comment
Единственная разница, которую я заметил при запуске этого кода, заключается в том, что вывод после загрузки формы был следующим: Нижнее положение элемента управления 0: 1; Нижнее положение регулятора 1: 122; Нижняя позиция элемента управления 2: 247. Очевидно, это связано с тем, что поля элементов управления 2 (нижнее), 6 (верхнее) и 6 (верхнее) были исключены из суммы. Еще раз спасибо, но не повезло. - person Simon Crowe; 04.08.2012

Чтобы получить актуальные значения свойств, я использовал следующий код для вызова делегата, ссылающегося на метод, содержащий исходный код, из метода обработчика событий Layout.

private void leftFlowLayoutPanel_Layout(object sender, LayoutEventArgs e) {
    if (this.IsHandleCreated) {
        this.BeginInvoke((Action)this.OriginalLayoutCode);
    }
}

Я использовал проверку IsHandleCreated, чтобы избежать возможного исключения InvalidOperationException.

person Simon Crowe    schedule 05.08.2012