2010-09-27 11 views
7

Okay, ich habe eine Lade-Animation, die ausgeführt wird, während eine große DataTable ausgefüllt wird, um den Benutzer wissen zu lassen, dass das Programm nicht eingefroren ist. Ich habe die Animation gut funktioniert, aber es friert ein, während die DataTable auch aktualisiert wird. Gibt es eine Möglichkeit, mehrere UI-Threads zu verwenden, sodass die Animation weiterhin ausgeführt wird, während die DataTable Informationen lädt?WPF lädt Animation in einem separaten UI-Thread? (C#)

BEARBEITEN: Der aktuelle Code ist unten.

private void CreateFileTable() 
{ 
    file_data = new DataSet(); 
    data_table = new DataTable(); 
    file_data.Tables.Add(data_table); 

    DataColumn tempCol = new DataColumn("File Name", typeof(string)); 
    data_table.Columns.Add(tempCol); 

    tempCol = new DataColumn("Ext", typeof(string)); 
    data_table.Columns.Add(tempCol); 

    tempCol = new DataColumn("Size", typeof(string)); 
    data_table.Columns.Add(tempCol); 

    tempCol = new DataColumn("Created", typeof(Label)); 
    data_table.Columns.Add(tempCol); 

    tempCol = new DataColumn("Modified", typeof(Label)); 
    data_table.Columns.Add(tempCol); 

    tempCol = new DataColumn("Accessed", typeof(Label)); 
    data_table.Columns.Add(tempCol); 

    tempCol = new DataColumn("Location", typeof(string)); 
    data_table.Columns.Add(tempCol); 

    File_List.ItemsSource = file_data.Tables[0].DefaultView; 
} 

private void PopulateDirectories(string[] directories) 
{ 
    for (int i = 0; i < directories.Length; i++) 
    { 
     DirectoryInfo tempDirInfo = new DirectoryInfo(directories[i]); 

     bool isSystem = ((tempDirInfo.Attributes & FileAttributes.System) == FileAttributes.System); 

     if (!isSystem) 
     { 
      DataRow tempRow = data_table.NewRow(); 
      tempRow["File Name"] = tempDirInfo.Name; 
      tempRow["Ext"] = ""; 
      tempRow["Size"] = ""; 

      tempLabel = new Label(); 
      tempLabel.Padding = new Thickness(2, 0, 2, 0); 
      tempLabel.Content = tempDirInfo.CreationTime.ToLongDateString() + ", " + tempDirInfo.CreationTime.ToLongTimeString(); 

      tempRow["Created"] = tempLabel; 

      tempLabel = new Label(); 
      tempLabel.Padding = new Thickness(2, 0, 2, 0); 
      tempLabel.Content = tempDirInfo.LastWriteTime.ToLongDateString() + ", " + tempDirInfo.LastWriteTime.ToLongTimeString(); 

      tempRow["Modified"] = tempLabel; 

      tempLabel = new Label(); 
      tempLabel.Padding = new Thickness(2, 0, 2, 0); 
      tempLabel.Content = tempDirInfo.LastAccessTime.ToLongDateString() + ", " + tempDirInfo.LastAccessTime.ToLongTimeString(); 

      tempRow["Accessed"] = tempLabel; 
      tempRow["Location"] = tempDirInfo.FullName; 

      data_table.Rows.Add(tempRow); 
     } 
    } 
} 

private void PopulateFiles(string[] files) 
{ 
    for (int i = 0; i < files.Length; i++) 
    { 
     FileInfo tempFileInfo = new FileInfo(files[i]); 

     bool isSystem = ((File.GetAttributes(files[i]) & FileAttributes.System) == FileAttributes.System); 

     if (!isSystem) 
     { 
      DataRow tempRow = data_table.NewRow(); 
      tempRow["File Name"] = tempFileInfo.Name; 
      tempRow["Ext"] = tempFileInfo.Extension; 

      int fileSize = (int)tempFileInfo.Length; 

      if (fileSize > 1048576) 
      { 
       tempRow["Size"] = "" + fileSize/1048576 + " MB"; 
      } 
      else if (fileSize > 1024) 
      { 
       tempRow["Size"] = "" + fileSize/1024 + " KB"; 
      } 
      else 
      { 
       tempRow["Size"] = "" + fileSize + " B"; 
      } 

      tempLabel = new Label(); 
      tempLabel.Padding = new Thickness(2, 0, 2, 0); 
      tempLabel.Content = tempFileInfo.CreationTime.ToLongDateString() + ", " + tempFileInfo.CreationTime.ToLongTimeString(); 

      tempRow["Created"] = tempLabel; 

      tempLabel = new Label(); 
      tempLabel.Padding = new Thickness(2, 0, 2, 0); 
      tempLabel.Content = tempFileInfo.LastWriteTime.ToLongDateString() + ", " + tempFileInfo.LastWriteTime.ToLongTimeString(); 

      tempRow["Modified"] = tempLabel; 

      tempLabel = new Label(); 
      tempLabel.Padding = new Thickness(2, 0, 2, 0); 
      tempLabel.Content = tempFileInfo.LastAccessTime.ToLongDateString() + ", " + tempFileInfo.LastAccessTime.ToLongTimeString(); 

      tempRow["Accessed"] = tempLabel; 
      tempRow["Location"] = tempFileInfo.DirectoryName; 

      data_table.Rows.Add(tempRow); 
     } 
    } 
} 

private string GetSelectedPath(TreeViewItem selectedNode) 
{ 
    return selectedNode.Tag as string; 
} 

private void PopulateFileList() 
{ 
    PopulateDirectories(Directory.GetDirectories(GetSelectedPath((TreeViewItem)Dir_Tree.SelectedItem))); 
    PopulateFiles(Directory.GetFiles(GetSelectedPath((TreeViewItem)Dir_Tree.SelectedItem))); 
} 

private void UpdateFileList() 
{ 
    LoadingWheel.Visibility = System.Windows.Visibility.Visible; 

    CreateFileTable(); 
    PopulateFileList(); 
    TxtFoundCount.Text = "Files/Folders Found: " + File_List.Items.Count; 

    LoadingWheel.Visibility = System.Windows.Visibility.Hidden; 
} 

Ich habe versucht, die Background verwenden und den UpdateFileList() -Methode im Innern nennen, aber ich habe kein Glück.

EDIT: Unten ist mein Code für den BackgroundWorker.

private BackgroundWorker bgWorker1; 

private void InitializeBackgroundWorker() 
{ 
    bgWorker1 = new BackgroundWorker(); 
    bgWorker1.DoWork += new DoWorkEventHandler(bgWorker1_DoWork); 
    bgWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker1_RunWorkerCompleted); 
} 

