Raise an event if observable list count is not updating - c#

I have a project in windows form application,in which i have created an event.This event is raised when i click on any page in browser(i.e. FiddlerApplication.AfterSessionComplete). Now, i am adding the data into an ObservableCollection whenever the event is firing.To check whether list count is increasing i am using CollectionChanged event of ObservableCollection.What i want is to fire an event if list count is not increasing.
ObservableCollection<Session> Sessions = new ObservableCollection<Session>();
public void FetchSession() {
#region AttachEventListeners
FiddlerApplication.AfterSessionComplete += new SessionStateHandler(FiddlerApplication_AfterSessionComplete);
Sessions.CollectionChanged += Sessions_CollectionChanged;
}
void Sessions_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
//this fires when Collection count changes
}
void FiddlerApplication_AfterSessionComplete(Session oSession)
{
Monitor.Enter(Sessions);
Sessions.Add(oSession);
Monitor.Exit(Sessions);
}
Their is nothing available in ObservableCollection class that can tell me that Collection count is not increasing. How can i do this?

You can create a subclass of ObservableCollecton<T> and add your own custom event when the Count hasn't changed between calls to CollectionChanged events. So you basically keep a private field that stores the Count of the collection and on the next CollectionChanged event you compare the current Count with the previous count. If they are equal you raise your custom event.
A possible implementation looks like this:
public delegate void CountEqual(object sender, EventArgs e);
public class ObservableCountCollection<T>: ObservableCollection<T>
{
public CountEqual CountUnchanged;
private int _previousCount;
private object locker = new object();
public ObservableCountCollection()
{
this.CollectionChanged += ObservableCountCollection_CollectionChanged;
_previousCount = 0;
}
void ObservableCountCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
bool raiseEvent = false;
var unchanged = CountUnchanged;
lock(locker)
{
raiseEvent = _previousCount == this.Count;
_previousCount = this.Count;
}
if (raiseEvent && unchanged!=null)
{
unchanged(this, new EventArgs());
}
}
}
Notice that the NotifyCollectionChangedEventArgs type does have an Action property. For this collection I think you could also do
raiseEvent = (e.Action == NotifyCollectionChangedAction.Replace ||
e.Action == NotifyCollectionChangedAction.Move);
so you wouldn't need to keep the count and lock construct but I didn't test how that works if you're able to call AddRange on a collection. I leave that for any follow-up questions.

Related

How to remove item from collection list and catch properly that CollectionChanged event and do something there

I'm using observable collection to show list of currently active customers,
I'm showing that list in DataGrid, here is how my list looks
public ObservableCollection<Company> list = new ObservableCollection<Company>(Controller.SelectAll());
What I want to achieve is next: When user selects a company from a DataGrid, there is a button which he can press to delete
that customer/company. And it looks like this (button's event where I'm removing item from observablecollection):
private void btnDeleteSelectedCustomer_Click(object sender, RoutedEventArgs e)
{
if (dtgMyCustomers.SelectedItem != null)
{
Company k = (Company)(dtgMyCustomers.SelectedItem);
list.Remove(k);
}
}
After this I need to catch CollectionChanged event on Remove action as much as I know?
There I want to remove that item from database, and I thought it might go something like this:
public partial class Companies : Window
{
public ObservableCollection<Company> list = new ObservableCollection<Company>(Controller.SelectAll());
public Companies()
{
InitializeComponent();
list.CollectionChanged += (ss, ee) =>
{
if (ee.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove
&& ee.OldItems != null && ee.OldItems.Count > 0)
{
Company removedItem = ee.OldItems[0] as Company;
Controller.DeleteFromDatabaseById(removedItem.Id);
}
};
}
private void btnDeleteSelectedCustomer_Click(object sender, RoutedEventArgs e)
{
if (dtgMyCustomers.SelectedItem != null)
{
Company k = (Company)(dtgMyCustomers.SelectedItem);
list.Remove(k);
}
}
}
But unfortunately I did something wrong, because CollectionChange is not firing when I remove item from database in my button click event
hander..
Whatever this is my second time I'm working with ObservableCollections so I'm probably doing this wrong?
Thanks guys
Cheers

C# Events - Raising, Subscribing from two different projects in one solution

