Best way of using ObservableCollection in a multi threaded environment - c#

Ok from below which one is best approach or any other better ways ?
Both are working (credits not belongs to me)
public static MTObservableCollection<string> ocEventsCollection = new MTObservableCollection<string>();
public static void AddMsgToEvents(string srMessage)
{
lock (ocEventsCollection)
ocEventsCollection.Insert(0, srMessage);
}
public class MTObservableCollection<T> : ObservableCollection<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged;
if (CollectionChanged != null)
foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList())
{
DispatcherObject dispObj = nh.Target as DispatcherObject;
if (dispObj != null)
{
Dispatcher dispatcher = dispObj.Dispatcher;
if (dispatcher != null && !dispatcher.CheckAccess())
{
dispatcher.BeginInvoke(
(Action)(() => nh.Invoke(this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
DispatcherPriority.DataBind);
continue;
}
}
nh.Invoke(this, e);
}
}
}
And the second approach
public static ObservableCollection<string> ocEventsCollection = new ObservableCollection<string>();
public static void AddMsgToEvents(string srMessage)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
ocEventsCollection.Insert(0, srMessage);
}));
}

Related

System.InvalidOperationException with BindingOperations.EnableCollectionSynchronization in wpf

We have an AsyncObservableCollection class which is inheriting from ObservableCollection in our wpf application.
This class is having :
public Dispatcher Dispatcher
{
get
{
if (_dispatcher == null)
{
if (Application.Current != null)
{
_dispatcher = Application.Current.Dispatcher;
}
else
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
}
return _dispatcher;
}
set
{
_dispatcher = value;
}
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke(
new Action<NotifyCollectionChangedEventArgs>(OnCollectionChanged), e);
return;
}
base.OnCollectionChanged(e);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke(new Action<PropertyChangedEventArgs>(OnPropertyChanged), e);
return;
}
base.OnPropertyChanged(e);
}
and there is another class RangeObservableCollection which is inheriting from this AsyncObservableCollectionClass, having only this inside it
private bool _suppressNotification;
public void ReplaceRange(IEnumerable<T> list)
{
_suppressNotification = true;
Clear();
if (list != null)
{
foreach (T item in list)
{
Add(item);
}
}
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_suppressNotification)
{
base.OnCollectionChanged(e);
}
}
now I tried to use BindingOperations.EnableCollectionSynchronization.
Then inside our ListViewModelBase.cs
created a lock object like this
private readonly static object _storiesCollectionLock = new object();
and inside the constructor of our ListViewModelBase
we are creating the RangeObservableCollection and binding BindingOperations.CollectionRegistering
protected ListViewModelBase()
{
_storiesCollection = new RangeObservableCollection<StoryInfoViewModel>();
BindingOperations.CollectionRegistering += BindingOperations_CollectionRegistering;
}
private void BindingOperations_CollectionRegistering(object sender, CollectionRegisteringEventArgs e)
{
if (e.Collection == _storiesCollection)
{
Dispatcher.BeginInvoke(new Action(() =>
{
BindingOperations.EnableCollectionSynchronization(_storiesCollection, _storiesCollectionLock);
}));
}
}
Then wherever in our project we are using this _storiesCollection, I am wrapping it in a lock which is using this _storiesCollectionLock object. Locking even the ListCollectionView which is getting populated with this observable collection.
Have created properties in the ListViewModelBase, for the _storiesCollection and the _storiesCollectionLock object.
public RangeObservableCollection<StoryInfoViewModel> StoriesCollection
{
get
{
lock (StoriesCollectionLock())
{
return _storiesCollection;
}
}
}
public static object StoriesCollectionLock()
{
return _storiesCollectionLock;
}
Then in a separate StoryListViewModel inside my add method when i am trying to insert in this StoriesCollection on a worker thread
lock (StoriesCollectionLock())
{
StoriesCollection.Insert(0, storyVM);
}
I am getting exceptions inside OnCollectionChanged in AsyncObservableCollection class
System.InvalidOperationException: 'Added item does not appear at given index '0'.'
also getting exception Message = "Cannot change ObservableCollection during a CollectionChanged event.", below is the stack trace for this
at System.Collections.ObjectModel.ObservableCollection1.CheckReentrancy() at System.Collections.ObjectModel.ObservableCollection1.InsertItem(Int32 index, T item)
at System.Collections.ObjectModel.Collection`1.Insert(Int32 index, T item)
and
System.InvalidOperationException: ''1510' index in collection change event is not valid for collection of size '1500'.'
Can someone please help, what am i doing wrong or missing here?
Or is it a known issue with EnableCollectionSynchronization?

Raising OnPropertyChanged when an object property is modified from another thread

So I have this object:
public class SomeObject: INotifyPropertyChanged
{
public decimal AlertLevel {
get {
return alertLevel;
}
set {
if(alertLevel == value) return;
alertLevel = value;
OnPropertyChanged("AlertLevel");
}
private void OnPropertyChanged(string propertyName) {
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Suppose I am changing this object on a thread that is NOT the GUI thread. How can I have this object raise the PropertyChanged event on the same thread as the GUI when I don't have a reference to any GUI component in this class?
Normally the event subscriber should be responsible for marshalling the calls to the UI thread if necessary.
But if the class in question is UI specific (a.k.a view model), as soon it is created on the UI thread, you can capture the SynchronizationContext and use it for raising the event like this:
public class SomeObject : INotifyPropertyChanged
{
private SynchronizationContext syncContext;
public SomeObject()
{
syncContext = SynchronizationContext.Current;
}
private decimal alertLevel;
public decimal AlertLevel
{
get { return alertLevel; }
set
{
if (alertLevel == value) return;
alertLevel = value;
OnPropertyChanged("AlertLevel");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
if (syncContext != null)
syncContext.Post(_ => handler(this, new PropertyChangedEventArgs(propertyName)), null);
else
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Alternatively you can pass SynchronizationContext via constructor.
Yet another way is to keep the object intact, but data bind to it via intermediate synchronized binding source as described here Update elements in BindingSource via separate task.
for WPF - Add the following references:
PresentationFramework.dll
WindowsBase.dll
In your background thread - wrap the code that needs access to UI into a dispatcher.Invoke()
using System.Windows;
using System.Windows.Threading;
...
//this is needed because Application.Current will be NULL for a WinForms application, since this is a WPF construct so you need this ugly hack
if (System.Windows.Application.Current == null)
new System.Windows.Application();
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
//Do Your magic here
}), DispatcherPriority.Render);
for WinForms use
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Render, new Action(() => {
//Do Your magic here
}));
An even better idea, without using any WPF references:
public class GUIThreadDispatcher {
private static volatile GUIThreadDispatcher itsSingleton;
private WeakReference itsDispatcher;
private GUIThreadDispatcher() { }
public static GUIThreadDispatcher Instance
{
get
{
if (itsSingleton == null)
itsSingleton = new GUIThreadDispatcher();
return itsSingleton;
}
}
public void Init(Control ctrl) {
itsDispatcher = new WeakReference(ctrl);
}
public void Invoke(Action method) {
ExecuteAction((Control ctrl) => DoInGuiThread(ctrl, method, forceBeginInvoke: false));
}
public void BeginInvoke(Action method) {
ExecuteAction((Control ctrl) => DoInGuiThread(ctrl, method, forceBeginInvoke: true));
}
private void ExecuteAction(Action<Control> action) {
if (itsDispatcher.IsAlive) {
var ctrl = itsDispatcher.Target as Control;
if (ctrl != null) {
action(ctrl);
}
}
}
public static void DoInGuiThread(Control ctrl, Action action, bool forceBeginInvoke = false) {
if (ctrl.InvokeRequired) {
if (forceBeginInvoke)
ctrl.BeginInvoke(action);
else
ctrl.Invoke(action);
}
else {
action();
}
}
}
}
And initialize like this:
private void MainForm_Load(object sender, EventArgs e) {
//setup the ability to use the GUI Thread when needed via a static reference
GUIThreadDispatcher.Instance.Init(this);
...
}
And use like this:
public class SomeObject: INotifyPropertyChanged
{
public decimal AlertLevel {
get {
return alertLevel;
}
set {
if(alertLevel == value) return;
alertLevel = value;
OnPropertyChanged("AlertLevel");
}
private void OnPropertyChanged(string propertyName) {
GUIThreadDispatcher.Instance.BeginInvoke(() => {
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
});
}}
Found an even better answer without having to use a WeakReference to the form control and NO WPF References based on https://lostechies.com/gabrielschenker/2009/01/23/synchronizing-calls-to-the-ui-in-a-multi-threaded-application/ and Ivan's answer above:
public class GUIThreadDispatcher {
private static volatile GUIThreadDispatcher itsSingleton;
private SynchronizationContext itsSyncContext;
private GUIThreadDispatcher() {}
/// <summary>
/// This needs to be called on the GUI Thread somewhere
/// </summary>
public void Init() {
itsSyncContext = AsyncOperationManager.SynchronizationContext;
}
public static GUIThreadDispatcher Instance
{
get
{
if (itsSingleton == null)
itsSingleton = new GUIThreadDispatcher();
return itsSingleton;
}
}
public void Invoke(Action method) {
itsSyncContext.Send((state) => { method(); }, null);
}
public void BeginInvoke(Action method) {
itsSyncContext.Post((state) => { method(); }, null);
}
}
}
And initialize like this:
private void MainForm_Load(object sender, EventArgs e) {
//setup the ability to use the GUI Thread when needed via a static reference
GUIThreadDispatcher.Instance.Init();
...
}
And use like this:
public class SomeObject: INotifyPropertyChanged
{
public decimal AlertLevel {
get {
return alertLevel;
}
set {
if(alertLevel == value) return;
alertLevel = value;
OnPropertyChanged("AlertLevel");
}
private void OnPropertyChanged(string propertyName) {
GUIThreadDispatcher.Instance.BeginInvoke(() => {
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
});
}}
This turned out to be a clean implementation (relatively). Just had to include a reference to WindowsBase.dll which turns out to be a WPF library so eh..., not extremely pleased with it but it's a solution...:
public class GUIThreadDispatcher {
private static volatile GUIThreadDispatcher itsSingleton;
private Dispatcher itsDispatcher;
private GUIThreadDispatcher() { }
public static GUIThreadDispatcher Instance
{
get
{
if (itsSingleton == null)
itsSingleton = new GUIThreadDispatcher();
return itsSingleton;
}
}
public void Init() {
itsDispatcher = Dispatcher.CurrentDispatcher;
}
public object Invoke(Action method, DispatcherPriority priority = DispatcherPriority.Render, params object[] args) {
return itsDispatcher.Invoke(method, priority, args);
}
public DispatcherOperation BeginInvoke(Action method, DispatcherPriority priority = DispatcherPriority.Render, params object[] args) {
return itsDispatcher.BeginInvoke(method, priority, args);
}
Then initialize it like this:
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
GUIThreadDispatcher.Instance.Init(); //setup the ability to use the GUI Thread when needed via a static reference
Application.Run(new MainForm());
}
}
And then use it like this:
public class SomeObject: INotifyPropertyChanged
{
public decimal AlertLevel {
get {
return alertLevel;
}
set {
if(alertLevel == value) return;
alertLevel = value;
OnPropertyChanged("AlertLevel");
}
private void OnPropertyChanged(string propertyName) {
GUIThreadDispatcher.Instance.BeginInvoke(() => {
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
});
}}

Is it possible to reduce the number of delegate instances for PropertyChanged event?

For instance, if I have a type that implements PropertyChanged event of INotifyPropertyChanged interface and create 10 thousand instances of that type and register event handlers somewhere else. Then there will be the matching number of delegate instances created for each event handler. I would like to reduce memory foot print, but I am afraid it is not possible to avoid this.
I am unregistering event handlers and memory cleaned up eventually when object disposed. However, I don't like that many instances of delegates created for each event handler.
Here is the code:
public class MyCollectionPropertyObserver : IDisposable
{
#region Fields
private IObservableList _sourceCollection;
private readonly SynchronizedObservableHashSet<string> _propNameFilter =
new SynchronizedObservableHashSet<string>();
#endregion
#region Events
public event EventHandler<PropertyObservedInfoEventArgs> ChangeDetected;
#endregion
#region Constructor
public MyCollectionPropertyObserver(IObservableList collection)
{
_sourceCollection = collection;
_sourceCollection.CollectionChanged += WeakEventHandler.Wrap(CollectionChanged, eh => _sourceCollection.CollectionChanged -= eh);
Subscribe(_sourceCollection);
}
#endregion
#region Properties
public IObservableList SourceCollection
{
get { return _sourceCollection; }
}
public SynchronizedObservableHashSet<string> PropertyNameFilters
{
get { return _propNameFilter; }
}
#endregion
#region Event Handlers
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
Subscribe(e.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
Unsubscribe(e.OldItems);
break;
case NotifyCollectionChangedAction.Replace:
Unsubscribe(e.OldItems);
Subscribe(e.NewItems);
break;
case NotifyCollectionChangedAction.Reset:
Unsubscribe(_subscribedItems.ToList());
Subscribe(_sourceCollection);
break;
}
RaiseChangeDetected(new PropertyObservedInfoEventArgs(e.Action, e.NewItems, e.OldItems));
}
private void PropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (!IsFilteredProperty(args.PropertyName)) return;
RaiseChangeDetected(new PropertyObservedInfoEventArgs(sender, args.PropertyName));
}
private void InstanceChanged(object sender, InstanceChangedEventArgs args)
{
if (!IsFilteredProperty(args.ChangedProperties)) return;
RaiseChangeDetected(new PropertyObservedInfoEventArgs(sender, args.ChangedProperties));
}
#endregion
#region Methods
private bool IsFilteredProperty(string propertyName)
{
//NOTE: It is important to perform Contains check before Count == 0. Count locks, and typically there are filtered properties
return PropertyNameFilters.Contains(propertyName) || PropertyNameFilters.Count == 0;
}
private bool IsFilteredProperty(IEnumerable<string> propertyNames)
{
if (propertyNames == null) return false;
//NOTE: It is important to perform Overlaps check before Count == 0. Count locks, and typically there are filtered properties
return PropertyNameFilters.Overlaps(propertyNames) || PropertyNameFilters.Count == 0;
}
private void Subscribe(IEnumerable entities)
{
if (entities == null) return;
foreach (var entity in entities)
{
Subscribe(entity);
}
}
private readonly SynchronizedObservableHashSet<object> _subscribedItems =
new SynchronizedObservableHashSet<object>();
private void Subscribe(object entity)
{
if (entity == null) return;
if (_subscribedItems.Contains(entity))
return;
_subscribedItems.Add(entity);
var propChange = entity as INotifyPropertyChanged;
if (propChange != null)
propChange.PropertyChanged += PropertyChanged;
var instChanged = entity as INotifyInstanceChanged;
if (instChanged != null)
instChanged.InstanceChanged += InstanceChanged;
}
private void Unsubscribe(IEnumerable entities)
{
if (entities == null) return;
foreach (var entity in entities)
{
Unsubscribe(entity);
}
}
private void Unsubscribe(object entity)
{
if (entity == null) return;
_subscribedItems.Remove(entity);
var propChanged = entity as INotifyPropertyChanged;
if (propChanged != null)
propChanged.PropertyChanged -= PropertyChanged;
var instChanged = entity as INotifyInstanceChanged;
if (instChanged != null)
instChanged.InstanceChanged -= InstanceChanged;
}
private void RaiseChangeDetected(PropertyObservedInfoEventArgs message)
{
var handler = Volatile.Read(ref ChangeDetected);
if (handler == null) return;
handler(this, message);
}
private void CleanUp()
{
if (_sourceCollection == null) return;
_sourceCollection.CollectionChanged -= CollectionChanged;
_sourceCollection = null;
Unsubscribe(_subscribedItems.ToList());
}
#endregion
#region IDisposable Members
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
CleanUp();
}
#endregion
}
If there is some redundancy between the event subscriptions, then yes you can dramatically reduce the number of delegate objects. Examples:
10000 sources, one sink
Wasteful, because each conversion from method group to delegate creates a new delegate object, even though the target object and target method are exactly the same in all of them:
foreach( source in source_list )
{
source.PropertyChanged += this.ItHappened;
}
Better:
PropertyChangedEventHandler common = this.ItHappened;
foreach( source in source_list )
{
source.PropertyChanged += common;
}
One source, 10000 sinks
Wasteful, because although the same method is called for each sink object, the delegate stores both the target and method information:
foreach( sink in sink_list )
{
source.PropertyChanged += sink.ItHappened;
}
Better:
source.PropertyChanged += delegate(sender, args) {
foreach ( sink in sink_list ) {
sink.ItHappened(sender, args);
}
}
Now that code has been added to the question, I can show you how to implement my suggestion. Change
public MyCollectionPropertyObserver(IObservableList collection)
{
_sourceCollection = collection;
_sourceCollection.CollectionChanged += WeakEventHandler.Wrap(CollectionChanged, eh => _sourceCollection.CollectionChanged -= eh);
Subscribe(_sourceCollection);
}
private void Subscribe(object entity)
{
if (entity == null) return;
if (_subscribedItems.Contains(entity))
return;
_subscribedItems.Add(entity);
var propChange = entity as INotifyPropertyChanged;
if (propChange != null)
propChange.PropertyChanged += PropertyChanged; // creates a new delegate object, wasteful!
var instChanged = entity as INotifyInstanceChanged;
if (instChanged != null)
instChanged.InstanceChanged += InstanceChanged; // same problem, wasteful!
}
to
private readonly PropertyChangedEventHandler reusablePropertyChangeDelegate;
private readonly InstanceChangedEventHandler reusableInstanceChangedDelegate;
public MyCollectionPropertyObserver(IObservableList collection)
{
reusablePropertyChangeDelegate = PropertyChanged;
reusableInstanceChangeDelegate = InstanceChanged;
_sourceCollection = collection;
_sourceCollection.CollectionChanged += WeakEventHandler.Wrap(CollectionChanged, eh => _sourceCollection.CollectionChanged -= eh);
Subscribe(_sourceCollection);
}
private void Subscribe(object entity)
{
if (entity == null) return;
if (_subscribedItems.Contains(entity))
return;
_subscribedItems.Add(entity);
var propChange = entity as INotifyPropertyChanged;
if (propChange != null)
propChange.PropertyChanged += reusablePropertyChangeDelegate;
var instChanged = entity as INotifyInstanceChanged;
if (instChanged != null)
instChanged.InstanceChanged += reusableInstanceChangeDelegate;
}
You should also change Unsubscribe to use the cached instances.

Relay Command can execute and a Task

i want to start a task when a relay command is called, however i want to disable the button as long as that task is running
take this example
private ICommand update;
public ICommand Update
{
get
{
if (update == null)
{
update = new RelayCommand(
param => Task.Factory.StartNew(()=> StartUpdate()),
param => true); //true means the button will always be enabled
}
return update;
}
}
what is the best way to check if that task is running?
here is my solution but not sure if its the best way
class Vm : ObservableObject
{
Task T;
public Vm()
{
T = new Task(() => doStuff());
}
private ICommand myCommand;
public ICommand MyCommand
{
get { return myCommand ?? (myCommand = new RelayCommand( p => { T = new Task(() => doStuff()); T.Start(); }, p => T.Status != TaskStatus.Running)); }
}
private void doStuff()
{
System.Threading.Thread.Sleep(5000);
}
}
Update : Every answer here works fine, but still they dont agree with each other, and i just reached a 100 reputation , i start a bounty whenever i reach 100, so what i am looking for is an implementation for an optimal non memory leaking asynchronous RelayCommand that executes within a task in .net 4.0
I strongly recommend that you avoid new Task as well as Task.Factory.StartNew. The proper way to start an asynchronous task on a background thread is Task.Run.
You can create an asynchronous RelayCommand easily using this pattern:
private bool updateInProgress;
private ICommand update;
public ICommand Update
{
get
{
if (update == null)
{
update = new RelayCommand(
async () =>
{
updateInProgress = true;
Update.RaiseCanExecuteChanged();
await Task.Run(() => StartUpdate());
updateInProgress = false;
Update.RaiseCanExecuteChanged();
},
() => !updateInProgress);
}
return update;
}
}
I think, you can use this implementation of AsyncCommand.
public class AsyncCommand : ICommand, IDisposable
{
private readonly BackgroundWorker _backgroundWorker = new BackgroundWorker {WorkerSupportsCancellation = true};
private readonly Func<bool> _canExecute;
public AsyncCommand(Action action, Func<bool> canExecute = null, Action<object> completed = null,
Action<Exception> error = null)
{
_backgroundWorker.DoWork += (s, e) =>
{
CommandManager.InvalidateRequerySuggested();
action();
};
_backgroundWorker.RunWorkerCompleted += (s, e) =>
{
if (completed != null && e.Error == null)
completed(e.Result);
if (error != null && e.Error != null)
error(e.Error);
CommandManager.InvalidateRequerySuggested();
};
_canExecute = canExecute;
}
public void Cancel()
{
if (_backgroundWorker.IsBusy)
_backgroundWorker.CancelAsync();
}
public bool CanExecute(object parameter)
{
return _canExecute == null
? !_backgroundWorker.IsBusy
: !_backgroundWorker.IsBusy && _canExecute();
}
public void Execute(object parameter)
{
_backgroundWorker.RunWorkerAsync();
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_backgroundWorker != null)
_backgroundWorker.Dispose();
}
}
}
So your solution to use RelayCommand almost works. The problem is that the UI won't immediately update after the task finishes running. This is because something needs to trigger the ICommand's CanExecuteChanged event in order for the UI to properly update.
One way to solve this problem is by creating a new kind of ICommand. For example:
class AsyncRelayCommand : ICommand
{
private Func<object, Task> _action;
private Task _task;
public AsyncRelayCommand(Func<object,Task> action)
{
_action = action;
}
public bool CanExecute(object parameter)
{
return _task == null || _task.IsCompleted;
}
public event EventHandler CanExecuteChanged;
public async void Execute(object parameter)
{
_task = _action(parameter);
OnCanExecuteChanged();
await _task;
OnCanExecuteChanged();
}
private void OnCanExecuteChanged()
{
var handler = this.CanExecuteChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
Now your view model can do something like the following
private ICommand myCommand;
public ICommand MyCommand
{
get { return myCommand ?? (myCommand = new AsyncRelayCommand(p => Task.Factory.StartNew(doStuff))); }
}
private void doStuff()
{
System.Threading.Thread.Sleep(5000);
}
Or you could make your doStuff function an "async" function like so
private ICommand myCommand2;
public ICommand MyCommand2
{
get { return myCommand2 ?? (myCommand2 = new AsyncRelayCommand(p => doStuff2())); }
}
private async Task doStuff2()
{
await Task.Delay(5000);
}
You could have a static variable IsRunning, which you can set to True when your task starts, to false when it finishes, and just bind that enabled button to the state of the IsRunning
I am trying to avoid Prism library to keep my control simple as possible from point of view of mount of reference assemblies and I ended up with this solution
_cmd = new RelayCommand(async delegate
{
await Task.Run(() => <YourMethod>());
}, delegate { return !IsInProgress; }) );
Seems to be working well. (if you don't need to pass commandParameter in). Unfortunately this is still a problem.
RelayCommand class inherits from ICommand
public class RelayCommand : ICommand
{
private Action<object> _execute;
private Predicate<object> _canExecute;
private event EventHandler CanExecuteChangedInternal;
public RelayCommand(Action<object> execute)
: this(execute, DefaultCanExecute)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
if (canExecute == null)
{
throw new ArgumentNullException("canExecute");
}
_execute = execute;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
CanExecuteChangedInternal += value;
}
remove
{
CommandManager.RequerySuggested -= value;
CanExecuteChangedInternal -= value;
}
}
public bool CanExecute(object parameter)
{
return _canExecute != null && _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public void OnCanExecuteChanged()
{
EventHandler handler = CanExecuteChangedInternal;
if (handler != null)
{
//DispatcherHelper.BeginInvokeOnUIThread(() => handler.Invoke(this, EventArgs.Empty));
handler.Invoke(this, EventArgs.Empty);
}
}
public void Destroy()
{
_canExecute = _ => false;
_execute = _ => { return; };
}
private static bool DefaultCanExecute(object parameter)
{
return true;
}
}

BindingList<T> INotifyPropertyChanged unexpected behavior

Suppose, I have objects:
public interface ITest
{
string Data { get; set; }
}
public class Test1 : ITest, INotifyPropertyChanged
{
private string _data;
public string Data
{
get { return _data; }
set
{
if (_data == value) return;
_data = value;
OnPropertyChanged("Data");
}
}
protected void OnPropertyChanged(string propertyName)
{
var h = PropertyChanged;
if (null != h) h(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
and its holder:
private BindingList<ITest> _listTest1;
public BindingList<ITest> ListTest1 { get { return _listTest1 ?? (_listTest1 = new BindingList<ITest>() { RaiseListChangedEvents = true }); }
}
Also, I subscribe to ListChangedEvent
public MainWindow()
{
InitializeComponent();
ListTest1.ListChanged += new ListChangedEventHandler(ListTest1_ListChanged);
}
void ListTest1_ListChanged(object sender, ListChangedEventArgs e)
{
MessageBox.Show("ListChanged1: " + e.ListChangedType);
}
And 2 test handlers:
For adding object
private void AddITestHandler(object sender, RoutedEventArgs e)
{
ListTest1.Add(new Test1 { Data = Guid.NewGuid().ToString() });
}
and for changing
private void ChangeITestHandler(object sender, RoutedEventArgs e)
{
if (ListTest1.Count == 0) return;
ListTest1[0].Data = Guid.NewGuid().ToString();
//if (ListTest1[0] is INotifyPropertyChanged)
// MessageBox.Show("really pch");
}
ItemAdded occurs, but ItemChanged not. Inside seeting proprty "Data" I found that no subscribers for my event PropertyChanged:
protected void OnPropertyChanged(string propertyName)
{
var h = PropertyChanged; // h is null! why??
if (null != h) h(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
Digging deeper i took reflector and discover BindingList:
protected override void InsertItem(int index, T item)
{
this.EndNew(this.addNewPos);
base.InsertItem(index, item);
if (this.raiseItemChangedEvents)
{
this.HookPropertyChanged(item);
}
this.FireListChanged(ListChangedType.ItemAdded, index);
}
private void HookPropertyChanged(T item)
{
INotifyPropertyChanged changed = item as INotifyPropertyChanged;
if (changed != null) // Its seems like null reference! really??
{
if (this.propertyChangedEventHandler == null)
{
this.propertyChangedEventHandler = new PropertyChangedEventHandler(this.Child_PropertyChanged);
}
changed.PropertyChanged += this.propertyChangedEventHandler;
}
}
Where am I wrong? Or this is known bug and i need to find some workaround?
Thanks!
BindingList<T> doesn't check if each particular item implements INotifyPropertyChanged. Instead, it checks it once for the Generic Type Parameter. So if your BindingList<T> is declared as follows:
private BindingList<ITest> _listTest1;
Then ITest should be inherited fromINotifyPropertyChanged in order to get BindingList raise ItemChanged events.
I think we may not have the full picture from your code here, because if I take the ITest interface and Test1 class verbatim (edit Oops - not exactly - because, as Nikolay says, it's failing for you because you're using ITest as the generic type parameter for the BindingList<T> which I don't here) from your code and write this test:
[TestClass]
public class UnitTest1
{
int counter = 0;
[TestMethod]
public void TestMethod1()
{
BindingList<Test1> list = new BindingList<Test1>();
list.RaiseListChangedEvents = true;
int evtCount = 0;
list.ListChanged += (object sender, ListChangedEventArgs e) =>
{
Console.WriteLine("Changed, type: {0}", e.ListChangedType);
++evtCount;
};
list.Add(new Test1() { Data = "yo yo" });
Assert.AreEqual(1, evtCount);
list[0].Data = "ya ya";
Assert.AreEqual(2, evtCount);
}
}
The test passes correctly - with evtCount ending up at 2, as it should be.
I found in constructor some interesting things:
public BindingList()
{
// ...
this.Initialize();
}
private void Initialize()
{
this.allowNew = this.ItemTypeHasDefaultConstructor;
if (typeof(INotifyPropertyChanged).IsAssignableFrom(typeof(T))) // yes! all you're right
{
this.raiseItemChangedEvents = true;
foreach (T local in base.Items)
{
this.HookPropertyChanged(local);
}
}
}
Quick fix 4 this behavior:
public class BindingListFixed<T> : BindingList<T>
{
[NonSerialized]
private readonly bool _fix;
public BindingListFixed()
{
_fix = !typeof (INotifyPropertyChanged).IsAssignableFrom(typeof (T));
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
if (RaiseListChangedEvents && _fix)
{
var c = item as INotifyPropertyChanged;
if (null!=c)
c.PropertyChanged += FixPropertyChanged;
}
}
protected override void RemoveItem(int index)
{
var item = base[index] as INotifyPropertyChanged;
base.RemoveItem(index);
if (RaiseListChangedEvents && _fix && null!=item)
{
item.PropertyChanged -= FixPropertyChanged;
}
}
void FixPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!RaiseListChangedEvents) return;
if (_itemTypeProperties == null)
{
_itemTypeProperties = TypeDescriptor.GetProperties(typeof(T));
}
var propDesc = _itemTypeProperties.Find(e.PropertyName, true);
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, IndexOf((T)sender), propDesc));
}
[NonSerialized]
private PropertyDescriptorCollection _itemTypeProperties;
}
Thanks for replies!
The type of elements that you parameterize BindingList<> with (ITest in your case) must be inherited from INotifyPropertyChanged. Options:
Change you inheritance tree ITest: INotifyPropertyChanged
Pass concrete class to the generic BindingList

Categories