SQL Dependency OnChange event calls multiple times on page refresh reason - c#

Can any body tell me what is the reason behind SQL dependeny OnChange event calls multiple times after page refresh. What could be the possible reason behind this? Before page refresh it is called only one time per change in the database.

Problem: When I refresh page each time a new SQL Dependency variable was created and also a new Change_Event_Handler associated with that new dependency variable, and when SQL dependency is called then it has to unsubscribe from all of the existing change events which made multiple calls to my function.
Solution: Define these both variable as static in class:
internal static SqlCommand command = null;
internal static SqlDependency dependency = null;
Then use the function like this, and in application start first stop the Dependency and then start again and then do other stuff like this.
Check if dependency is started already then don't create new dependency connection and similarly new ChangeEvent,
using (EmailController.command = new SqlCommand(SQL.emailmessagesbyaccount_sql(), conn.getDbConnection()))
{
defaultemailid = emailid;
EmailController.command.Parameters.Add(new SqlParameter("#emailaccountid", emailid));
EmailController.command.Notification = null;
if (EmailController.dependency == null)
{
EmailController.dependency = new SqlDependency(EmailController.command);
EmailController.dependency.OnChange += new OnChangeEventHandler(emailMessages_OnChange);
}
var reader = EmailController.command.ExecuteReader();
}
and finally you have to implement the onchange_event like this:
private void emailMessages_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
//if not null then unsubscribe the calling event
if (EmailController.dependency != null)
{
EmailController.dependency.OnChange -= emailMessages_OnChange;
}
//do my email updates
NotificationHub.EmailUpdateRecords();
// here again subscribe for the new event call re initialize the
// exising dependecy variable the one which we defined as static
SingletonDbConnect conn = SingletonDbConnect.getDbInstance();
using (EmailController.command = new SqlCommand(SQL.emailmessagesbyaccount_sql(), conn.getDbConnection()))
{
EmailController.command.Parameters.Add(new SqlParameter("#emailaccountid", defaultemailid));
EmailController.command.Notification = null;
EmailController.dependency = new SqlDependency(EmailController.command);
EmailController.dependency.OnChange += new OnChangeEventHandler(emailMessages_OnChange);
var reader = EmailController.command.ExecuteReader();
}
}
}
Actually this was my code logic but hope you will get pretty much good idea from this implementation how to handle this kind of issue which made me stumbling my head for a week.

I was having the same problem and #usman's answer helped me a lot. I change the logic of dependency_OnChange method a little bit.
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (dependency != null)
{
dependency.OnChange -= dependency_OnChange;
dependency = null;
}
if (e.Type == SqlNotificationType.Change)
{
MessagesHub.SendMessages();
}
}
I set the dependency to null if it is not null. If we don't set it to null it fires, on every page refresh and if the page is opened multiple times or opened from multiple browsers. As #usman did define the dependency as internal static and set dependency to null in the onChange method. This made my day. Hope it helps another who are facing the same problem.

Related

Need to implement "Scan" method in dll (non blocking)

