私のコードは以下の通りです
public CountryStandards()
{
InitializeComponent();
try
{
FillPageControls();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Country Standards", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// Fills the page controls.
/// </summary>
private void FillPageControls()
{
popUpProgressBar.IsOpen = true;
lblProgress.Content = "Loading. Please wait...";
progress.IsIndeterminate = true;
worker = new BackgroundWorker();
worker.DoWork += new System.ComponentModel.DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(worker_ProgressChanged);
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
GetGridData(null, 0); // filling grid
}
private void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progress.Value = e.ProgressPercentage;
}
private void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
worker = null;
popUpProgressBar.IsOpen = false;
//filling Region dropdown
Standards.UDMCountryStandards objUDMCountryStandards = new Standards.UDMCountryStandards();
objUDMCountryStandards.Operation = "SELECT_REGION";
DataSet dsRegionStandards = objStandardsBusinessLayer.GetCountryStandards(objUDMCountryStandards);
if (!StandardsDefault.IsNullOrEmptyDataTable(dsRegionStandards, 0))
StandardsDefault.FillComboBox(cmbRegion, dsRegionStandards.Tables[0], "Region", "RegionId");
//filling Currency dropdown
objUDMCountryStandards = new Standards.UDMCountryStandards();
objUDMCountryStandards.Operation = "SELECT_CURRENCY";
DataSet dsCurrencyStandards = objStandardsBusinessLayer.GetCountryStandards(objUDMCountryStandards);
if (!StandardsDefault.IsNullOrEmptyDataTable(dsCurrencyStandards, 0))
StandardsDefault.FillComboBox(cmbCurrency, dsCurrencyStandards.Tables[0], "CurrencyName", "CurrencyId");
if (Users.UserRole != "Admin")
btnSave.IsEnabled = false;
}
/// <summary>
/// Gets the grid data.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="pageIndex">Index of the page.( used in case of paging) </pamam>
private void GetGridData(object sender, int pageIndex)
{
Standards.UDMCountryStandards objUDMCountryStandards = new Standards.UDMCountryStandards();
objUDMCountryStandards.Operation = "SELECT";
objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null;
DataSet dsCountryStandards = objStandardsBusinessLayer.GetCountryStandards(objUDMCountryStandards);
if (!StandardsDefault.IsNullOrEmptyDataTable(dsCountryStandards, 0) && (chkbxMarketsSearch.IsChecked == true || chkbxBudgetsSearch.IsChecked == true || chkbxProgramsSearch.IsChecked == true))
{
DataTable objDataTable = StandardsDefault.FilterDatatableForModules(dsCountryStandards.Tables[0], "Country", chkbxMarketsSearch, chkbxBudgetsSearch, chkbxProgramsSearch);
dgCountryList.ItemsSource = objDataTable.DefaultView;
}
else
{
MessageBox.Show("No Records Found", "Country Standards", MessageBoxButton.OK, MessageBoxImage.Information);
btnClear_Click(null, null);
}
}
Get grid dataのステップobjUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null;
は例外を投げます
別のスレッドが所有しているため、呼び出し側スレッドはこのオブジェクトにアクセスできません。
ここはどうしたの?
これは、始めたばかりの人々に共通の問題です。メインスレッド以外のスレッドからUI要素を更新するときはいつでも、次のものを使う必要があります。
this.Dispatcher.Invoke(() =>
{
...// your code here.
});
現在のスレッドがコントロールを所有しているかどうかを確認するために control.Dispatcher.CheckAccess()
を使用することもできます。それがそれを所有していれば、あなたのコードは普通に見えます。それ以外の場合は、上記のパターンを使用してください。
Dispatcher.Invoke
のもう1つの良い使い方は、他のタスクを実行する関数でUIを即座に更新することです。
// Force WPF to render UI changes immediately with this magic line of code...
Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle);
これを使ってボタンのテキストを "Processing ..."に更新し、WebClient
リクエストをするときに無効にします。
私の2セントを追加するために、あなたがSystem.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
を通してあなたのコードを呼んでも例外が発生する可能性があります。
重要なのは、コントロールのDispatcher
のInvoke()
をにアクセスしようとしているということです。 System.Windows.Threading.Dispatcher.CurrentDispatcher
と同じです。安全のためにYourControl.Dispatcher.Invoke()
を使うべきです。これに気づく前に、私は数時間頭をぶつけていました。
将来の読者にとって、これは新しいバージョンの.NET(4.0以降)で変更されたようです。 VMのUIバッキングプロパティを更新するときに、正しいディスパッチャについて心配する必要はもうありません。 WPFエンジンは、正しいUIスレッドでスレッド間呼び出しをマーシャリングします。詳細はこちら をご覧ください 。情報とリンクを提供してくれた@aaronburroに感謝します。あなたはまたコメントで下記の私達の会話を読みたいかもしれません。
誰もがWPFおよびスレッドでBitmapSource
を操作しようとし、これと同じメッセージを持っている場合:スレッドパラメータとしてBitmapSource
を渡す前にまずFreeze()
メソッドを呼び出してください。
私はaccess UI
のanother thread insted of UI thread
コンポーネントを使おうとしたのでこれは私と一緒に起こりました
このような
private void button_Click(object sender, RoutedEventArgs e)
{
new Thread(SyncProcces).Start();
}
private void SyncProcces()
{
string val1 = null, val2 = null;
//here is the problem
val1 = textBox1.Text;//access UI in another thread
val2 = textBox2.Text;//access UI in another thread
localStore = new LocalStore(val1);
remoteStore = new RemoteStore(val2);
}
この問題を解決するためには、Candideが上記の答えで述べたように、すべてのui呼び出しを で囲んでください
private void SyncProcces()
{
string val1 = null, val2 = null;
this.Dispatcher.Invoke((Action)(() =>
{//this refer to form in WPF application
val1 = textBox.Text;
val2 = textBox_Copy.Text;
}));
localStore = new LocalStore(val1);
remoteStore = new RemoteStore(val2 );
}
どういうわけかCandideの答えは構築されませんでした。それは私がこれを見つけるように導いたので、それは、役に立ちました、しかし、それは完全に働きました:
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() =>
{
//your code here...
}));
あなたはUIに更新する必要があります。
Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)}));
DotNetが彼の答えで書いたように、私はSystem.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
が常にターゲットコントロールのディスパッチャではないこともわかりました。私はコントロール自身のディスパッチャにアクセスできないので、Application.Current.Dispatcher
を使って問題を解決しました。
問題は、バックグラウンドスレッドからGetGridData
を呼び出していることです。このメソッドは、メインスレッドにバインドされているいくつかのWPFコントロールにアクセスします。バックグラウンドスレッドからそれらにアクセスしようとすると、このエラーが発生します。
正しいスレッドに戻るにはSynchronizationContext.Current.Post
を使うべきです。しかしこの特定のケースでは、あなたがしている仕事の大部分はUIベースであるように思えます。したがって、ただちにUIスレッドに戻って作業を行うために、バックグラウンドスレッドを作成することになります。バックグラウンドスレッドで高価な作業を行い、その後新しいデータをUIスレッドに投稿できるように、コードを少しリファクタリングする必要があります
これは私のために働きます。
new Thread(() =>
{
Thread.CurrentThread.IsBackground = false;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate {
//Your Code here.
}, null);
}).Start();
ここ で述べたように、Dispatcher.Invoke
はUIをフリーズさせる可能性があります。代わりにDispatcher.BeginInvoke
を使用してください。
これは、チェックと呼び出しのディスパッチャー呼び出しを単純化するための便利な拡張クラスです。
使用例:( WPFウィンドウから呼び出します)
this Dispatcher.InvokeIfRequired(new Action(() =>
{
logTextbox.AppendText(message);
logTextbox.ScrollToEnd();
}));
拡張クラス:
using System;
using System.Windows.Threading;
namespace WpfUtility
{
public static class DispatcherExtension
{
public static void InvokeIfRequired(this Dispatcher dispatcher, Action action)
{
if (dispatcher == null)
{
return;
}
if (!dispatcher.CheckAccess())
{
dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
return;
}
action();
}
}
}
WPFアプリケーションにカスケードコンボボックスを追加したときにエラーが発生し続け、このAPIを使用してエラーを解決しました。
using System.Windows.Data;
private readonly object _lock = new object();
private CustomObservableCollection<string> _myUiBoundProperty;
public CustomObservableCollection<string> MyUiBoundProperty
{
get { return _myUiBoundProperty; }
set
{
if (value == _myUiBoundProperty) return;
_myUiBoundProperty = value;
NotifyPropertyChanged(nameof(MyUiBoundProperty));
}
}
public MyViewModelCtor(INavigationService navigationService)
{
// Other code...
BindingOperations.EnableCollectionSynchronization(AvailableDefectSubCategories, _lock );
}
また、別の解決策は、例えばバックグラウンドワーカースレッドではなく、UIスレッドでコントロールが作成されるようにすることです。