Вызывающий поток не может получить доступ к этому объекту, потому что другой поток владеет им - PullRequest
1 голос
/ 14 декабря 2011

Я прочитал много сообщений об этой ошибке, но я не понимаю, как ее можно решить в моем решении ((У меня есть диалоговое окно выполнения с некоторой логикой, которое вызывается из MainFrame ButtonClick

void OnBtnClick(object sender, RoutedEventArgs e)
{  
    ProgressDialog dlg = new ProgressDialog("");
    dlg.Closing += new CancelEventHandler(dlg_Closing);
    dlg.Closed += new EventHandler(dlg_Closed);
    //dlg.AutoIncrementInterval = 0;
    LibWrap lwrap = new LibWrap();
    DoWorkEventHandler handler = delegate
    {
        BitmapFrame bf = wrap.engine(BitmapFrame.Create(FXPhotoStudio.App
                                                            .draggedImage),
                                     this.fxPSEditorView); 
    };
    dlg.CurrentLibWrap = lwrap;
    dlg.AutoIncrementInterval = 100;
    dlg.IsCancellingEnabled = true;
    dlg.Owner = Application.Current.MainWindow;
    dlg.RunWorkerThread(0, handler); 
}

Существует также обработчик в том же классе (MainFrame) при закрытом событии для этого диалогового окна индикатора выполнения

void dlg_Closed(object sender, EventArgs e)
{
    try
    { 
        mainFrameView.CurrentImage = effectedImage;//!error here!
    }
}

Эффектное изображение - это поле MainFrame. Это установлено моим ProgressDialog. В ProgressDialog.cs я делаю следующее:

(this.Owner as MainFrame).effectedImage = currentLibVrap.GetEffectedImage;

currentLibVrap был установлен в OnBtnClick - см. Выше Может ли кто-нибудь помочь мне решить эту проблему?

Это код, закрывающий ProgressBarDialog:

    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
      if (!Dispatcher.CheckAccess())
      {
        //run on UI thread
        RunWorkerCompletedEventHandler handler = worker_RunWorkerCompleted;
        Dispatcher.Invoke(DispatcherPriority.SystemIdle, handler, new object[] {sender, e}, null);
        return;
      }

      if (e.Error != null)
      {
        error = e.Error;
      }
      else if (!e.Cancelled)
      {
        //assign result if there was neither exception nor cancel

          (this.Owner as MainWindow).effectedImage = currentLibVrap.GetEffectedImage;//! ok there

        result = e.Result;
      }

  //update UI in case closing the dialog takes a moment
 // progressTimer.Stop();
  progressBar.Value = progressBar.Maximum;
  btnCancel.IsEnabled = false;

  //set the dialog result, which closes the dialog
  DialogResult = error == null && !e.Cancelled;
}

А есть рабочий процесс:

/// Launches a worker thread which is intended to perform
/// work while progress is indicated, and displays the dialog
/// modally in order to block the calling thread.
/// </summary>
/// <param name="argument">A custom object which will be
/// submitted in the <see cref="DoWorkEventArgs.Argument"/>
/// property <paramref name="workHandler"/> callback method.</param>
/// <param name="workHandler">A callback method which is
/// being invoked on a background thread in order to perform
/// the work to be performed.</param>


   public bool RunWorkerThread(object argument, DoWorkEventHandler workHandler)
    {
      if (autoIncrementInterval.HasValue)
      {
        //run timer to increment progress bar
        progressTimer.Interval = TimeSpan.FromMilliseconds(autoIncrementInterval.Value);
        progressTimer.Start();
         // LibWrap lwrap = new LibWrap();
         // BitmapFrame bf = lwrap.engine(BitmapFrame.Create(FXPhotoStudio.App.draggedImage));//(aa.Image);

      }

      //store the UI culture
      uiCulture = CultureInfo.CurrentUICulture;

      //store reference to callback handler and launch worker thread
      workerCallback = workHandler;
      worker.RunWorkerAsync(argument);

      //display modal dialog (blocks caller)
      return ShowDialog() ?? false;
    }

/// <summary>
/// Worker method that gets called from a worker thread.
/// Synchronously calls event listeners that may handle
/// the work load.
/// </summary>
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
  try
  {
    //make sure the UI culture is properly set on the worker thread
    Thread.CurrentThread.CurrentUICulture = uiCulture;
    //invoke the callback method with the designated argument
    workerCallback(sender, e);
  }
  catch (Exception)
  {
    //disable cancelling and rethrow the exception
    //Dispatcher.BeginInvoke(DispatcherPriority.Normal,
     //                      (SendOrPostCallback) delegate { btnCancel.SetValue(Button.IsEnabledProperty, false); },
    //                       null);
    return;
    //throw;
  }
}

Ответы [ 2 ]

1 голос
/ 14 декабря 2011

Вы можете использовать либо Dispather.Invoke, либо Dispatcher.BeginInvoke. Оба будут перенаправлять вызов в поток пользовательского интерфейса (именно об этом ваша ошибка), BeginInvoke предназначен для выполнения тяжелых операций в фоновом потоке, а Invoke - просто маршаллер, поэтому для вашего типа задачи я буду придерживаться последнего один.

Вот как вы это делаете (при условии, что mainFrameView.CurrentImage имеет тип Image, в противном случае просто измените его на то, что есть):

C #

Обновление 1 Используйте уникальное имя для аргумента, чтобы избежать перенаправления с существующими именами переменных.

mainFrameView.Dispatcher.Invoke(new Action<object>((myImage2012) => 
{ mainFrameView.CurrentImage = (Image)myImage2012; }), 
new object[1] { effectedImage });
1 голос
/ 14 декабря 2011

Вам нужно отправить его в поток пользовательского интерфейса, используя Dispatcher.BeginInvoke (отправляет и возвращает немедленно) или Dispatcher.Invoke (блокируется до отправки действия)

Это потому, что по умолчанию потоковая модель для приложения представляет собой однопотоковую квартиру (STA). Это означает, что только поток, создавший элемент пользовательского интерфейса, может взаимодействовать с ним, другие потоки, которые хотят что-то сделать с элементом пользовательского интерфейса, должны передавать свои действия над этими элементами потоку пользовательского интерфейса

void dlg_Closed(object sender, EventArgs e)
{
    try
    { 
        mainFrameView.CurrentImage = effectedImage;//!error here!
    }
}

Вы уверены, что пользовательский интерфейс отправляет это событие? Вы пытались отправить настройку CurrentImage? Не могли бы вы также упростить свой код и оставить только соответствующие методы?

UPD: я имею в виду, вы пытались отправить настройку CurrentImage?

    Application.Current.MainWindow.Dispatcher.BeginInvoke(new Action(()=>
                                          {
                                              mainFrameView.CurrentImage = effectedImage;
                                          }));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...