My goal is to create an event, raise the event in Project #1, have Project #2 subscribe to the event so that I can hit some code in Project #2. I was advised to do it this way because I cannot reference the code in Project #2.
I'm not familiar with events and have been researching the last hour. Is there anyone that can provide example code on how to do this? What I have now is not working.
// Creating the event
public event EventHandler UpdateUIEvent;
// Raising the event in Project #1
protected override void ResetProperties()
{
this.filePath = string.Empty;
EventArgs e = new EventArgs();
UpdateUIEvent(this, e); // this kills my program
}
// Subscribing to the event in Project #2
protected override void ClearAll()
{
boEncrypt.UpdateUIEvent += new EventHandler ?? // Not sure how to subscribe here
tbFile.text = string.Empty;
}
You're close... Please read the docs as usual for a better understanding of what needs to be where.
For your code, here's what we'll do:
In project 1, incorporate Michael HB's good recommendation to always check if there's any subscribers.
public class ProjectOneClass
{
public event EventHandler UpdateUIEvent;
// other stuff
protected override void ResetProperties()
{
this.filePath = string.Empty;
EventArgs e = new EventArgs();
var handler = UpdateUIEvent;
if (handler != null)
UpdateUIEvent(this, e);
}
}
Then in project 2, you have to have a reference to the event, which means you also need a reference to the class that contains it. You define a method in the class in project 2 that has the same signature as the event in project 1. In your case (in many cases) this signature is void (object sender, EventArgs e). EventHandler is a pre-defined delegate with this signature.
"Subscribing" to the event is adding your new method to the handler, like pOne.UpdateUIEvent += SomeMethodWithTheSameSignature; You're basically saying "whenever UpdateUIEvent is called, call this other method as well".
So now that we know that we can have a method called when the event fires, we need to define that event. If you want to call ClearAll() when the event fires, that's what goes in the method body.
public class ProjectTwoClass
{
public ProjectOneClass pOne;
// other stuff
public ProjectTwoClass()
{
pOne = new ProjectOneClass();
pOne.UpdateUIEvent += POneOnUpdateUIEvent;
}
public void POneOnUpdateUIEvent(object sender, EventArgs eventArgs)
{
ClearAll();
}
private void ClearAll()
{
tbFile.Text = string.Empty; // could probably just call tbFile.Clear();
}
}
Before raising an event, you should always check if it is not null.
protected override void ResetProperties()
{
this.filePath = string.Empty;
EventArgs e = new EventArgs();
var handler = UpdateUIEvent;
if(handler != null)
UpdateUIEvent(this, e);
}
Even when you are in different projects, you should only matter about the "public" keyword (internal or private won't work).
In project #2, you'll have
protected overeride void ClearAll()
{
boEncrypt.UpdateUIEvent += yourFunctionToTriggerWhenTheEventIsRaised;
tbFile.text = string.Empty;
}

Is it a bad idea to use the invocation list of an inherited event?

I have a class that inherits from ObservableCollection<T>. In that class I have a method that changes the collection internally and for which I'd like to suppress CollectionChanged events.
public class ContentBlockList : ObservableCollection<int> {
public void SomeMethod() {
var handlers = CollectionChanged.GetInvocationList();
foreach (NotifyCollectionChangedEventHandler handler in handlers) {
CollectionChanged -= handler;
}
// do stuff here
foreach (NotifyCollectionChangedEventHandler handler in handlers) {
CollectionChanged += handler;
}
}
}
Intuitively it seems like this should work since I'm accessing the event from within its containing object. Unfortunately, the compiler says
The event 'ObservableCollection.CollectionChanged' can only
appear on the left hand side of += or -=
I can get the code to work if I override both CollectionChanged and OnCollectionChanged(), essentially replacing the .NET versions with copies of my own. However, having to do something like that makes me suspicious that I'm ignoring some reason why doing this is a bad idea in the first place. Thanks for any thoughts on this.
As unsubscribing and re-subscribing to an event is a relatively (not really painful but I don't know how many subscribers there is likely to be) slow process I would recommend that you look into overriding both the the OnCollectionChanged and OnPropertyChanged methods for the base ObservableCollection.
So have something that resembles:
public class ContentBlockList : ObservableCollection<int>
{
private bool internallyUpdating;
public void SomeMethod()
{
this.internallyUpdating = true;
// Do Stuff (Add to base collection)
this.internallyUpdating = false;
this.OnPropertyChanged(new PropertyChangedEventArgs(#"Count");
this.OnPropertyChanged(new PropertyChangedEventArgs(#"Item[]");
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if(this.internallyUpdating)
{
return;
}
base.OnCollectionChanged(e);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if(this.internallyUpdating)
{
return;
}
base.OnPropertyChanged(e);
}
}
What this allows for is the ability to suppress the events being raised while you update internally but doing this in a way that means you do not have to unsubscribe and resubscribe to events.
When adding to this collection normally (i.e. with contentBlockList.Add(1)) you'll fall straight through to calling the base event. But when you are trying to update internally you'll suppress these events until you have finished.
I'd say this is both more efficient in terms of performance but also much neater code than what you were looking at.
On a last note I'd also say that the NotifyCollectionChangedEventAction that you provide is Reset. You've probably done quite a big change to the collection and to handle it you'll want any subscriber to have to refresh their look on the collection, be it a control in a WPF view or even another class that uses the collection.
better use this :
public class ContentBlockList : ObservableCollection<int>
{
ContentBlockList()
{
this.CollectionChanged += ContentBlockList_CollectionChanged;
}
void ContentBlockList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
}
}
if you maintain your code try this
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ContentBlockList pp = new ContentBlockList();
pp.CollectionChanged += pp_CollectionChanged;
pp.CollectionChanged += pp_CollectionChanged1;
pp.Add(11112);
pp.SomeMethod();
}
void pp_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
}
void pp_CollectionChanged1(object sender, NotifyCollectionChangedEventArgs e)
{
}
}
public class ContentBlockList : ObservableCollection<int>
{
public void SomeMethod()
{
var handlers = CollectionChanged.GetInvocationList();
foreach (NotifyCollectionChangedEventHandler handler in handlers)
{
CollectionChanged -= handler;
}
// do stuff here
foreach (NotifyCollectionChangedEventHandler handler in handlers)
{
CollectionChanged += handler;
}
}
public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;
}
As far as I understood you need to interrupt firing of CollectionChanged to do some work silently. So you can create boolean field like __FireCollectionChanged and then override OnCollectionChanged() to do:
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (__FireCollectionChanged)
base.OnCollectionChanged(e);
}
Then you can control whether event is fired by that boolean field.
And answering the actual question: you can't use the invocation list directly, because the event is not the delegate type field. It's basically just two methods add and remove for subscribe/unsubscribe behavior. Underlying delegate field is created behind the scenes and you typically don't want to use it.

Get a reference to the ObservableCollection that fired the change event

ObservableCollection<> exposes the CollectionChanged event. From the handler of that event is there any way to get a reference to the ObservableCollection that fired the event? I would have thought the sender argument would be the ObservableCollection but it's not.
This code sample illustrates that 10 ObservableCollections all have their CollectionChanged event registered to one method. From that one method I'd like to get the reference to the ObservableCollection that changed:
internal class Program
{
private static void Main(string[] args)
{
List<ObservableCollection<int>> collections = new List<ObservableCollection<int>>();
for (int i = 0; i < 10; i++)
{
ObservableCollection<int> collection = new ObservableCollection<int>();
collection.CollectionChanged += CollectionOnCollectionChanged;
collections.Add(collection);
}
collections[5].Add(1234);
}
private static void CollectionOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Add)
{
// Before proceeding I need to get a reference to the ObservableCollection<int> where the change occured which fired this event.
}
}
}
Looking at the arguments passed into the event handler, I don't see a reference to the ObservableCollection so I'm assuming I can't get it.
The sender object is the instance of the ObservableCollection that fired the event. You can cast it.
private static void CollectionOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Add)
{
ObservableCollection<int> myCollection = sender as ObservableCollection<int>;
if(myCollection != null){
//do whatever you want
}
}
}
One other option you have is to use an in-line delegate and form a closure over the collection like this:
private static void Main(string[] args)
{
List<ObservableCollection<int>> collections = new List<ObservableCollection<int>>();
for (int i = 0; i < 10; i++)
{
ObservableCollection<int> collection = new ObservableCollection<int>();
collection.CollectionChanged += (s, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
// Can reference `collection` directly here
}
};
collections.Add(collection);
}
collections[5].Add(1234);
}
The saves casting from the sender object (which I find messy.)
A further way to write this could would be:
private static void Main(string[] args)
{
List<ObservableCollection<int>> collections =
Enumerable
.Range(0, 10)
.Select(n => new ObservableCollection<int>())
.ToList();
collections
.ForEach(collection =>
{
collection.CollectionChanged += (s, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
// Can reference `collection` directly here
}
};
});
collections[5].Add(1234);
}

