I am trying to update a Listbox whenever a new 'key','value' is added to a dictionary.
I have a class that implements IDictionary and INotifyPropertyChanged ( copied from MSDN) and it has a method Add. (I have not added other methods to keep the questions short);
public class SimpleDictionary : IDictionary, INotifyPropertyChanged
{
public DictionaryEntry[] items;
public Int32 ItemsInUse = 0;
public void Add(object key, object value)
{
// Add the new key/value pair even if this key already exists in the dictionary.
if (ItemsInUse == items.Length)
throw new InvalidOperationException("The dictionary cannot hold any more items.");
items[ItemsInUse++] = new DictionaryEntry(key, value);
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("key"));
}
}
In Main void method I have following:
public static SimpleDictionary clients = new SimpleDictionary(3);
static void Main()
{
clients.Add("key1", "Value1");
display_List();
clients.PropertyChanged += Clients_PropertyChanged;
}
private static void Clients_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
MessageBox.Show("Dictionary changed: "+e.PropertyName);
}
static void display_List()
{
try {
form_m.listBox1.DataSource = new BindingSource(clients,null);
form_m.listBox1.DisplayMember = "key";
}
catch (Exception e) {
form_m.Program_console_1("Displat Binding Error: "+e.ToString());
}
}
public static void Update_Dictionary(object sender, System.EventArgs e)
{
clients.Add("key3","Value3");
}
When the program starts listBox1 is initialized and "key1","value" added to 'clients'. Method display_List binds clients and displays all the keys.
When I invoke Update_Dictionary method "key2","value2" are added and Clients_PropertyChanged is fired. However, listbox1 does not reflect the new key added i.e "key2". It still only shows "key1".
If I add the Datasource again then "key1" and "key2" are shown. What needs to be done so that listbox is updated when Clients_PropertyChanged is called automatically?
UPDATE: Tried using INotifyCollectionChanged still the same problem
He is the complete class I am using
using System;
using System.Collections;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
namespace x_server
{
// This class implements a simple dictionary using an array of DictionaryEntry objects (key/value pairs).
class ObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
{
private const string CountString = "Count";
private const string IndexerName = "Item[]";
private const string KeysName = "Keys";
private const string ValuesName = "Values";
private IDictionary<TKey, TValue> _Dictionary;
protected IDictionary<TKey, TValue> Dictionary
{
get { return _Dictionary; }
}
#region Constructors
public ObservableDictionary()
{
_Dictionary = new Dictionary<TKey, TValue>();
}
public ObservableDictionary(IDictionary<TKey, TValue> dictionary)
{
_Dictionary = new Dictionary<TKey, TValue>(dictionary);
}
public ObservableDictionary(IEqualityComparer<TKey> comparer)
{
_Dictionary = new Dictionary<TKey, TValue>(comparer);
}
public ObservableDictionary(int capacity)
{
_Dictionary = new Dictionary<TKey, TValue>(capacity);
}
public ObservableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
{
_Dictionary = new Dictionary<TKey, TValue>(dictionary, comparer);
}
public ObservableDictionary(int capacity, IEqualityComparer<TKey> comparer)
{
_Dictionary = new Dictionary<TKey, TValue>(capacity, comparer);
}
#endregion
#region IDictionary<TKey,TValue> Members
public void Add(TKey key, TValue value)
{
Insert(key, value, true);
}
public bool ContainsKey(TKey key)
{
return Dictionary.ContainsKey(key);
}
public ICollection<TKey> Keys
{
get { return Dictionary.Keys; }
}
public bool Remove(TKey key)
{
if (key == null) throw new ArgumentNullException("key");
TValue value;
Dictionary.TryGetValue(key, out value);
var removed = Dictionary.Remove(key);
if (removed)
//OnCollectionChanged(NotifyCollectionChangedAction.Remove, new KeyValuePair<TKey, TValue>(key, value));
OnCollectionChanged();
return removed;
}
public bool TryGetValue(TKey key, out TValue value)
{
return Dictionary.TryGetValue(key, out value);
}
public ICollection<TValue> Values
{
get { return Dictionary.Values; }
}
public TValue this[TKey key]
{
get
{
return Dictionary[key];
}
set
{
Insert(key, value, false);
}
}
#endregion
#region ICollection<KeyValuePair<TKey,TValue>> Members
public void Add(KeyValuePair<TKey, TValue> item)
{
Insert(item.Key, item.Value, true);
}
public void Clear()
{
if (Dictionary.Count > 0)
{
Dictionary.Clear();
OnCollectionChanged();
}
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return Dictionary.Contains(item);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
Dictionary.CopyTo(array, arrayIndex);
}
public int Count
{
get { return Dictionary.Count; }
}
public bool IsReadOnly
{
get { return Dictionary.IsReadOnly; }
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
return Remove(item.Key);
}
#endregion
#region IEnumerable<KeyValuePair<TKey,TValue>> Members
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return Dictionary.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)Dictionary).GetEnumerator();
}
#endregion
#region INotifyCollectionChanged Members
public event NotifyCollectionChangedEventHandler CollectionChanged;
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public void AddRange(IDictionary<TKey, TValue> items)
{
if (items == null) throw new ArgumentNullException("items");
if (items.Count > 0)
{
if (Dictionary.Count > 0)
{
if (items.Keys.Any((k) => Dictionary.ContainsKey(k)))
throw new ArgumentException("An item with the same key has already been added.");
else
foreach (var item in items) Dictionary.Add(item);
}
else
_Dictionary = new Dictionary<TKey, TValue>(items);
OnCollectionChanged(NotifyCollectionChangedAction.Add, items.ToArray());
}
}
private void Insert(TKey key, TValue value, bool add)
{
if (key == null) throw new ArgumentNullException("key");
TValue item;
if (Dictionary.TryGetValue(key, out item))
{
if (add) throw new ArgumentException("An item with the same key has already been added.");
if (Equals(item, value)) return;
Dictionary[key] = value;
OnCollectionChanged(NotifyCollectionChangedAction.Replace, new KeyValuePair<TKey, TValue>(key, value), new KeyValuePair<TKey, TValue>(key, item));
}
else
{
Dictionary[key] = value;
OnCollectionChanged(NotifyCollectionChangedAction.Add, new KeyValuePair<TKey, TValue>(key, value));
}
}
private void OnPropertyChanged()
{
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerName);
OnPropertyChanged(KeysName);
OnPropertyChanged(ValuesName);
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private void OnCollectionChanged()
{
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, KeyValuePair<TKey, TValue> changedItem)
{
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, changedItem));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, KeyValuePair<TKey, TValue> newItem, KeyValuePair<TKey, TValue> oldItem)
{
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItem, oldItem));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, IList newItems)
{
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItems));
}
}
}
INotifyPropertyChanged is used if the value of a property changes. The collection itself does not change, its contents do. This is signaled by another interface: INotifyCollectionChanged.
You don't need a custom collection type for that, to get updates when a collection is updated use ObservableCollection instead.
EDIT: I just realized ObservableCollection is not available by default in Windows Forms, but it looks like you can easily add it.
Related
UWP Community Toolkit's (now known as Windows Community Toolkit) AdvancedCollectionView collection implements the ISupportIncrementalLoading interface, and so I'm trying to use it with my ListView to only load portions of items at one time, but the ListView still loads all items at once. What am I missing?
Here is the XAML:
<ListView x:Name="MyListView"
DataFetchSize="10"
IncrementalLoadingTrigger="Edge"
IncrementalLoadingThreshold="10"
ItemSource="{x:Bind ACV, Mode=TwoWay}">
</ListView>
And here is the code-behind:
public class MainPage
{
public AdvancedCollectionView ACV { get; set; }
// Lets say that DocCollection contains 1000 items
public ObservableCollection<Document> DocCollection;
public MainPage()
{
ACV = new AdvancedCollectionView(DocCollection, true);
}
}
The AdvancedCollectionView is a collection view implementation that support filtering, sorting and incremental loading. But according to the usage section of the document I linked,
incremental loading: if your source collection supports the feature then AdvancedCollectionView will do as well (it simply forwards the calls)
So that the AdvancedCollectionView does't have incremental feature itself, it simple forwards the calls. That means the source collection you provided for the AdvancedCollectionView should inherit from ISupportIncrementalLoading interface. Also if you check the AdvancedCollectionView.LoadMoreItemsAsync method, it shows Not implemented yet which indicates AdvancedCollectionView doesn't implement the ISupportIncrementalLoading interface.
And in your case, you just use ObservableCollection for the source collection that doesn't support ISupportIncrementalLoading by default. For creating collection views that support incremental loading please reference this official sample.
DocCollection = new GeneratorIncrementalLoadingClass<DataTest>(1000, (count) =>
{
return new DataTest() { Country = "Ghana" + count, City = "Wa" + count };
});
DocCollection.CollectionChanged += DocCollection_CollectionChanged;
ACV = new AdvancedCollectionView(DocCollection, true);
MyListView.ItemsSource = ACV;
public class GeneratorIncrementalLoadingClass<T> : IncrementalLoadingBase
{
public GeneratorIncrementalLoadingClass(uint maxCount, Func<int, T> generator)
{
_generator = generator;
_maxCount = maxCount;
}
protected async override Task<IList<object>> LoadMoreItemsOverrideAsync(System.Threading.CancellationToken c, uint count)
{
uint toGenerate = System.Math.Min(count, _maxCount - _count);
// Wait for work
await Task.Delay(10);
// This code simply generates
var values = from j in Enumerable.Range((int)_count, (int)toGenerate)
select (object)_generator(j);
_count += toGenerate;
return values.ToArray();
}
protected override bool HasMoreItemsOverride()
{
return _count < _maxCount;
}
#region State
Func<int, T> _generator;
uint _count = 0;
uint _maxCount;
#endregion
}
public abstract class IncrementalLoadingBase : IList, ISupportIncrementalLoading, INotifyCollectionChanged
{
#region IList
public int Add(object value)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(object value)
{
return _storage.Contains(value);
}
public int IndexOf(object value)
{
return _storage.IndexOf(value);
}
public void Insert(int index, object value)
{
throw new NotImplementedException();
}
public bool IsFixedSize
{
get { return false; }
}
public bool IsReadOnly
{
get { return true; }
}
public void Remove(object value)
{
throw new NotImplementedException();
}
public void RemoveAt(int index)
{
throw new NotImplementedException();
}
public object this[int index]
{
get
{
return _storage[index];
}
set
{
throw new NotImplementedException();
}
}
public void CopyTo(Array array, int index)
{
((IList)_storage).CopyTo(array, index);
}
public int Count
{
get { return _storage.Count; }
}
public bool IsSynchronized
{
get { return false; }
}
public object SyncRoot
{
get { throw new NotImplementedException(); }
}
public IEnumerator GetEnumerator()
{
return _storage.GetEnumerator();
}
#endregion
#region ISupportIncrementalLoading
public bool HasMoreItems
{
get { return HasMoreItemsOverride(); }
}
public Windows.Foundation.IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
if (_busy)
{
throw new InvalidOperationException("Only one operation in flight at a time");
}
_busy = true;
return AsyncInfo.Run((c) => LoadMoreItemsAsync(c, count));
}
#endregion
#region INotifyCollectionChanged
public event NotifyCollectionChangedEventHandler CollectionChanged;
#endregion
#region Private methods
async Task<LoadMoreItemsResult> LoadMoreItemsAsync(CancellationToken c, uint count)
{
try
{
var items = await LoadMoreItemsOverrideAsync(c, count);
var baseIndex = _storage.Count;
_storage.AddRange(items);
// Now notify of the new items
NotifyOfInsertedItems(baseIndex, items.Count);
return new LoadMoreItemsResult { Count = (uint)items.Count };
}
finally
{
_busy = false;
}
}
void NotifyOfInsertedItems(int baseIndex, int count)
{
if (CollectionChanged == null)
{
return;
}
for (int i = 0; i < count; i++)
{
var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, _storage[i + baseIndex], i + baseIndex);
CollectionChanged(this, args);
}
}
#endregion
#region Overridable methods
protected abstract Task<IList<object>> LoadMoreItemsOverrideAsync(CancellationToken c, uint count);
protected abstract bool HasMoreItemsOverride();
#endregion
#region State
List<object> _storage = new List<object>();
bool _busy = false;
#endregion
}
i had some problems with the speed of my application and done some performance profiling. The result shows that there is a lot of time in my application that is spended with linq querys especialy to the ID's of the Models. My idea was to create a observable dictionary with the ID as the key and the model as the Value. This works pretty good and is much faster than the linq querys with
.Any(x => x.ID == id)
or the linq query with
.First(x => x.ID == id)
As ObservableDictionary i used this sample
http://blogs.microsoft.co.il/shimmy/2010/12/26/observabledictionarylttkey-tvaluegt-c/
the problem now is that i need to create a ObservableCollection wich i can bind to my Views. I tried to expand the ObservableDictionary with a ObservableValue Property but that does not work
public ObservableCollection<TValue> ObservableValues
{
get
{
if (observableValues == null)
{
lock (lockObject)
{
if (observableValues == null)
observableValues = new ObservableCollection<TValue>(Dictionary.Values);
}
}
return observableValues;
}
}
When i add a Model to my dictionary or update a model the ObservableCollection which is bound to the Views will not update.
The best solution I can think of is to keep in your ObservableDictionnary an ObservableCollection containing all the values of the dictionnary.
You then have to change your class so that when you insert/update/delete a value in the dicionnary, it does the same in the ObservableCollection thus, firing the event to update the view.
Thanks for the suggestion but i tryed some implementations of a KeyedCollection and decided to switch all Collections / Dictionarys to my custom implementation of a KeyedCollection. The Performance is as fast as with the dictionary but without the using of the KVP's. Heres my observable implementation with some Replace Methods and it worked very fast.
public class ObservableKeyedCollection<TKey, TItem> : KeyedCollection<TKey, TItem>, INotifyCollectionChanged
{
private const string CountString = "Count";
private readonly Func<TItem, TKey> _getKeyForItemDelegate;
// Constructor now requires a delegate to get the key from the item
public ObservableKeyedCollection(Func<TItem, TKey> getKeyForItemDelegate) : base()
{
if (getKeyForItemDelegate == null)
throw new ArgumentNullException("Delegate passed can't be null!");
_getKeyForItemDelegate = getKeyForItemDelegate;
}
protected override TKey GetKeyForItem(TItem item)
{
return _getKeyForItemDelegate(item);
}
/// <summary>
/// Method to add a new object to the collection, or to replace an existing one if there is
/// already an object with the same key in the collection.
/// </summary>
public void AddOrReplace(TItem newObject)
{
int i = GetItemIndex(newObject);
if (i != -1)
SetItem(i, newObject);
else
Add(newObject);
}
/// <summary>
/// Method to replace an existing object in the collection, i.e., an object with the same key.
/// An exception is thrown if there is no existing object with the same key.
/// </summary>
public void Replace(TItem newObject)
{
int i = GetItemIndex(newObject);
if (i != -1)
SetItem(i, newObject);
else
throw new Exception("Object to be replaced not found in collection.");
}
/// <summary>
/// Method to get the index into the List{} in the base collection for an item that may or may
/// not be in the collection. Returns -1 if not found.
/// </summary>
private int GetItemIndex(TItem itemToFind)
{
TKey keyToFind = GetKeyForItem(itemToFind);
if (this.Contains(keyToFind))
return this.IndexOf(this[keyToFind]);
else return -1;
}
// Overrides a lot of methods that can cause collection change
protected override void SetItem(int index, TItem item)
{
var oldItem = base[index];
base.SetItem(index, item);
OnCollectionChanged(NotifyCollectionChangedAction.Replace, item, oldItem);
}
protected override void InsertItem(int index, TItem item)
{
base.InsertItem(index, item);
OnCollectionChanged(NotifyCollectionChangedAction.Add, item);
}
protected override void ClearItems()
{
base.ClearItems();
OnCollectionChanged();
}
protected override void RemoveItem(int index)
{
TItem item = this[index];
base.RemoveItem(index);
OnCollectionChanged(NotifyCollectionChangedAction.Remove, item);
}
private bool _deferNotifyCollectionChanged = false;
public void AddRange(IEnumerable<TItem> items)
{
_deferNotifyCollectionChanged = true;
foreach (var item in items)
Add(item);
_deferNotifyCollectionChanged = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (_deferNotifyCollectionChanged)
return;
if (CollectionChanged != null)
CollectionChanged(this, e);
}
#region INotifyCollectionChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public event NotifyCollectionChangedEventHandler CollectionChanged;
private void OnPropertyChanged()
{
OnPropertyChanged(CountString);
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private void OnCollectionChanged()
{
if (_deferNotifyCollectionChanged)
return;
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, TItem changedItem)
{
if (_deferNotifyCollectionChanged)
return;
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, changedItem));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, TItem newItem, TItem oldItem)
{
if (_deferNotifyCollectionChanged)
return;
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItem, oldItem));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, IList newItems)
{
if (_deferNotifyCollectionChanged)
return;
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItems));
}
#endregion
}
If one binds to a listbox's ItemsSource to a list type structure such as an ObservableCollection (or any value-only container really) with the type of a class (List< MyClass>) one can access the properties of said class.
But if one has a Dictionary<long, MyClass> and binds the itemssource to Dictionary.Values one can not access the class properties anymore.
So basically this is my XAML
<ListBox ItemsSource="{Binding bm.Values}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Property2}"/>
<TextBlock Text="{Binding Property3}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And in the view-model it's
public Dictionary<long,MyClass> bm { get; set; }
With code like this you have the Texblock DataContext set to MyClass, but you do not have access to Name, property2 and property3. But if you change the dictionary to a List the TextBlock DataContext will also be set to MyClass, but it will have access to those fields.
For architectural reasons I can not switch from the Dictionary to a non-key collection.
So the question is: how can you make XAML see the properties and why exactly does this situation actually happen?
Feel free to use this code of ObservableDictionary class
public class ObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged {
private const string CountString = "Count";
private const string IndexerName = "Item[]";
private const string KeysName = "Keys";
private const string ValuesName = "Values";
private IDictionary<TKey, TValue> _Dictionary;
protected IDictionary<TKey, TValue> Dictionary {
get { return _Dictionary; }
}
#region Constructors
public ObservableDictionary() {
_Dictionary = new Dictionary<TKey, TValue>();
}
public ObservableDictionary(IDictionary<TKey, TValue> dictionary) {
_Dictionary = new Dictionary<TKey, TValue>(dictionary);
}
public ObservableDictionary(IEqualityComparer<TKey> comparer) {
_Dictionary = new Dictionary<TKey, TValue>(comparer);
}
public ObservableDictionary(int capacity) {
_Dictionary = new Dictionary<TKey, TValue>(capacity);
}
public ObservableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) {
_Dictionary = new Dictionary<TKey, TValue>(dictionary, comparer);
}
public ObservableDictionary(int capacity, IEqualityComparer<TKey> comparer) {
_Dictionary = new Dictionary<TKey, TValue>(capacity, comparer);
}
#endregion
#region IDictionary<TKey,TValue> Members
public void Add(TKey key, TValue value) {
Insert(key, value, true);
}
public bool ContainsKey(TKey key) {
return Dictionary.ContainsKey(key);
}
public ICollection<TKey> Keys {
get { return Dictionary.Keys; }
}
public bool Remove(TKey key) {
if(key == null) throw new ArgumentNullException("key");
TValue value;
Dictionary.TryGetValue(key, out value);
var removed = Dictionary.Remove(key);
if(removed)
//OnCollectionChanged(NotifyCollectionChangedAction.Remove, new KeyValuePair<TKey, TValue>(key, value));
OnCollectionChanged();
return removed;
}
public bool TryGetValue(TKey key, out TValue value) {
return Dictionary.TryGetValue(key, out value);
}
public ICollection<TValue> Values {
get { return Dictionary.Values; }
}
public TValue this[TKey key] {
get {
return Dictionary[key];
}
set {
Insert(key, value, false);
}
}
#endregion
#region ICollection<KeyValuePair<TKey,TValue>> Members
public void Add(KeyValuePair<TKey, TValue> item) {
Insert(item.Key, item.Value, true);
}
public void Clear() {
if(Dictionary.Count > 0) {
Dictionary.Clear();
OnCollectionChanged();
}
}
public bool Contains(KeyValuePair<TKey, TValue> item) {
return Dictionary.Contains(item);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
Dictionary.CopyTo(array, arrayIndex);
}
public int Count {
get { return Dictionary.Count; }
}
public bool IsReadOnly {
get { return Dictionary.IsReadOnly; }
}
public bool Remove(KeyValuePair<TKey, TValue> item) {
return Remove(item.Key);
}
#endregion
#region IEnumerable<KeyValuePair<TKey,TValue>> Members
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
return Dictionary.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator() {
return ((IEnumerable) Dictionary).GetEnumerator();
}
#endregion
#region INotifyCollectionChanged Members
public event NotifyCollectionChangedEventHandler CollectionChanged;
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public void AddRange(IDictionary<TKey, TValue> items) {
if(items == null) throw new ArgumentNullException("items");
if(items.Count > 0) {
if(Dictionary.Count > 0) {
if(items.Keys.Any((k) => Dictionary.ContainsKey(k)))
throw new ArgumentException("An item with the same key has already been added.");
else
foreach(var item in items) Dictionary.Add(item);
} else
_Dictionary = new Dictionary<TKey, TValue>(items);
OnCollectionChanged(NotifyCollectionChangedAction.Add, items.ToArray());
}
}
private void Insert(TKey key, TValue value, bool add) {
if(key == null) throw new ArgumentNullException("key");
TValue item;
if(Dictionary.TryGetValue(key, out item)) {
if(add) throw new ArgumentException("An item with the same key has already been added.");
if(Equals(item, value)) return;
Dictionary[key] = value;
OnCollectionChanged(NotifyCollectionChangedAction.Replace, new KeyValuePair<TKey, TValue>(key, value), new KeyValuePair<TKey, TValue>(key, item));
} else {
Dictionary[key] = value;
OnCollectionChanged(NotifyCollectionChangedAction.Add, new KeyValuePair<TKey, TValue>(key, value));
}
}
private void OnPropertyChanged() {
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerName);
OnPropertyChanged(KeysName);
OnPropertyChanged(ValuesName);
}
protected virtual void OnPropertyChanged(string propertyName) {
if(PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private void OnCollectionChanged() {
OnPropertyChanged();
if(CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, KeyValuePair<TKey, TValue> changedItem) {
OnPropertyChanged();
if(CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, changedItem));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, KeyValuePair<TKey, TValue> newItem, KeyValuePair<TKey, TValue> oldItem) {
OnPropertyChanged();
if(CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItem, oldItem));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, IList newItems) {
OnPropertyChanged();
if(CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItems));
}
}
I copied this ObservableDictionary implementation from a VS2013 generated project. It is a strongly typed implementation (Key is string and Value is object). However, I must be getting the bindings wrong because I don't see any data, I get a BindingExpression path error.
One thing I note is that the MapChangedEventHandler is always null, meaning that nobody every registered for the event!
1. Who or what registers for the MapChangedEventHandler?
2. What should my bindings be? I've tried several VARIOUS permutations of bindings. All to no avail, but here's the latest permutation of the code:
public class ObservableDictionary : IObservableMap<string, object>
{
private class ObservableDictionaryChangedEventArgs : IMapChangedEventArgs<string>
{
public ObservableDictionaryChangedEventArgs(CollectionChange change, string key)
{
this.CollectionChange = change;
this.Key = key;
}
public CollectionChange CollectionChange { get; private set; }
public string Key { get; private set; }
}
private Dictionary<string, object> _dictionary = new Dictionary<string, object>();
public event MapChangedEventHandler<string, object> MapChanged;
private void InvokeMapChanged(CollectionChange change, string key)
{
var eventHandler = MapChanged;
if (eventHandler != null)
{
eventHandler(this, new ObservableDictionaryChangedEventArgs(change, key));
}
}
public void Add(string key, object value)
{
this._dictionary.Add(key, value);
this.InvokeMapChanged(CollectionChange.ItemInserted, key);
}
public void Add(KeyValuePair<string, object> item)
{
this.Add(item.Key, item.Value);
}
public bool Remove(string key)
{
if (this._dictionary.Remove(key))
{
this.InvokeMapChanged(CollectionChange.ItemRemoved, key);
return true;
}
return false;
}
public bool Remove(KeyValuePair<string, object> item)
{
object currentValue;
if (this._dictionary.TryGetValue(item.Key, out currentValue) &&
Object.Equals(item.Value, currentValue) && this._dictionary.Remove(item.Key))
{
this.InvokeMapChanged(CollectionChange.ItemRemoved, item.Key);
return true;
}
return false;
}
public object this[string key]
{
get
{
return this._dictionary[key];
}
set
{
this._dictionary[key] = value;
this.InvokeMapChanged(CollectionChange.ItemChanged, key);
}
}
public void Clear()
{
var priorKeys = this._dictionary.Keys.ToArray();
this._dictionary.Clear();
foreach (var key in priorKeys)
{
this.InvokeMapChanged(CollectionChange.ItemRemoved, key);
}
}
public ICollection<string> Keys
{
get { return this._dictionary.Keys; }
}
public bool ContainsKey(string key)
{
return this._dictionary.ContainsKey(key);
}
public bool TryGetValue(string key, out object value)
{
return this._dictionary.TryGetValue(key, out value);
}
public ICollection<object> Values
{
get { return this._dictionary.Values; }
}
public bool Contains(KeyValuePair<string, object> item)
{
return this._dictionary.Contains(item);
}
public int Count
{
get { return this._dictionary.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return this._dictionary.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this._dictionary.GetEnumerator();
}
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
int arraySize = array.Length;
foreach (var pair in this._dictionary)
{
if (arrayIndex >= arraySize) break;
array[arrayIndex++] = pair;
}
}
}
And the xaml:
<custom:RESTAPHandler
x:Class="K1MobilePhone.Views.HomePageAdmin"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:custom="clr-namespace:K1MobilePhone.Utilities"
xmlns:cells="clr-namespace:K1MobilePhone.Common"
xmlns:wptoolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True" Loaded="phoneApplicationPage_Loaded" Margin="0,6,0,-6">
<custom:RESTAPHandler.Resources>
<CollectionViewSource
x:Name="sd_source" Source="{Binding Path=SDSummaries, Mode=TwoWay}" />
<CollectionViewSource
x:Name="ticketsViewSource" Source="{Binding Path=Tickets, Mode=TwoWay}" />
</custom:RESTAPHandler.Resources>
And now the xaml.cs:
public sealed partial class HomePageAdmin : RESTAPHandler
{
private ObservableDictionary defaultViewModel = new ObservableDictionary();
public ObservableDictionary DefaultViewModel { get { return this.defaultViewModel; } }
public HomePageAdmin() : base()
{
this.InitializeComponent();
DataContext = this.DefaultViewModel;
this.DefaultViewModel["SDSummaries"] = new SDSummaries();
this.DefaultViewModel["Tickets"] = new RecentlyViewedTickets();
}
Okay I wasn't able to ever figure out why I couldn't access string-based keys in my xaml code. The default Windows store app works great. What I wound up doing is this:
Page.xaml:
<custom:RESTAPHandler.Resources>
<CollectionViewSource
x:Name="sd_source"/>
<CollectionViewSource
x:Name="ticketsViewSource"/>
</custom:RESTAPHandler.Resources>
I built everything up in the code-behind:
public sealed partial class HomePageAdmin : RESTAPHandler
{
private Grid visibleGrid;
private SDSummaries _sdSummaries = new SDSummaries();
public SDSummaries SDSummaries { get { return this._sdSummaries; } }
public HomePageAdmin() : base()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.sd_source.Source = this.SDSummaries;
base.OnNavigatedTo(e);
}
private void phoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
this.Address = this.NavigationContext.QueryString["URL"];
}
private void TicketsStatsResponse (TicketsStatsResponse response)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
PlainTitleDisclosureCellItem cell = (PlainTitleDisclosureCellItem)this.SDSummaries[1];
cell.Label = response.NewTicketCount.ToString();
cell = (PlainTitleDisclosureCellItem)this.SDSummaries[2];
cell.Label = response.UnassignedTicketCount.ToString();
});
}
Seems a bit of a hack, but I'm not sure.
I have wrote my public sealed class ObservableDictionary<TKey,TValue> : NotifyPropertyChangedClass, INotifyCollectionChanged, IDictionary<TKey, TValue> where TKey: IEquatable<TKey> class.
It marked as [Serializable].
But I got exception, when tried to serialize instance of ObservableDictionary<Int64, String>.
Exception message:
The MS.Internal.Data.EnumerableCollectionView type in assembly
"PresentationFramework, Version=4.0.0.0, Culture=neutral," isn't
marked with PublicKeyToken=31bf3856ad364e35 as serializable.
But I never used the MS.Internal.Data.EnumerableCollectionView type.
Where my mistake? My code below are located:
using System;
using System.ComponentModel;
namespace RememberEmployees {
[Serializable]
public abstract class NotifyPropertyChangedClass : INotifyPropertyChanged {
protected void NotifyPropertyChanged(string propertyName) {
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null) {
temp(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
and
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.Specialized;
namespace RememberEmployees {
[Serializable]
public sealed class ObservableDictionary<TKey,TValue> : NotifyPropertyChangedClass,
INotifyCollectionChanged, IDictionary<TKey, TValue> where TKey: IEquatable<TKey> {
SortedDictionary<TKey, TValue> dict;
IComparer<TKey> Comparer {
get { return dict.Comparer; }
}
public ObservableDictionary() {
dict = new SortedDictionary<TKey, TValue>();
IsReadOnly = false;
}
private void NotifyCollectionChanged(NotifyCollectionChangedAction action) {
NotifyCollectionChangedEventHandler temp = CollectionChanged;
if (temp != null) {
temp(this, new NotifyCollectionChangedEventArgs(action));
}
}
private void NotifyCollectionChanged(NotifyCollectionChangedAction action, object item) {
NotifyCollectionChangedEventHandler temp = CollectionChanged;
if (temp != null) {
temp(this, new NotifyCollectionChangedEventArgs(action, item));
}
}
private void NotifyCollectionChanged(NotifyCollectionChangedAction action, int index) {
NotifyCollectionChangedEventHandler temp = CollectionChanged;
if (temp != null) {
temp(this, new NotifyCollectionChangedEventArgs(action, index));
}
}
private void NotifyCollectionChanged(NotifyCollectionChangedAction action, KeyValuePair<TKey, TValue> obj, int index) {
NotifyCollectionChangedEventHandler temp = CollectionChanged;
if (temp != null) {
temp(this, new NotifyCollectionChangedEventArgs(action, obj, index));
}
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
public void Add(TKey key, TValue value) {
if (IsReadOnly)
throw new Exception("Is Read Only");
dict.Add(key, value);
NotifyPropertyChanged("Count");
NotifyCollectionChanged(NotifyCollectionChangedAction.Add,
dict.Cast<KeyValuePair<TKey, TValue>>().FirstOrDefault(n => n.Key.Equals(key)));
}
public bool ContainsKey(TKey key) {
return dict.ContainsKey(key);
}
public ICollection<TKey> Keys {
get { return dict.Keys; }
}
public bool Remove(TKey key) {
if (IsReadOnly)
throw new Exception("Is Read Only");
if (!dict.Keys.Contains(key))
return false;
int x = 0;
foreach (TKey item in dict.Keys) {
if (item.Equals(key))
break;
++x;
}
KeyValuePair<TKey, TValue> val = dict.Cast<KeyValuePair<TKey, TValue>>().FirstOrDefault(n => n.Key.Equals(key));
bool result = dict.Remove(key);
if (result) {
NotifyPropertyChanged("Count");
NotifyCollectionChanged(NotifyCollectionChangedAction.Remove, val, x);
}
return result;
}
public bool Remove(KeyValuePair<TKey, TValue> item) {
return Remove(item.Key);
}
public bool TryGetValue(TKey key, out TValue value) {
return dict.TryGetValue(key, out value);
}
public ICollection<TValue> Values {
get { return dict.Values; }
}
public TValue this[TKey key] {
get {
return dict[key];
}
set {
dict[key] = value;
NotifyCollectionChanged(NotifyCollectionChangedAction.Reset);
}
}
public void Add(KeyValuePair<TKey, TValue> item) {
Add(item.Key, item.Value);
}
public void Clear() {
if (IsReadOnly)
throw new Exception("Is Read Only");
dict.Clear();
NotifyCollectionChanged(NotifyCollectionChangedAction.Reset);
}
public bool Contains(KeyValuePair<TKey, TValue> item) {
return dict.Contains(item);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
if (arrayIndex > dict.Count)
throw new IndexOutOfRangeException();
int max = dict.Count - arrayIndex <= array.Count() ? dict.Count - arrayIndex : array.Count();
for (int i = 0; i < max; i++) {
array[i] = dict.Skip(arrayIndex).ToArray()[i];
}
}
public int Count {
get { return dict.Count; }
}
bool readOnly;
public bool IsReadOnly {
get { return readOnly; }
set { readOnly = value; NotifyPropertyChanged("IsReadOnly"); }
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
return dict.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return dict.GetEnumerator();
}
}
}
This has less to do with your class but with the data you did store in your collection. It seems that in your collection you have stored a ViewObject which internally does contain an EnumerableCollectionView object.
When you serialize data you must be sure what parts of your object graph you do want to serialize. Just putting objects in your collection could cause half of you application data sent over the wire or to disc. There is a good reason why DataContractSerializer was invented.
You should know before the serialize call what data you are going to serialize. Otherwise it could happen that e.g. in a client server application you are trying to deserialize types which are located in assemblies that do exist only on the server.