C # - Межпотоковая операция - Создать элемент управления в потоке, добавить в основную форму - PullRequest
2 голосов
/ 13 октября 2011

У меня есть старая форма, которую я действительно не хочу переписывать на этом этапе, поэтому я загружаю форму и затем добавляю ее на панель в новой форме пользовательского интерфейса.Это работает нормально, но медленно.Старая форма выполняет большую часть загрузки и сбора данных, и она не очень эффективна.Таким образом, для загрузки больших записей требуется до 30 секунд.Как вы знаете, создание формы затем блокирует основной интерфейс на 30 секунд, пока загружается старая форма.Это действие, которое я пытаюсь предотвратить.Я хочу загрузить новую форму, отобразить gif «Загрузка» на пустой панели, а затем, после загрузки старой формы, удалить изображение «Загрузка» и добавить форму в качестве элемента управления.

И вот начинаетсяпроблема.

Я пытался создать Background Worker, но это вызывает ошибку STA (старая форма имеет несколько собственных потоков загрузки данных), и так как я не могу изменить рабочий на STA, я остановилсяпытаюсь.

Я пытался создать Invoke (и BeginInvoke), и хотя это работает, он не загружает старую форму в потоке.Он просто отправляет его обратно в поток пользовательского интерфейса и выполняет работу там.Опять же это висит пользовательский интерфейс.IE: не то, что я хочу.

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

I 'Мы создали поток, установили STA, запустили его, а затем сделали цикл while с DoEvents, ожидающими его завершения.Конечно, кажется, что все это работает вплоть до обычного добавления формы на панель, а затем я получаю доступ «Control 'ChartForm' из потока, отличного от потока, в котором он был создан».В этой ошибке ChartForm - это старая диаграмма, которая была загружена в потоке.

Я попробовал описанный выше метод, но вместо этого я использовал частное статическое поле для хранения создания старой формы, а затемдобавив его на панель после завершения потока.Это в методе, который создал поток, сразу после цикла while.Та же ошибка.

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

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

private static _ChartForm;
private void LoadPatientChart()
{
    ClearMainPanel(); // Removes any loaded ChartForms from Panel
    if (_Patient == null) // Test to make sure a patient is loaded
        return;

    loadingPanel.Visible = true; // Displays the "Loading" gif

    Thread thread = new Thread(new ThreadStart(this.GetChartForm));
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
    while (thread.ThreadState != ThreadState.Stopped)
        Application.DoEvents(); // Keeps the UI active and waits for the form to load

    this.ChartPanel.Controls.Add(_ChartForm); // This is where the error is
    loadingPanel.Visible = false; // Hide the "Loading" gif

}

private void GetChartForm()
{
    ChartForm chartForm = new ChartForm(_Patient.AcctNum.ToString(), false);
    chartForm.TopLevel = false;
    chartForm.FormBorderStyle = FormBorderStyle.None;
    chartForm.Dock = DockStyle.Fill;
    chartForm.Visible = true;
    _ChartForm = chartForm;
}

Ответы [ 3 ]

3 голосов
/ 13 октября 2011

На самом деле не очень хорошая идея создавать элементы управления UI в любом другом потоке, кроме потока UI.Это технически возможно, но трудно управлять, особенно если новый поток является «временным».

Что вам действительно нужно, так это реорганизовать работу, которую выполняет ChartForm (в процессе созданияон появляется?), и он работает в фоновом потоке, а затем возвращает его в свой поток пользовательского интерфейса, а затем создает ChartForm, передавая результаты этой работы.ИМХО это лучший дизайн в любом случае;хотя это может быть много работы для вас.

0 голосов
/ 13 октября 2011

Я пытался создать Invoke (и BeginInvoke), и пока это работает, это действительно не загружает старую форму в потоке. Просто отправляет вернуться к потоку пользовательского интерфейса и делает работу там. Опять это зависает UI. И.Е .: Не то, что я хочу.

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

0 голосов
/ 13 октября 2011

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

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

...