2016-06-29 5 views
0

Ich bin derzeit verzweifelt von meiner C# WPF GUI-Erfahrung. Im ruhigen neu in WPF und Data-Binding, auch keinen C# Experten im ...Databinding-Leistungsproblem beim Laden riesiger Listen

habe ich versucht, die Demo-Vorlage für grundlegenden MVVM WPF Muster: https://msdn.microsoft.com/en-us/magazine/dd419663.aspx

... und fügte hinzu ~ 2000 Kunden zu prüfen das gui-verhalten auf riesigen datenlisten, weil ich mich in meinem persönlichen projekt mit riesigen listen komplexer daten beschäftigen muss.

Da die Anzeige der Kundenliste in WPF-Data-Binding-Magic erfolgt, ist unklar, wie die GUI-Ladezeit kontrolliert werden kann (bis zu 12 Sek. Mit +2000 Daten).

CustomerRepository:

readonly List<Customer> _customers; 

/// <summary> 
/// Returns a shallow-copied list of all customers in the repository. 
/// </summary> 
public List<Customer> GetCustomers() 
{ 
    return new List<Customer>(_customers); 
} 

AllCustomerView.xaml/Listview, die die Kundenliste enthält:

<ListView 
    AlternationCount="2" 
    DataContext="{StaticResource CustomerGroups}" 
    ItemContainerStyle="{StaticResource CustomerItemStyle}" 
    ItemsSource="{Binding}" 
    > 
    <ListView.GroupStyle> 
    <StaticResourceExtension 
     ResourceKey="CustomerGroupStyle" 
     /> 
    </ListView.GroupStyle> 

    <ListView.View> 
    <GridView> 
     <GridViewColumn 
     Header="Name" 
     DisplayMemberBinding="{Binding Path=DisplayName}" 
     /> 
     <GridViewColumn 
     Header="E-mail" 
     DisplayMemberBinding="{Binding Path=Email}" 
     /> 
     <GridViewColumn Header="Total Sales"> 
     <GridViewColumn.CellTemplate> 
      <DataTemplate> 
      <ContentPresenter 
       Content="{Binding Path=TotalSales}" 
       ContentStringFormat="c" 
       HorizontalAlignment="Right" 
       /> 
      </DataTemplate> 
     </GridViewColumn.CellTemplate> 
     </GridViewColumn> 
    </GridView> 
    </ListView.View> 
</ListView> 



<UserControl.Resources> 
<CollectionViewSource 
    x:Key="CustomerGroups" 
    Source="{Binding Path=AllCustomers}" 
    > 
    <CollectionViewSource.GroupDescriptions> 
    <PropertyGroupDescription PropertyName="IsCompany" /> 
    </CollectionViewSource.GroupDescriptions> 
    <CollectionViewSource.SortDescriptions> 
    <!-- 
    Sort descending by IsCompany so that the 'True' values appear first, 
    which means that companies will always be listed before people. 
    --> 
    <scm:SortDescription PropertyName="IsCompany" Direction="Descending" /> 
    <scm:SortDescription PropertyName="DisplayName" Direction="Ascending" /> 
    </CollectionViewSource.SortDescriptions> 
</CollectionViewSource> 

Meine Frage ist:

  1. Gibt es eine Möglichkeit i assync kann. lade die Daten, ohne die GUI einzufrieren, während die Listenansicht geladen wird?

  2. Gibt es eine Möglichkeit, den Fortschritt zu verfolgen, oder sogar den Fortschritt der bereits angezeigten Daten zu erhalten, bis alle Elemente korrekt geladen sind?

  3. Plus: Ich struggelaging mit der Datenverbindung zwischen der Methode GetCustomers() und den XAML-Bindings. Wo ist die „richtige Verbindung‘ zwischen diesen beiden Seiten?

+0

Verwenden Sie eine Art und Weise verbindlich, –

Antwort

0

Das folgende vereinfachte Beispiel ein Listview zu einer Ansicht Modell mit einem asynchronen LoadCustomers Verfahren bindet.

Customer Instanzen in einem separaten Thread erstellt werden (durch Task.Run()) und über einen Aufruf zu einem Dispatcher ObservableCollection hinzugefügt (weil die Sammlung nur in dem UI-Thread modifiziert werden muß).

die LoadCustomers Loaded Ereignisbehandler in dem Fenster der ausgeführt wird, weil es sein muss await ed, was im MainWindow-Konstruktor nicht möglich ist.

<ListView ItemsSource="{Binding Customers}"> 
    <ListView.View> 
     <GridView> 
      <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}"/> 
     </GridView> 
    </ListView.View> 
</ListView> 

-Code hinter:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 

     var viewModel = new ViewModel(); 
     DataContext = viewModel; 

     Loaded += async (s, e) => await viewModel.LoadCustomers(); 
    } 
} 

public class Customer 
{ 
    public string Name { get; set; } 
} 

public class ViewModel 
{ 
    public ObservableCollection<Customer> Customers { get; private set; } 
     = new ObservableCollection<Customer>(); 

    public async Task LoadCustomers() 
    { 
     await Task.Run(() => 
     { 
      for (int i = 0; i < 2000; i++) 
      { 
       var customer = new Customer 
       { 
        Name = string.Format("Customer {0}", i + 1) 
       }; 

       Application.Current.Dispatcher.Invoke(() => Customers.Add(customer)); 
      } 
     }); 
    } 
} 
+0

Nun, danke, aber da ich verwende MVVM-Pattern kann ich nicht Zugang zu " Loaded 'im Customer-ViewModel kann ich in der Tat nicht auf das Fenster zugreifen. – Sweeed

+0

@Sweeed Sie können Methode in vm geladen haben und laden Sie diese aus dem Konstruktor selbst – Eldho

+1

Nun, das ist MVVM. Es ist nicht * erforderlich *, dass eine Loaded-Ereignisbehandlungsroutine LoadCustomers aufruft (aber nur ein Beispiel). Sie können es auch in Ihrem Ansichtsmodell beliebig benennen. – Clemens

0

Ich würde es tun, um diese Art und Weise

public class ViewModel : INotifyPropertyChanged 
{ 
    public ViewModel() 
    { 
     ... 
     LoadCostumersAsync(); //code after this line executes before LoadCostumersAsync() is finished, so your UI remains responsive 
     ... 
    } 

    private ObservableCollection<Customer> _customers; 
    public ObservableCollection<Customer> Customers { get { return _customers; } } 

    private async void LoadStopsAsync() 
    { 
     IsLoading= true; // you can use this property to show a busyindicator on the UI 

     _customers = await LoadCostumers; 

     IsLoading = false; 
    } 

    public System.Threading.Tasks.Task<ObservableCollection<Customer>> LoadCostumers() 
    { 
     return System.Threading.Tasks.Task.Factory.StartNew(() => 
     { 
      return YourMethodThatGetsCostumers(); 
     }); 
    } 
}