call function when a list gets modified? [duplicate] - c#

I created a Class EventList inheriting List which fires an Event each time something is Added, Inserted or Removed:
public class EventList<T> : List<T>
{
public event ListChangedEventDelegate ListChanged;
public delegate void ListChangedEventDelegate();
public new void Add(T item)
{
base.Add(item);
if (ListChanged != null
&& ListChanged.GetInvocationList().Any())
{
ListChanged();
}
}
...
}
At the Moment I use it as a Property like this:
public EventList List
{
get { return m_List; }
set
{
m_List.ListChanged -= List_ListChanged;
m_List = value;
m_List.ListChanged += List_ListChanged;
List_ListChanged();
}
}
Now my Problem is, can I somehow handle if a new Object is referred to it or prevent that, so I do not have to do the event wiring stuff in the setter?
Of course, I can change the property to "private set" but I would like to be able to use the class as variable as well.

You seldom create a new instance of a collection class in a class. Instantiate it once and clear it instead of creating a new list. (and use the ObservableCollection since it already has the INotifyCollectionChanged interface inherited)
private readonly ObservableCollection<T> list;
public ctor() {
list = new ObservableCollection<T>();
list.CollectionChanged += listChanged;
}
public ObservableCollection<T> List { get { return list; } }
public void Clear() { list.Clear(); }
private void listChanged(object sender, NotifyCollectionChangedEventArgs args) {
// list changed
}
This way you only have to hook up events once, and can "reset it" by calling the clear method instead of checking for null or equality to the former list in the set accessor for the property.
With the changes in C#6 you can assign a get property from a constructor without the backing field (the backing field is implicit)
So the code above can be simplified to
public ctor() {
List = new ObservableCollection<T>();
List.CollectionChanged += OnListChanged;
}
public ObservableCollection<T> List { get; }
public void Clear()
{
List.Clear();
}
private void OnListChanged(object sender, NotifyCollectionChangedEventArgs args)
{
// react to list changed
}

