Add Element By Element to ListView Without blockin

2019-06-14 16:51发布

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.

2条回答
Bombasti
2楼-- · 2019-06-14 17:10

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);
    }
}
查看更多
劫难
3楼-- · 2019-06-14 17:21

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.

查看更多
登录 后发表回答