0

Normally I check if I have access to the ObservableCollection and if not, I call a Dispatcher. Here (on Stackoverflow) are also some other solutions, but I don't know what is the best and cleanest way. I think my solution is obsolete and should not be used anymore.

In my example is the ItemsCollection bound to the UI. The _UpdateTheCollectionFromAnotherThread() would being called (from another thread or would open another thread) and save the data temporary to items. After that I would check the access and call a dispatcher if needed.

Is this the way I have to go or are there some better and cleaner solutions?

FooClass

public class FooClass
{
    // ##############################################################################################################################
    // Properties
    // ##############################################################################################################################

    /// <summary>
    /// This Items are bound to my UI.
    /// </summary>
    public ObservableCollection<string> ItemsCollection { get; } = new ObservableCollection<string>();

    // ##############################################################################################################################
    // Singleton pattern
    // ##############################################################################################################################

    /// <summary>
    /// The instance of <see cref="FooClass"/>.
    /// </summary>
    internal static FooClass Instance => _Instance ?? (_Instance = new FooClass());
    private static FooClass _Instance;

    // ##############################################################################################################################
    // Konstruktor
    // ##############################################################################################################################

    private FooClass()
    {

    }


    // ##############################################################################################################################
    // Method
    // ##############################################################################################################################

    private void _UpdateTheCollectionFromAnotherThread()
    {
        List<string> items = new List<string>();

        //Here would be some logic to create and fill the items list....


        //and now apply them to the public list

        if (System.Windows.Application.Current.Dispatcher.CheckAccess())
        {
            ItemsCollection.Clear();
            foreach (string item in items)
            {
                ItemsCollection.Add(item);
            }
        }
        else
        {
            System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
            {
                ItemsCollection.Clear();
                foreach (string item in items)
                {
                    ItemsCollection.Add(item);
                }
            }));
        }
    }
}
Dominic Jonas
  • 4,717
  • 1
  • 34
  • 77
  • 1
    `I think my solution is obsolete and should not be used anymore.` Why? Did you have issues with it? If so, enumerate them. If not, there's no reason to change. We can't possibly find a better solution than one that already fits your requirements – Kevin Gosse Jun 30 '17 at 06:52
  • You could *write* it cleaner, without replicating the code that updates the collection – Clemens Jun 30 '17 at 07:04
  • All I can add to this is to make this work for your tests put a check for a dispatcher being null. `public class RootDispatcherFetcher {private static Dispatcher _rootDispatcher = null;public static Dispatcher RootDispatcher{ get { _rootDispatcher = _rootDispatcher ?? Application.Current != null ? Application.Current.Dispatcher : new Dispatcher(...); return _rootDispatcher;} // unit tests can get access to this via InternalsVisibleTo internal set { _rootDispatcher = value; } } }` – XAMlMAX Jun 30 '17 at 07:11
  • See [here](https://stackoverflow.com/q/23108045/5045688) and [here](https://stackoverflow.com/q/3899940/5045688). – Alexander Petrov Jun 30 '17 at 09:33
  • What about this solution? http://www.jonathanantoine.com/2011/09/24/wpf-4-5-part-7-accessing-collections-on-non-ui-threads/ use `BindingOperations.EnableCollectionSynchronization()` ? – Dominic Jonas Jun 30 '17 at 10:02

2 Answers2

6

If you are on .NET Framework 4.5 or later you can enable the collection to be accessed across multiple threads by calling the BindingOperations.EnableCollectionSynchronization method. Note that this method must be called on the UI thread:

public class FooClass
{
    private readonly object _lock = new object();
    public ObservableCollection<string> ItemsCollection { get; } = new ObservableCollection<string>();

    internal static FooClass Instance => _Instance ?? (_Instance = new FooClass());
    private static FooClass _Instance;

    private FooClass()
    {
        BindingOperations.EnableCollectionSynchronization(ItemsCollection, _lock);
    }

    private void _UpdateTheCollectionFromAnotherThread()
    {
        List<string> items = new List<string>();
        ItemsCollection.Clear();
        foreach (string item in items)
        {
            ItemsCollection.Add(item);
        }
    }
}

If the FooClass may be created on a background thread, you might still improve your code a bit by not repeating yourself:

public class FooClass
{
    public ObservableCollection<string> ItemsCollection { get; } = new ObservableCollection<string>();

    internal static FooClass Instance => _Instance ?? (_Instance = new FooClass());
    private static FooClass _Instance;

    private FooClass() { }

    private void _UpdateTheCollectionFromAnotherThread()
    {
        List<string> items = new List<string>();

        if (System.Windows.Application.Current.Dispatcher.CheckAccess())
            ClearCollection(items);
        else
            System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => ClearCollection(items)));
    }

    private void ClearCollection(List<string> items)
    {
        ItemsCollection.Clear();
        foreach (string item in items)
        {
            ItemsCollection.Add(item);
        }
    }
}
mm8
  • 163,881
  • 10
  • 57
  • 88
0

I can't tell if you're implementing a MVC or MVW pattern here, but it's good practice to do that even with desktop apps.

As such I think ObservableCollection is part of the view, rather than model, as it's usually not required for a functioning model, but is for WPF UI binding. The primary reason I say this is that it is mutable & the whole way it interacts with WPF relies on mutability.

I would not have an _UpdateTheCollectionFromAnotherThread, because you should avoid updating the UI from another thread. Doing so usually means there's some mixing of roles of model-view-controller in your design.

Rather, do all the async/multithreading stuff in your model, because that is where the bulk of processing will happen.

Then it's simpler to just update your UI with the model, in the UI thread.

Ashley Pillay
  • 868
  • 4
  • 9
  • But I still will need a kind of `Dispatcher` to update my `UI`?! – Dominic Jonas Jun 30 '17 at 10:00
  • Not if you don't update your UI from another thread. For progress reporting or similar updating, use the IProgress pattern. Yes it's doing alot of stuff behind the scenes, but the code you have to write & maintain is what matters. IProgress is a cleaner paradigm. Just make sure that whatever progress object you pass back is loosely coupled - i.e. it doesn't create a strong relationship between the model & view parts of your code. – Ashley Pillay Jun 30 '17 at 10:42