System.Windows.Threading.Dispatcher и WinForms?
Работает ли System.Windows.Threading.Dispatcher
пользовательский интерфейс приложения WinForms
?
Если да, то почему? Он поступает из WindowsBase.dll, который представляется компонентом WPF
.
Если нет, как я могу вызвать рабочие единицы обратно на UI-поток? Я нашел Control.BeginInvoke()
, но кажется неуклюжим создать элемент управления только для ссылки на исходный поток.
Ответы
Ответ 1
Вы можете использовать Dispatcher
даже в приложении WinForms.
Если вы уверены, что находитесь в потоке пользовательского интерфейса (например, в обработчике button.Click), Dispatcher.CurrentDispatcher
предоставляет диспетчер потоков пользовательского интерфейса, который позже можно использовать для отправки из потоков фона в поток пользовательского интерфейса, как обычно.
Ответ 2
Dispatcher - это компонент WPF, а не компонент WinForms.
Если вы хотите отправлять рабочие элементы в поток пользовательского интерфейса, вам придется либо использовать Control.BeginInvoke, как вы уже нашли, либо отреагировать на ResetEvents/WaitObjects по потокам.
Обычно вызывать рабочие элементы в потоке пользовательского интерфейса - это плохо, если только это не является частью работы пользовательского интерфейса (т.е. обновлением контрольного контента или чего-то еще), и в этом случае Control.BeginInvoke() будет достаточным.
Ответ 3
Я привел пример использования System.Windows.Threading.Dispatcher
в Windows Form в ответе на вопрос "Параллельное программирование с использованием TPL на WinForms", поскольку предыдущий ответ на ваш вопрос:
Если вы уверены, что находитесь в потоке пользовательского интерфейса (например, в кнопке button.Click handler), Dispatcher.CurrentDispatcher предоставляет диспетчер потоков пользовательского интерфейса, который вы можете использовать для отправки из фонового потока в поток пользовательского интерфейса, как обычно.
либо вводит в заблуждение, либо вводит в заблуждение, либо не имеет конкретного контекста использования:
-
button.Click
обработчик не гарантирует, что будет в потоке пользовательского интерфейса;
- Если вы не используете поток пользовательского интерфейса, по-прежнему можно использовать несовпадение нити пользовательского интерфейса формы WinForms
Можно получить диспетчера потока WinForm UI:
Dispatcher dispatcherUI = Dispatcher.CurrentDispatcher;
в любом обработчике событий нажатия кнопки или в другом месте (в конструкторе форм)
И затем используйте его для выполнения в пользовательском интерфейсе из других потоков, более подробно см. ниже в мой ответ:
private void button1_Click(object sender, EventArgs e)
{
Dispatcher dispUI = Dispatcher.CurrentDispatcher;
for (int i = 2; i < 20; i++)
{
int j = i;
var t = Task.Factory.StartNew
(() =>
{
var result = SumRootN(j);
dispUI.BeginInvoke
(new Action
(() => richTextBox1.Text += "root " + j.ToString()
+ " " + result.ToString() + Environment.NewLine
)
, null
);
}
);
}
Ответ 4
Использовать поток рабочего потока, поскольку он информирован о сообщении сообщения пользовательского интерфейса, Эта статья MSDN, но в основном о WPF указывает, что BWT является пользовательским интерфейсом, для оконных форм.
Ответ 5
У меня была аналогичная проблема с использованием класса зависимостей Oracle, который работает в своем потоке в Winforms,
Когда событие OnChange запускается из Oracle Dependency, я хотел показать изменения в DataGridView, просто установив
DataSource для eventargs.Details(который по существу является DataTable)
и он бросает:
System.InvalidOperationException не был обработан кодом пользователя Сообщение = операция поперечного потока недействительна: элемент управления 'dataGridView1' доступен из потока, отличного от потока, в котором он был создан.
Пользователь StackOverflow Брайан Пейрис ([email protected]), мой коллега показал мне так:
void dep_OnChange(object sender, OracleNotificationEventArgs arg)
{
Console.WriteLine("Notification received");
int infoSum = int.Parse(arg.Details.Compute("Sum(Info)", "Info is not null").ToString());
InfoSum x = (InfoSum)infoSum;
foreach (DataRow dr in arg.Details.Rows)
{
Console.WriteLine(string.Format("Operation(InfoSum)= {0}", Enum.GetName(typeof(InfoSum), x)));
Console.WriteLine(string.Format("ontable={0} Rowid={1},info={2}", dr.Field<string>("ResourceName"), dr.Field<string>("rowid"), dr.Field<Int32>("info")));
}
// Following will throw cross-thread
// dataGridView1.DataSource = arg.Details;
// instead of line above use the following
dataGridView1.BeginInvoke((Action)(()=>dataGridView1.DataSource = arg.Details));
IsNotified = true;
}
}
Ответ 6
Посмотрите backgrounder и посмотрите, подходит ли оно вашим потребностям.
Ответ 7
Иногда компонент Timer полезен и легко настраивается в WinForms, просто устанавливает его интервал, а затем включает его, а затем убедитесь, что первое, что вы делаете в обработчике событий Tick, - отключить себя.
Я думаю, что Timer запускает код в своем потоке, поэтому вам может понадобиться выполнить BeginInvoke (вызываемый объектом WinForm [this]) для запуска вашего действия.
private WebBrowserDocumentCompletedEventHandler handler; //need to make it a class field for the handler below (anonymous delegates seem to capture state at point of definition, so they can't capture their own reference)
private string imageFilename;
private bool exit;
public void CaptureScreenshot(Uri address = null, string imageFilename = null, int msecDelay = 0, bool exit = false)
{
handler = (s, e) =>
{
webBrowser.DocumentCompleted -= handler; //must do first
this.imageFilename = imageFilename;
this.exit = exit;
timerScreenshot.Interval = (msecDelay > 0)? msecDelay : 1;
timerScreenshot.Enabled = true;
};
webBrowser.DocumentCompleted += handler;
Go(address); //if address == null, will use URL from UI
}
private void timerScreenshot_Tick(object sender, EventArgs e)
{
timerScreenshot.Enabled = false; //must do first
BeginInvoke((Action)(() => //Invoke at UI thread
{ //run in UI thread
BringToFront();
Bitmap bitmap = webBrowser.GetScreenshot();
if (imageFilename == null)
imageFilename = bitmap.ShowSaveFileDialog();
if (imageFilename != null)
{
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(imageFilename))); //create any parent directories needed
bitmap.Save(imageFilename);
}
bitmap.Dispose(); //release bitmap resources
if (exit)
Close(); //this should close the app, since this is the main form
}), null);
}
вы можете видеть выше в действии с помощью инструмента WebCapture (http://gallery.clipflair.net/WebCapture, исходный код: http://ClipFlair.codeplex.com, см. папку Tools/WebCapture), которая захватывает скриншоты с веб-сайтов. BTW, если вы хотите вызвать исполняемый файл из командной строки, убедитесь, что вы перешли в "Свойства проекта" и на вкладке "Безопасность" отключите защиту ClickOnce (иначе он не сможет получить доступ к командной строке)