private void bgWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    if (Dispatcher.CheckAccess()) 
    { 
     PopulateFileList(); 
    } 
    else 
    { 
     Dispatcher.Invoke(new Action(() => PopulateFileList())); 
    } 
} 

private void bgWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    TxtFoundCount.Text = "Files/Folders Found: " + File_List.Items.Count; 
    LoadingWheel.Visibility = System.Windows.Visibility.Hidden; 
} 

private void UpdateFileList() 
{ 
    LoadingWheel.Visibility = System.Windows.Visibility.Visible; 
    CreateFileTable(); 
    bgWorker1.RunWorkerAsync(); 
} 

Ich habe ein Selection Ereignis auf einem TreeView, die InitializeBackgroundWorker() und UpdateFileList() aufruft. Die Liste lädt immer noch, und ich bekomme keine Fehler, aber die Lade-Animation wird nie sichtbar.

Jede Hilfe würde sehr geschätzt werden.

Danke.

+0

Wo sind Ihre Mitarbeiter im Hintergrund? –

+0

Ich nahm den BackgroundWorker-Code wieder heraus, weil es mehr Code war und nichts half. Ich kann es wieder einfügen, aber ich bin mir ziemlich sicher, dass ich es falsch gemacht habe. –

+0

In diesem Fall würden Sie wahrscheinlich die RunWorkerAsync-Funktion des Hintergrundarbeiters von UpdateFileList aufrufen, nachdem Sie LoadingWheel aktiviert haben. Verschieben Sie die beiden Funktionsaufrufe an bgWorker_DoWork, die Aktualisierung an den TextBlock an bgWorker_ProgressChanged, und deaktivieren Sie LoadingWheel in bgWorker_RunWorkerCompleted. Siehe aktualisierte Antwort. –

Antwort

10

Es gibt nur einen UI-Thread. Was Sie tun müssen, ist, die Daten in der DataTable auf einem anderen Thread zu laden.

Wenn Sie den Fortschritt beim Laden der DataTable auf dem Weg anzeigen möchten (entweder direkt oder über eine ProgressBar oder einen anderen Mechanismus), ist BackgroundWorker eine ziemlich unkomplizierte Möglichkeit, dies zu tun.

