I'm just starting to force myself to use Entity Framework (I know I'm a long way behind), but I've fallen at the first hurdle.
The program I'm writing simply watches a PLC for a bit to become true. When it does become true an event fires and then values (random at the moment) will be entered into SQL. I then set the bit to false.
The problem I'm having is that the event fires once, the entries get added, and bit gets set false. However the event never triggers again. If I comment out all of the SQL bits then and just set the bit false then it works perfectly and fires multiple times.
Here is most of the code that I've got.
Any help would be really appreciated.
static void Main(string[] args)
{
PlcListener plcListener = new PlcListener();
plcLister.BitChanged += (bitVal) => On_BitChanged(bitVal, plcListener)
plcListner.Start();
}
private static void On_BitChanged(bool bitVal, PlcListener plcListner)
{
SqlEntities sqlEntity = new SqlEntities();
SampleData sampleData = new SampleData(){ Data = new Random().Next(); };
sqlEntity.AddToSampleDatas(sampleData);
sqlEntity.SaveChanges();
plcListener.Confirm();
}
public class PlcListener
{
public void Start()
{
OPCServer opcServer = new OPCServer();
opcServer.DataChanged += On_DataChanged;
}
public void Confirm()
{
//Code to set bit to false
}
public void On_DataChanged(bool bitVal.......)
{
if(bitVal)
{
BitChangedEventHandler handler = BitChanged;
if (handler != null)
{
handler(bitVal);
}
}
}
public delegate void BitChangedEventHandler(bool bitValue);
public event BitChangedEventHandler BitChanged;
}
You did not mention which OpcServer component you are using, but the most probable problem is that the DataChange comes into your application in a COM callback thread and you are not allowed to write back to COM while handling the callback. You need to just record the incoming value and do the actual handling in your own handler thread, which then sets the bit back to false in the PLC.
Related
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.
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).
I'm using a subscriber/notifier pattern to raise and consume events from my .Net middle-tier in C#. Some of the events are raised in "bursts", for instance, when data is persisted from a batch program importing a file. This executes a potentially long-running task, and I'd like to avoid firing the event several times a second by implementing a "quiet period", whereby the event system waits until the event stream slows down to process the event.
How should I do this when the Publisher takes an active role in notifying subscribers? I don't want to wait until an event comes in to check to see if there are others waiting out the quiet period...
There is no host process to poll the subscription model at the moment. Should I abandon the publish/subscribe pattern or is there a better way?
Here's a rough implementation that might point you in a direction. In my example, the task that involves notification is saving a data object. When an object is saved, the Saved event is raised. In addition to a simple Save method, I've implemented BeginSave and EndSave methods as well as an overload of Save that works with those two for batch saves. When EndSave is called, a single BatchSaved event is fired.
Obviously, you can alter this to suit your needs. In my example, I kept track of a list of all objects that were saved during a batch operation, but this may not be something that you'd need to do...you may only care about how many objects were saved or even simply that a batch save operation was completed. If you anticipate a large number of objects being saved, then storing them in a list as in my example may become a memory issue.
EDIT: I added a "threshold" concept to my example that attempts to prevent a large number of objects being held in memory. This causes the BatchSaved event to fire more frequently, though. I also added some locking to address potential thread safety, though I may have missed something there.
class DataConcierge<T>
{
// *************************
// Simple save functionality
// *************************
public void Save(T dataObject)
{
// perform save logic
this.OnSaved(dataObject);
}
public event DataObjectSaved<T> Saved;
protected void OnSaved(T dataObject)
{
var saved = this.Saved;
if (saved != null)
saved(this, new DataObjectEventArgs<T>(dataObject));
}
// ************************
// Batch save functionality
// ************************
Dictionary<BatchToken, List<T>> _BatchSavedDataObjects = new Dictionary<BatchToken, List<T>>();
System.Threading.ReaderWriterLockSlim _BatchSavedDataObjectsLock = new System.Threading.ReaderWriterLockSlim();
int _SavedObjectThreshold = 17; // if the number of objects being stored for a batch reaches this threshold, then those objects are to be cleared from the list.
public BatchToken BeginSave()
{
// create a batch token to represent this batch
BatchToken token = new BatchToken();
_BatchSavedDataObjectsLock.EnterWriteLock();
try
{
_BatchSavedDataObjects.Add(token, new List<T>());
}
finally
{
_BatchSavedDataObjectsLock.ExitWriteLock();
}
return token;
}
public void EndSave(BatchToken token)
{
List<T> batchSavedDataObjects;
_BatchSavedDataObjectsLock.EnterWriteLock();
try
{
if (!_BatchSavedDataObjects.TryGetValue(token, out batchSavedDataObjects))
throw new ArgumentException("The BatchToken is expired or invalid.", "token");
this.OnBatchSaved(batchSavedDataObjects); // this causes a single BatchSaved event to be fired
if (!_BatchSavedDataObjects.Remove(token))
throw new ArgumentException("The BatchToken is expired or invalid.", "token");
}
finally
{
_BatchSavedDataObjectsLock.ExitWriteLock();
}
}
public void Save(BatchToken token, T dataObject)
{
List<T> batchSavedDataObjects;
// the read lock prevents EndSave from executing before this Save method has a chance to finish executing
_BatchSavedDataObjectsLock.EnterReadLock();
try
{
if (!_BatchSavedDataObjects.TryGetValue(token, out batchSavedDataObjects))
throw new ArgumentException("The BatchToken is expired or invalid.", "token");
// perform save logic
this.OnBatchSaved(batchSavedDataObjects, dataObject);
}
finally
{
_BatchSavedDataObjectsLock.ExitReadLock();
}
}
public event BatchDataObjectSaved<T> BatchSaved;
protected void OnBatchSaved(List<T> batchSavedDataObjects)
{
lock (batchSavedDataObjects)
{
var batchSaved = this.BatchSaved;
if (batchSaved != null)
batchSaved(this, new BatchDataObjectEventArgs<T>(batchSavedDataObjects));
}
}
protected void OnBatchSaved(List<T> batchSavedDataObjects, T savedDataObject)
{
// add the data object to the list storing the data objects that have been saved for this batch
lock (batchSavedDataObjects)
{
batchSavedDataObjects.Add(savedDataObject);
// if the threshold has been reached
if (_SavedObjectThreshold > 0 && batchSavedDataObjects.Count >= _SavedObjectThreshold)
{
// then raise the BatchSaved event with the data objects that we currently have
var batchSaved = this.BatchSaved;
if (batchSaved != null)
batchSaved(this, new BatchDataObjectEventArgs<T>(batchSavedDataObjects.ToArray()));
// and clear the list to ensure that we are not holding on to the data objects unnecessarily
batchSavedDataObjects.Clear();
}
}
}
}
class BatchToken
{
static int _LastId = 0;
static object _IdLock = new object();
static int GetNextId()
{
lock (_IdLock)
{
return ++_LastId;
}
}
public BatchToken()
{
this.Id = GetNextId();
}
public int Id { get; private set; }
}
class DataObjectEventArgs<T> : EventArgs
{
public T DataObject { get; private set; }
public DataObjectEventArgs(T dataObject)
{
this.DataObject = dataObject;
}
}
delegate void DataObjectSaved<T>(object sender, DataObjectEventArgs<T> e);
class BatchDataObjectEventArgs<T> : EventArgs
{
public IEnumerable<T> DataObjects { get; private set; }
public BatchDataObjectEventArgs(IEnumerable<T> dataObjects)
{
this.DataObjects = dataObjects;
}
}
delegate void BatchDataObjectSaved<T>(object sender, BatchDataObjectEventArgs<T> e);
In my example, I choose to use a token concept in order to create separate batches. This allows smaller batch operations running on separate threads to complete and raise events without waiting for a larger batch operation to complete.
I made separete events: Saved and BatchSaved. However, these could just as easily be consolidated into a single event.
EDIT: fixed race conditions pointed out by Steven Sudit on accessing the event delegates.
EDIT: revised locking code in my example to use ReaderWriterLockSlim rather than Monitor (i.e. the "lock" statement). I think there were a couple of race conditions, such as between the Save and EndSave methods. It was possible for EndSave to execute, causing the list of data objects to be removed from the dictionary. If the Save method was executing at the same time on another thread, it would be possible for a data object to be added to that list, even though it had already been removed from the dictionary.
In my revised example, this situation can't happen and the Save method will throw an exception if it executes after EndSave. These race conditions were caused primarily by me trying to avoid what I thought was unnecessary locking. I realized that more code needed to be within a lock, but decided to use ReaderWriterLockSlim instead of Monitor because I only wanted to prevent Save and EndSave from executing at the same time; there wasn't a need to prevent multiple threads from executing Save at the same time. Note that Monitor is still used to synchronize access to the specific list of data objects retrieved from the dictionary.
EDIT: added usage example
Below is a usage example for the above sample code.
static void DataConcierge_Saved(object sender, DataObjectEventArgs<Program.Customer> e)
{
Console.WriteLine("DataConcierge<Customer>.Saved");
}
static void DataConcierge_BatchSaved(object sender, BatchDataObjectEventArgs<Program.Customer> e)
{
Console.WriteLine("DataConcierge<Customer>.BatchSaved: {0}", e.DataObjects.Count());
}
static void Main(string[] args)
{
DataConcierge<Customer> dc = new DataConcierge<Customer>();
dc.Saved += new DataObjectSaved<Customer>(DataConcierge_Saved);
dc.BatchSaved += new BatchDataObjectSaved<Customer>(DataConcierge_BatchSaved);
var token = dc.BeginSave();
try
{
for (int i = 0; i < 100; i++)
{
var c = new Customer();
// ...
dc.Save(token, c);
}
}
finally
{
dc.EndSave(token);
}
}
This resulted in the following output:
DataConcierge<Customer>.BatchSaved: 17
DataConcierge<Customer>.BatchSaved: 17
DataConcierge<Customer>.BatchSaved: 17
DataConcierge<Customer>.BatchSaved: 17
DataConcierge<Customer>.BatchSaved: 17
DataConcierge<Customer>.BatchSaved: 15
The threshold in my example is set to 17, so a batch of 100 items causes the BatchSaved event to fire 6 times.
I am not sure if I understood your question correctly, but I would try to fix the problem at source - make sure the events are not raised in "bursts". You could consider implementing batch operations, which could be used from the file importing program. This would be treated as a single event in your middletier and raise a single event.
I think it will be very tricky to implement some reasonable solution if you can't make the change outlined above - you could try to wrap your publisher in a "caching" publisher, which would implement some heuristic to cache the events if they are coming in bursts. The easiest would be to cache an event if another one of the same type is being currently processed (so your batch would cause at least 2 events - one at the very beginning, and one at the end). You could wait for a short time and only raise an event when the next one hasn't come during that time, but you get a time lag even if there is a single event in the pipeline. You also need to make sure you will raise the event from time to time even if there is constant queue of events - otherwise the publishers will potentially get starved.
The second option is tricky to implement and will contain heuristics, which might go very wrong...
Here's one idea that's just fallen out of my head. I don't know how workable it is and can't see an obvious way to make it more generic, but it might be a start. All it does is provide a buffer for button click events (substitute with your event as necessary).
class ButtonClickBuffer
{
public event EventHandler BufferedClick;
public ButtonClickBuffer(Button button, int queueSize)
{
this.queueSize= queueSize;
button.Click += this.button_Click;
}
private int queueSize;
private List<EventArgs> queuedEvents = new List<EventArgs>();
private void button_Click(object sender, EventArgs e)
{
queuedEvents.Add(e);
if (queuedEvents.Count >= queueSize)
{
if (this.BufferedClick!= null)
{
foreach (var args in this.queuedEvents)
{
this.BufferedClick(sender, args);
}
queuedEvents.Clear();
}
}
}
}
So your subscriber, instead of subscribing as:
this.button1.Click += this.button1_Click;
Would use a buffer, specifying how many events to wait for:
ButtonClickBuffer buffer = new ButtonClickBuffer(this.button1, 5);
buffer.BufferedClick += this.button1_Click;
It works in a simple test form I knocked up, but it's far from production-ready!
You said you didn't want to wait for an event to see if there is a queue waiting, which is exactly what this does. You could substitute the logic inside the buffer to spawn a new thread which monitors the queue and dispatches events as necessary. God knows what threading and locking issues might arise from that!
I need help trying to understand the Observer Pattern and Delegates. I found this code on another website and I am trying to understand what it is actually doing. Can someone help me out.
When I execute the code, I get both of the messages "Server is up and running" and "Server is down, We are working on it it will be back soon". I think I am getting both of the message because in the Main, there is a server.ServerStatus = true; and a server.ServerStatus = false. However, if I comment out the server.ServerStatus = true; and run then I I get the message "Server is up and running" but I expected to only see "Server is down, We are working on it it will be back soon.". Can someone explain?
Susan
class Program
{
static void Main(string[] args)
{
Server server = new Server();
server.ServerStatusChanged += new EventHandler(ProcessServerStatus);
server.ServerStatus = true;
server.ServerStatus = false;
Console.Read();
}
public class Server
{
public event EventHandler ServerStatusChanged;
private bool _ServerStatus;
public bool ServerStatus
{
get { return this._ServerStatus; }
set {
if (this._ServerStatus == value) return; // Dont need to do anything;
if (this.ServerStatusChanged != null) // make sure the invocation list is not empty
ServerStatusChanged(value, new EventArgs()); // Firing Event
this._ServerStatus = value;
}
}
}
public static void ProcessServerStatus(object sender, EventArgs e)
{
bool status = (bool)sender;
if (status)
Console.WriteLine("Server is up and running");
else
Console.WriteLine("Server is down, We are working on it it will be back soon");
}
}
Here is code that does what you want.
Notice the _initialized variable. This is needed because otherwise nothing happens the the first time through. You would have to set the status to True before it starts working correctly.
Also, I what you describe didn't happen for me. I didn't get the message saying it was up and running. I didn't get anything at all.
class Program
{
static void Main(string[] args)
{
Server server = new Server();
server.ServerStatusChanged += new EventHandler(ProcessServerStatus);
//server.ServerStatus = true;
server.ServerStatus = false;
Console.Read();
}
public class Server
{
public event EventHandler ServerStatusChanged;
private bool _ServerStatus = false;
private bool _initialized = false;
public bool ServerStatus
{
get { return this._ServerStatus; }
set
{
if (this._initialized == true && this._ServerStatus == value)
return; // Dont need to do anything;
else
this._initialized = true;
if (this.ServerStatusChanged != null) // make sure the invocation list is not empty
ServerStatusChanged(value, new EventArgs()); // Firing Event
this._ServerStatus = value;
}
}
}
public static void ProcessServerStatus(object sender, EventArgs e)
{
bool status = (bool)sender;
if (status)
Console.WriteLine("Server is up and running");
else Console.WriteLine("Server is down, We are working on it it will be back soon");
}
}
This is an event registration ...
server.ServerStatusChanged += new EventHandler(ProcessServerStatus);
It says "whenever there is a ServerStatusChanged, call the ProcesServerStatus method." So, when you set the ServerStatus to true, you get a call to ProcessServerStatus passing true and it prints "Server is up..."; when you change it to false, another event is fired, but this time ServerStatus is false, so you get "Server is down ..." So, you are watching the server status and doing something (ProcessServerStatus) when it changes.
Essentially what is occuring is that the assignments you have in Main that are setting ServerStatus to true/false will call the "set" function of the ServerStatus property.
Within that function, an event is created and fired to the ServerStatusChanged handler, which will be received by anyone who registered for events. Also note that the event is fired only if the status changes.
In this case, Main registered for the event in the
Server.ServerStatusChanged += new EventHandler(ProcessServerStatus);
line.
Since you set ServerStatus to true, then false in Main, two events are fired, causing your console to display both "Server is up and running", and "Server is down, We are working on it...."
If you haven't come across the Observer pattern before, it might be instinctive to think that all method calls are done quite statically - the methods we call in our code are what's called. However, the observer pattern lets you do things more dynamically. You can have something say at runtime "hey, when such and such happens, let me know! You can let me know by calling such and such a method" The one being observed keeps a record of everyone that has said to it "when such and such happens, let me know" - and then, when it happens, it does just that - lets all of them know. This is done by keeping a list of the methods which must be called, and then calling them all when the event is 'triggered'.
The line:
server.ServerStatusChanged += new EventHandler(ProcessServerStatus);
is telling the server object "hey, when the server status is changed, let me know. You can let me know by calling the ProcessServerStatus method". And so, when the line:
server.ServerStatus = true;
is run, it triggers the ServerStatusChanged event, which then calls everything that has said "let me know". So it will call the ProcessServerStatus method, since that's the only method in its list.
Every method that is in that list must have a certain method signature; in this case, void ProcessServerStatus(object sender, EventArgs e). The sender parameter in this case is the server status - you can see this in the line:
ServerStatusChanged(value, new EventArgs());
And so when ProcessServerStatus is called, it casts it so a boolean value, and prints the appropriate string.
When I run this code, it works as expected - if I comment out the serverStatus = true; line, it prints nothing (since _serverStatus defaults to false, and the event is not triggered if the status hasn't changed). Instead, if I comment out the serverStatus = false; line, it says "the server is up and running"
My app has a DataGridView object and a List of type MousePos. MousePos is a custom class that holds mouse X,Y coordinates (of type "Point") and a running count of this position. I have a thread (System.Timers.Timer) that raises an event once every second, checks the mouse position, adds and/or updates the count of the mouse position on this List.
I would like to have a similar running thread (again, I think System.Timers.Timer is a good choice) which would again raise an event once a second to automatically Refresh() the DataGridView so that the user can see the data on the screen update. (like TaskManager does.)
Unfortunately, calling the DataGridView.Refresh() method results in VS2005 stopping execution and noting that I've run into a cross-threading situation.
If I'm understanding correctly, I have 3 threads now:
Primary UI thread
MousePos List thread (Timer)
DataGridView Refresh thread (Timer)
To see if I could Refresh() the DataGridView on the primary thread, I added a button to the form which called DataGridView.Refresh(), but this (strangely) didn't do anything. I found a topic which seemed to indicate that if I set DataGridView.DataSource = null and back to my List, that it would refresh the datagrid. And indeed this worked, but only thru the button (which gets handled on the primary thread.)
So this question has turned into a two-parter:
Is setting DataGridView.DataSource to null and back to my List an acceptable way to refresh the datagrid? (It seems inefficient to me...)
How do I safely do this in a multi-threaded environment?
Here's the code I've written so far (C#/.Net 2.0)
public partial class Form1 : Form
{
private static List<MousePos> mousePositionList = new List<MousePos>();
private static System.Timers.Timer mouseCheck = new System.Timers.Timer(1000);
private static System.Timers.Timer refreshWindow = new System.Timers.Timer(1000);
public Form1()
{
InitializeComponent();
mousePositionList.Add(new MousePos()); // ANSWER! Must have at least 1 entry before binding to DataSource
dataGridView1.DataSource = mousePositionList;
mouseCheck.Elapsed += new System.Timers.ElapsedEventHandler(mouseCheck_Elapsed);
mouseCheck.Start();
refreshWindow.Elapsed += new System.Timers.ElapsedEventHandler(refreshWindow_Elapsed);
refreshWindow.Start();
}
public void mouseCheck_Elapsed(object source, EventArgs e)
{
Point mPnt = Control.MousePosition;
MousePos mPos = mousePositionList.Find(ByPoint(mPnt));
if (mPos == null) { mousePositionList.Add(new MousePos(mPnt)); }
else { mPos.Count++; }
}
public void refreshWindow_Elapsed(object source, EventArgs e)
{
//dataGridView1.DataSource = null; // Old way
//dataGridView1.DataSource = mousePositionList; // Old way
dataGridView1.Invalidate(); // <= ANSWER!!
}
private static Predicate<MousePos> ByPoint(Point pnt)
{
return delegate(MousePos mPos) { return (mPos.Pnt == pnt); };
}
}
public class MousePos
{
private Point position = new Point();
private int count = 1;
public Point Pnt { get { return position; } }
public int X { get { return position.X; } set { position.X = value; } }
public int Y { get { return position.Y; } set { position.Y = value; } }
public int Count { get { return count; } set { count = value; } }
public MousePos() { }
public MousePos(Point mouse) { position = mouse; }
}
You have to update the grid on the main UI thread, like all the other controls. See control.Invoke or Control.BeginInvoke.
UPDATE! -- I partially figured out the answer to part #1 in the book "Pro .NET 2.0 Windows Forms and Customer Controls in C#"
I had originally thought that Refresh() wasn't doing anything and that I needed to call the Invalidate() method, to tell Windows to repaint my control at it's leisure. (which is usually right away, but if you need a guarantee to repaint it now, then follow up with an immediate call to the Update() method.)
dataGridView1.Invalidate();
But, it turns out that the Refresh() method is merely an alias for:
dataGridView1.Invalidate(true);
dataGridView1.Update(); // <== forces immediate redraw
The only glitch I found with this was that if there was no data in the dataGridView, no amount of invalidating would refresh the control. I had to reassign the datasource. Then it worked fine after that. But only for the amount of rows (or items in my list) -- If new items were added, the dataGridView would be unaware that there were more rows to display.
So it seems that when binding a source of data (List or Table) to the Datasource, the dataGridView counts the items (rows) and then sets this internally and never checks to see if there are new rows/items or rows/items deleted. This is why re-binding the datasource repeatedly was working before.
Now to figure out how to update the number of rows to display in dataGridView without having to re-bind the datasource... fun, fun, fun! :-)
After doing some digging, I think I have my answer to part #2 of my question (aka. safe Multi-threading):
Rather than using System.Timers.Timer, I found that I should be using System.Windows.Forms.Timer instead.
The event occurs such that the method that is used in the Callback automatically happens on the primary thread. No cross-threading issues!
The declaration looks like this:
private static System.Windows.Forms.Timer refreshWindow2;
refreshWindow2 = new Timer();
refreshWindow2.Interval = 1000;
refreshWindow2.Tick += new EventHandler(refreshWindow2_Tick);
refreshWindow2.Start();
And the method is like this:
private void refreshWindow2_Tick(object sender, EventArgs e)
{
dataGridView1.Invalidate();
}
Looks like you have your answer right there!
Just in cawse you're curious about how to do cross thread calls back to ui:
All controls have a Invoke() method (or BEginInvoke()- in case you want to do things asynchronously), this is used to call any method on the control within the context of the main UI thread.
So, if you were going to call your datagridview from another thread you would need to do the following:
public void refreshWindow_Elapsed(object source, EventArgs e)
{
// we use anonymous delgate here as it saves us declaring a named delegate in our class
// however, as c# type inference sometimes need a bit of 'help' we need to cast it
// to an instance of MethodInvoker
dataGridView1.Invoke((MethodInvoker)delegate() { dataGridView1.Invalidate(); });
}