I am attempting to bind a TextBox for error logging in Windows Forms.
I bind the TextBox like this:
this.operationEventHistoryTextbox.DataBindings.Add("Text", this.Logger, "OperationLog")
The Logger object is an instance of the Logger class, which contains the following property.
public string OperationLog
{
get
{
return this.operationLog;
}
set
{
if (this.operationLog != value)
{
this.operationLog = value;
System.ComponentModel.PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new System.ComponentModel.PropertyChangedEventArgs("OperationLog"));
}
}
}
}
And I get the error when calling this.Logger.LogEvent("message"). My LogEvent method contains the following:
public void LogEvent(string msg)
{
this.OperationLog += System.DateTime.Now + ": " + msg + "\r\n";
}
The InvalidOperationException says that there was an invalid Cross-thread operation, and that the Control operationEventHistoryTextBox was accessed from a thread other than the thread it was created on.
I understand this to mean that I've written parts of my code in a way that wasn't thread-safe, but I don't really understand why, or what to fix.
I could just go around setting all of these functions to invoke rather than be directly called, but I'd like really understand what isn't working.
Update: I've attempted to use System.Threading.ScynchronizationContext to raise the PropertyChanged Event on the correct thread, however, I continue to get the error. Here is the new setter for the property:
set
{
if (this.operationLog != value)
{
System.Threading.SynchronizationContext context = System.Threading.SynchronizationContext.Current
?? new System.Threading.SynchronizationContext();
this.operationLog = value;
context.Send(
(s) =>
{
System.ComponentModel.PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new System.ComponentModel.PropertyChangedEventArgs("OperationLog"));
}
},
null);
}
}
Am I not correctly creating the SynchronizationContext? Or is there something else at work here?
Update 2:
If I replace the call of handler(this, ... ) with handler(null, ... ) or handler(this.OperationLog), the setter will run without errors, but does not actually update the text.
For now I'm using a workaround where I will, instead of using a DataBinding to link the text, just manually do that by adding my own handler to the PropertyChanged Event.
The problem seems to be that you are calling the LogEvent method from a background thread. Because of the databinding, the text box is then being updated from that background thread resulting in the exception.
The solution is to make sure that either the LogEvent method is always executing on the UI thread or - better - the OperationLog setter.
You trying update view from another thread. Textbox.Text can't be set from other thread then UI thread
Ok, I've found workaround that is almost a solution.
I've done the following:
set
{
if (this.operationLog != value)
{
this.operationLog = value;
System.ComponentModel.PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
System.Action a = () => handler(this, new PropertyChangedEventArgs("OperationLog"));
System.Windows.Forms.Form.ActiveForm.Invoke(a);
}
}
}
This guarantees that the event is raised on the UI thread, and allows it to update the textbox safely. However, to me, it doesn't feel quite right. While I've done what it needs, there is something that feels like it doesn't fit the Model-View separation that I was looking for. Be that as it may, it is certainly better than manually going operationEventHistoryTextbox.Invoke( ... ).
An interesting drawback is that this can cause errors when the active form does not share threads with what you are working on -- this will include forms in other processes, so alt tabbing will not be allowed.
EDIT: I've found a fix to that problem. Instead of using System.Windows.Forms.Form.ActiveForm, I used System.Windows.Forms.Application.OpenForms[0] to reference the first form within the app. This gives me the context to run an invoke or begin invoke.
Related
I have a C# class which contains the following:
public event Action<int> UnsupportedMessage;
It also has a method containing
if (template != null)
{
...
}
else
{
if (UnsupportedMessage != null)
{
UnsupportedMessage(templateId);
}
}
Another C# class called HealthCheck contains public void MyMessage(int id).
What's supposed to happen is if template is null, an event should call HealthCheck.MyMessage().
So many things are wrong here. UnsupportedMessage is always null so UnsupportedMessage(templateId); never gets executed. Even is I remove if (UnsupportedMessage != null) UnsupportedMessage(templateId); will throw System.NullReferenceException: 'Object reference not set to an instance of an object.'. Even if it did work, there's no reference to HealthCheck.MyMessage() so I don;t know how it could ever be run.
How do I create an event to do what I need?
Update
I tried changing to UnsupportedMessage?.Invoke(templateId); as mentioned in the below answer and added this in the line above it:
UnsupportedMessage += _handlerClass.UnsupportedMessage;
_handlerClass is initialized by the constructor contains this method:
public void UnsupportedMessage(int value)
{
...
}
This initially seemed to work but it seems to be getting called many more times than I expected.
It sounds like you're simply missing the event subscription step; you'd presumably need:
objectThatIsGoingToRaiseTheEvent.UnsupportedMessage += healthCheckInstance.MyMessage;
which will make UnsupportedMessage become non-null, and make everything work. You should also change:
if (UnsupportedMessage != null)
{
UnsupportedMessage(templateId);
}
to either
UnsupportedMessage?.Invoke(templateId);
or (on older C# versions, noting that this is semantically identical)
var handler = UnsupportedMessage;
if (handler != null) { handler(templateId); }
to avoid a race condition that is possible if the last event-handler unsubscribes (-=) between the two reads of UnsupportedMessage in the original version.
In an example, I find this code:
public event EventHandler ThresholdReached;
protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
if (handler != null)
handler(this, e);
}
I would like to understand the reason for the line:
EventHandler handler = ThresholdReached;
Can we not just do it this way:
public event EventHandler ThresholdReached;
protected virtual void OnThresholdReached(EventArgs e)
{
if (ThresholdReached != null)
ThresholdReached(this, e);
}
Are there some advantages/disadvantages to either way of doing it?
The problem is that between this line
if (ThresholdReached != null)
and this line
ThresholdReached(this, e);
a different thread could have removed the handlers from the ThresholdReached event. So it would be null and an exception would be thrown.
By storing the value in a local variable, you make the call thread-safe.
Since C# 6 you can shorten the code to this
ThresholdReached?.Invoke(this, e);
Now the compiler takes care of storing the value in a temporary variable.
There's a theoretical risk of a thread race in the second version where someone unsubscribes the event between the check and the invoke, causing a NullReferenceException in the invoke step. Capturing the value into a local and testing/invoking that prevents this. However, perhaps use the third version, possible with C# 6 or above (thanks #Cid):
ThresholdReached?.Invoke(this, e);
This is basically a short-hand version of the first version - all the safety, but now with terseness.
How do you set them up?
If I have the following code in a HttpModule.
public static event EventHandler<PostProcessingEventArgs> OnPostProcessing;
And in an async PostAuthorizeRequest task set up using EventHandlerTaskAsyncHelper.
// Fire the post processing event.
EventHandler<PostProcessingEventArgs> handler = OnPostProcessing;
if (handler != null)
{
handler(this, new PostProcessingEventArgs { CachedPath = cachedPath });
}
And then tap into it using this.
ProcessingModule.OnPostProcessing += this.WritePath;
private async void WritePath(object sender, PostProcessingEventArgs e)
{
await Task.Factory.StartNew(() => Debug.WriteLine(e.CachedPath));
}
I get the following error.
An asynchronous module or handler completed while an asynchronous
operation was still pending.
Edit
Ok so before I saw all these answers I got it to not throw the error by raising the event handler as follows.
EventHandlerTaskAsyncHelper postProcessHelper =
new EventHandlerTaskAsyncHelper(this.PostProcessImage);
context.AddOnPostRequestHandlerExecuteAsync(postProcessHelper.BeginEventHandler,
postProcessHelper.EndEventHandler);
private Task PostProcessImage(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
object cachedPathObject = context.Items[CachedPathKey];
if (cachedPathObject != null)
{
string cachedPath = cachedPathObject.ToString();
// Fire the post processing event.
EventHandler<PostProcessingEventArgs> handler = OnPostProcessing;
if (handler != null)
{
context.Items[CachedPathKey] = null;
return Task.Run(() => handler(this,
new PostProcessingEventArgs { CachedImagePath = cachedPath }));
}
}
return Task.FromResult<object>(null);
}
From what I can see below though this seems unwise.
The single purpose of this eventhandler would be to allow someone to run longer running tasks on the file like using something like jpegtran or pngout to post-process an image to further optimize it. What's the best approach for that?
You can add async event handlers using the AddOn* methods in the HttpApplication class. I'm sure that async void methods are not supported by all of them. Maybe by none of them.
To use those methods despite the fact that they do not directly support tasks, you need to adapt your task to be compatible with the APM pattern which ASP.NET uses here.
Maybe it is just sample code but you use of Task.Factory.StartNew is not helpful in the context of a web application.
The key is that you need to avoid async void. There are a couple of places where async void can trip you up.
You're already handling the first one correctly by using EventHandlerTaskAsyncHelper. I assume your setup code looks something like this:
public void Init(HttpApplication context)
{
var helper = new EventHandlerTaskAsyncHelper(InvokePostAuthEvents);
context.AddOnPostAuthorizeRequestAsync(helper.BeginEventHandler,
helper.EndEventHandler);
}
With this kind of setup, you're avoiding an async void PostAuthorizeRequest.
The other side is when you raise the OnPostProcessing event. This is where you are having problems with async void. There are a variety of ways to raise async-aware events (I cover a number of them on my blog), but I prefer the "deferral" method which is used by WinStore apps, so it will likely be more familiar to developers.
I have a DeferralManager in my AsyncEx library that is designed to be used in your event args like this:
public class PostProcessingEventArgs
{
private readonly DeferralManager _deferrals;
public PostProcessingEventArgs(DeferralManager deferrals, ...)
{
_deferrals = deferrals;
...
}
public IDisposable GetDeferral()
{
return deferrals.GetDeferral();
}
...
}
When you raise the event, you do this:
Task RaisePostProcessingEventAsync()
{
EventHandler<PostProcessingEventArgs> handler = OnPostProcessing;
if (handler == null)
return TaskConstants.Completed;
var deferrals = new DeferralManager();
var args = new PostProcessingEventArgs(deferrals) { CachedPath = cachedPath };
handler(this, args);
return deferrals.SignalAndWaitAsync();
}
Note that raising the event is now an asynchronous operation, since it will (asynchronously) wait for all the event handler deferrals to complete.
Regular (synchronous) event handlers require no changes, but asynchronous event handlers need to use a deferral, like this:
private async void WritePath(object sender, PostProcessingEventArgs e)
{
using (e.GetDeferral())
{
await Task.Delay(1000);
Debug.WriteLine(e.CachedPath);
}
}
As a final note, neither StartNew nor Run is a good idea on ASP.NET. If you have synchronous code to run, just run it directly.
It's complaining that the worker thread hasn't completed before the request thread is terminated. This is a no no... for all it knows your worker thread might not ever terminate, which would lead to thread starvation in a very short space of time.
If you want to have a worker thread you need to create it in the Init of your HttpModule. I think this is a pretty good example...
http://msdn.microsoft.com/en-us/library/hh567803(v=cs.95).aspx
So have a single worker thread that runs for the duration of the module and and let the requests simply add work for the worker thread to process
Consider the "application" where an object (Thrower) is throwing a ball to another object (Receiver). The event (BallIsThrowed) happens when the ball is thrown.
Here are the 2 classes :
then the entry point :
And finally the methods pointed by the delegate when events are fired :
This is working well.
Now I want to comment this line :
because I want to say that the ball was not thrown.
The result is a null Exception :
This is normal because at this point BallIsThrowed is null
To solve this, I initilise my event :
But then my problem is that my code is never taking the event when I decomment "receiver.Register(thrower)"
My questions are :
How can I have the 2 method EventMethod fired ?
The best practice way to fire an event looks like this:
EventHandler ballIsThrowed = BallIsThrowed;
if (ballIsThrowed != null)
{
ballIsThrowed(this, EventArgs.Empty);
}
The reason for the temporary variable is to prevent race conditions between the null check and the execution.
Bear with me: you are setting an event handler to BallIsThrowed, then you use IthrowTheBall() to actually trigger the event, with this code:
BallIsThrowed(this, EventArgs.Empty);
When you try to call an event handler, in this case the BallIsThrowed, it must exists or it will throw a NullReferenceException. So, in order for this to work, it must exists an event handler BallIsThrowed. It actually does, until you comment this line:
//BallIsThrowed += new EventHandler(method.EventMethod2);
So basically you need to verify if the EventHandler exists before firing it:
if (BallIsThrowed != null)
BallIsThrowed(this, EventArgs.Empty);
Another (more elegant, if you ask me) way of doing this is:
(BallIsThrowed??delegate{})(this, EventArgs.Empty);
Compact, thread safe...
Two things that need to be corrected:
In your Event Invoker, check EventHandler != null because you dont know if anyone registered to your handler.
var ballThrown = BallIsThrowed;
if (ballThrown != null)
{
ballThrown(this, EventArgs.Empty);
}
For general knowledge, In order to register more then one delegate to your
EventHandler, dont register it via the = operator, but via the
+= operator, meaning you want to append a new delegate:
public void IThrowTheBall()
{
// Do stuff
// You dont have to register this delegate, you can append it to the current
// delegates already registered
BallIsThrowed += method.EventMethod1;
}
Problem:
I am working on a application where in for some time consuming operation, i am supposed to show a progress bar on a form (WinForm) with a cancel button. So obviously i am using BackgroundWorker thread for it. Below is the code which simulates roughly of what i am trying to achieve.
namespace WindowsFormsApplication1
{
public delegate void SomeDelegateHandler();
public partial class Form1 : Form
{
public event SomeDelegateHandler DoSomeAction;
BackgroundWorker bgWorker;
public Form1()
{
InitializeComponent();
bgWorker = new BackgroundWorker();
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
//Some logic code here.
for (int i = 0; i < 100; i++)
{
DoSomeAction();
}
}
private void Form1_Shown(object sender, EventArgs e)
{
if (DoSomeAction != null)
bgWorker.RunWorkerAsync();
else throw new EventNotSubscribedException();//Is this a valid style??
}
}
public class EventNotSubscribedException : ApplicationException
{
//Some custom code here
}
}
My Solution
As per the above code, as soon as the form is displayed to the user (OnShown event) i am starting the backgroundworker thread. This is because, the user need not to initiate any action for this to happen. So onshown does time consuming operation job. But the issue is, as i have shown above, the main time consuming job is executed on other class/component where it is kind of tight bounded too (legacy code: cant refactor). Hence i have subscribed to the event DoSomeAction in that legacy code class which launches this form.
Doubt/Question:
Is it valid to throw exception as shown above? (Please read my justification below).
Justification:
The OnShown event does check for null on event handler object. This is because, to make this form usable, the event has to be subscribed by the subscriber (usage code), then only it shall work. If not, then the form just displays and does noting at all and usage code may not know why it is happenings so. The usage code may assume that subscribing to the event is option just like button click events per say.
Hope my post is clear and understandable.
Thanks & Happy Coding,
Zen :)
Do you mean that you need to throw an exception to the caller of the form? Is it called using showDialog or Show?
BTW, I dont prefer to generate an exception from an event. Rather it would be rather nice to keep it such that it returns from the place with some status set on the Form class.
for instance, I would prefer using
IsEventSubscribed = false
this.Close()
rather than EventNotSubscribedException
BTW, One problem I can see in the code, when the bgWorker_DoWork is called, you should check DoSomeAction to null, because otherwise it might cause NullReferenceException.
Preferably,
Start the run the RunWorkerAsync from Form_shown
Check Delegate to null in DoWork, if it is null, do not call DoSomeAction otherwise call it.
On RunWorkerCompleted of the BackgroundWorker, close the form.
Let me know if you need anything more.
I would suggest making the consuming code construct the BackgroundWorker and pass it to the form's constructor. You can do a null test in the constructor and side-step this whole issue. Alternatively, take the delegate as a constructor argument instead. I mean, how likely is it that the consuming code will need to change the worker delegate mid-operation?
Another approach is to have the dialog monitor a task, instead of having a dialog control a task (as you have here). For example, you could have an interface like this:
public interface IMonitorableTask {
void Start();
event EventHandler<TData> TaskProgress;
}
Where TData is a type that provides any information you might need to update the dialog (such as percent completed).
The downside to this is that each task needs to be a type of its own. This can lead to very ugly, cluttered code. You could mitigate that issue somewhat by creating a helper class, something like:
public class DelegateTask : IMonitorableTask {
private Action<Action<TData>> taskDelegate;
public event EventHandler<TData> TaskProgress;
public DelegateTask(Action<Action<TData>> taskDelegate) {
if (taskDelegate == null)
throw new ArgumentNullException("taskDelegate");
this.taskDelegate = taskDelegate;
}
protected void FireTaskProgress(TData data) {
var handler = TaskProgress;
if (handler != null)
handler(this, data);
}
public void Start() {
taskDelegate(FireTaskProgress);
}
}
Then your task methods become factories:
public IMonitorableTask CreateFooTask(object argument) {
return new DelegateTask(progress => {
DoStuffWith(argument);
progress(new TData(0.5));
DoMoreStuffWith(argument);
progress(new TData(1));
});
}
And now you can easily(*) support, say, a command-line interface. Just attach a different monitor object to the task's event.
(*) Depending on how clean your UI/logic separation already is, of course.