UPDATE: Sehr einfache Hintergrund Worker Beispiel
Hier ist ein ziemlich einfaches Beispiel. Es fügt 100 Zufallszahlen zu einer Sammlung hinzu und pausiert den Thread für kurze Zeit zwischen ihnen, um einen langen Ladevorgang zu simulieren. Sie können dies einfach ausschneiden und in ein Testprojekt einfügen, damit es funktioniert.

Die Sache zu beachten ist, dass das schwere Heben (das Zeug, das eine Weile dauert) in der DoWork getan wird, während alle UI-Updates in ProgressChanged und RunWorkerCompleted durchgeführt werden. Tatsächlich wird im DoWork-Handler eine separate Liste (Nummern) erstellt, da sich die globale mNumbers-Auflistung im UI-Thread befindet und nicht mit dem DoWork-Handler interagieren kann.

XAML

<Button x:Name="btnGenerateNumbers" 
     Grid.Row="1" 
     HorizontalAlignment="Center" 
     VerticalAlignment="Center" 
     Content="Generate Numbers" /> 

C# -Code-Behind

BackgroundWorker bgWorker = new BackgroundWorker(); 
ObservableCollection<int> mNumbers = new ObservableCollection<int>(); 

public Window1() 
{ 
    InitializeComponent(); 
    bgWorker.DoWork += 
     new DoWorkEventHandler(bgWorker_DoWork); 
    bgWorker.ProgressChanged += 
     new ProgressChangedEventHandler(bgWorker_ProgressChanged); 
    bgWorker.RunWorkerCompleted += 
     new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted); 
    bgWorker.WorkerReportsProgress = true; 

    btnGenerateNumbers.Click += (s, e) => UpdateNumbers(); 

    this.DataContext = this; 
} 

void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    progress.Visibility = Visibility.Collapsed; 
    lstItems.Opacity = 1d; 
    btnGenerateNumbers.IsEnabled = true; 
} 

void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    List<int> numbers = (List<int>)e.UserState; 
    foreach (int number in numbers) 
    { 
     mNumbers.Add(number); 
    } 

    progress.Value = e.ProgressPercentage; 
} 

void bgWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    Random rnd = new Random(); 
    List<int> numbers = new List<int>(10); 

    for (int i = 1; i <= 100; i++) 
    { 
     // Add a random number 
     numbers.Add(rnd.Next());    

     // Sleep from 1/8 of a second to 1 second 
     Thread.Sleep(rnd.Next(125, 1000)); 

     // Every 10 iterations, report progress 
     if ((i % 10) == 0) 
     { 
      bgWorker.ReportProgress(i, numbers.ToList<int>()); 
      numbers.Clear(); 
     } 
    } 
} 

public ObservableCollection<int> NumberItems 
{ 
    get { return mNumbers; } 
} 

private void UpdateNumbers() 
{ 
    btnGenerateNumbers.IsEnabled = false; 
    mNumbers.Clear(); 
    progress.Value = 0; 
    progress.Visibility = Visibility.Visible; 
    lstItems.Opacity = 0.5; 

    bgWorker.RunWorkerAsync(); 
} 
+0

Ich habe meinen aktuellen Code oben gepostet. Ich habe versucht, den BackgroundWorker damit zu implementieren, hatte aber kein Glück. Es hat die Benutzeroberfläche immer noch eingefroren, wie zuvor. –

+1

Meine Antwort wird mit einem Beispiel aktualisiert. –

+0

Works Bild perfekt !!! .... Tolle Arbeit .. – iamCR

3

Ich schrieb ein kleines Testprogramm, das die Verwendung der Dispatcher-Klasse zeigt. Es benötigt nur ein WPF-Fenster und eine ListBox mit dem Namen "listBox". Sollte einfach sein, diese Lösung auf Ihr Problem anzuwenden.

public void Populate() { 
     // for comparison, freezing the ui thread 
     for (int i = 0; i < 1000000; i++) { 
      listBox.Items.Add(i); 
     } 
    } 

    private delegate void AddItemDelegate(int item); 
    public void PopulateAsync() { 
     // create a new thread which is iterating the elements 
     new System.Threading.Thread(new System.Threading.ThreadStart(delegate() { 
      // inside the new thread: iterate the elements 
      for (int i = 0; i < 1000000; i++) { 
       // use the dispatcher to "queue" the insertion of elements into the UI-Thread 
       // DispatcherPriority.Background ensures Animations have a higher Priority and the UI does not freeze 
       // possible enhancement: group the "jobs" to small units to enhance the performance 
       listBox.Dispatcher.Invoke(new AddItemDelegate(delegate(int item) { 
        listBox.Items.Add(item); 
       }), System.Windows.Threading.DispatcherPriority.Background, i); 
      } 
     })).Start(); 
    }