2013-05-24 20 views
8

Obecnie piszę prostą aplikację WPF 3.5, która wykorzystuje komputer SharePoint do nawiązywania połączeń z witrynami programu SharePoint i generowania informacji o grupach i użytkownikach. Ponieważ ten proces trwa chwilę, chcę pokazać pasek ProgressBar podczas generowania grup. Żądany proces jest następujący:Aktualizowanie interfejsu użytkownika przy użyciu usługi BackgroundWorker w pakiecie WPF

  1. Użytkownik wprowadza przycisk url i kliknięć w celu pobrania danych witryny.
  2. ProgressBar zaczyna animację
  3. Grupy są generowane i nazwy są dodawane do ListView
  4. Upon animacji ukończenie ProgressBar kończy

Problem używam na to, że interfejs nie jest aktualizowana. Ani ProgressBar, ani ListView nie dokonują żadnych zmian. Jeśli ktoś ma jakieś pomysły, aby pomóc w poniższym kodzie, byłoby to bardzo cenne.

private void GetGroupsAndUsersButton_Click(object sender, RoutedEventArgs e) 
{ 
    siteUrl = ""; 

    if (SiteURLTextBox.Text.Length > 0) 
    { 
     FetchDataProgressBar.IsIndeterminate = true; 

     mWorker = new BackgroundWorker(); 
     mWorker.DoWork += new DoWorkEventHandler(worker_DoWork); 
     mWorker.WorkerSupportsCancellation = true; 
     mWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
     mWorker.RunWorkerAsync(); 
    } 
    else 
    { 
     System.Windows.MessageBox.Show("Please enter a URL for the SharePoint site you wish to retrieve data"); 
    } 
} 

private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) 
{ 
    siteUrl = SiteURLTextBox.Text; 
    GroupListView.ItemsSource = null; 

    try 
    { 
     using (SPSite site = new SPSite(siteUrl)) 
     { 
      SPWeb web = site.OpenWeb(); 
      SPGroupCollection collGroups = web.SiteGroups; 
      if (GroupNames == null) 
       GroupNames = new List<string>(); 

      foreach (SPGroup oGroup in collGroups) 
      { 
       GroupListView.Items.Add(new ListViewItem() { Content = oGroup.Name }); 
      } 

      foreach (ListViewItem item in GroupListView.Items) 
      { 
       item.MouseLeftButtonUp += item_MouseLeftButtonUp; 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     System.Windows.MessageBox.Show("Unable to locate a SharePoint site at: " + siteUrl); 
    } 
} 

private void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) 
{ 
    FetchDataProgressBar.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, 
    new Action(
     delegate() 
     { 
      FetchDataProgressBar.IsIndeterminate = false; 
     } 
     )); 
} 

Odpowiedz

7

Początkowo Ciebie trzeba obsługiwać zdarzenia ProgressChanged. Aktualizacja inicjalizacji BackgroundWorker do:

GroupListView.ItemSource = null; 
mWorker = new BackgroundWorker(); 
mWorker.DoWork += new DoWorkEventHandler(worker_DoWork); 
mWorker.WorkerSupportsCancellation = true; 
mWorker.WorkerReportsProgress = true; 
mWorker.ProgressChanged += OnProgressChanged; 
mWorker.RunWorkerCompleted += 
     new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
mWorker.RunWorkerAsync(SiteURLTextBox.Text); 

Potem trzeba dodać OnProgressChanged Handler:

private void OnProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    FetchDataProgressBar.Value = e.ProgressPercentage; 
    ListViewItem toAdd = (ListViewItem)e.UserState; 
    toAdd.MouseLeftButtonUp += item_MouseLeftButtonUp; 
    GroupListView.Items.Add(toAdd); 
} 

Dlatego trzeba zmienić DoWork:

private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) 
{ 
    BackgroundWorker worker = (BackgroundWorker)sender;    
    try 
    { 
     using (SPSite site = new SPSite((String)e.Argument)) 
     { 
      SPWeb web = site.OpenWeb(); 
      SPGroupCollection collGroups = web.SiteGroups; 
      if(GroupNames == null) 
       GroupNames = new List<string>(); 
      int added = 0; 
      foreach(SPGroup oGroup in collGroups) 
      { 
       added++; 
       ListViewItem tmp = new ListViewItem() { 
        Content = oGroup.Name 
       }; 
       worker.ReportProgress((added * 100)/collGroups.Count,tmp); 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show("Unable to locate a SharePoint site at: " + siteUrl); 
    } 
} 

To dlatego, że” nie można zmienić GUI na DoWork.

Po tym, każdy ListViewItem jest dodawany oddzielnie do twojego ListView. Polecam również, aby Twój adres URL był przekazywany jako argument do RunWorkerAsync.

Edytuj: Dodaj wartość procentową do OnProgressChanged.

+0

Dziękuję za odpowiedź. Niestety, wciąż nie ma aktualizacji GUI. Ponadto, podczas debugowania, zmiana zdarzenia nigdy nie zostaje trafiona. Natychmiast pęka na worker_RunWorkerCompleted chociaż (???) – pstricker

+0

czy kiedykolwiek dotarłeś do pętli do try/catch? – Kooki

+0

Po małym majsterkowaniu udało mi się dotrzeć do try/catch, przesuwając kod odwołujący się do elementów UI (GroupListView i SiteUrlTextBox) do zdarzenia click button, ale teraz rzuca wyjątek podczas inicjowania ListViewItem: "Wywołujący wątek musi być STA, ponieważ wiele komponentów interfejsu użytkownika tego wymaga. " – pstricker

1

Musisz:

mWorker.WorkerReportsProgress = true; 
mWorker.ProgressChanged += 
    new ProgressChangedEventHandler(worker_ProgressChanged); 

Następnie w DoWork trzeba zadzwonić:

var worker = (BackgroundWorker)sender; 
worker.ReportProgress(progressAmount); 

Dobry pracował przykład tutaj: http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

+0

Dziękuję za odpowiedź. Niestety nie próbuję aktualizować paska postępu wartością, chcę tylko pokazać animację nieokreśloną. – pstricker

+0

Jeśli tak jest, nie musisz wywoływać połączenia z '.IsIndeterminate = true;' tak jak wykonujesz wywołanie '.IsIndeterminate = false;' – paul

2

W swojej metodzie DoWork manipulujesz kontrolkami WPF w kodzie na wątku tła, czego nie powinieneś robić. W rzeczywistości powinieneś otrzymywać błędy typu "Nie można uzyskać dostępu do kontroli z innego wątku". Prawdopodobnie te wyjątki są przechwytywane przez moduł obsługi błędów typu catch-all, a może nawet MessageBox nie działa z wątku tła.

Jako szybką poprawkę, należałoby utworzyć pola klasy siteURL i collGroups, przenieść wszystko przed blokiem przy użyciu metody GetGroupsAndUsersButton_Click i wszystko począwszy od pierwszej pętli foreach do zdarzenia RunworkerCompleted, tak aby cały kod, który uzyskuje dostęp kontrole działają w wątku UI.

Inną rzeczą, którą powinieneś zmienić jest to, że nie powinieneś tworzyć ListViewItems w kodzie, ale użyj DataTemplate zamiast ... nie jest to jednak związane z twoim problemem.

Powiązane problemy