加载大型列表时的数据绑定性能问题

时间:2016-06-29 08:25:28

标签: c# wpf xaml mvvm data-binding

我目前对我的C#WPF GUI体验感到绝望。 我是WPF和Data-Binding的新手,也是我没有C#专家...

我尝试了基本MVVM WPF模式的演示模板: https://msdn.microsoft.com/en-us/magazine/dd419663.aspx

...并添加〜2000客户检查巨大数据列表上的gui行为,因为我必须处理个人项目中的大量复杂数据。

由于Customer-List的显示发生在WPF-Data-Binding-Magic中,我不确定如何控制GUI加载时间(使用+2000数据最多12秒)。

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,其中包含客户列表:

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

我的问题是:

  1. 有没有办法可以同步。在列表视图加载时加载数据而不冻结gui?

  2. 有没有什么方法可以跟踪进度,甚至可以获得已显示数据的进度,直到所有项目都正确加载?

  3. 另外:我正在使用GetCustomers()方法和XAML-Bindings之间的数据绑定。这两方之间的“真正联系”在哪里?

2 个答案:

答案 0 :(得分:0)

以下简化示例使用异步LoadCustomers方法将ListView绑定到视图模型。

Customer实例在单独的线程中创建(通过Task.Run()),并通过Dispatcher调用添加到ObservableCollection中(因为只能在UI线程中修改集合)。

LoadCustomers在Window的Loaded事件处理程序中执行,因为它必须是await ed,这在MainWindow构造函数中无法完成。

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

代码背后:

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

答案 1 :(得分:0)

我会这样做

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