ObservableCollection is a List with a CollectionChanged event
ObservableCollection.CollectionChanged Event
For how to wire up the event handler see answer from Patrick. +1
Not sure what you are looking for but I use this for a collection with one event that fires on add, remove, and change.
public class ObservableCollection<T>: INotifyPropertyChanged
{
private BindingList<T> ts = new BindingList<T>();
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged( String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public BindingList<T> Ts
{
get { return ts; }
set
{
if (value != ts)
{
Ts = value;
if (Ts != null)
{
ts.ListChanged += delegate(object sender, ListChangedEventArgs args)
{
OnListChanged(this);
};
}
NotifyPropertyChanged("Ts");
}
}
}
private static void OnListChanged(ObservableCollection<T> vm)
{
// this will fire on add, remove, and change
// if want to prevent an insert this in not the right spot for that
// the OPs use of word prevent is not clear
// -1 don't be a hater
vm.NotifyPropertyChanged("Ts");
}
public ObservableCollection()
{
ts.ListChanged += delegate(object sender, ListChangedEventArgs args)
{
OnListChanged(this);
};
}
}

If you do not want to or can not convert to an Observable Collection, try this:
public class EventList<T> : IList<T> /* NOTE: Changed your List<T> to IList<T> */
{
private List<T> list; // initialize this in your constructor.
public event ListChangedEventDelegate ListChanged;
public delegate void ListChangedEventDelegate();
private void notify()
{
if (ListChanged != null
&& ListChanged.GetInvocationList().Any())
{
ListChanged();
}
}
public new void Add(T item)
{
list.Add(item);
notify();
}
public List<T> Items {
get { return list; }
set {
list = value;
notify();
}
}
...
}
Now, for your property, you should be able to reduce your code to this:
public EventList List
{
get { return m_List.Items; }
set
{
//m_List.ListChanged -= List_ListChanged;
m_List.Items = value;
//m_List.ListChanged += List_ListChanged;
//List_ListChanged();
}
}
Why? Setting anything in the EventList.Items will call your private notify() routine.

I have a Solution for when someone calls the Generic method from IList.add(object). So that you also get notified.
using System;
using System.Collections;
using System.Collections.Generic;
namespace YourNamespace
{
public class ObjectDoesNotMatchTargetBaseTypeException : Exception
{
public ObjectDoesNotMatchTargetBaseTypeException(Type targetType, object actualObject)
: base(string.Format("Expected base type ({0}) does not match actual objects type ({1}).",
targetType, actualObject.GetType()))
{
}
}
/// <summary>
/// Allows you to react, when items were added or removed to a generic List.
/// </summary>
public abstract class NoisyList<TItemType> : List<TItemType>, IList
{
#region Public Methods
/******************************************/
int IList.Add(object item)
{
CheckTargetType(item);
Add((TItemType)item);
return Count - 1;
}
void IList.Remove(object item)
{
CheckTargetType(item);
Remove((TItemType)item);
}
public new void Add(TItemType item)
{
base.Add(item);
OnItemAdded(item);
}
public new bool Remove(TItemType item)
{
var result = base.Remove(item);
OnItemRemoved(item);
return result;
}
#endregion
# region Private Methods
/******************************************/
private static void CheckTargetType(object item)
{
var targetType = typeof(TItemType);
if (item.GetType().IsSubclassOf(targetType))
throw new ObjectDoesNotMatchTargetBaseTypeException(targetType, item);
}
#endregion
#region Abstract Methods
/******************************************/
protected abstract void OnItemAdded(TItemType addedItem);
protected abstract void OnItemRemoved(TItemType removedItem);
#endregion
}
}

If an ObservableCollection is not the solution for you, you can try that:
A) Implement a custom EventArgs that will contain the new Count attribute when an event will be fired.
public class ChangeListCountEventArgs : EventArgs
{
public int NewCount
{
get;
set;
}
public ChangeListCountEventArgs(int newCount)
{
NewCount = newCount;
}
}
B) Implement a custom List that inherits from List and redefine the Count attribute and the constructors according to your needs:
public class CustomList<T> : List<T>
{
public event EventHandler<ChangeListCountEventArgs> ListCountChanged;
public new int Count
{
get
{
ListCountChanged?.Invoke(this, new ChangeListCountEventArgs(base.Count));
return base.Count;
}
}
public CustomList()
{ }
public CustomList(List<T> list) : base(list)
{ }
public CustomList(CustomList<T> list) : base(list)
{ }
}
C) Finally subscribe to your event:
var myList = new CustomList<YourObject>();
myList.ListCountChanged += (obj, e) =>
{
// get the count thanks to e.NewCount
};

Related

List<> propertychanged event

