別のスレッドが所有しているため、呼び出しスレッドはこのオブジェクトにアクセスできません


341

私のコードは以下の通りです

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

objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null;グリッドデータの取得のステップで例外がスローされる

別のスレッドが所有しているため、呼び出しスレッドはこのオブジェクトにアクセスできません。

ここで何が問題になっていますか?


回答:


697

これは、入門者に共通の問題です。メインスレッド以外のスレッドからUI要素を更新する場合は常に、次のものを使用する必要があります。

this.Dispatcher.Invoke(() =>
{
    ...// your code here.
});

を使用control.Dispatcher.CheckAccess()して、現在のスレッドがコントロールを所有しているかどうかを確認することもできます。所有している場合、コードは通常のように見えます。それ以外の場合は、上記のパターンを使用します。


3
OPと同じ問題があります。私の問題は、イベントがスタックオーバーフローを引き起こすことです。:\
Malavos 2014年

2
私の古いプロジェクトに戻ってこれを解決しました。また、+ 1するのを忘れていました。この方法はかなりうまくいきます!スレッドを使用してローカライズされたリソースを読み込むだけで、アプリケーションの読み込み時間を10秒以上改善します。乾杯!
Malavos 2014年

4
私が間違っていなければ、所有者以外のスレッドからUIオブジェクトを読み取ることさえできません。少し驚いた。
Elliot

32
Application.Current.Dispatcher.Invoke(MyMethod, DispatcherPriority.ContextIdle);この回答
JumpingJezza '21

2
+1。ハ!これをWPFハッカーに使用して、物事を分離しました。私は静的なコンテキストにいたため、使用できませんでしたthis.Dispatcher.Invoke....代わりに... myControl.Dispatcher.Invoke:)オブジェクトを返す必要があったので、そうしましたmyControlDispatcher.Invoke<object>(() => myControl.DataContext)
C.テワルト2016

52

のもう1つの有効な使用法Dispatcher.Invokeは、他のタスクを実行する関数のUIをすぐに更新することです。

// Force WPF to render UI changes immediately with this magic line of code...
Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle);

これを使用してボタンのテキストを「Processing ...」に更新し、WebClientリクエストを行う間は無効にします。


4
この答えはメタで議論されています。meta.stackoverflow.com/questions/361844/...
JDBはまだモニカ覚え

これにより、インターネットからデータを取得できなくなりましたか?
Waseem Ahmad Naeem

41

私の2セントを追加するために、を介してコードを呼び出しても例外が発生する可能性がありますSystem.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
ポイントは、あなたが呼び出す必要があることであるInvoke()Dispatcherアクセスしようとしていることをコントロールいくつかのケースではと同じではない可能性があります、System.Windows.Threading.Dispatcher.CurrentDispatcher。だから代わりにあなたは使うべきですYourControl.Dispatcher.Invoke()に安全のする必要があります。私はこれに気付く前に数時間頭を叩いていた。

更新

将来の読者にとって、これは.NETの新しいバージョン(4.0以降)で変更されたようです。VMのUIバッキングプロパティを更新するときに、正しいディスパッチャーについて心配する必要がなくなりました。WPFエンジンは、正しいUIスレッドでクロススレッド呼び出しをマーシャリングします。詳細はこちらをご覧ください。情報とリンクを提供してくれた@aaronburroに感謝します。コメントで以下の会話を読むこともできます。


4
@ l33t:WPFは1つのアプリケーションで複数のUIスレッドをサポートし、それぞれに独自のがありDispatcherます。それらのケース(確かにまれです)では、呼び出しControl.Dispatcherが安全なアプローチです。参考までに、この記事このSO投稿(特にSquidwardの回答)をご覧ください。
dotNET 2016

1
興味深いことに、私がグーグルしてこのページにアクセスしたとき、私はこの例外に直面していました。そして、私たちのほとんどがそうであるように、最高の投票を試みましたが、私の問題は解決しませんでした。次に、この理由を見つけて、ピア開発者向けにここに投稿しました。
dotNET 2016

1
@ l33t、MVVMを正しく使用している場合は問題ありません。ビューは必然的にDispatcherが使用しているものを認識しますが、ViewModelsおよびModelsはコントロールについて何も認識せず、コントロールについて知る必要もありません。
aaronburro

