System.Windows.Threading.Dispatcher and WinForms?

2019-01-03 10:59发布

Does a System.Windows.Threading.Dispatcher work on the UI-thread of a WinForms application?

If yes, why? It is coming from WindowsBase.dll which seems to be a WPF component.

If not, how can I invoke work units back onto the UI-thread? I've found Control.BeginInvoke(), but it seems clumsy to create a control only to reference the originating thread.

7条回答
看我几分像从前
2楼-- · 2019-01-03 11:27

Dispatcher is a WPF component, not a WinForms component.

If you want to dispatch work items on the UI thread, then you would have to either use Control.BeginInvoke as you've already found, or react to ResetEvents/WaitObjects across threads.

Usually invoking work items on the UI thread is a bad thing unless it's a UI piece of work (ie. updating a control's content or something) in which case the Control.BeginInvoke() would be sufficient.

查看更多
我只想做你的唯一
3楼-- · 2019-01-03 11:27

I provided an example of using System.Windows.Threading.Dispatcher in Windows Form in my answer to question "Parallel Programming using TPL on WinForms" since the previous answer to your question:

If you are sure to be in UI thread (eg. in an button.Click handler), Dispatcher.CurrentDispatcher gives you the UI thread dispatcher that you can use to dispatch from background thread to UI thread as usual.

is either misleading or confusing or lacks the concrete usage context:

  • button.Click handler does not assure to be on UI thread;
  • if you are not on UI thread, it is still possible to use the disparcher of UI thread of WinForms form

One can get dispatcher of WinForm UI thread:

Dispatcher dispatcherUI = Dispatcher.CurrentDispatcher;

in either button click event handler or anywhere else (in form constructor)

And then use it to execute on UI from other threads, see more details on example below in my answer:

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
            );
      }
           );
}
查看更多
▲ chillily
4楼-- · 2019-01-03 11:30

You can use Dispatcher even in a WinForms app.

If you are sure to be on a UI thread (e.g. in an button.Click handler), Dispatcher.CurrentDispatcher gives you the UI thread dispatcher that you can later use to dispatch from background threads to the UI thread as usual.

查看更多
甜甜的少女心
5楼-- · 2019-01-03 11:38

I had similar problem using Oracle dependency class which runs on its own thread within Winforms,

When OnChange event fired from Oracle Dependency, I wanted to show the changes in DataGridView by simply setting DataSource to eventargs.Details (which is essentially a DataTable), and it throws: System.InvalidOperationException was unhandled by user code Message=Cross-thread operation not valid: Control 'dataGridView1' accessed from a thread other than the thread it was created on.

StackOverflow user Brian Peiris (bpeiris@gmail.com) ,collegue of mine showed me this way around:

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;
         }

      }
查看更多
\"骚年 ilove
6楼-- · 2019-01-03 11:43

Use background worker thread as it's UI message pump aware, This MSDN Article though mostly about WPF does state that the BWT is UI aware even for windows forms.

查看更多
Juvenile、少年°
7楼-- · 2019-01-03 11:48

Take a look at backgrounder and see if it fits your needs.

查看更多
登录 后发表回答