Çağıran evre bu nesneye farklı bir evrenin sahibi olduğundan erişemiyor


342

Kodum aşağıdaki gibidir

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;Izgara verilerini alma adımındaki istisna atılır

Farklı bir iş parçacığının sahibi olduğu için çağıran iş parçacığı bu nesneye erişemiyor.

Burada yanlış olan ne?


Yanıtlar:


699

Bu, insanların başlamasıyla ilgili yaygın bir sorundur. Kullanıcı arayüzü öğelerinizi ana iş parçacığı dışındaki bir iş parçacığından güncellediğinizde şunları kullanmanız gerekir:

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

control.Dispatcher.CheckAccess()Geçerli iş parçacığının denetime sahip olup olmadığını denetlemek için de kullanabilirsiniz . Eğer sahipse, kodunuz normal görünüyor. Aksi takdirde, yukarıdaki deseni kullanın.


3
OP ile aynı problemim var; Benim sorun şimdi olay şimdi bir yığın taşmasına neden olmasıdır. : \
Malavos

2
Eski projeme geri döndüm ve bunu çözdüm. Ayrıca, bunu + 1'lemeyi unutmuştum. Bu yöntem oldukça iyi çalışıyor! Yalnızca yerelleştirilmiş kaynaklarımızı yüklemek için iş parçacıkları kullanarak uygulama yükleme süremi 10 saniye veya daha fazla artırır. Şerefe!
Malavos

4
Eğer yanılmıyorsam, sahibi olmayan bir diziden bir UI nesnesi bile okuyamazsınız; beni biraz şaşırttı.
Elliot

32
Application.Current.Dispatcher.Invoke(MyMethod, DispatcherPriority.ContextIdle);cevap
JumpingJezza

2
+1. Ha! Bazı şeyleri ayırmak için bazı WPF hackery için kullandım. Statik bir bağlamdaydım, bu yüzden kullanamadım this.Dispatcher.Invoke... bunun yerine ... myControl.Dispatcher.Invoke:) Bir nesneyi geri döndürmem gerekiyordu myControlDispatcher.Invoke<object>(() => myControl.DataContext);
C. Tewalt

53

Bunun için bir başka iyi kullanım Dispatcher.Invoke, diğer görevleri gerçekleştiren bir işlevde UI'yi hemen güncellemektir:

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

Düğme metnini " İşleniyor ... " olarak güncellemek ve WebClientistekte bulunurken devre dışı bırakmak için bunu kullanıyorum .


4
Bu cevap Meta'da tartışılıyor. meta.stackoverflow.com/questions/361844/…
JDB hala Monica

Bu benim kontrolümün internetten veri almasını engelledi mi?
Waseem Ahmad Naeem

41

Benim 2 sent eklemek için, kodunuzu çağırsanız bile istisna oluşabilir System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke().
Mesele şu ki Invoke(),Dispatcher bir erişmeye çalıştığınızı kontrol bazı durumlarda aynı olmayabilir, hangi System.Windows.Threading.Dispatcher.CurrentDispatcher. Bunun yerine YourControl.Dispatcher.Invoke()güvenli olmak için kullanmalısınız . Bunu fark etmeden önce birkaç saat başıma vuruyordum.

Güncelleme

Gelecekteki okuyucular için, .NET'in daha yeni sürümlerinde (4.0 ve üstü) bu değişmiş gibi görünüyor. Artık VM'nizdeki UI destek özelliklerini güncellerken doğru dağıtım programı hakkında endişelenmenize gerek yok. WPF motoru doğru UI iş parçacığında çapraz iş parçacığı çağrılarını marshal edecektir. Daha fazla ayrıntıyı burada görebilirsiniz . Bilgi ve bağlantı için @aaronburro'ya teşekkürler. Görüşmelerimizde aşağıdaki görüşmemizi de okumak isteyebilirsiniz.