1
@aaronburro:問題は、VMが代替スレッド(タスク、タイマーベースのアクション、並列クエリなど)でアクションを起動したり、操作の進行に応じて(RaisePropertyChangedなどを介して)UIを更新したりする可能性があることです。非UIスレッドからUIコントロールにアクセスするため、この例外が発生します。この問題を解決する正しいMVVMアプローチを知りません。
dotNET 2018

1
WPFバインディングエンジンは、プロパティ変更イベントを適切なDispatcherに自動的にマーシャリングします。これが、VMがDispatcherを知る必要がない理由です。プロパティの変更イベントを発生させるだけです。WinFormsバインディングは別の話です。
aaronburro

34

この問題が発生し、UIコントロールがWPF でまたはWPFで作業しているときに別のワーカースレッドで作成されたBitmapSource場合ImageSourceは、Freeze()最初にメソッドを呼び出してから、BitmapSourceまたはImageSourceをパラメーターとして任意のメソッドに渡します。このApplication.Current.Dispatcher.Invoke()ような場合、使用は機能しません


24
ああ、誰も理解していない何かを解決するための古き良き漠然とした不思議なトリックのようなものはありません。
エドウィン

2
これが機能する理由と、自分でそれを理解する方法についての詳細情報が欲しいです。
Xavier Shay


25

これは私が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 );
}

1
これは重複した回答や盗作ではないため、賛成票を投じられましたが、代わりに、以前に投稿されたものを信用しながら、他の回答が欠けていた良い例を提供しています。
Panzercrisis 2016

賛成票は明確な答えです。同じことが他の人によって書かれましたが、これは行き詰まっている人にとっては明らかです。
NishantM 2017年

15

なんらかの理由で、Candideの答えは構築されませんでした。しかし、それは私がこれを見つけることにつながり、完全に機能しましたので、役に立ちました:

System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() =>
{
   //your code here...
}));

フォームのクラスから呼び出していない可能性があります。ウィンドウへの参照を取得することも、提案したものを使用することもできます。
Simone、

4
うまくいったなら、そもそも使う必要はありませんでした。System.Windows.Threading.Dispatcher.CurrentDispatcherある現在のスレッドのディスパッチャ。つまり、バックグラウンドスレッドを使用している場合は、UIスレッドのディスパッチャにはなりません。UIスレッドのディスパッチャーにアクセスするには、を使用しますSystem.Windows.Application.Current.Dispatcher

13

UIを更新する必要があるので、

Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)})); 

4

これでうまくいきます。

new Thread(() =>
        {

        Thread.CurrentThread.IsBackground = false;
        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate {

          //Your Code here.

        }, null);
        }).Start();

3

System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()dotNetが彼の回答で書いたように、それは常にターゲットコントロールのディスパッチャではないこともわかりました。私はコントロール自身のディスパッチャーにアクセスできなかったので、それを使用Application.Current.Dispatcherして問題を解決しました。


2

問題はGetGridData、バックグラウンドスレッドから呼び出していることです。このメソッドは、メインスレッドにバインドされているいくつかのWPFコントロールにアクセスします。バックグラウンドスレッドからアクセスしようとすると、このエラーが発生します。

正しいスレッドに戻るには、を使用する必要がありますSynchronizationContext.Current.Post。ただし、この特定のケースでは、実行している作業の大部分がUIベースであるようです。したがって、UIスレッドにすぐに戻り、いくつかの作業を行うだけのバックグラウンドスレッドを作成します。バックグラウンドスレッドで負荷の高い作業を実行できるようにコードを少しリファクタリングし、その後新しいデータをUIスレッドにポストする必要があります。


2

ここで述べたように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();
        }
    }
}

0

また、別の解決策は、たとえばバックグラウンドワーカースレッドではなく、UIスレッドでコントロールが作成されるようにすることです。


0

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

    }

詳細については、https://msdn.microsoft.com/query/dev14.query?appId = Dev14IDEF1&l = EN-US&k = k(System.Windows.Data.BindingOperations.EnableCollectionSynchronization) ; k(TargetFrameworkMoniker-.NETFrameworkVersion %3Dv4.7); k(DevLang-csharp)&rd = true

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.