Sorry for the title, i didn't find it easy to resume.
My issue is that I need to implement a c# dll that implements a 'scan' method, but this scan, when invoked, must not block the main thread of the application using the dll. Moreover, it is a duty that after the scan resolves it rises an Event.
So my issue (in the deep) is that i'm not so experienced at c#, and after very hard investigation i've come up with some solutions but i'm not very sure if they are the "right" procedures.
In the dll i've come up with:
public class Reader
{
public delegate void ReaderEventHandler(Object sender, AlertEventArgs e);
public void Scan(String ReaderName)
{
AlertEventArgs alertEventArgs = new AlertEventArgs();
alertEventArgs.uuiData = null;
//Code with blocking scan function here
if (ScanFinnished)
{
alertEventArgs.uuiData = "Scan Finnished!";
}
alertEventArgs.cardStateData = readerState[0].eventState;
ReaderEvent(new object(), alertEventArgs);
}
public event ReaderEventHandler ReaderEvent;
}
public class AlertEventArgs : EventArgs
{
#region AlertEventArgs Properties
private string _uui = null;
private uint cardState = 0;
#endregion
#region Get/Set Properties
public string uuiData
{
get { return _uui; }
set { _uui = value; }
}
public uint cardStateData
{
get { return cardState; }
set { cardState = value; }
}
#endregion
}
While in the main app I do:
Reader reader;
Task polling;
String SelectedReader = "Some_Reader";
private void bButton_Click(object sender, EventArgs e)
{
reader = new Reader();
reader.ReaderEvent += new Reader.ReaderEventHandler(reader_EventChanged);
polling = Task.Factory.StartNew(() => reader.Scan(SelectedReader));
}
void reader_EventChanged(object sender, AlertEventArgs e)
{
MessageBox.Show(e.uuiData + " Estado: " + e.cardStateData.ToString("X"));
reader.Dispose();
}
So here, it works fine but i don't know if it's the proper way, in addition i'm not able to handle possible Exceptions generated in the dll.
Also tried to use async/await but found it difficult and as I understand it's just a simpler workaround Tasks.
What are the inconvinients of this solution? how can i capture Exceptions (are they in other threads and that's why i cant try/catch them)? Possible concept faults?
When your class sends events, the sender usually is that class, this. Having new object() as sender makes absolutely no sense. Even null would be better but... just use this.
You shouldn't directly raise events as it might result in race conditions. Might not happen easily in your case but it's just a good guideline to follow. So instead of calling ReaderEvent(new object(), alertEventArgs); call RaiseReaderEvent(alertEventArgs); and create method for it.
For example:
private void RaiseReaderEvent(AlertEventArgs args)
{
var myEvent = ReaderEvent; // This prevents race conditions
if (myEvent != null) // remember to check that someone actually subscribes your event
myEvent(this, args); // Sender should be *this*, not some "new object()".
}
Though I personally like a bit more generic approach:
private void Raise<T>(EventHandler<T> oEvent, T args) where T : EventArgs
{
var eventInstance = oEvent;
if (eventInstance != null)
eventInstance(this, args);
}
Which can then be used to raise all events in same class like this:
Raise(ReaderEvent, alertEventArgs);
Since your scan should be non-blocking, you could use tasks, async/await or threads for example. You have chosen Tasks which is perfectly fine.
In every case you must understand that when you are not blocking your application, your application's main thread continues going like a train. Once you jump out of that train, you can't return. You probably should declare a new event "ErrorEvent" that is raised if your scan-procedure catches an exception. Your main application can then subscribe to that event as well, but you still must realize that those events are not (necessarily) coming from the main thread. When not, you won't be able to interact with your GUI directly (I'm assuming you have one due to button click handler). If you are using WinForms, you'll have to invoke all GUI changes when required.
So your UI-thread safe event handler should be something like this:
void reader_EventChanged(object sender, AlertEventArgs e)
{
if (InvokeRequired) // This true for others than UI Thread.
{
Invoke((MethodInvoker)delegate
{
Text = "My new title!";
});
}
else
Text = "My new title!";
}
In WPF there's Dispather that handles similar invoking.

SqlDependency onchange fires multiple times for each browser instance

