0

Model:

public class User : INotifyPropertyChanged
{
    public override string ToString()
    {
        return Username;
    }

    private Int32 _ID;
    public Int32 ID
    {
        get { return _ID; }
        set
        {
            if (value != _ID)
            {
                _ID = value;
                OnPropertyChanged();
                OnPropertyChanged("ID");
            }
        }
    }

    private String _Username;
    public String Username
    {
        get { return _Username; }
        set
        {
            if (value != _Username)
            {
                _Username = value;
                OnPropertyChanged();
                OnPropertyChanged("Username");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public virtual void OnPropertyChanged([CallerMemberName]string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

DataContext:

public class DC
{
    public DC()
    {
        Users = new ObservableCollection<User>();
        Users.Clear();
    }

    public ObservableCollection<User> Users
    {
        get;
        set;
    }

    public async void LoadData()
    {
        await Task.Run(() =>
        {
            // time-consuming load from DB
            Users.Add(new User() { Username = "Steve", ID = 1 });
            Users.Add(new User() { Username = "Bill", ID = 2 });
        });
    }
}

App.xaml.cs:

public partial class App : System.Windows.Application
{ 
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        DC dc = new DC();
        var view = new ViewMain();
        view.DataContext = dc;
        view.Show();
        dc.LoadData();
    }
}

There are 2 controls in the view: RadGridView and RadListBox.
When i bind ItemsSource="{Binding Users}" to RadGridView - it is working.
When i bind ItemsSource="{Binding Users}" to RadListBox - i get exeption:

This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread

Why is it not working in this case?
Of course, I can use App.Current.Dispatcher.Invoke, but i don't know if this is a good solution when I want the view to be thread safe? (like not freezeing on adding a lot of items to ObservableCollection).

Dominik
  • 209
  • 2
  • 13
  • The question was more about why it works in the case of GridView. – Dominik Jul 04 '18 at 19:28
  • You would need to look into the internals of both controls to figure out what the functional differences are that lead to RadListBox's limitation. Blind-guess: Perhaps the RadGridView does not support selection, thus it has no qualms if the collection is modified in another thread. The listbox on the other hand could get screwed, if the underlying collection changes while the UI thread might be busy with some work related to ListBox selection. Just guessing, though... –  Jul 04 '18 at 19:29
  • Using `BindingOperations.EnableCollectionSynchronization` as demonstrated in some answers to the duplicate question should do the trick. –  Jul 04 '18 at 19:34
  • Fair enough :) But what is the best practice when it comes to loading large amounts of data (every 10 secounds) without hanging UI. What is the point of using async await if you still need to modify the ObservableCollection on the UI thread using invoke? – Dominik Jul 04 '18 at 19:37
  • If you want to display a huge amount of items in an ItemsControl (such as a ListBox, for example), the control could become overwhelmed by the sheer number of items. The standard WPF listbox has built-in virtualization support (can be enabled/disabled via the attached property `VirtualizingStackPanel.IsVirtualizing`). Not sure how precisely Telerik RadListBox supports UI virtualization, though... –  Jul 04 '18 at 19:47
  • 1
    async/await allows you to run the task (adding elements to the collection) asynchronously without blocking the UI thread. The modification of the collection has still to be synchronized with the UI though (whether you use Dispatcher. Invoke or EnableCollectionSynchronization). Synchronization does not mean blocking of the UI thread. It means your task is blocked until the UI thread found time to do the action that is synchronized via Invoke/EnableCollectionSynchronization. –  Jul 04 '18 at 19:52
  • 1
    If you do not want the UI to be in some intermediate state while you are filling the collection, do not let the `Users` property return the empty collection. Create the collection and fill it before assigning it to the User property. If you do it this way, don't forget to signal this change of the `Users` property via PropertyChanged event. –  Jul 04 '18 at 19:55
  • Well explained. Can You write something more, why should not I let empty collection return? In my case, the DataContext is assigned to View with an empty collection. Later after form shown i run method that use async await to add individual elements to the collection. – Dominik Jul 04 '18 at 20:27
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/174368/discussion-between-elgonzo-and-dominik). –  Jul 04 '18 at 20:29

0 Answers0