I am working on a project in which I need to notify my GUI that my list has new items.
I tried this with an observablecollection, but I use timers, and when I try to add or remove items from the observablecollection, then an exception is thrown that the collection is being marshalled from another thread.
Therefore, I started thinking about using the PropertyChanged event. However, I have trouble with getting it to work. What I understand is that it is used for properties. I have tried the following code without success:
public class MyCollection : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ConcurrentQueue<PC_Info_Item> data;
public List<string> table;
public MyCollection()
{
data = new ConcurrentQueue<PC_Info_Item>();
table = new List<string>();
}
public void Add(PC_Info_Item item)
{
data.Enqueue(item);
OnPropertyChanged(nameof(table));
}
public void Add(string item)
{
table.Add(item);
OnPropertyChanged(nameof(table));
}
public void delete()
{
data.TryDequeue(out PC_Info_Item item);
OnPropertyChanged(nameof(table));
}
public void delete_string(string item)
{
table.Remove(item);
OnPropertyChanged(nameof(table));
}
protected void OnPropertyChanged(string name = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Is there a way to make this work for lists?
you can wrap the calls into lock, that will make other threads wait till the call is finished.
private readonly object CollectionLock = new object();
public void Add(PC_Info_Item item)
{
lock(CollectionLock) {
data.Enqueue(item);
}
}
public void Add(string item)
{
lock(CollectionLock) {
table.Add(item);
}
}
public void delete()
{
lock(CollectionLock) {
data.TryDequeue(out PC_Info_Item item);
}
}
public void delete_string(string item)
{
lock(CollectionLock) {
table.Remove(item);
}
}
I think you should call on property changed as below where we should get the invocation list and call invoke on each receiver with lock.
private object _lock = new object();
protected void OnPropertyChanged(string name = "")
{
var receivers = this.PropertyChanged.GetInvocationList();
foreach (EventHandler<PropertyChangedEventArgs> receiver in receivers)
{
lock (this._lock)
{
receiver?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}

C# Add event to value inside Dictionary<TKey, TValue>

I'm trying to add an event to the value of a dictionary. I made my own ObservableDictionary with an event on the Add button, but now I'm trying to do the same for the value. The dictionary looks like Dictionary<string, ObservableCollection<string>>.
The calling function:
public void AddExtension(string key, string value)
{
if (_settings.ExtensionsPerFolder.ContainsKey(key))
{
_settings.ExtensionsPerFolder[key].Add(value);
var values = _settings.ExtensionsPerFolder[key];
values.Add(value);
_settings.ExtensionsPerFolder[key] = values;
}
else
_settings.ExtensionsPerFolder.Add(key, new ObservableCollection<string> { value });
}
My own dictionary implementation:
public class ObservableDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
public event EventHandler CollectionChanged;
public new TValue this[TKey key]
{
get => base[key];
set
{
base[key] = value;
OnCollectionChanged();
}
}
public new void Add(TKey key, TValue value)
{
base.Add(key, value);
OnCollectionChanged();
}
protected void OnCollectionChanged() => CollectionChanged?.Invoke(this, EventArgs.Empty);
}
The _settings.ExtensionsPerFolder[key].Add(value) doesn't trigger the OnCollectionChanged event, but the _settings.ExtensionsPerFolder[key] = values does.
[EDIT] The file in which the Dictionary is initialized:
public class Settings
{
[JsonIgnore]
public string SettingsPath { get; } = "Settings.json";
public ObservableCollection<string> Directories { get; set; } = new ObservableCollection<string>();
public ObservableDictionary<string, ObservableCollection<string>> ExtensionsPerFolder { get; set; } = new ObservableDictionary<string, ObservableCollection<string>>();
public Settings() { }
public void CollectionChanged()
{
Directories.CollectionChanged += CollectionChanged;
ExtensionsPerFolder.CollectionChanged += CollectionChanged;
}
private void CollectionChanged(object sender, EventArgs e)
{
try
{
using StreamWriter writer = new StreamWriter(SettingsPath);
writer.WriteLine(this.ToJson());
}
catch (Exception ex)
{
Logger log = new Logger();
log.Log($"Unable to save: {ex.ToString()}");
}
}
}
Is there any way that I can achieve this power of event control, or do I have to fiddle with the Add functionality within my own Dictionary for it to work.
Thanks in advance.
You have a collection of collections and you've only ever subscribed to observe the outer collection. You've subscribed to ExtensionsPerFolder.CollectionChanged which will tell you when you do something like ExtensionsPerFolder[key] = values, but you haven't subscribed to each inner collection. In this case values should be an ObservableCollection<string>.
Each time you create a new ObservableCollection<string>, you'll want to subscribe to it.
Here's where you created a new one:
_settings.ExtensionsPerFolder.Add(key, new ObservableCollection<string> { value });
You'll also want to add a CollectionChanged handler for this new collection at this point in your code. That way a statement like _settings.ExtensionsPerFolder[key].Add(value) will trigger a CollectionChanged event on that particular collection.
I'll leave it to you to decide what you want that event handler to do.

MVVM Expose List from Model to ViewModel and View

I have a model which currently looks through a series of different log files and then makes an object for each item in those files and appends them to a list (ListOfLogs). Once the model is done parsing the log files it does a property changed event to notify the VM that the ListOfLogs is ready.
The Viewmodel then handles the property changed event and creates an ObservableCollection from the model's ListOfLogs. The view then binds to that observablecollection.
Now that I have switched from an ObservableCollection to a ICollectionView I get an invalid operation exception since the calling thread doesn't own ListOfLogs object. This makes me thing that the way I expose the List is not following the MVVM pattern
Added Code:
ViewModel.cs:
public class ViewModel : INotifyPropertyChanged {
#region Fields
#endregion // Fields
#region Properties
public Model myModel { get; private set; }
public ObservableCollection<MyObject> collectionView { get; set; }
#endregion // Properties
#region Constructor
public ViewModel() {
myModel = new Model();
myModel.PropertyChanged += propertyChanged;
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion // Constructor
#region Methods
private void propertyChanged(object sender, PropertyChangedEventArgs e) {
switch (e.PropertyName ) {
case "Objects":
// Is there a better way to do this
collectionView = new ObservableCollection<MyObject>(myModel.Objects);
//
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("collectionView"));
break;
default:
Console.WriteLine(string.Format("No case for {0}, ", e.PropertyName));
break;
}
}
Model.cs:
Edit: fixed mistake when invoking the property changed event
namespace TestApp1 {
public class Model : INotifyPropertyChanged {
#region Fields
private IList<MyObject> _Objects;
public event PropertyChangedEventHandler PropertyChanged;
#endregion // Fields
#region Properties
public IList<MyObject> Objects { get => _Objects ?? (_Objects = new List<MyObject>()); private set { if (Objects != value) _Objects = value; } }
#endregion // Properties
#region Constructor
public Model() {
}
#endregion // Constructor
#region Methods
public void LoadObjects() {
// Parse through files normally for now just junk works
Parallel.For(0, 10000, dostuff => {
var myOb = new MyObject(){ dt = DateTime.Now, message = "Message" };
lock (Objects) {
Objects.Add(myOb);
}
});
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Objects"));
}
#endregion // Methods
}
public class MyObject {
public DateTime dt { get; set; }
public string message { get; set; }
public string stuff1 { get; set; }
public string stuff2 { get; set; }
}
}
The problem is, that you are modifying the Objects list while passing it to the constructor of the observable collection. (https://referencesource.microsoft.com/#system/compmod/system/collections/objectmodel/observablecollection.cs,cfaa9abd8b214ecb in the constructor where "copyfrom")
The InvalidOperationException belongs to your Objects.Add() call in the Parallel.For.
private void CopyFrom(IEnumerable<T> collection)
{
IList<T> items = Items;
if (collection != null && items != null)
{
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext())
{
items.Add(enumerator.Current);
}
}
}
}
In the delegate of Parallel.For you are using a lock. You could use this as well for the property changed event:
lock(myModel.Objects)
{
collectionView = new ObservableCollection<MyObject>(myModel.Objects);
}
Or add the event raising to the lock in the Parallel.For delegate
lock (Objects)
{
Objects.Add(myOb);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Objects"));
}
Or you could just wait until all items are read and then raise one property changed event after completing the Parallel.For.

How to make c# classes aware of each other?

I have two classes that inherit from the same abstract class. I want both of them or at least one to be aware of changes in a specific property of the other. Is there any simple method for doing this? I've been trying to move the variable to the parent class, but that just creates 2 of the same variable, and when I create a reference to the other class inside the first one the same thing happens. thanks.
This is what my code looks like:
public abstract class Animal
{
public int MovementSpeed;
public bool Death;
public string Feedback;
public bool DeerCaught;
public int tiredRate;
public virtual int Movement()
{
MovementSpeed = MovementSpeed - tiredRate;
return MovementSpeed;
}
public virtual string Print()
{
return Feedback;
}
}
public class Deer : Animal
{
public string hidden;
public string Foraging;
public int DeerCount;
public Deer()
{
this.DeerCount = 10;
this.DeerCaught = false;
this.MovementSpeed = 10;
this.tiredRate = 2;
}
public void Hide()
{
if (Hunting)
{
Feedback = "The deer is hiding.";
if (DeerCount > 0)
{
Print();
}
}
else
{
//Forage();
}
}
public void Forage()
{
if (!Hunting)
{
Feedback = "The deer is searching for food.";
if (DeerCount > 0)
{
Print();
}
}
else
{
//Hide();
}
}
}
public class Wolf : Animal
{
public int Hunger;
public bool Hunting;
public Wolf()
{
this.Hunting = false;
this.Hunger = 10;
this.MovementSpeed = 10;
this.tiredRate = 1;
}
public bool Hunt()
{
if (Hunger < 5)
{
Hunting = true;
Feedback = "The wolf is searching for his next meal.";
if (DeerCaught == true)
{
Hunger++;
}
else
{
Hunger--;
}
return Hunting;
}
else
{
Hunting = false;
Feedback = "The wolf decides to rest.";
Hunger--;
return Hunting;
}
}
public void Die()
{
if (Hunger < 0)
{
Death = true;
Feedback = "The wolf has lost the hunt.";
}
}
}
I've tried setting Hunting as static in the base class, but I just end up getting two different versions of 'Hunting' when I run the methods of each class.
If this is intended as a simulation, then Deer isn't told when a wolf is hunting, it has to find out. The analogue here is to have some way that the Deer can query about the presence of wolves (something like Deer.LookForWolves(), then to check the value of the Hunting property on each wolf. This will require some sort of controller class, representing the world.
class World
{
public static List<Animal> Animals = new List<Animal>();
//...
}
class Deer : Animal
{
//...
bool IsSafe()
{
return LookForWolves().All(wolf => !wolf.Hunting);
}
List<Wolf> LookForWolves()
{
return World.Animals.OfType<Wolf>();
}
//...
Alternatively, you could reference World as a member of each Animal, passed in via the constructor. It's up to you, and will depend on whether you need to have multiple World objects, each with a different list of Animals.
Something like implementing INotifyPropertyChanged could help:
First, declare some classes that implement INotifyPropertyChanged:
abstract class Base {
}
class ClassA : Base, INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private string _property;
public string ClassAProperty {
get {
return _property;
}
set {
_property = value;
PropertyChanged(this, new PropertyChangedEventArgs("ClassAProperty"));
}
}
}
class ClassB : Base, INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private string _property;
public string ClassBProperty {
get {
return _property;
}
set {
_property = value;
PropertyChanged(this, new PropertyChangedEventArgs("ClassBProperty"));
}
}
}
Then, wire up new instances to subscribe to the PropertyChanged event:
using System.ComponentModel;
static void Main(string[] args) {
ClassA a = new ClassA();
a.PropertyChanged += PropertyChanged;
a.ClassAProperty = "Default value";
ClassB b = new ClassB();
b.PropertyChanged += PropertyChanged;
b.ClassBProperty = "Default value";
b.ClassBProperty = "new value in B";
a.ClassAProperty = "new value in A";
Console.Read();
}
static void PropertyChanged(object sender, PropertyChangedEventArgs e) {
Console.WriteLine("Property {0} on object {1} was changed, the value is \"{2}\"", e.PropertyName, sender.GetType().Name, sender.GetType().GetProperty(e.PropertyName).GetValue(sender));
}
Output of this is:
Property ClassAProperty on object ClassA was changed, the value is "Default value"
Property ClassBProperty on object ClassB was changed, the value is "Default value"
Property ClassBProperty on object ClassB was changed, the value is "new value in B"
Property ClassAProperty on object ClassA was changed, the value is "new value in A"
Each time either property is set, PropertyChanged is called, which in the above example writes the details to the console.
In your use case, you would have the event call a method in the other class (if I understand you correctly).
A very basic way to notify property changed with your own delegate definition. Since you do not provide any code I made up some classes myself. Use this as an example to modify your own code:
public delegate void PropertyChangedEventHandler();
public abstract class Base
{
}
public class A : Base
{
public event PropertyChangedEventHandler PropertyChanged;
private int _value;
public int Value
{
get { return _value; }
set
{
_value = value;
if (PropertyChanged != null)
{
PropertyChanged();
}
}
}
public class B : Base
{
private A _a;
public B(A a)
{
_a = a;
a.PropertyChanged += new PropertyChangedEventHandler(a_PropertyChanged);
}
private void a_PropertyChanged()
{
Console.WriteLine(_a.Value);
}
}
public class Application()
{
public void DoStuff()
{
var a = new A();
var b = new B(a);
}
}
The basic idea is to pass a reference of one object to the other. For example tell the deer it is being hunted by the wolf:
public class Wolf : Animal
{
public void Hunt(Deer deer)
{
deer.SetHunter(this);
}
}
Now the deer can check whether a wolf is hunting it:
public class Deer : Animal
{
Wolf _hunter;
public void SetHunter(Wolf wolf)
{
_hunter = wolf;
}
public void Hide()
{
if (_hunter != null)
{
Feedback = "The deer is hiding.";
}
else
{
//Forage();
}
}
}
This can be improved to be more generic, but it's the basic idea of passing a reference of one object to the other.
Don't use public fields for the properties of your classes. This way you will never be aware of changes and therefore can not notify others. Put the public fields into properties and always use these properties to change the value even from inside the Animal class. The property setter can then be used to notify others of changes.
public abstract class Animal
{
private int _movementSpeed;
public int MovementSpeed
{
get
{
return _movementSpeed;
}
set
{
if (_movementSpeed != value)
{
_movementSpeed = value;
OnMovementSpeedChanged();
}
}
}
protected virtual void OnMovementSpeedChanged()
{
// Derived classes can override this method.
// It will be called each time MovementSpeed changes.
}
public virtual int Movement()
{
// always use the property to change the value
// otherwise OnMovementSpeedChanged would never be called
MovementSpeed -= tiredRate;
return MovementSpeed;
}
}
Like others already mentioned you can also implement INotifyPropertyChanged in your base class. Since this uses events for notification not only derived classes can use that but also any other object that has a reference to an animal. The approach is basically the same. Each time the property value changes you call a method that fires the event. Any other object can then handle that event.
public abstract class Animal : INotifyPropertyChanged
{
private int _movementSpeed;
public int MovementSpeed
{
get
{
return _movementSpeed;
}
set
{
if (_movementSpeed != value)
{
_movementSpeed = value;
// call this method each time a property changes
OnPropertyChanged(new PropertyChangedEventArgs("MovementSpeed"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
// always implement events like this
// -> check if the event handler is not null, then fire it
if (PropertyChanged != null)
{
PropertyChanged(this, args);
}
}
}
A class that wants to handle the event can do it like so:
public class AnyClass
{
public AnyClass(Animal anAnimal)
{
TheAnimal = anAnimal;
anAnimal += Animal_PropertyChanged;
}
public Animal TheAnimal { get; private set; }
private void Animal_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "MovementSpeed")
{
Console.WriteLine("MovementSpeed changed");
}
}
}
Derived classes however don't need to handle the event. Since the OnPropertyChanged method is declared as protected virtual they can just override it.
public class Deer : Animal
{
protected override void OnPropertyChanged(PropertyChangedEventArgs args)
{
if (args.PropertyName == "MovementSpeed")
{
Console.WriteLine("MovementSpeed changed");
}
// don't forget to call the base class otherwise the event will never get fired
base.OnPropertyChanged(args);
}
}

WPF PropertyChanged code error: cannot implement 'System.ComponentModel.INotifyPropertyChanged

i have this code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
namespace Bix
{
public class SettingsDataObject
{
private int id;
public int Id
{
get { return id; }
set { id = value == 0 ? Db.GetNextSettingsId() : value; }
}
private string adminEmail; public string AdminEmail {
get { return adminEmail; }
set { adminEmail = value; }
}
private int state; public int State { get { return state; } set { state = value == 0 ? 1 : value; } }
public object[] GetArray()
{
return new object[] { id, adminEmail, state };
}
public SettingsDataObject()
{
}
}
public class SettingsUIObjects : ObservableCollection<SettingsUIObject>,INotifyPropertyChanged
{
protected override void InsertItem(int index, SettingsUIObject item)
{
base.InsertItem(index, item);
// handle any EndEdit events relating to this item
item.ItemEndEdit += new SettingsUIObject.ItemEndEditEventHandler(ItemEndEditHandler);
item.PropertyChanged += new SettingsUIObject.PropertyChangedEventHandler(PropertyChanged);
}
public void ItemEndEditHandler(IEditableObject sender)
{
// simply forward any EndEdit events
if (ItemEndEdit != null)
{
ItemEndEdit(sender);
}
}
public event SettingsUIObject.ItemEndEditEventHandler ItemEndEdit;
public event SettingsUIObject.PropertyChangedEventHandler PropertyChanged;
}
public class SettingsDataProvider
{
private DataAccessLayer dl;
public SettingsDataProvider()
{
dl = new DataAccessLayer();
}
public SettingsUIObjects GetSettings()
{
try
{
SettingsUIObjects objs = new SettingsUIObjects();
List<SettingsDataObject> objDataObjects = dl.GetSettings();
foreach (SettingsDataObject obj in objDataObjects)
{
objs.Add(new SettingsUIObject(obj));
}
objs.ItemEndEdit += new SettingsUIObject.ItemEndEditEventHandler(SettingsItemEndEdit);
objs.CollectionChanged += new
NotifyCollectionChangedEventHandler(SettingsCollectionChanged);
objs.PropertyChanged += new SettingsUIObject.PropertyChangedEventHandler(SettingsPropertyChanged);
return objs;
}
catch (Exception) { return new SettingsUIObjects(); }
}
void SettingsItemEndEdit(IEditableObject sender)
{
SettingsUIObject obj = sender as SettingsUIObject;
// use the data access layer to update the wrapped data object
dl.UpdateSettings(obj.GetDataObject());
}
void SettingsPropertyChanged(INotifyPropertyChanged sender, PropertyChangedEventArgs e)
{
SettingsUIObject obj = sender as SettingsUIObject;
// use the data access layer to update the wrapped data object
dl.UpdateSettings(obj.GetDataObject());
}
void SettingsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (object item in e.OldItems)
{
SettingsUIObject obj = item as SettingsUIObject;
// use the data access layer to delete the wrapped data object
dl.DeleteSettings(obj.GetDataObject());
}
}
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (object item in e.NewItems)
{
SettingsUIObject obj = item as SettingsUIObject;
// use the data access layer to delete the wrapped data object
dl.UpdateSettings(obj.GetDataObject());
}
}
}
}
public class SettingsUIObject : IEditableObject, INotifyPropertyChanged
{
private SettingsDataObject obj;
public SettingsUIObject(SettingsDataObject o)
{
obj = o;
}
public SettingsDataObject GetDataObject()
{
return obj;
}
public int Id { get { return obj.Id; } set { obj.Id = value; } }
public string AdminEmail {
get { return obj.AdminEmail; }
set { obj.AdminEmail = value; }
}
public delegate void ItemEndEditEventHandler(IEditableObject sender);
public event ItemEndEditEventHandler ItemEndEdit;
#region IEditableObject Members
public void BeginEdit() { }
public void CancelEdit() { }
public void EndEdit()
{
if (ItemEndEdit != null)
{
ItemEndEdit(this);
}
}
#endregion
public delegate void PropertyChangedEventHandler(INotifyPropertyChanged sender, PropertyChangedEventArgs e);
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
}
and i keep getting the compile error:
'Bix.SettingsUIObject' does not implement interface member 'System.ComponentModel.INotifyPropertyChanged.PropertyChanged'. 'Bix.SettingsUIObject.PropertyChanged' cannot implement 'System.ComponentModel.INotifyPropertyChanged.PropertyChanged' because it does not have the matching return type of 'System.ComponentModel.PropertyChangedEventHandler'
can anyone tell me why?
thanks
Orson
public delegate void PropertyChangedEventHandler(INotifyPropertyChanged sender, PropertyChangedEventArgs e);
public event PropertyChangedEventHandler PropertyChanged;
Your code redeclares a PropertyChangedEventHandler delegate, which hides the one declared in System.ComponentModel. So your event is of type SettingsUIObject.PropertyChangedEventHandler, not System.ComponentModel.PropertyChangedEventHandler. Since the type doesn't match the one declared in INotifyPropertyChanged, your PropertyChanged event doesn't a valid implementation of the interface.
Just remove your PropertyChangedEventHandler delegate and it should work fine.

Categories

Resources