Моя форма не отображается должным образом, когда она запускается из другого потока - PullRequest
4 голосов
/ 02 октября 2008

Вот ситуация: Я разрабатываю простое приложение со следующей структурой:

  • FormMain (точка запуска)
  • FormNotification
  • CompleFunctions

правый

Ну, в FormMain У меня есть следующая функция:

private void DoItInNewThread(ParameterizedThreadStart pParameterizedThreadStart, object pParameters, ThreadPriority pThreadPriority)
{
    Thread oThread = new Thread(pParameterizedThreadStart);
    oThread.CurrentUICulture = Settings.Instance.Language;
    oThread.IsBackground = true;
    oThread.Priority = pThreadPriority;
    oThread.Name = "μRemote: Background operation";
    oThread.Start(pParameters);
}

Итак, каждый раз, когда мне нужно вызвать трудоемкий метод, расположенный в ComplexFunctions , я делаю следующее:

// This is FormMain.cs
string strSomeParameter = "lala";
DoItInNewThread(new ParameterizedThreadStart(ComplexFunctions.DoSomething), strSomeParameter, ThreadPriority.Normal);

Другой класс, FormNotification, это форма, которая отображает некоторую информацию о процессе для пользователя. Это FormNotification может быть вызвано из FormMain или ComplexFunctions. Пример: * 1 023 *

// This is ComplexFunctions.cs
public void DoSomething(string pSomeParameter)
{
    // Imagine some time consuming task
    FormNotification formNotif = new FormNotification();
    formNotif.Notify();
}

FormNotify имеет таймер, поэтому через 10 секунд форма закрывается. Я не использую formNotif.ShowDialog, потому что я не хочу уделять внимание этой форме. Вы можете проверить эту ссылку , чтобы увидеть, что я делаю в уведомлении.

Хорошо, вот проблема: Когда я вызываю FormNotify из ComplexFunction , которая вызывается из другого потока в FormMain ... это FormNotify исчезает через несколько миллисекунд. Это тот же эффект, что когда вы делаете что-то вроде этого:

using(FormSomething formSomething = new FormSomething)
{
   formSomething.Show();
}

Как этого избежать?

Это возможные решения, которые я не хочу использовать:

  • Использование Thread.Sleep (10000) в FormNotify
  • Использование FormNotif.ShowDialog ()

Это упрощенный сценарий (FormNotify делает некоторые другие интересные вещи, которые просто остаются на 10 секунд, но они не имеют отношения к проблеме).

Спасибо за ваше время !!! И, пожалуйста, извините, мой английский.

Ответы [ 5 ]

5 голосов
/ 02 октября 2008

Почти каждая библиотека графического интерфейса предназначена для разрешения только тех вызовов, которые изменяют графический интерфейс, в одном потоке, предназначенном для этой цели (называемом потоком пользовательского интерфейса). Если вы находитесь в другом потоке, вам необходимо организовать вызов для изменения графического интерфейса в потоке пользовательского интерфейса. В .NET способ сделать это - вызвать Invoke (синхронно) или BeginInvoke (асинхронно). Эквивалентным вызовом Java Swing является invokeLater () - подобные функции есть почти в каждой библиотеке GUI.

Есть нечто, называемое сродством потока. В приложении WinForm есть два потока, один для рендеринга и один для управления пользовательским интерфейсом. Вы имеете дело только с темой пользовательского интерфейса. Поток рендеринга остается скрытым - работает в фоновом режиме. Единственные объекты, созданные в потоке пользовательского интерфейса, могут манипулировать пользовательским интерфейсом, т. Е. Объекты имеют сходство потоков с потоком пользовательского интерфейса.

Поскольку вы пытаетесь обновить пользовательский интерфейс (показать уведомление) из потока, отличного от потока пользовательского интерфейса. Поэтому в вашем рабочем потоке определите делегата и заставьте FormMain прослушивать это событие. В обработчике событий (определите в FormMain) напишите код для отображения FormNotify.

Запустить событие из рабочего потока, когда вы хотите показать уведомление.

Когда поток, отличный от потока создания элемента управления, пытается получить доступ к одному из методов или свойств этого элемента управления, это часто приводит к непредсказуемым результатам. Обычная недопустимая активность потока - это вызов неправильного потока, который обращается к свойству Handle элемента управления. Установите для CheckForIllegalCrossThreadCalls значение true, чтобы легче находить и диагностировать эту активность потока во время отладки. Обратите внимание, что незаконные вызовы между потоками всегда вызывают исключение, когда приложение запускается вне отладчика.

Примечание: установка CheckForIllegalCrossThreadCalls в значение ture должна выполняться только в СИТУАЦИЯХ ОТЛАДКИ. Произойдут непредсказуемые результаты, и вы будете пытаться преследовать ошибки, которые вам будет трудно найти.

5 голосов
/ 02 октября 2008

Вам не разрешено делать вызовы WinForms из других потоков. Посмотрите на BeginInvoke в форме - вы можете вызвать делегата, чтобы показать форму из потока пользовательского интерфейса.

Редактировать: из комментариев (не устанавливайте CheckForIllegalCrossThreadCalls в false).

Подробнее Практически каждая библиотека GUI предназначена для того, чтобы разрешать вызовы, которые изменяют GUI, только в одном потоке, предназначенном для этой цели (называемом потоком UI). Если вы находитесь в другом потоке, вам необходимо организовать вызов для изменения графического интерфейса в потоке пользовательского интерфейса. В .NET способ сделать это - вызвать Invoke (синхронно) или BeginInvoke (асинхронно). Эквивалентным вызовом Java Swing является invokeLater () - подобные функции есть почти в каждой библиотеке GUI.

1 голос
/ 02 октября 2008

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

Поскольку вы пытаетесь обновить пользовательский интерфейс (показать уведомление) из потока, отличного от потока пользовательского интерфейса. Поэтому в вашем рабочем потоке определите делегата и заставьте FormMain прослушивать это событие. В обработчике событий (определите в FormMain) напишите код для отображения FormNotify.

Запустить событие из рабочего потока, когда вы хотите показать уведомление.

0 голосов
/ 15 сентября 2010

Если у вас есть кнопка в форме и вы хотите открыть другую форму Form1, когда пользователь нажимает эту кнопку

private void button1_Click(object sender, EventArgs e)
{
    Thread t = new Thread(new ThreadStart(this.ShowForm1));
    t.Start();
}

Все, что вам нужно сделать, это проверить свойство InvokeRequired и, если да, вызвать метод Invoke вашей формы, передавая ShowForm1 делегат, который в результате вызовет рекурсивный вызов, где InvokeRequired будет ложным

delegate void Func();
private void ShowForm1()
{            
    if (this.InvokeRequired)
    {
        Func f = new Func(ShowForm1);
        this.Invoke(f);
    }
    else
    {
        Form1 form1 = new Form1();
        form1.Show();
    }            
}
0 голосов
/ 02 октября 2008

Используйте API-вызов SetWindowPos , чтобы убедиться, что форма уведомления является самым верхним окном. Этот пост объясняет, как:

http://www.pinvoke.net/default.aspx/user32/SetWindowPos.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...