Apologize for duplicate question. I have googled and everywhere I found only the question but no successful solution. I am facing exactly the same issue described here and here and struggling from past week,but I did not find any success, so finally I decided to ask a question here. I have tried to solve it myself by following the comments suggested in these question, but to success. I want SqlDependency onchange event to fire exactly once for any number for instance or users logged in.
This method is called once whenever a new user login into the application
public List<TransactionMaster> GetUnregisteredTransactions()
{
List<TransactionMaster> ltrans = new List<TransactionMaster>();
TransactionMaster trans = new TransactionMaster();
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
string query = "SELECT TransId, ReceivedFrom,ReceivedOn,Mask FROM [dbo].TransactionMaster WHERE StageId =1";
SqlDependency.Start(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
using (SqlCommand cmd = new SqlCommand(query, connection))
{
cmd.Notification = null;
DataTable dt = new DataTable();
SqlDependency dependency = new SqlDependency(cmd);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(dt);
ltrans = dal.ConvertDataTable<TransactionMaster>(dt);
}
}
return ltrans;
}
And then again this method is called from onchange event
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
TransactionHub.GetUnregTransactions(GetUnregisteredTransactions());
}
For my case each time event fired I added events to the dependency
dependency.OnChange += new OnChangeEventHandler(SqlNotification.dependency_OnChange);
after I found this solution, i changed my code to :
dependency.OnChange -= new OnChangeEventHandler(SqlNotification.dependency_OnChange);
dependency.OnChange += new OnChangeEventHandler(SqlNotification.dependency_OnChange);
and this solved my problem.
Every time you call GetUnregisteredTransactions you are calling dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);, This is causing a new event to registered, so now you have two events that will fire. when the two events fire it will call dependency.OnChange += new OnChangeEventHandler(dependency_OnChange); two more times making it 4 events that will fire. This number will double with every call till the object dependency is pointing at is garbage collected.
You need to either not re-register the dependency on calls from the event version (for example pass a bool to the function to know if it should do the registration or not) or you need to unregister the old notification before you create a new one.
Here is a example of unregistering.
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= new OnChangeEventHandler(dependency_OnChange);
if (e.Type == SqlNotificationType.Change)
TransactionHub.GetUnregTransactions(GetUnregisteredTransactions());
}
I know this is a late answer but I wanted to share my very fun experience with the SqlDependecy firing OnChange event multiple times.
So I ran into this problem as some people did and started looking for answers and quickly realized that there were no code examples that painted the whole picture of how to go about this problem.
This DatabaseWatcher class that I have snatched from
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql/detecting-changes-with-sqldependency, I had to modify it to prevent multiple subscriptions to OnChange events that in the end cause multiple OnChange events to be fired due to what I suspect, multiple instances of SqlDepency floating around with those OnChange subscriptions still attached.
The way I go about is by applying the solution from Scott Chamberlain and then catching the current instance of the SqlDepency when subscribing to the OnChange event to make sure to unsubscribe from the OnChange event before liquidating the DatabaseWatcher instance.
Unrelated to the subject, I also forward the OnChange event to hide it in my business class.
public class DatabaseWatcher : IDisposable
{
/// <summary>
/// Forwards the original OnChangeEventHandler event with the database table notification data
/// </summary>
public event EventHandler<SqlNotificationEventArgs> OnTableChange;
public SqlDependency CurrentSqlDependencyInstance { get; set; }
public string ConnectionString { get; set; }
public string Query { get; set; }
public DatabaseWatcher(string connectionString, string query)
{
ConnectionString = connectionString;
Query = query;
}
/// <summary>
/// Starts the database notification listener. Needs to be started before subscribing to the database notifications.
/// </summary>
public void StartDatabaseNotificationsListener()
{
// Create a dependency connection.
SqlDependency.Start(ConnectionString);
}
/// <summary>
/// Subscribes to the database table change notifications. After each notification, a new re-subscription is needed to receive the next database/table notification.
/// </summary>
public void SubscribeToDatabaseNotifications()
{
//create database connection
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
//open database conneciton
connection.Open();
// Create a new SqlCommand object.
using (SqlCommand command = new SqlCommand(Query, connection))
{
// Create a dependency and associate it with the SqlCommand.
SqlDependency dependency = new SqlDependency(command);
//catch current SqlDependency instance
CurrentSqlDependencyInstance = dependency;
// Subscribe to databas/table notifications
dependency.OnChange += new OnChangeEventHandler(OnDependencyChange);
// Execute the command for the initial table snapshot to later compare with when changes arise
var affectedRowsCount = command.ExecuteNonQuery();
}
}
}
private void OnDependencyChange(object sender, SqlNotificationEventArgs e)
{
var sqlDependency = (SqlDependency)sender;
//don't unsubscribe after the subscription event
if (e.Type == SqlNotificationType.Subscribe)
return;
//unsubscribe from the OnChange event from the current instance of the SqlDepency object
sqlDependency.OnChange -= new OnChangeEventHandler(OnDependencyChange);
//don't fire the event if no changes were done to the table records
if (e.Type != SqlNotificationType.Change)
return;
if (e.Info != SqlNotificationInfo.Insert && e.Info != SqlNotificationInfo.Delete)
return;
OnTableChange = new EventHandler<SqlNotificationEventArgs>(OnTableChange);
OnTableChange?.Invoke(sender, e);
}
/// <summary>
/// Stops listening to database notifications, method is called on Dispose
/// </summary>
public void StopDatabaseNotificationsListener()
{
//unsubscribe from the OnChange event from the current instance of the SqlDepency object
CurrentSqlDependencyInstance.OnChange -= new OnChangeEventHandler(OnDependencyChange);
SqlDependency.Stop(ConnectionString);
}
public void Dispose()
{
StopDatabaseNotificationsListener();
}
}
So there it is, please leave comments, thoughts, etc,
Cheers.

