Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top