_System.Windows.Threading.Dispatcher
_ はWinForms
アプリケーションのUIスレッドで機能しますか?
はいの場合、なぜですか? WPF
コンポーネントのように見えるWindowsBase.dllから来ています。
そうでない場合、どのようにしてワークユニットをUIスレッドに呼び出すことができますか?私は Control.BeginInvoke()
を見つけましたが、元のスレッドを参照するためだけにコントロールを作成するのは扱いにくいようです。
WinFormsアプリでもDispatcher
を使用できます。
UIスレッドを使用している場合(button.Clickハンドラーなど)、Dispatcher.CurrentDispatcher
は、通常どおりバックグラウンドスレッドからUIスレッドにディスパッチするために後で使用できるUIスレッドディスパッチャーを提供します。
DispatcherはWPFコンポーネントであり、WinFormsコンポーネントではありません。
UIスレッドで作業項目をディスパッチしたい場合は、既に見つけたようにControl.BeginInvokeを使用するか、スレッド間でResetEvents/WaitObjectsに反応する必要があります。
通常、UIスレッドでの作業項目の呼び出しは、UIの作業(つまり、コントロールのコンテンツの更新など)でない限り、悪いことです。その場合は、Control.BeginInvoke()で十分です。
WindowsフォームでSystem.Windows.Threading.Dispatcher
を使用した例を提供しました 質問への私の回答で "WinFormsでのTPLを使用した並列プログラミング" 質問への以前の回答 :
UIスレッド(たとえば、button.Clickハンドラー)にいることが確実な場合、Dispatcher.CurrentDispatcherは、通常どおりバックグラウンドスレッドからUIスレッドにディスパッチするために使用できるUIスレッドディスパッチャーを提供します。
誤解を招く、または混乱する、または具体的な使用コンテキストが欠けている:
button.Click
ハンドラは、UIスレッド上にあることを保証しません。WinForm UIスレッドのディスパッチャーを取得できます。
Dispatcher dispatcherUI = Dispatcher.CurrentDispatcher;
ボタンクリックイベントハンドラーまたはその他の場所(フォームコンストラクター内)
そして、それを使用して他のスレッドからUIで実行します。以下の例の詳細を 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
);
}
);
}
Winforms内の独自のスレッドで実行されるOracle依存クラスを使用して同様の問題がありました。
OnChangeイベントがOracle Dependencyから発生したとき、DataSourceをeventargs.Details(基本的にはDataTable)に設定するだけでDataGridViewに変更を表示したかったので、System.InvalidOperationExceptionが処理されませんでしたユーザーコードによってメッセージ=クロススレッド操作は無効です:作成されたスレッド以外のスレッドからアクセスされたコントロール 'dataGridView1'
StackOverflowのユーザーであるBrian Peiris([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;
}
}
UIメッセージポンプを認識するため、バックグラウンドワーカースレッドを使用します このMSDN記事 ほとんどのWPFについては、WindowsフォームでもBWTがUIを認識すると述べています。
時々、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フォルダーを参照)。ところで、コマンドラインから実行可能ファイルを呼び出す場合は、プロジェクトの[プロパティ]に移動し、[セキュリティ]タブでClickOnceセキュリティをオフにしてください(そうでない場合、コマンドラインにアクセスできません)。
backgrounder を見て、ニーズに合っているかどうかを確認してください。