How can I do that? I need a list (of type ObservableCollection) where the latest item is first.
Try using
collection.Insert(0, item);
This would add item to the beginning of the collection (while Add adds to the end). More info here.
You should use a stack instead.
This is based on Observable Stack and Queue
Create an observable Stack, where stack is always last in first out (LIFO).
from Sascha Holl
public class ObservableStack<T> : Stack<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
public ObservableStack()
{
}
public ObservableStack(IEnumerable<T> collection)
{
foreach (var item in collection)
base.Push(item);
}
public ObservableStack(List<T> list)
{
foreach (var item in list)
base.Push(item);
}
public new virtual void Clear()
{
base.Clear();
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public new virtual T Pop()
{
var item = base.Pop();
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
return item;
}
public new virtual void Push(T item)
{
base.Push(item);
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
public virtual event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
this.RaiseCollectionChanged(e);
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
this.RaisePropertyChanged(e);
}
protected virtual event PropertyChangedEventHandler PropertyChanged;
private void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (this.CollectionChanged != null)
this.CollectionChanged(this, e);
}
private void RaisePropertyChanged(PropertyChangedEventArgs e)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, e);
}
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add { this.PropertyChanged += value; }
remove { this.PropertyChanged -= value; }
}
}
This calls INotifyCollectionChanged, does the same as a ObservableCollection, but in a stack manner.
you can try this
collection.insert(0,collection.ElementAt(collection.Count - 1));
Related
Where have I gone wrong with my implementation of a ObservableStack<T>? The XAML is failing to bind to it some how and so the information contained in the stack is not showing in the UI. If I only change the type of the property in the ViewModel from my ObservableStack<T> to ObservableCollection<T> then the UI elements appear. This makes me think it's the implementation.
I want to use this so my elements appear in the UI in Stack order and not collection order.
Here is my implementation:
public class ObservableStack<T> : INotifyPropertyChanged, INotifyCollectionChanged, ICollection , IEnumerable<T>, IEnumerable , IReadOnlyCollection<T>
{
ObservableCollection<T> _coll = new ObservableCollection<T>();
public ObservableStack()
{
_coll.CollectionChanged += _coll_CollectionChanged;
}
public ObservableStack(IEnumerable<T> items) : base()
{
foreach (var item in items)
{
Push(item);
}
}
private void _coll_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
OnCollectionChanged(e);
}
public void Push(T item)
{
_coll.Insert(0, item);
}
public void Pop()
{
_coll.RemoveAt(0);
}
public T Peek
{
get { return _coll[0]; }
}
public int Count
{
get
{
return ((ICollection)_coll).Count;
}
}
public bool IsSynchronized
{
get
{
return ((ICollection)_coll).IsSynchronized;
}
}
public object SyncRoot
{
get
{
return ((ICollection)_coll).SyncRoot;
}
}
public void CopyTo(Array array, int index)
{
((ICollection)_coll).CopyTo(array, index);
}
public IEnumerator GetEnumerator()
{
return ((ICollection)_coll).GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return ((IEnumerable<T>)_coll).GetEnumerator();
}
#region INotifyPropertyChanged
protected void OnPropertyChanged([CallerMemberName] string caller = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region INotifyCollectionChanged
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
CollectionChanged?.Invoke(this, e);
}
#endregion
}
You forgot to attach the internal CollectionChanged handler in the second constructor.
So call the other constructor (instead of the base class constructor) like this:
public ObservableStack(IEnumerable<T> items)
: this() // instead of base()
{
foreach (var item in items)
{
Push(item);
}
}
The solution I've found is that I needed to implement IList on my ObservableStack. After that everything worked as expected. Curiously, just implementing IList<T> didn't work. It needed the non generic interface. Thanks again to those who offered help.
I have myType1 with one dependency property string Text. I crated myType2 that contains dependency property ObservableCollection<myType1> Items. I also have graphical representation of Items. When i press button, it setsmyType1.Text to null. When Item.Text from Items is null I want to delete this item. I try to do this via `
private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
ObservableCollection<StringDP> ocdp = e.NewValue as ObservableCollection<StringDP>;
foreach (var sdp in ocdp)
{
if (sdp == null)
{
ocdp.Remove(sdp);
}
}
dependencyObject.SetValue(e.Property, ocdp);
}
but it's not raises when Item.Text is setted to null. What am i doing wrong. Thank you!
Update
According to documentation ObservableCollection doesn't raise CollectionChanged event when item's property is changed. I solved my problem by inheritance from ObservableCollection.
`public class ElObservableCollection<T>: ObservableCollection<T> where T: INotifyPropertyChanged
{
public ElObservableCollection(): base()
{
CollectionChanged += OnCollectionChanged;
}
public virtual void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (var item in Items)
{
item.PropertyChanged += OnItemChanged;
}
}
if (e.OldItems != null)
{
foreach (var item in Items)
{
item.PropertyChanged -= OnItemChanged;
}
}
}
private void OnItemChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "TextProperty" && sender is StringDP)
{
StringDP sdp = sender as StringDP;
if (sdp.Text == null)
{
this.Remove((T) sender);
}
}
}
public ElObservableCollection(List<T> list)
: base(list)
{
CollectionChanged += OnCollectionChanged;
}
public ElObservableCollection(IEnumerable<T> collection)
: base(collection)
{
CollectionChanged += OnCollectionChanged;
}
}`
Create a property inside myType1 that references myType2, this way if the text property inside myType1 is set to null, it can remove itself.
According to documentation ObservableCollection doesn't raise CollectionChanged event when item's property is changed. I solved my problem by inheritance from ObservableCollection.
public class ElObservableCollection<T>: ObservableCollection<T> where T: INotifyPropertyChanged
{
public ElObservableCollection(): base()
{
CollectionChanged += OnCollectionChanged;
}
public virtual void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (var item in Items)
{
item.PropertyChanged += OnItemChanged;
}
}
if (e.OldItems != null)
{
foreach (var item in Items)
{
item.PropertyChanged -= OnItemChanged;
}
}
}
private void OnItemChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "TextProperty" && sender is StringDP)
{
StringDP sdp = sender as StringDP;
if (sdp.Text == null)
{
this.Remove((T) sender);
}
}
}
public ElObservableCollection(List<T> list)
: base(list)
{
CollectionChanged += OnCollectionChanged;
}
public ElObservableCollection(IEnumerable<T> collection)
: base(collection)
{
CollectionChanged += OnCollectionChanged;
}
}
In every constructor I subscribe on CollectionChanged event.
In CollectionChanged handler method i subscribe on PropertyChanged of each item.
In PropertyChanged handler method i write needed behavior.
Guess it will be helpful for somebody.
I'd like an ISet<T> with two additional events ItemAdded and ItemRemoved.
One option I considered was deriving MyHashSet<T> from HashSet<T> but since Add and Remove are not virtual, it would require the use of new. Maybe this is a valid use of the keyword?
Another option I thought would be to implement ISet<T> and delegate everything to a private instance of HashSet<T>. This feels like a bulky solution.
Is there a pattern or framework class that gets me the same result but doesn't require less than elegant/ideal coding?
Based on the comments I received (thanks) here's what I've got:
public class NotifyingHashSet<T>
{
private HashSet<T> hashSet = new HashSet<T>();
public bool Add(T item)
{
bool added = hashSet.Add(item);
if(added && ItemAdded != null)
{
ItemAdded(this, new NotifyingHashSetEvent<T>(item));
}
return added;
}
public bool Remove(T item)
{
bool removed = hashSet.Remove(item);
if(removed && ItemRemoved != null)
{
ItemRemoved(this, new NotifyingHashSetEvent<T>(item));
}
return removed;
}
public event EventHandler<NotifyingHashSetEvent<T>> ItemAdded;
public event EventHandler<NotifyingHashSetEvent<T>> ItemRemoved;
}
public class NotifyingHashSetEvent<T> : EventArgs
{
public NotifyingHashSetEvent(T item)
{
Item = item;
}
public T Item { get; set; }
}
I would recommend inheriting rather than composing in this case.
This will ensure that you get all that HashSet offers like:
Other collection methods such as Contains and other Set operations such as IsSubsetOf
Collection initializers
You could assign it to base type, HashSet<int> foo = new NotifyingHashSet<int>()
My implementation looks like this:
public class NotifyingHashSet<T> : HashSet<T>
{
public new void Add(T item)
{
OnItemAdded(new NotifyHashSetChanged<T>(item));
base.Add(item);
}
public new void Remove(T item)
{
OnItemRemoved(new NotifyHashSetChanged<T>(item));
base.Remove(item);
}
public event EventHandler<NotifyHashSetChanged<T>> ItemAdded;
public event EventHandler<NotifyHashSetChanged<T>> ItemRemoved;
protected virtual void OnItemRemoved(NotifyHashSetChanged<T> e)
{
if (ItemRemoved != null) ItemRemoved(this, e);
}
protected virtual void OnItemAdded(NotifyHashSetChanged<T> e)
{
if (ItemAdded != null) ItemAdded(this, e);
}
}
public class NotifyHashSetChanged<T> : EventArgs
{
private readonly T _item;
public NotifyHashSetChanged(T item)
{
_item = item;
}
public T ChangedItem
{
get { return _item; }
}
}
Some tests to check stuff:
[TestClass]
public class NotifyingHashSetTests
{
[TestMethod]
public void ShouldAddToNotifyingHashSet()
{
var notifyingHashSet = new NotifyingHashSet<int>();
notifyingHashSet.ItemAdded += (sender, changed) => Assert.AreEqual(changed.ChangedItem, 1);
notifyingHashSet.Add(1);
}
[TestMethod]
public void ShouldRemoveFromNotifyingHashSet()
{
//can use collection initializer
var notifyingHashSet = new NotifyingHashSet<int> { 1 };
notifyingHashSet.ItemRemoved += (sender, changed) => Assert.AreEqual(changed.ChangedItem, 1);
notifyingHashSet.Remove(1);
}
[TestMethod]
public void ShouldContainValueInNotifyingHashSet()
{
//can use collection initializer
var notifyingHashSet = new NotifyingHashSet<int> { 1 };
Assert.IsTrue(notifyingHashSet.Contains(1));
}
[TestMethod]
public void ShouldAssignToHashSet()
{
HashSet<int> notifyingHashSet = new NotifyingHashSet<int> { 1 };
Assert.IsTrue(notifyingHashSet.IsSubsetOf(new List<int>{ 1,2 }));
}
}
Your own answer demonstrates how you can wrap a HashSet<T> and Srikanth's answer demonstrates how you can derive from HashSet<T>. However, when you derive from HashSet<T> you have to make sure the new class also correctly implements the Add and Remove methods of the ICollection<T> interface. So I have modified Srikanth's answer to properly create an ISet<T> implementation with notifications that derives from HashSet<T> by using explicit interface implementation of the relevant methods of ICollection<T>:
public class NotifyingHashSet<T> : HashSet<T>, ICollection<T> {
new public void Add(T item) {
((ICollection<T>) this).Add(item);
}
new public Boolean Remove(T item) {
return ((ICollection<T>) this).Remove(item);
}
void ICollection<T>.Add(T item) {
var added = base.Add(item);
if (added)
OnItemAdded(new NotifyHashSetEventArgs<T>(item));
}
Boolean ICollection<T>.Remove(T item) {
var removed = base.Remove(item);
if (removed)
OnItemRemoved(new NotifyHashSetEventArgs<T>(item));
return removed;
}
public event EventHandler<NotifyHashSetEventArgs<T>> ItemAdded;
public event EventHandler<NotifyHashSetEventArgs<T>> ItemRemoved;
protected virtual void OnItemRemoved(NotifyHashSetEventArgs<T> e) {
var handler = ItemRemoved;
if (handler != null)
ItemRemoved(this, e);
}
protected virtual void OnItemAdded(NotifyHashSetEventArgs<T> e) {
var handler = ItemAdded;
if (handler != null)
ItemAdded(this, e);
}
}
public class NotifyHashSetEventArgs<T> : EventArgs {
public NotifyHashSetEventArgs(T item) {
Item = item;
}
public T Item { get; private set; }
}
This class also behaves the same way as your class by only firing events when an element actually is added or removed from the set. E.g., adding the same element twice in succession will only fire one event.
I have a list box:
<ListBox x:Name="lbxAF" temsSource="{Binding}">
that gets its data from this from this modified Observable Collection:
public ObservableCollectionEx<FileItem> folder = new ObservableCollectionEx<FileItem>();
which is created within a class that uses FileSystemWatcher to monitor a specific folder for addition, deletion and modification of files.
The ObservableCollection was modified (hence the Ex at the end) so that I can modify it from an outside thread (code is not mine, I actually did some searching through this website and found it, works like a charm):
// This is an ObservableCollection extension
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
// Override the vent so this class can access it
public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
using (BlockReentrancy())
{
System.Collections.Specialized.NotifyCollectionChangedEventHandler eventHanlder = CollectionChanged;
if (eventHanlder == null)
return;
Delegate[] delegates = eventHanlder.GetInvocationList();
// Go through the invocation list
foreach (System.Collections.Specialized.NotifyCollectionChangedEventHandler handler in delegates)
{
DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
// If the subscriber is a DispatcherObject and different thread do this:
if (dispatcherObject != null && dispatcherObject.CheckAccess() == false)
{
// Invoke handler in the target dispatcher's thread
dispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind, handler, this, e);
}
// Else, execute handler as is
else
{
handler(this, e);
}
}
}
}
}
The collection is made up of these:
public class FileItem
{
public string Name { get; set; }
public string Path { get; set; }
}
which allow me to store names and paths of files.
Everything works great as far as deletion and addition of files, and the List Box gets updated flawlessly with respect to those two... however, if I change the name of any of the files, it doesn't update the list box.
How would I notify list box of the changes in FileItem's properties? I assumed that ObservableCollection would handle that, but apparently it raises flag only when FileItem is added or deleted, not when its contents are changed.
Your FileItem class should implement INotifyPropertyChanged. Below is a simple working implementation of it.
public class FileItem : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get { return _Name; }
set {
if (_Name != value)
{
_Name = value;
OnPropertyChanged("Name");
}
}
}
private string _Path;
public string Path
{
get { return _Path; }
set {
if (_Path != value)
{
_Path = value;
OnPropertyChanged("Path");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
That's how the ObservableCollection works - it monitors only insertion/deletion/moving of items.
To update the View when each item (or FileItem, in your case) changes, the FileItem must implement INotifyPropertyChanged and fire the appropriate event when you set each property that you want to observe.
Here's an example of how to do this: http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx
Try this simple one:
public class NotifyObservableCollection<TItem> : ObservableCollection<TItem>
where TItem : class , INotifyPropertyChanged, new()
{
#region Fields
private Action _itemPropertyChanged;
#endregion
#region Constructor
public NotifyObservableCollection(Action itemPropertyChanged)
{
_itemPropertyChanged = itemPropertyChanged;
}
#endregion
#region Methods
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var item in e.NewItems)
{
var notifyItem = item as INotifyPropertyChanged;
if (notifyItem != null)
{
notifyItem.PropertyChanged += ItemPropertyChanged;
}
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (var item in e.OldItems)
{
var notifyItem = item as INotifyPropertyChanged;
if (notifyItem != null)
{
notifyItem.PropertyChanged -= ItemPropertyChanged;
}
}
}
base.OnCollectionChanged(e);
}
#endregion
#region Private Methods
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(_itemPropertyChanged!=null)
{
_itemPropertyChanged();
}
}
#endregion
}
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