2015-05-22 3 views
1

У меня есть длинная операция, я бы хотел показать индикатор занятости Extended Toolkits. Я сделал предыдущий пост об этом, и он был исправлен Wpf Extended toolkit BusyIndicator not showing during operation. Однако во время этого вызова мне нужно взаимодействовать с элементом пользовательского интерфейса (canvas), и я получаю «вызывающий поток должен быть STA, потому что для этого требуется множество компонентов пользовательского интерфейса». Я понимаю (сейчас), что фоновой работник (см. Код):Показание индикатора занятости в потоке STA

 private void CboItemId_SelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 

     BackgroundWorker _backgroundWorker = new BackgroundWorker(); 
     _backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork); 
     _backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted); 
     ItemSearchBusyIndicator.IsBusy = true; 

     // Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; 
     if (RdoItemSearch.IsChecked == false) return; 
     ///backgroundWorker_DoWork(null, null); 

     if (CboItemId.SelectedValue == null) return; 
     if (CboItemId.SelectedValue.ToString() != string.Empty) 
     { 
      selectedItem = CboItemId.SelectedValue.ToString(); 
      _backgroundWorker.RunWorkerAsync(); 
     } 
     // Mouse.OverrideCursor = System.Windows.Input.Cursors.Arrow; 
    } 

    public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
    { 

      LoadItemData(selectedItem); 
    } 

использует MTA и не может быть установлен в STA. Так что я попытался назвать внутреннюю функцию, которая использует elelment UI в своем собственном потоке:

 public void LoadItemData(string itemId) 
    { 

     Axapta ax = new Axapta(); 
     files.Clear(); 
     try 
     { 
      ax.Logon(Settings.Default.Server, null, Settings.Default.Test, null); 
      AxaptaContainer path = (AxaptaContainer)ax.CallStaticClassMethod(Settings.Default.ClassName, Settings.Default.ItemData, itemId); 
      for (int i = 1; i <= path.Count; i++) 
      { 
       AxaptaContainer somestring = (AxaptaContainer)path.get_Item(i); 
       for (int j = 1; j <= somestring.Count; j += 2) 
       { 
        string extension = Path.GetExtension(somestring.get_Item(j + 1).ToString().ToLower()); 
        if (extension == ".jpg" 
         || extension == ".jpeg" 
         || extension == ".gif" 
         || extension == ".png" 
         || extension == ".bmp" 
         || extension == ".pdf") 
         /* key=path - value=description */ 
         files.Add(somestring.get_Item(j + 1).ToString(), somestring.get_Item(j).ToString()); 
       } 
      } 

      // _canvas.Children.Clear(); 
      Thread t = new Thread(new ThreadStart(LoadPictures)); 
      t.SetApartmentState(ApartmentState.STA); 
      t.Start(); 

     } 
     catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message); 
     } 
     finally 
     { 
      ax.Logoff(); 
     } 
    } 

Heres где я взаимодействую с холста элемента:

 private void LoadPictures() 
    { 

     foreach (DictionaryEntry filePath in files) 
     { 

      try 
      { 

       Picture p = new Picture(); 
       ToolTip t = new ToolTip(); 
       t.Content = filePath.Value; 
       p.ToolTip = t; 
       TextBlock tb = new TextBlock(); 
       tb.Text = filePath.Value.ToString(); 
       Canvas.SetTop(tb, y); 
       Canvas.SetLeft(tb, x); 

        p.ImagePath = filePath.Key.ToString(); 
        p.OriginalImagePath = filePath.Key.ToString(); 
        p.ImageName = filePath.Value.ToString(); 
        _canvas.Children.Add(p); //<-------This is where i seem to error 
      } 
      catch (Exception ex) 
      { 
       MessageBox.Show("Error:" + ex.Message,"File Load Error",MessageBoxButton.OK,MessageBoxImage.Error); 
      } 
     } 
    } 

, но я получаю «Вызывающий поток не может получить доступ к этот объект, потому что ему принадлежит другой поток » Я не знаю, как вызвать функцию long (LoadItemData()), показывая BusyIndicator без фонового рабочего. Любая помощь приветствуется

+0

Почему бы вам не сделать его проще и использовать объект control.Dispatcher? –

+0

см. Здесь http://stackoverflow.com/questions/7428260/wp7-invalid-cross-thread-access-scheduledtaskagent/7428442#7428442, он должен быть похож на wpf – thumbmunkeys

+1

Вы должны вызвать LoadPictures() из обработчика события RunWorkerCompleted BGW , –

ответ

1

Есть несколько подходов:

1) Async binding, это не рекомендуется, но она есть. Вы можете запускать долго выполняемую задачу в getter свойства, инфраструктура предотвратит блокировку пользовательского интерфейса, когда он будет завершен - пользовательский интерфейс будет обновлен.

2) Используйте BackgroundWorker или Task/Thread для запуска кода, но вызывайте его в поток пользовательского интерфейса. В вашем примере:

Dispatcher.InvokeAsync(() => _canvas.Children.Add(p)); 

3) Вы можете полностью заблокировать поток пользовательского интерфейса главного окна, никаких проблем. Но, чтобы указать о его занятости вы можете создать окно в другом потоке и показать там состояние занятости (запуск анимации и т.д.):

 var thread = new Thread(() => 
     { 
      var window = new SomeWindow(); 
      window.ShowDialog(); 
     }); 
     thread.SetApartmentState(ApartmentState.STA); 
     thread.IsBackground = true; 
     thread.Start(); 
+0

при использовании Dispatcher.Invoke (() => _canvas.Children.Add (p)); Я получаю сообщение об ошибке: «Невозможно преобразовать лямбда-выражение в тип« System.Delegate », потому что это не тип делегата« – rigamonk

+0

». Я преобразовал второе предложение в: Dispatcher.Invoke ((Action) (() => _canvas.Children.Add (п))); потому что он не будет компилироваться. Но я все равно получаю «вызывающий поток не может получить доступ к этому объекту, потому что другой поток владеет им». Это потому, что я называю LoadPicture() новым потоком? – rigamonk

+0

Я не уверен, что, вероятно, означает, что мой ответ неправильный. Можете ли вы отправить сообщение о полном исключении? – Sinatr

Смежные вопросы