问题
I am developing a Wpf application which retrieves the data from a database using EF.
I have some ListView controls which are filled with some tables of the database so in order to prevent from blocking the UI while retrieving the data I do as follows:
Task tsk = Task.Factory.StartNew(() =>
{
ItemsSource = Database.SomeTable();
});
The ItemsSource variable is an ObservableCollection which is bound to the ItemsSource property of a ListView.
The thing is that, as expected, while loading the data the UI keeps responsive. My problem is that the ListView is empty until all the data is loaded. So I would like to see element by element appearing in the ListView. Is there a way to do that?? I have tried which a foreach loop with no luck.
Thanks in advance.
回答1:
This can be accomplished by dispatching the addition of new elements to your observable collection using the Disptacher's BeginInvoke method, called from your task. Something like:
//MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView ItemsSource="{Binding MyList}" Grid.Row="0" />
<Button Content="Load" Click="OnLoadClicked" Grid.Row="1" Height="30" />
</Grid>
</Window>
//MainWindow.xaml.cs
using System;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private VM _vm = new VM();
public MainWindow()
{
InitializeComponent();
this.DataContext = _vm;
}
private void OnLoadClicked(object sender, RoutedEventArgs e)
{
Load10Rows();
}
private void Load10Rows()
{
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 10; i++)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
_vm.MyList.Add(DateTime.Now.ToString());
}), DispatcherPriority.Background);
// Just to simulate some work on the background
Thread.Sleep(1000);
}
});
}
}
public class VM
{
private ObservableCollection<string> _myList;
public VM()
{
_myList = new ObservableCollection<string>();
}
public ObservableCollection<string> MyList
{
get { return _myList; }
}
}
}
If you have a large amount of records you may want to chunk it, otherwise just call the Disptacher for each record.
回答2:
Perhaps using Task.Delay
to allow the UI to render the changes before adding the next item in the foreach will work
Example:
private async Task AddItems()
{
foreach (var item in Database.SomeTable())
{
ItemsSource.Add(item);
await Task.Delay(1);
}
}
来源:https://stackoverflow.com/questions/19579734/add-element-by-element-to-listview-without-blocking-ui