4
@ l33t: WPF, bir uygulamada her birinin kendine ait birden fazla kullanıcı arayüzü iş parçacığını destekler Dispatcher. Bu durumlarda (kuşkusuz nadirdir), arama Control.Dispatchergüvenli yaklaşımdır. Referans için bu makaleyi ve bu SO yazısını (özellikle Squidward'ın cevabı) görebilirsiniz.
dotNET

1
İlginç bir şekilde, bu sayfaya googled ve indiğimde bu istisna ile karşı karşıya kaldım ve çoğumuzun yaptığı gibi, o zaman sorunumu çözmeyen en yüksek oy alan cevabı denedim. Daha sonra bu nedeni buldum ve akran geliştiricileri için buraya gönderdim.
dotNET

1
@ l33t, MVVM'yi doğru kullanıyorsanız, o zaman sorun olmamalıdır. Görünüm mutlaka hangi Dispatcher kullandığını bilirken, ViewModels ve Modeller hiçbir kontrol bilmez ve kontrolleri bilmeye gerek yoktur.
aaronburro

1
@aaronburro: Sorun, VM'nin alternatif iş parçacıkları (örneğin Görevler, Zamanlayıcı tabanlı eylemler, Paralel sorgular) üzerinde eylemler başlatmak isteyebileceği ve işlem ilerledikçe UI'yi (RaisePropertyChanged vb. aracılığıyla) güncellemek isteyebileceğidir. UI olmayan iş parçacığından bir UI denetimine erişmek ve bu nedenle bu istisnayla sonuçlanır. Bu sorunu çözecek doğru bir MVVM yaklaşımı bilmiyorum .
dotNET

1
WPF ciltleme motoru, özellik değiştirme olaylarını otomatik olarak doğru Dağıtım Programına aktarır. Bu nedenle VM'nin Dispatcher'ı bilmesine gerek yoktur; tek yapması gereken sadece mülk değiştiren olayları gündeme getirmek. WinForms bağlayıcı farklı bir hikaye.
aaronburro

34

Bu sorunla karşılaşırsanız ve UI Kontrolleri bir oluşturulmuş olsaydı ayrı işçisi ile çalışırken iplik BitmapSourceveya ImageSourceçağrı, WPF Freeze()ilk geçirmeden önce yöntemiyle BitmapSourceveya ImageSourceherhangi bir yönteme parametre olarak. Kullanmak Application.Current.Dispatcher.Invoke()bu gibi durumlarda çalışmaz


24
Ah, kimsenin anlamadığı bir şeyi çözmek için iyi bir eski belirsiz ve gizemli hile gibi bir şey yok.
Edwin

2
Bunun neden işe yaradığına ve bunu nasıl kendi başıma çözebileceğime dair daha fazla bilgi isterim.
Xavier Shay


25

Denedim çünkü bu benimle oldu access UIbileşen inanother thread insted of UI thread

bunun gibi

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

Bu sorunu çözmek için, Candide'nin cevabında yukarıda belirtilenlerin içine herhangi bir kullanıcı arayüzü çağrısını sarın

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
Upvoted, çünkü bu değil yinelenen bir cevap veya plagiaristic, ancak bunun yerine daha önce yayınlanan ne için kredi verirken diğer cevaplar, eksik olduğunu iyi bir örnek oluşturmaktadır.
Panzercrisis

Olumlu oy açık bir cevap içindir. Her ne kadar diğerleri tarafından yazılmış olsa da, bu sıkışmış herkes için açıkça anlaşılır.
NishantM

15

Nedense Candide'nin cevabı oluşmadı. Yine de, bana yardımcı oldu, bu da mükemmel çalıştı:

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

Formun sınıfından arama yapmamış olabilirsiniz. Ya Pencereye bir referans alabilir ya da önerdiklerinizi kullanabilirsiniz.
Simone

4
Sizin için işe yaradıysa, ilk etapta kullanmak gereksizdi. System.Windows.Threading.Dispatcher.CurrentDispatcherolduğu geçerli iş parçacığı memuru . Bir arka plan iş parçacığı üzerinde iseniz Yani, o oluyor değil UI parçacığının memuru olacak. UI iş parçacığının dağıtım programına erişmek için tuşunu kullanın System.Windows.Application.Current.Dispatcher.

13

Kullanıcı arayüzünde güncelleme yapmanız gerekiyor.

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

4

Bu benim için çalışıyor.

new Thread(() =>
        {

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

          //Your Code here.

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

3

Ben de System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()bunun dotNet'in cevabına yazdığı gibi, her zaman hedef kontrol dağıtıcısı olmadığını gördüm . Kontrolün kendi dağıtım programına erişemedim, bu yüzden kullandım Application.Current.Dispatcherve sorunu çözdüm.


2

Sorun şu ki, GetGridData bir arka plan iş parçacığından olmasıdır. Bu yöntem, ana iş parçacığına bağlı birkaç WPF denetimine erişir. Bir arka plan iş parçacığından bunlara erişme girişimleri bu hataya neden olur.

Doğru iş parçacığına geri dönmek için kullanmalısınız SynchronizationContext.Current.Post. Ancak bu özel durumda yaptığınız işin çoğunluğu kullanıcı arayüzüne dayalı gibi görünüyor. Bu nedenle, hemen UI iş parçacığına geri dönmek ve biraz iş yapmak için bir arka plan iş parçacığı oluşturacaksınız. Arka plan iş parçacığında pahalı çalışmasını yapabilmek ve daha sonra yeni verileri UI iş parçacığına göndermek için kodunuzu biraz yeniden düzenlemeniz gerekir.


2

Belirtildiği gibi burada , Dispatcher.InvokeUI donabilirler. Bunun Dispatcher.BeginInvokeyerine kullanmalısınız .

İşte kontrol ve çağrı dağıtıcı çağrılmasını basitleştirmek için kullanışlı bir uzantı sınıfı.

Örnek kullanım: (WPF penceresinden arama)

this Dispatcher.InvokeIfRequired(new Action(() =>
{
    logTextbox.AppendText(message);
    logTextbox.ScrollToEnd();
}));

Uzantı sınıfı:

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

Ayrıca, başka bir çözüm, kontrollerinizin, örneğin bir arka plan iş parçacığı tarafından değil, UI iş parçacığında oluşturulmasını sağlamaktır.


0

WPF uygulamama basamaklı birleşik giriş kutuları eklediğimde hatayı almaya devam ettim ve bu API'yi kullanarak hatayı çözdüm:

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

    }

Ayrıntılar için lütfen bkz https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Windows.Data.BindingOperations.EnableCollectionSynchronization);k(TargetFrameworkMoniker-.NETFramework,Version % 3Dv4.7), k (DevLang-csharp) rd = true

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.