2017-04-01 2 views
4

Ich versuche einen asynchronen Aufruf zu machen, um Daten in mein Grid zu laden. Problem ist, dass die Operation die Benutzeroberfläche blockiert (läuft im Hauptthread) - und ich weiß nicht warum. Ich versuche, es zu machen, Daten im Hintergrund zu holen ... Hier ist der Code:Task.Run blockierendes Hauptthread (eingefrorene UI)

Bindung Viewmodel-Klasse Datacontext des Hauptfensters:

<Window.DataContext> 
    <vm:MainWindowViewModel WindowTitle="MVVM" BtnLoadText="LOAD DATA"/> 
</Window.DataContext> 

Datagrid mit Spalte zur Sammlung Eigenschaft Bindung (PeopleList) in Ansichtsmodell Klasse:

<DataGrid AutoGenerateColumns="False" IsReadOnly="True" ItemsSource="{Binding Path=PeopleList, Mode=TwoWay}" Margin="5"> 
     <DataGrid.Columns> 
      <DataGridTextColumn Header="First name" Binding="{Binding Path=FirstName}"/> 
      <DataGridTextColumn Header="Last name" Binding="{Binding Path=LastName}"/> 
      <DataGridTextColumn Header="Age" Binding="{Binding Path=Age}"/> 
     </DataGrid.Columns> 
    </DataGrid> 
    <Button x:Name="btn_LoadData" Margin="5" Grid.Row="1" Content="{Binding Path=BtnLoadText}" Click="btn_LoadData_Click"/> 

-Code-Behind der Mainwindow - laufen asynchron Schaltfläche klicken Ereignis:

public partial class MainWindow : Window 
{ 
    private MainWindowViewModel mainWindowViewModel; 
    public MainWindow() 
    { 
     InitializeComponent(); 
     mainWindowViewModel = (MainWindowViewModel)DataContext; 
    } 

    private async void btn_LoadData_Click(object sender, RoutedEventArgs e) 
    { 
     await mainWindowViewModel.LoadData(); 
    } 
} 

Ansichtsmodell Klasse verantwortlich für Mainwindow:

class MainWindowViewModel 
{ 
    public string WindowTitle { get; set; } 
    public string BtnLoadText { get; set; } 

    public ObservableCollection<Person> PeopleList { get; set; } 

    private Database database = new Database(); 

    public MainWindowViewModel() 
    { 
     PeopleList = new ObservableCollection<Person>(); 
    } 

    public async Task LoadData() 
    { 
     PeopleList.Clear(); 

     var result = await database.GetPeopleListLongOperationAsync(); 

     PeopleList.Add(result.First()); 
    } 
} 

Wie Sie sehen, ich bin einen asynchronen Aufruf mit Loaddata Methode machen, die Daten aus der Datenbank erhält und Hinzufügen zu ObservableCollection, die Datagrid-Updates (Bindung von der Datenraster)

Database-Klasse, die Daten-Abruf "emuliert":

public class Database 
{ 
    public IEnumerable<Person> GetPeopleListLongOperation() 
    { 
     // forcing "long" data load 
     Thread.Sleep(5000); 
     yield return new Person() { FirstName = "Name", LastName = "LastName", Age = new Random().Next(18, 40) }; 
    } 

    public Task<IEnumerable<Person>> GetPeopleListLongOperationAsync() 
    { 
     return Task.Run<IEnumerable<Person>>(() => 
     { 
      return GetPeopleListLongOperation(); 
     }); 
    } 
} 

ich verwende Task.Run zu holen Daten im Hintergrund-Thread. Das Problem ist - es läuft im Haupt-Thread und es blockiert die Benutzeroberfläche.

Irgendwelche Vorschläge? Ich bin mir nicht mehr sicher, ob ich richtig async Operationen verstehen ...

EDIT

Wechsel von IEnumerable Aufgabenergebnistyp zur Liste machte es funktionieren. Kann mir jemand erklären warum?

public Task<List<Person>> GetPeopleListLongOperationAsync() 
    { 
     return Task.Run<List<Person>>(() => 
     { 
      return GetPeopleListLongOperation().ToList(); 
     }); 
    } 

Antwort

5

jetzt über die Threading-Komplikation Forget, und nehmen Sie einfach diesen Code:

public IEnumerable<Person> GetPeopleListLongOperation() 
{ 
    // forcing "long" data load 
    Thread.Sleep(5000); 
    yield return new Person(); 
} 

Wenn Sie GetPeopleListLongOperation() nennen es sofort zurückkehren wird, ist es nicht 5 Sekunden warten. Dies liegt daran, dass Iteratorblöcke wie dieser langsam ausgewertet werden; Thread.Sleep(5000) wird nur aufgerufen, wenn Sie die Sequenz aufzählen.

Jetzt verstehen Sie, dass Sie sehen sollten, dass Sie "beheben" funktioniert, weil ToList die Sequenz bis zum Abschluss auf dem Thread-Pool-Thread und die gecachten Ergebnisse zurückgegeben werden. Ohne dies würden Sie die Sequenz auf dem UI-Thread beim Aufruf von result.First() aufzählen und blockieren.

+0

Danke. Ich wusste von faulen Laden von IEnumerable Sammlung, aber ich wusste nicht, dass es so funktioniert. – asiesom

Verwandte Themen