I have this DependencyProperty which holds an entity with a property that is a collection (ShoutBox.Entities):
public static readonly DependencyProperty ShoutBoxProperty = DependencyProperty.Register("ShoutBox",typeof (ShoutBox),typeof (ShoutBoxViewerControl));
public ShoutBox ShoutBox
{
get { return (ShoutBox) GetValue(ShoutBoxProperty); }
set { SetValue(ShoutBoxProperty, value); }
}
It is being binded in xaml like such:
<ItemsControl ItemsSource="{Binding ShoutBox.Entries}">
.
.
</ItemsControl>
When I bind it the first time, it works as expected but there are times when I need to add items to the collection (with a method that is in the same control), like such:
public void AddNewEntry(ShoutBoxEntry newEntry)
{
Dispatcher.Invoke(new Action(() =>{
ShoutBox.Entries.Add(newEntry); //Adding directly the the Dependency property
}));
}
The problem is that when I add a new element with the above method, the item isn't being displayed in the ItemsControl.
My question is, why isn't the new element that I am adding isn't being displayed in the ItemsControl ?
[Edit]
Entries (ShoutBox.Entries) is of type List<ShoutBoxEntry>
What is the type of Entries? It either needs to be ObservableCollection or implement ICollectionChanged. Otherwise the binding doesn't know that a new item has been added.
Changing the type of Entries should indeed solve the problem...
If you want to avoid the explicit call to Dispatcher.Invoke, I wrote a collection that raises the CollectionChanged and PropertyChanged events on the thread that created the collection :
public class AsyncObservableCollection<T> : ObservableCollection<T>
{
private SynchronizationContext _synchronizationContext = SynchronizationContext.Current;
public AsyncObservableCollection()
{
}
public AsyncObservableCollection(IEnumerable<T> list)
: base(list)
{
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (SynchronizationContext.Current == _synchronizationContext)
{
// Execute the CollectionChanged event on the current thread
RaiseCollectionChanged(e);
}
else
{
// Post the CollectionChanged event on the creator thread
_synchronizationContext.Post(RaiseCollectionChanged, e);
}
}
private void RaiseCollectionChanged(object param)
{
// We are in the creator thread, call the base implementation directly
base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (SynchronizationContext.Current == _synchronizationContext)
{
// Execute the PropertyChanged event on the current thread
RaisePropertyChanged(e);
}
else
{
// Post the PropertyChanged event on the creator thread
_synchronizationContext.Post(RaisePropertyChanged, e);
}
}
private void RaisePropertyChanged(object param)
{
// We are in the creator thread, call the base implementation directly
base.OnPropertyChanged((PropertyChangedEventArgs)param);
}
}
More details can be found here :
http://www.thomaslevesque.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/
Related
How to Override PropertyChangedCallback of a predefined Dependency Property ItemsSource in a WPF ItemsControl.
I developed a WPF Custom Control inherited from ItemsControl. In that I used the predefined Dependency Property ItemsSource. In that I need to monitor and check data once the Collection gets updated.
I searched a lot in google, but I can't able to find any related solution to fulfill my requirement.
https://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.itemssource(v=vs.110).aspx
Kindly assist me, whats the method name to Override ?...
Call OverrideMetadata in a static constructor of your derived ItemsSource class:
public class MyItemsControl : ItemsControl
{
static MyItemsControl()
{
ItemsSourceProperty.OverrideMetadata(
typeof(MyItemsControl),
new FrameworkPropertyMetadata(OnItemsSourcePropertyChanged));
}
private static void OnItemsSourcePropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((MyItemsControl)obj).OnItemsSourcePropertyChanged(e);
}
private void OnItemsSourcePropertyChanged(DependencyPropertyChangedEventArgs e)
{
var oldCollectionChanged = e.OldValue as INotifyCollectionChanged;
var newCollectionChanged = e.NewValue as INotifyCollectionChanged;
if (oldCollectionChanged != null)
{
oldCollectionChanged.CollectionChanged -= OnItemsSourceCollectionChanged;
}
if (newCollectionChanged != null)
{
newCollectionChanged.CollectionChanged += OnItemsSourceCollectionChanged;
// in addition to adding a CollectionChanged handler
// any already existing collection elements should be processed here
}
}
private void OnItemsSourceCollectionChanged(
object sender, NotifyCollectionChangedEventArgs e)
{
// handle collection changes here
}
}
I have a datagrid in a WPF app that is bound to an ObservableCollection like so
<DataGrid ItemsSource="{Binding Spring.SpringData, Mode=OneWay}" />
My data displays fine, and I can edit the data in my grid, but it does not fire the PublishSpringChange event when I manually edit the data in the grid. The underlying data changes, but the event does not fire, what am I missing?
With a model of Spring that has the following
public class Spring : INotifyPropertyChanged
{
private ObservableCollection<SpringData> _SpringData;
public ObservableCollection<SpringData> SpringData
{
get { return _SpringData; }
}
public Spring()
{
....
_SpringData = new ObservableCollection<SpringData>();
SpringData.CollectionChanged += PublishSpringChange;
...
}
private void PublishSpringChange(object sender, NotifyCollectionChangedEventArgs e)
{
// Code that does not run!
}
}
with a SpringData class of
public class SpringData: BindableBase
{
private double _force;
private double _displacement;
public SpringData(double displacement, double force)
{
Displacement = displacement;
Force = force;
}
public double Displacement
{
get { return _displacement; }
set { SetProperty(ref _displacement, value); }
}
public double Force
{
get { return _force; }
set { SetProperty(ref _force, value); }
}
}
INotifyCollectionChanged only fires when you actually modify the collection. This is when you Add, Remove, Move, Replace or Reset items in the collection. It will not fire when one of the properties in a SpringData object is changed.
In order to listen to changes for a SpringData object, assuming it implements INotifyPropertyChanged, you will need to hook up listeners to the PropertyChanged event of each of the items.
Its quite useful to have a single handler for all properties changing sometimes. Here's how you can do it.
Handle CollectionChanged as you are above:
_SpringData = new ObservableCollection<SpringData>();
SpringData.CollectionChanged += PublishSpringChange;
Now for all added and removed objects to the collection add a handler to PropertyChanged:
private void PublishSpringChange(object sender, NotifyCollectionChangedEventArgs e)
{
foreach (INotifyPropertyChanged added in e.NewItems)
{
added.PropertyChanged += SpringDataOnPropertyChanged;
}
foreach (INotifyPropertyChanged removed in e.OldItems)
{
removed.PropertyChanged -= SpringDataOnPropertyChanged;
}
}
private SpringDataOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
//your code here
}
i'm profiling a silverlight component wrote by someone else.
I found many hotspots and bottlenecks, now i came across this one:
public static class CollectionExtensions
{
public static void AddRange<T>(this ObservableCollection<T> collection, IEnumerable<T> items)
{
foreach (var item in items)
{
collection.Add(item);
}
}
}
this extension method, of course, add the AddRange method to an ObservableCollection, but it's pretty intensive in calculation.
Does anyone have a better implementation, or any suggestion on how increase performance of this piece of cose?
Thank you
Calling Add multiple times results in the INotifyCollectionChanged being raised multiple times often causing the UI to redraw itself.
While Lee's answer is technically correct that raising a Reset event is the correct approach once all items have been added, I have found from experience that many grid controls (for example) do not actively support the Reset event.
The option that is most universally supported is to modify the collection away from the ObservableCollection and recreate the ObservableCollection property itself.
In other words with your ObservableCollection defined as follows on your VM...
private ObservableCollection<MyItem> _items;
public ObservableCollection<MyItem> Items {
get { return _items;}
set
{
_items = value;
OnPropertyChanged(()=> Items);
}
}
...add your new items as follows...
var tempColl = _items.ToList();
tempColl.AddRange(newItems);
Items = new ObservableCollection(tempColl);
Another thing to bear in mind about this technique is that it is thread-safe because you can add items to the ObservableCollection from a background thread if you recreate the ObservableCollection. A normal ObservableCollection cannot have items added to it via the Add method from a non-Dispatcher thread.
This is due to the ObservableCollection firing the PropertyChanged event each time an item is added to the collection. Preventing this event from firing while bulk-adding items is what you want to look at. Here is an elegant solution, though I have not tried this myself.
https://peteohanlon.wordpress.com/2008/10/22/bulk-loading-in-observablecollection/
The cost here is generally due to the change notification that is raised for each individual add. What can be preferable to do is to create a new collection implementation that is optimized for accepting ranges of data. Instead of raising change notifications for each change, and then the Binding engine processing each as single updates, you can add all the values, then raise a single event. This event can either have the big hammer of being a Reset, or you can provide the items that changed, and the index at which they changed from.
This is an example that uses a single Reset notification on its AddRange method:
/// <summary>
/// An implementation of <seealso cref="ObservableCollection{T}"/> that provides the ability to suppress
/// change notifications. In sub-classes that allows performing batch work and raising notifications
/// on completion of work. Standard usage takes advantage of this feature by providing AddRange method.
/// </summary>
/// <typeparam name="T">The type of elements in the list.</typeparam>
public class ObservableList<T> : ObservableCollection<T>
{
#region Fields
private readonly Queue<PropertyChangedEventArgs> _notifications = new Queue<PropertyChangedEventArgs>();
private readonly Queue<NotifyCollectionChangedEventArgs> _collectionNotifications = new Queue<NotifyCollectionChangedEventArgs>();
private int _notificationSupressionDepth;
#endregion
public ObservableList()
{
}
public ObservableList(IEnumerable<T> collection)
: base(collection)
{
}
public void AddRange(IEnumerable<T> list)
{
using (SupressNotifications())
{
foreach (var item in list)
{
Add(item);
}
}
OnPropertyChanged("Count");
OnPropertyChanged("Item[]");
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void RemoveRange(IEnumerable<T> list)
{
using (SupressNotifications())
{
foreach (var item in list)
{
Remove(item);
}
}
OnPropertyChanged("Count");
OnPropertyChanged("Item[]");
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void ReplaceRange(IEnumerable<T> list)
{
using (SupressNotifications())
{
Clear();
foreach (var item in list)
{
Add(item);
}
}
OnPropertyChanged("Count");
OnPropertyChanged("Item[]");
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (_notificationSupressionDepth == 0)
{
base.OnCollectionChanged(e);
}
else
{
//We cant filter duplicate Collection change events as this will break how UI controls work. -LC
_collectionNotifications.Enqueue(e);
}
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (_notificationSupressionDepth == 0)
{
base.OnPropertyChanged(e);
}
else
{
if (!_notifications.Contains(e, NotifyEventComparer.Instance))
{
_notifications.Enqueue(e);
}
}
}
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected IDisposable QueueNotifications()
{
_notificationSupressionDepth++;
return Disposable.Create(() =>
{
_notificationSupressionDepth--;
TryNotify();
});
}
protected IDisposable SupressNotifications()
{
_notificationSupressionDepth++;
return Disposable.Create(() =>
{
_notificationSupressionDepth--;
});
}
private void TryNotify()
{
if (_notificationSupressionDepth == 0)
{
while (_collectionNotifications.Count > 0)
{
var collectionNotification = _collectionNotifications.Dequeue();
base.OnCollectionChanged(collectionNotification);
}
while (_notifications.Count > 0)
{
var notification = _notifications.Dequeue();
base.OnPropertyChanged(notification);
}
}
}
}
EDIT: Adding the missing NotifyEventComparer class and an example Disposable.Create method
public sealed class NotifyEventComparer : IEqualityComparer<PropertyChangedEventArgs>
{
public static readonly NotifyEventComparer Instance = new NotifyEventComparer();
bool IEqualityComparer<PropertyChangedEventArgs>.Equals(PropertyChangedEventArgs x, PropertyChangedEventArgs y)
{
return x.PropertyName == y.PropertyName;
}
int IEqualityComparer<PropertyChangedEventArgs>.GetHashCode(PropertyChangedEventArgs obj)
{
return obj.PropertyName.GetHashCode();
}
}
//Either use Rx to access Disposable.Create or this simple implementation will do
public static class Disposable
{
public static IDisposable Create(Action dispose)
{
if (dispose == null)
throw new ArgumentNullException("dispose");
return new AnonymousDisposable(dispose);
}
private sealed class AnonymousDisposable : IDisposable
{
private Action _dispose;
public AnonymousDisposable(Action dispose)
{
_dispose = dispose;
}
public void Dispose()
{
var dispose = Interlocked.Exchange(ref _dispose, null);
if (dispose != null)
{
dispose();
}
}
}
}
You can see the AddRange method implementation here (for List):
http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs
There is already an accepted answer in this thread, but for all who are looking for a good implementation of ObservableRangeCollection supporting AddRange and ReplaceRange with single CollectionChanged notification I would really recommend this piece of code written by James Montemagno.
I have a MVVM application and somewhere in the application our company use a Third-Party that cannot use {Binding}. It's a component that draw shapes, etc. What I want it, when the ViewModel load from the persisted storage all shapes to notify the View to draw them. In a perfect world I would just have the take the Third-party and bind it to the ViewModel Shapes collection but I cannot.
From there, my idea was that I could get from the View the ViewModel (via the DataContext) and to hook the PropertyChanged event. The problem is that the DataContext is not yet initialized in the constructor, so it's NULL, and I cannot hook the event. Here is a sample of the code:
public CanvasView()
{
InitializeComponent();
((CanvasViewModel)this.DataContext).PropertyChanged += new PropertyChangedEventHandler(CanvasView_PropertyChanged); //Exception Throw here because DataContext is null
}
void CanvasView_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Shapes")
{
DrawShapes();
}
}
How can I get information from my ViewModel to my View in that case?
All of the answers so far breaks the MVVM pattern with having code-behind on the view. Personally I would wrap the 3rd party control in a UserControl and then wire up a few dependency properties with property change events.
C#
public partial class MyWrappedControl : UserControl
{
public static readonly DependencyProperty ShapesProperty = DependencyProperty.Register("Shapes", typeof(ObservableCollection<IShape>), typeof(MyWrappedControl),
new PropertyMetadata(null, MyWrappedControl.OnShapesPropertyChanged);
public ObservableCollection<IShape> Shapes
{
get { return (ObservableCollection<IShape>)GetValue(ShapesProperty); }
set { SetValue(ShapesProperty, value); }
}
private static void OnShapesPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((MyWrappedControl)o).OnShapesPropertyChanged(e);
}
private void OnShapesPropertyChanged(DependencyPropertyChangedEventArgs e)
{
// Do stuff, e.g. shapeDrawer.DrawShapes();
}
}
XAML
<UserControl
Name="MyWrappedControl"
x:Class="MyWrappedControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<!-- your control -->
<shapeDrawerControl x:Name="shapeDrawer" />
</UserControl>
you could also attach your handler in the Loaded event.
public CanvasView()
{
InitializeComponent();
this.Loaded += this.ViewLoaded;
}
void ViewLoaded(object sender, PropertyChangedEventArgs e)
{
((CanvasViewModel)this.DataContext).PropertyChanged += new PropertyChangedEventHandler(CanvasView_PropertyChanged);
}
void CanvasView_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Shapes")
{
DrawShapes();
}
}
I want to comment Dennis Roche answer.
Really, in this case we can use wrap approach, because we need to redraw view when Shapes collection changed. But view model logic can be too complex, and ,for instance, instead of redraw on PropertyChanged we should redraw on some custom event (f.i. ModelReloadEvent). In this case, wrapping doesn't help, but subscription on this event does, as in Muad'Dib solution - view model use event based communication with view, but this event should be view specific.
Using code-behind with View specific logic doesn't break MVVM. Yes, this code can be decorated with behavior/action, but using code behind - just simple solution.
Also, take a look at this view on MVVM. According to structure, ViewModel knows about abstract IView.If you worked with Caliburn/Caliburn.Micro MVVM frameworks you remember ViewAware class and IViewAware, which allows get view in view model.
So, more flexible solution I guess is next:
View:
public class CanvasView() : ICanvasView
{
public CanvasView()
{
InitializeComponent();
}
public void DrawShapes()
{
// implementation
}
}
ICanvasView:
public interface ICanvasView
{
void DrawShapes();
}
CanvasViewModel:
public class CanvasViewModel : ViewAware
{
private ObservableCollection<IShape> _shapes;
public ObservableCollection<IShape> Shapes
{
get
{
return _shapes;
}
set
{
_shapes = value;
NotifyOfPropertyChange(() => Shapes);
RedrawView();
}
}
private void RedrawView()
{
ICanvasView abstractView = (ICanvasView)GetView();
abstractView.DrawShapes();
}
}
Use the DataContextChanged event on the View (Window or UserControl)
public CanvasView()
{
InitializeComponent();
Action wireDataContext += new Action ( () => {
if (DataContext!=null)
((CanvasViewModel)this.DataContext).PropertyChanged += new PropertyChangedEventHandler(CanvasView_PropertyChanged);
});
this.DataContextChanged += (_,__) => wireDataContext();
wireDataContext();
}
void CanvasView_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Shapes")
{
DrawShapes();
}
}
update: Here is a documented way to get DataContextChanged in Silverlight 3 and 4 http://www.lhotka.net/weblog/AddingDataContextChangedInSilverlight.aspx
Some properties on my viewmodel:
public ObservableCollection<Task> Tasks { get; set; }
public int Count
{
get { return Tasks.Count; }
}
public int Completed
{
get { return Tasks.Count(t => t.IsComplete); }
}
What's the best way to update these properties when Tasks changes?
My current method:
public TaskViewModel()
{
Tasks = new ObservableCollection<Task>(repository.LoadTasks());
Tasks.CollectionChanged += (s, e) =>
{
OnPropertyChanged("Count");
OnPropertyChanged("Completed");
};
}
Is there a more elegant way to do this?
With respect to Count, you don't have to do this at all. Simply bind to Tasks.Count and your bindings will get notified of the change by the ObservableCollection.
Completed is a different story, because this is outside of ObservableCollection. Still, from the level of the abstraction/interface, you really want Completed to be a property of that Tasks collection.
For this, I think a better approach would be to create "sub" view-model for your Tasks property:
public class TasksViewModel : ObservableCollection<Task>
{
public int Completed
{
get { return this.Count(t => t.IsComplete); }
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if(e.PropertyName == "Count") NotifyCompletedChanged();
}
protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
NotifyCompletedChanged();
}
void NotifyCompletedChanged()
{
OnPropertyChanged(_completedChangedArgs);
}
readonly PropertyChangedEventArgs _completedChangedArgs = new PropertyChangedEventArgs("Completed");
}
This gives you all of the benefits of the ObservableCollection, and effectively makes the Completed property part of it. We still haven't captured only the cases where the number of completed items truly changes, but we have reduced the number of redundant notifications somewhat.
Now the viewmodel just has the property:
public TasksViewModel Tasks { get; set; }
…and you can bind to Tasks, Tasks.Count, and Tasks.Completed with ease.
As an alternative, if you would rather create these other properties on the "main" view-model, you can take this notion of a subclassed ObservableCollection<T> to create one with some method where you can pass in an Action<string> delegate, which would represent raising a property change notification on the main view-model, and some list of property names. This collection could then effectively raise the property change notifications on the view-model:
public class ObservableCollectionWithSubscribers<T> : ObservableCollection<T>
{
Action<string> _notificationAction = s => { }; // do nothing, by default
readonly IList<string> _subscribedProperties = new List<string>();
public void SubscribeToChanges(Action<string> notificationAction, params string[] properties)
{
_notificationAction = notificationAction;
foreach (var property in properties)
_subscribedProperties.Add(property);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
NotifySubscribers();
}
protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
NotifySubscribers();
}
void NotifySubscribers()
{
foreach (var property in _subscribedProperties)
_notificationAction(property);
}
}
You could even leave the property type as ObservableCollection<Task>.
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
var tasks = new ObservableCollectionWithSubscribers<Task>();
tasks.SubscribeToChanges(Notify, "Completed");
Tasks = tasks;
}
public ObservableCollection<Task> Tasks { get; private set; }
public int Completed
{
get { return Tasks.Count(t => t.IsComplete); }
}
public event PropertyChangedEventHandler PropertyChanged;
void Notify(string property)
{
var handler = PropertyChanged;
if(handler != null) handler(this, new PropertyChangedEventArgs(property));
}
}
Looks rather elegant to me. I really don't know how you'd make that more succinct.
(How odd to write an answer like this. If somebody actually comes up with something more elegant, I might delete this.)
Okay, I noticed one thing, unrelated to the original question: Your Tasks property has a public setter. Make it private set;, or you'll need to implement the set with a backing field so you can remove the delegate on the previous instance, replace and wire up the new one, and do OnPropertyChanged with "Tasks", "Count", and "Completed". (And seeing how Tasks is set in the constructor, I'm guessing private set; is the better option.)
Doesn't make notifying about Count and Completed more elegant, but it fixes a bug.
And many MVVM frameworks get the property name from a lambda, so that instead of OnPropertyChanged("Count"), you can write OnPropertyChanged(() => Count) so that it will follow renames done with the help of refactoring tools. I don't think renaming happens all that often, though, but it does avoid some string literals.