[C#] 다른 스레드가 소유하고 있기 때문에 호출 스레드가이 오브젝트에 액세스 할 수 없습니다.

내 코드는 다음과 같습니다

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;그리드 데이터 가져 오기 단계 에서 예외가 발생 함

다른 스레드가 소유하고 있으므로 호출 스레드가이 오브젝트에 액세스 할 수 없습니다.

무슨 일이야?



답변

이것은 사람들이 시작하는 일반적인 문제입니다. 메인 스레드 이외의 스레드에서 UI 요소를 업데이트 할 때마다 다음을 사용해야합니다.

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

control.Dispatcher.CheckAccess()현재 스레드가 컨트롤을 소유하는지 여부를 확인할 수도 있습니다 . 그것이 소유하고 있다면 코드는 정상적으로 보입니다. 그렇지 않으면 위 패턴을 사용하십시오.


답변

다른 용도로 Dispatcher.Invoke는 다른 작업을 수행하는 함수에서 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().
요점은 당신이 전화를해야한다는 것입니다 Invoke()Dispatcher액세스하려는 것을 제어 일부의 경우와 동일하지 않을 수있는 System.Windows.Threading.Dispatcher.CurrentDispatcher. 대신 YourControl.Dispatcher.Invoke()안전을 위해 사용해야합니다. 나는 이것을 깨닫기 전에 두 시간 동안 머리를 두드리고 있었다.

최신 정보

향후 독자를 위해 최신 버전의 .NET (4.0 이상)에서는 이것이 변경된 것으로 보입니다. 이제 VM에서 UI 백업 속성을 업데이트 할 때 올바른 디스패처에 대해 더 이상 걱정할 필요가 없습니다. WPF 엔진은 올바른 UI 스레드에서 스레드 간 호출을 마샬링합니다. 자세한 내용은 여기를 참조 하십시오 . 정보 및 링크에 대한 @aaronburro에게 감사합니다. 아래의 의견을 주석으로 읽으십시오.


답변

이 문제가 발생하고 UI 컨트롤은에 작성된 경우 별도의 작업자 와 작업 할 때 스레드 BitmapSource또는 ImageSource전화, WPF에 Freeze()먼저 전달하기 전에 방법 BitmapSource또는 ImageSource어떤 메서드에 매개 변수로. Application.Current.Dispatcher.Invoke()그러한 경우에는 사용 이 작동하지 않습니다


답변

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