Differentiating between events raised by user interaction and my own code

The SelectedIndexChanged event gets fired in my application from a combo box when:
the user chooses a different
item in the combo box, or when:
my own code updates the combo
box's SelectedItem to reflect that
the combo box is now displaying
properties for a different object.
I am interested in the SelectedIndexChanged event for case 1, so that I can update the current object's properties. But in case 2, I do not want the event to fire, because the object's properties have not changed.
An example may help. Let's consider that I have a list box containing a list of people and I have a combo box representing the nationality of the currently selected person in the list. Case 1 could happen if Fred is currently selected in the list, and I use the combo box to change his nationality from English to Welsh. Case 2 could happen if I then select Bob, who is Scottish, in the list. Here, my list update event-handler code sees that Bob is now selected, and updates the combo box so that Scottish is now the selected item. This causes the combo box's SelectedIndexChanged event to be fired to set Bob's nationality to Scottish, even though it already is Scottish.
How can I update my combo box's SelectedItem property without causing the SelectedIndexChanged event to fire? One way would be to unregister the event handler, set SelectedItem, then re-register the event handler, but this seems tedious and error prone. There must be a better way.
I created a class I called SuspendLatch. Offers on a better name are welcome, but it does what you need and you would use it like this:
void Method()
{
using (suspendLatch.GetToken())
{
// Update selected index etc
}
}
void listbox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (suspendLatch.HasOutstandingTokens)
{
return;
}
// Do some work
}
It's not pretty, but it does work, and unlike unregistering events or boolean flags, it supports nested operations a bit like TransactionScope. You keep taking tokens from the latch and it's only when the last token is disposed that the HasOutstandingTokens returns false. Nice and safe. Not threadsafe, though...
Here's the code for SuspendLatch:
public class SuspendLatch
{
private IDictionary<Guid, SuspendLatchToken> tokens = new Dictionary<Guid, SuspendLatchToken>();
public SuspendLatchToken GetToken()
{
SuspendLatchToken token = new SuspendLatchToken(this);
tokens.Add(token.Key, token);
return token;
}
public bool HasOutstandingTokens
{
get { return tokens.Count > 0; }
}
public void CancelToken(SuspendLatchToken token)
{
tokens.Remove(token.Key);
}
public class SuspendLatchToken : IDisposable
{
private bool disposed = false;
private Guid key = Guid.NewGuid();
private SuspendLatch parent;
internal SuspendLatchToken(SuspendLatch parent)
{
this.parent = parent;
}
public Guid Key
{
get { return this.key; }
}
public override bool Equals(object obj)
{
SuspendLatchToken other = obj as SuspendLatchToken;
if (other != null)
{
return Key.Equals(other.Key);
}
else
{
return false;
}
}
public override int GetHashCode()
{
return Key.GetHashCode();
}
public override string ToString()
{
return Key.ToString();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Dispose managed resources.
parent.CancelToken(this);
}
// There are no unmanaged resources to release, but
// if we add them, they need to be released here.
}
disposed = true;
// If it is available, make the call to the
// base class's Dispose(Boolean) method
//base.Dispose(disposing);
}
}
}
I think the best way would be to use a flag variable:
bool updatingCheckbox = false;
void updateCheckBox()
{
updatingCheckBox = true;
checkbox.Checked = true;
updatingCheckBox = false;
}
void checkbox_CheckedChanged( object sender, EventArgs e )
{
if (!updatingCheckBox)
PerformActions()
}
[Edit: Posting only the code is not really clear]
In this case, the event handler wouldn't perform its normal operations when the checkbox is changed through updateCheckBox().
I have always used a boolean flag variable to protect against unwanted event handlers. The TaskVision sample application taught me how to do this.
Your event handler code for all of your events will look like this:
private bool lockEvents;
protected void MyEventHandler(object sender, EventArgs e)
{
if (this.lockEvents)
{
return;
}
this.lockEvents = true;
//Handle your event...
this.lockEvents = false;
}
I let the event fire. But, I set a flag before changing the index and flip it back after. In the event handler, I check if the flag is set and exit the handler if it is.
I think your focus should be on the object and not on the event that's occuring.
Say for example you have the event
void combobox_Changed( object sender, EventArgs e )
{
PerformActions()
}
and PerformActions did something to the effect of
void PerformActions()
{
(listBox.SelectedItem as IPerson).Nationality =
(comboBox.SelectedItem as INationality)
}
then inside the Person you would expect to see something to the effect of
class Person: IPerson
{
INationality Nationality
{
get { return m_nationality; }
set
{
if (m_nationality <> value)
{
m_nationality = value;
this.IsDirty = true;
}
}
}
}
the point here is that you let the object keep track of what is happening to itself, not the UI. This also lets you keep track of dirty flag tracking on your objects, which could be useful for persistence later on.
This also keeps your UI clean and keeps it from getting odd event registration code that will most likely be error prone.
I have finally found a solution to avoid the uncessary event from being fired too many time.
I use a counter and I only hook/unhook the events I want to mask once when it is not needed, and when it is needed again.
The example below shows how I hide the CellValueChanged event of a datagrid.
EventMask valueChangedEventMask;
// In the class constructor
valueChangedEventMask = new EventMask(
() => { dgv.CellValueChanged += new DataGridViewCellEventHandler(dgv_CellValueChanged); },
() => { dgv.CellValueChanged -= new DataGridViewCellEventHandler(dgv_CellValueChanged); }
);
// Use push to hide the event and pop to make it available again. The operation can be nested or be used in the event itself.
void changeCellOperation()
{
valueChangedEventMask.Push();
...
cell.Value = myNewCellValue
...
valueChangedEventMask.Pop();
}
// The class
public class EventMask
{
Action hook;
Action unHook;
int count = 0;
public EventMask(Action hook, Action unHook)
{
this.hook = hook;
this.unHook = unHook;
}
public void Push()
{
count++;
if (count == 1)
unHook();
}
public void Pop()
{
count--;
if (count == 0)
hook();
}
}

Categories

Resources