Temporarily stop form events from either being raised or being handled?

I have a ton on controls on a form, and there is a specific time when I want to stop all of my events from being handled for the time being. Usually I just do something like this if I don't want certain events handled:
private bool myOpRunning = false;
private void OpFunction()
{
myOpRunning = true;
// do stuff
myOpRunning = false;
}
private void someHandler(object sender, EventArgs e)
{
if (myOpRunning) return;
// otherwise, do things
}
But I have A LOT of handlers I need to update. Just curious if .NET has a quicker way than having to update each handler method.
You will have to create your own mechanism to do this. It's not too bad though. Consider adding another layer of abstraction. For example, a simple class called FilteredEventHandler that checks the state of myOpRunning and either calls the real event handler, or suppresses the event. The class would look something like this:
public sealed class FilteredEventHandler
{
private readonly Func<bool> supressEvent;
private readonly EventHandler realEvent;
public FilteredEventHandler(Func<bool> supressEvent, EventHandler eventToRaise)
{
this.supressEvent = supressEvent;
this.realEvent = eventToRaise;
}
//Checks the "supress" flag and either call the real event handler, or skip it
public void FakeEventHandler(object sender, EventArgs e)
{
if (!this.supressEvent())
{
this.realEvent(sender, e);
}
}
}
Then when you hook up the event, do this:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
When WhateverEvent gets raised, it will call the FilteredEventHandler.FakeEventHandler method. That method will check the flag and either call, or not call the real event handler. This is pretty much logically the same as what you're already doing, but the code that checks the myOpRunning flag is in only one place instead of sprinkled all over your code.
Edit to answer question in the comments:
Now, this example is a bit incomplete. It's a little difficult to unsubscribe from the event completely because you lose the reference to the FilteredEventHandler that's hooked up. For example, you can't do:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
//Some other stuff. . .
this.Control.WhateverEvent -= new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler; //Not gonna work!
because you're hooking up one delegate and unhooking a completely different one! Granted, both delegates are the FakeEventHandler method, but that's an instance method and they belong to two completely different FilteredEventHandler objects.
Somehow, you need to get a reference to the first FilteredEventHandler that you constructed in order to unhook. Something like this would work, but it involves keeping track of a bunch of FilteredEventHandler objects which is probably no better than the original problem you're trying to solve:
FilteredEventHandler filter1 = new FilteredEventHandler(() => myOpRunning, RealEventHandler);
this.Control.WhateverEvent += filter1.FakeEventHandler;
//Code that does other stuff. . .
this.Control.WhateverEvent -= filter1.FakeEventHandler;
What I would do, in this case, is to have the FilteredEventHandler.FakeEventHandler method pass its 'this' reference to the RealEventHandler. This involves changing the signature of the RealEventHandler to either take another parameter:
public void RealEventHandler(object sender, EventArgs e, FilteredEventHandler filter);
or changing it to take an EventArgs subclass that you create that holds a reference to the FilteredEventHandler. This is the better way to do it
public void RealEventHandler(object sender, FilteredEventArgs e);
//Also change the signature of the FilteredEventHandler constructor:
public FilteredEventHandler(Func<bool> supressEvent, EventHandler<FilteredEventArgs> eventToRaise)
{
//. . .
}
//Finally, change the FakeEventHandler method to call the real event and pass a reference to itself
this.realEvent(sender, new FilteredEventArgs(e, this)); //Pass the original event args + a reference to this specific FilteredEventHandler
Now the RealEventHandler that gets called can unsubscribe itself because it has a reference to the correct FilteredEventHandler object that got passed in to its parameters.
My final advice, though is to not do any of this! Neolisk nailed it in the comments. Doing something complicated like this is a sign that there's a problem with the design. It will be difficult for anybody who needs to maintain this code in the future (even you, suprisingly!) to figure out the non-standard plumbing involved.
Usually when you're subscribing to events, you do it once and forget it - especially in a GUI program.
You can do it with reflection ...
public static void UnregisterAllEvents(object objectWithEvents)
{
Type theType = objectWithEvents.GetType();
//Even though the events are public, the FieldInfo associated with them is private
foreach (System.Reflection.FieldInfo field in theType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance))
{
//eventInfo will be null if this is a normal field and not an event.
System.Reflection.EventInfo eventInfo = theType.GetEvent(field.Name);
if (eventInfo != null)
{
MulticastDelegate multicastDelegate = field.GetValue(objectWithEvents) as MulticastDelegate;
if (multicastDelegate != null)
{
foreach (Delegate _delegate in multicastDelegate.GetInvocationList())
{
eventInfo.RemoveEventHandler(objectWithEvents, _delegate);
}
}
}
}
}
You could just disable the container where all these controls are put in. For example, if you put them in a GroupBox or Panel simply use: groupbox.Enabled = false; or panel.Enabled = false;. You could also disable the form From1.Enabled = false; and show a wait cursor. You can still copy and paste these controls in a container other than the form.

