Как определить, когда пользовательский элемент управления полностью загружен и показан?

В stackoverflow уже было несколько похожих вопросов, но я не нашел ответа

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

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

ИЗМЕНИТЬ

Сделать это более понятным. У меня есть дочерние элементы управления на контейнере страниц, и у меня есть список настраиваемых элементов управления, которые я пытаюсь загрузить позже. Проблема с двумя подходами, описанными в нескольких ответах ниже, заключается в том, что когда я начинаю загружать элементы управления, они не позволяют показывать другим дочерним элементам управления в контейнере, и именно поэтому у меня есть эти уродливые эффекты (и я делаю это с BackgroundWorker, но в любом случае он должен взаимодействовать с основным потоком, чтобы добавить элементы управления в список)

Ответ 1

Чтобы сделать пользовательский интерфейс более отзывчивым, вы должны отправить сообщение (Control.BeginInvoke), выполнить одну операцию, отправить себе другое сообщение. Затем каждый раз, когда вы делаете что-либо, следующий шаг будет поставлен в очередь после всех пользовательских сообщений, поэтому действия пользователя будут обработаны быстро.

Один действительно отличный подход - использовать yield return и позволить компилятору заботиться обо всех логиках замыкания:

IEnumerable AsyncLoadUI()
{
    var p = new Panel();
    Controls.Add(p);
    yield return null;

    for( int i = 0; i < 50; ++i ) {
        var txt = new TextBox();
        p.Controls.Add(txt);
        yield return null;
    }
}

override void OnLoad(EventArgs e)
{
    IEnumerator tasks = AsyncLoadUI().GetEnumerator();
    MethodInvoker msg = null;
    msg = delegate { if (tasks.MoveNext()) BeginInvoke(msg); };
    msg();
}

Ответ 2

Взгляните на мое решение, предлагаемое другому. У них была очень похожая проблема. Подождите, пока все закончит свою загрузку, прежде чем делать какое-то действие, но не каждый раз, когда форма обязательно становится "активирована" или "показана". Это связано с привязкой к Load loader вашего внешнего контроля интереса. В вашем случае, с вкладкой страницы, но пример решения я предоставил был на уровне FORM.

Ответ 3

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

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

Если вы предпочитаете загружать свой список асинхронно (в фоновом потоке), то идея состоит в том, чтобы начать фоновый поток (обычно с помощью BackgroundWorker), который делает сложную работу по подготовке списка элементов, которые нужно добавить - когда это закончится (или не удалось), поле формы/списка обновляется с предоставленным списком элементов.

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