2013-05-24 14 views
8

Attualmente sto scrivendo una semplice applicazione WPF 3.5 che utilizza la COM di SharePoint per effettuare chiamate ai siti di SharePoint e generare informazioni su gruppi e utenti. Dato che questo processo richiede un po 'di tempo, voglio mostrare un ProgressBar mentre i gruppi vengono generati. Il processo desiderato è il seguente:Aggiornamento dell'interfaccia utente con BackgroundWorker in WPF

  1. L'utente inserisce il pulsante url e fa clic per recuperare i dati del sito.
  2. ProgressBar inizia l'animazione
  3. gruppi vengono generati e nomi si aggiungono a un ListView
  4. Al termine dell'animazione ProgressBar finisce

Il problema che sto funzionando in è che l'interfaccia utente non viene mai aggiornato. Né ProgressBar o ListView apporta alcuna modifica. Se qualcuno ha qualche idea per aiutare con il codice qui sotto sarebbe molto apprezzato.

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

risposta

7

In un primo momento è necessario supportare gli eventi ProgressChanged. Aggiorna il tuo BackgroundWorker inizializzazione:

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

Dopo di che si deve aggiungere un gestore OnProgressChanged:

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

Pertanto è necessario cambiare il vostro 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); 
    } 
} 

Questo perché' Non è consentito cambiare la GUI su DoWork.

Successivamente, ogni ListViewItem viene aggiunto separatamente al numero ListView. Vorrei anche raccomandare che il tuo URL sia passato come argomento a RunWorkerAsync.

Modifica: aggiungere la percentuale a OnProgressChanged.

+0

Grazie per la risposta. Purtroppo, nessun aggiornamento della GUI rimane. Inoltre, durante il debug, l'evento modificato non viene mai colpito. Si rompe immediatamente a worker_RunWorkerCompleted però (???) – pstricker

+0

hai mai raggiunto il ciclo in try/catch? – Kooki

+0

Dopo un po 'di tentativi sono riuscito a raggiungere il try/catch spostando il codice che fa riferimento agli elementi dell'interfaccia utente (GroupListView e SiteUrlTextBox) all'evento clic del pulsante, tuttavia ora sta generando un'eccezione all'avvio di ListViewItem: "Il thread chiamante deve essere STA, perché molti componenti dell'interfaccia utente richiedono questo. " – pstricker

1

Avrete bisogno di:

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

Poi, nel tuo DoWork è necessario chiamare:

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

Buono lavorato esempio qui: http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

+0

Grazie per la risposta. Purtroppo non sto cercando di aggiornare la barra di avanzamento con un valore, voglio solo mostrare l'animazione indeterminata. – pstricker

+0

In tal caso, non è sufficiente richiamare la chiamata a '.IsIndeterminate = true;' proprio come si fa con la chiamata a '.IsIndeterminate = false;' – paul

2

Nel metodo DoWork, si stanno manipolando i controlli WPF nel codice su un thread in background, cosa che non si dovrebbe fare. In realtà, dovresti ricevere errori come "Impossibile accedere al controllo da altro thread". Probabilmente queste eccezioni sono catturate dal tuo gestore di errori catch-all, e forse anche il MessageBox non funziona dal thread in background.

Come soluzione rapida, è necessario creare campi classe siteURL e collGroups, spostare tutto prima del blocco using nel metodo GetGroupsAndUsersButton_Click e tutto ciò che inizia con il primo ciclo foreach fino all'evento RunworkerCompleted, in modo che tutto il codice che accede i controlli vengono eseguiti sul thread dell'interfaccia utente.

Un'altra cosa che dovresti cambiare è che non dovresti creare ListViewItems nel codice, ma usare un DataTemplate invece ... questo non è collegato al tuo problema, però.

Problemi correlati