Updating an ObservableCollection<T> asynchronously results in hangs, and no GUI update

I'm implementing a visual version of Tracert (as a learning exercise) in WPF where results go to a listbox. The issues are (1) the listbox bound to tracertDataView is not updating, but (2) my entire application hangs.
I'm sure #2 is a threading issue but I'm not sure how to correct it (in the right way). In addition I'm not sure my technique of updating / binding the results of "DoTrace" are correct.
Here is my datasource in App.xaml
<Window.Resources>
<CollectionViewSource
Source="{Binding Source={x:Static Application.Current}, Path=TracertResultNodes}"
x:Key="tracertDataView" />
</Window.Resources>
App.xaml.cs
public partial class App : Application
{
private ObservableCollection<TracertNode> tracertResultNodes = new ObservableCollection<TracertNode>();
public void AppStartup(object sender, StartupEventArgs e)
{
// NOTE: Load sample data does work correctly.. and displays on the screen.
// subsequent updates do not display
LoadSampleData();
}
private void LoadSampleData()
{
TracertResultNodes = new ObservableCollection<TracertNode>();
TracertNode t = new TracertNode();
t.Address = new System.Net.IPAddress(0x2414188f);
t.RoundTripTime = 30;
t.Status = System.Net.NetworkInformation.IPStatus.BadRoute;
TracertResultNodes.Add(t);
}
public ObservableCollection<TracertNode> TracertResultNodes
{
get { return this.tracertResultNodes; }
set { this.tracertResultNodes = value; }
}
}
Here is the MainWindow code
public partial class MainWindow : Window
{
CollectionViewSource tracertDataView;
TraceWrapper _tracertWrapper = null;
public MainWindow()
{
InitializeComponent();
_tracertWrapper = new TraceWrapper();
tracertDataView = (CollectionViewSource)(this.Resources["tracertDataView"]);
}
private void DoTrace_Click(object sender, RoutedEventArgs e)
{
((App)Application.Current).TracertResultNodes = _tracertWrapper.Results;
_tracertWrapper.DoTrace("8.8.8.8", 30, 50);
}
}
FYI Internal implementation Detail of instance object "traceWrapper.DoTrace"
/// <summary>
/// Trace a host. Note that this object internally calls the Async implementation of .NET's PING.
// It works perfectly fine in a CMD host, but not in WPF
/// </summary>
public ObservableCollection<TracertNode> DoTrace(string HostOrIP, int maxHops, int TimeOut)
{
tracert = new Tracert();
// The following is triggered for every host that is found, or upon timeout
// (up to 30 times by default)
AutoResetEvent wait = new AutoResetEvent(false);
tracert.waiter = wait;
tracert.HostNameOrAddress = HostOrIP;
tracert.Trace();
this.Results = tracert.NodeList;
while (tracert.IsDone == false)
{
wait.WaitOne();
IsDone = tracert.IsDone;
}
return tracert.NodeList;
}
I don't understand how u used AutoResetEvent, i guess it is not supposed to be used in this way :)
But since Trace run already in another thread, are you sure there is not an event "OnTracertComplete" or something like that in your Tracert class?
If there is not, why you just don't put a DispatchTimer into your application?
That timer would periodically poll until tracert.IsDone becomes true.
If you block the execution of the application thread until an operation completes, you block the execution of the window event loop so window will never be updated.
Another important thing: you cannot update ObservableCollections from another thread.
Be careful and be sure that everything that is updated in the WPF window is executed from the same thread of the window. Don't know what your Trace class do exactly, but your problem here seems to be of course the wait loop, that don't makes sense in a GUI application.
Use notification events or a timer to poll the result. A timer with 1 second resolution seems good to me for this particular implementation and the performance inpact is absolutely minimal.
This is a possible implementation if you are able to modify the Tracert class.
public delegate void TracertCallbacHandler(Tracert sender, TracertNode newNode);
public class Tracert
{
public event TracertCallbacHandler NewNodeFound;
public event EventHandler TracertCompleted;
public void Trace()
{
....
}
// This function gets called in tracert thread\async method.
private void FunctionCalledInThreadWhenPingCompletes(TracertNode newNode)
{
var handler = this.NewNodeFound;
if (handler != null)
handler(this, newNode);
}
// This function gets called in tracert thread\async methods when everything ends.
private void FunctionCalledWhenEverythingDone()
{
var handler = this.TracertCompleted;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
And here is the code to run the tracert,
This is TracertWrapper.
// Keep the observable collection as a field.
private ObservableCollection<TracertNode> pTracertNodes;
// Keep the instance of the running tracert as a field, we need it.
private Tracert pTracert;
public bool IsTracertRunning
{
get { return this.pTracert != null; }
}
public ObservableCollection<TracertNode> DoTrace(string hostOrIP, int maxHops, int timeOut)
{
// If we are not already running a tracert...
if (this.pTracert == null)
{
// Clear or creates the list of tracert nodes.
if (this.pTracertNodes == null)
this.pTracertNodes = new ObservableCollection<TracertNode>();
else
this.pTracertNodes.Clear();
var tracert = new Tracert();
tracert.HostNameOrAddress = hostOrIP;
tracert.MaxHops = maxHops;
tracert.TimeOut = timeOut;
tracert.NewNodeFound += delegate(Tracert sender, TracertNode newNode)
{
// This method is called inside Tracert thread.
// We need to use synchronization context to execute this method in our main window thread.
SynchronizationContext.Current.Post(delegate(object state)
{
// This method is called inside window thread.
this.OnTracertNodeFound(this.pTracertNodes, newNode);
}, null);
};
tracert.TracertCompleted += delegate(object sender, EventArgs e)
{
// This method is called inside Tracert thread.
// We need to use synchronization context to execute this method in our main window thread.
SynchronizationContext.Current.Post(delegate(object state)
{
// This method is called inside window thread.
this.OnTracertCompleted();
}, null);
};
tracert.Trace();
this.pTracert = tracert;
}
return this.pTracertNodes;
}
protected virtual void OnTracertCompleted()
{
// Remove tracert object,
// we need this to let the garbage collector being able to release that objects.
// We need also to allow another traceroute since the previous one completed.
this.pTracert = null;
System.Windows.MessageBox.Show("TraceRoute completed!");
}
protected virtual void OnTracertNodeFound(ObservableCollection<TracertNode> collection, TracertNode newNode)
{
// Add our tracert node.
collection.Add(newNode);
}
The issue is that not only is the listbox not updating, but my entire application hangs.
This is probably due to the AutoResetEvent blocking in DoTrace. You explicitly call Wait.WaitOne(); on the event handle, but as far as I can tell, never Set() it. This will cause the application to hang forever as soon as you call Wait.WaitOne().
It sounds like tracert.Trace() is an asynchronous method. Does it include some form of callback/event to notify you upon completion? If so, you should use that, not poll in a loop, to determine when it's complete.
(1) the listbox bound to tracertDataView is not updating
You won't see the updates to your listbox, as you're assigning a new collection to the TracertResultNodes property, the binding in this case simply does not work, because a new collection was assigned.
In addition to ensuring that the collection is updated in the same thread as outlined by Salvatore below, you should only add or remove items from the existing collection, and NOT assign the new one generated by your DoTrace function.
private void DoTrace_Click(object sender, RoutedEventArgs e)
{
foreach(var traceNode in _tracertWrapper.Results)
{
((App)Application.Current).TracertResultNodes.Add(traceNode);
}
_tracertWrapper.DoTrace("8.8.8.8", 30, 50);
}
If you do assign a new one, then you'd need to implement INotifyPropertyChanged on your App class, am not sure how (or whether) that would work though (I have not tried this before).

Event Handler problem

I have event handlers for intercepting call logs on windows mobile. The problem is more specific to event handlers in C# rather than windows mobile. I am able to attach and detach event handlers for the first time. The proble is: I am not able to attach event handler after its detached at least once. Why are the event handlers not invoked after its detached and again attached?
Below is my code:
private static SystemState stateIncoming = null;
private static SystemState stateOutgoing = null;
private static SystemState stateTalking = null;
public static void StartCallLogInterception()
{
if (stateIncoming == null && stateOutgoing == null && stateTalking == null)
{
stateIncoming = new SystemState(SystemProperty.PhoneIncomingCall);
stateIncoming.Changed += new ChangeEventHandler(stateIncoming_Changed);
stateOutgoing = new SystemState(SystemProperty.PhoneCallCalling);
stateOutgoing.Changed += new ChangeEventHandler(stateOutgoing_Changed);
stateTalking = new SystemState(SystemProperty.PhoneCallTalking);
stateTalking.Changed += new ChangeEventHandler(stateTalking_Changed);
}
}
public static void EndCallLogInterception()
{
if (stateIncoming != null && stateOutgoing != null && stateTalking != null)
{
stateIncoming.Changed -= new ChangeEventHandler(stateIncoming_Changed);
stateIncoming = null;
stateOutgoing.Changed -= new ChangeEventHandler(stateOutgoing_Changed);
stateOutgoing = null;
stateTalking.Changed -= new ChangeEventHandler(stateTalking_Changed);
stateTalking = null;
}
}
EDIT: I updated code to include class level variable. Also, below answers conflict with each other. If I am disposing object, I must re-create the object when I need to attach event handler. Does this make sense?
EDIT 2: The problem is not with objects or event handling code. I am using LargeIntervalTimer from OpenNETCF. Whenever I am running timer using LargeIntervalTimer, the event handler is not attached properly. Without LargeIntervalTimer, everything is working fine.
Well, it's not really clear from just the code you've given, but I wonder whether it's because you're never disposing of the SystemState objects you're creating. If you change your code to dispose of them properly when you unsubscribe, that may help.
Alternatively, don't bother keeping on creating new objects - just create the three objects up-front, and then subscribe/unsubscribe as appropriate.
You don't need the
stateTalking.Changed -= new ChangeEventHandler(stateTalking_Changed);
code. First, you are not removing the same thing you put in, you are removing a new instance og the ChangeVenetHandler. Second, all event handlers are removed when you run
stateTalking = null;
because of the Garage Collection.
As Jon Skeet said, you never run the code
stateTalking.Dispose();
before you remove it.
Check your if statements. Place a break point and make sure that you even enter the condition that wires-up the handlers. I suspect that you are not reaching the code in subsequent calls, likely because one of the objects is not null.

Categories