WPF: What is the difference between an Action and an Action that executes the Action? - wpf

Since Pipe_Disconnected is called by another thread, MainWindow.Close must be called by Dispatcher.
What is the difference between the codes below?
I am using .Net7.
Work
private void Pipe_Disconnected(object sender, object e)
{
Application.Current.Dispatcher.InvokeAsync(() => Application.Current.MainWindow.Close());
}
Not Work
private void Pipe_Disconnected(object sender, object e)
{
Application.Current.Dispatcher.InvokeAsync(Application.Current.MainWindow.Close);
}
Not Work
private void Pipe_Disconnected(object sender, object e)
{
var temp = new Action(Application.Current.MainWindow.Close);
Application.Current.Dispatcher.InvokeAsync(temp);
}

The practical difference when Application.MainWindow is being accessed.
In the first example, the property is accessed in dispatcher thread.
In the second and third it is accessed from another thread.
If you examine stacktrace of the exception, you won't see the Close method, because it is never called. The Exception is thrown when accessing MainWindows instance. The Dispatcher. InvokeAsync won't even get a chance so be called.

Related

Calling asynchronous function synchronously is more responsive

I have an asynchronous method like this:
private async Task TaskAsync()
{
await Task.Run(() => Task.Delay(2000));
}
I then call it in a button-click event, which I've declared like this:
private async void button1_Click(object sender, EventArgs e)
{
await TaskAsync();
MessageBox.Show("Afterwards.");
}
Now, when I click on the button, TaskAsync() is literally awaited on, and the message box isn't shown until TaskAsync() has finished executing. However, when I remove the await command when calling TaskAsync() in the click event, then execution immediately jumps to the message box.
Am I doing something wrong here? Is this normal behavior of async...await?
My project is a .NET Core 5 C# winform project.
There are two problems here. First, the TaskAsync method is using Task.Run to run another async method. That just wastes a thread. It should be just :
private async Task TaskAsync()
{
await Task.Delay(2000);
}
if not
private Task TaskAsync()=>Task.Delay(2000);
Second, if a Task isn't awaited execution will proceed immediately. That's the whole point of using await - awaiting an already executing asynchronous task to complete without blocking the calling thread.
The original code is equivalent to :
private async void button1_Click(object sender, EventArgs e)
{
await Task.Delay(2000);
MessageBox.Show("Afterwards.");
}
Without await, the task returned by Task.Delay() will be ignored and the message box will be displayed immediately.
If one wanted to start a long operation, eg reading a big file, and display a message at the same time, the task can be stored in a field and awaited after the dialog box is closed:
private async void button1_Click(object sender, EventArgs e)
{
var task=File.ReadAllTextAsync(...);
MessageBox.Show("Reading a file");
var text=await task;
MessageBox.Show("Afterwards.");
}
or
private async void button1_Click(object sender, EventArgs e)
{
var task=Task.Run(()=>SomeBackgroundProcessing(someArgs));
MessageBox.Show("Processing");
var text=await task;
MessageBox.Show("Afterwards.");
}

Is there "LoadComplete" such an event in Windows Forms?

I want my windows form to be loaded first, render its children and all. After that load heavy data in it. This is why I am looking for any event which I could use just after form loading is complete.
Any thoughts on this?
I have never found a better solution than Activated; although that is raised every time the form receives focus - so you need to filter out all the times after the first:
bool _firstActivation = true;
void Form1_Activated(object sender, EventArgs e)
{
if (_firstActivation)
{
_firstActivation = false;
OnFirstActivation();
}
}
private void OnFirstActivation()
{
}
Perhaps you're looking for the Form.Shown event. If you're doing a lot of intensive work though, perhaps you should be using a background thread anyway to avoid locking up the UI.
Like MikeP said you want to handle the Form.Shown event just once. So just attach to the even and detach once done.
private void frmMain_Load(object sender, System.EventArgs e)
{
// Do stuff in form load.
Shown += FirstShown;
}
private void FirstShown(object sender, EventArgs eventArgs)
{
Refresh();
// Do something here
// Detach from this event.
Shown -= FirstShown;
}
I do that in a way that I fire a timer with duration of 1, and kill it in the event, and with that method, I know that message loop will be empty and form initialization will be complete when my event comes.
Event is set up from Form_OnLoad() method.

Can I use Monitor.Enter/Exit (c# lock) with WPF without fear of reentrancy bugs?

If I use Monitor.Enter/Exit (through the c# lock syntax) in a WPF application, can the dispatcher cause re-entrance?
In the sample below, presuming OnTextChanged is called when the text in a textbox changes, could the call to _worker.RunWorkerAsync() be called incorrectly?
public class SomeClass
{
private object _locker = new object();
private bool _running = false;
private BackgroundWorker _worker;
public void SomeClass()
{
// initialize worker...
}
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lock (_locker)
_running = false;
}
void _worker_DoWork(object sender, DoWorkEventArgs e)
{
// ... do something time consuming ...
}
private void OnTextChanged()
{
lock(_locker)
{
if (!_running)
{
_worker.RunWorkerAsync();
_running = true;
}
}
}
}
I believe it's possible, but I've not been able to reproduce this. Does WPF somehow prevent the dispatcher from invoking waiting tasks when waiting on monitor?
Not sure what you fear. Both OnTextChanged and RunWorkerCompleted run on the UI thread. It won't be re-entrant, you don't need the lock either. Either method can only start running when the UI thread is idle, pumping the message loop.
While not directly related to your question, you could run into register caching issues if you don't mark _running as volatile.
Actually this isn't strictly true, as you are not using a double-checked lock. I've left the information related to volatile there anyway, for your reference.

Form Dispose() or Close()

I'm having 2 forms. From one form I created and shown the other form. It's working great. But when I try to close or Dispose that form from the form that created it I get following Exception:
Exception :
Value Dispose() cannot be called while doing CreateHandle().
Stack Trace :
========================
at System.Windows.Forms.Control.Dispose(Boolean disposing)
at System.Windows.Forms.Label.Dispose(Boolean disposing)
at System.ComponentModel.Component.Dispose()
at System.Windows.Forms.Control.Dispose(Boolean disposing)
at System.Windows.Forms.ContainerControl.Dispose(Boolean disposing)
at System.Windows.Forms.Form.Dispose(Boolean disposing)
at Speedometer_Application.frmSpeedometer.Dispose(Boolean disposing)
Any idea????
The error Value Close() cannot be called while doing CreateHandle() usually happens when we try to close the form in the constructor or Load event.
For example, the following code gives the error:
private void frmCustomer_Load(object sender, EventArgs e)
{
if (!Valid())
this.Close;
}
The Solution:
private void frmCustomer_Load(object sender, EventArgs e)
{
if (!Valid())
this.BeginInvoke(new MethodInvoker(Close));
}
You can use this in your code.
it is hard to say what is the problem from the code you posted.
The code that you posted should work (form shown with Show() should be possible to close with Dispose() method).
The reason why it does not work is probably somewhere in the form that you are trying to dispose of. When you call Dispose() method (according to the error message this is what happens) on an object, that object will try to dispose of all its children and do some cleanup. That is the place to look for error.
My suggestion is to comment out all your code in the form objfrm (or make new EMPTY form) and see if error happens. It should not happen. Then start adding commented code and see when the error happens. I bet it will be in the code that is being called as consequence of Dispose method.
The code is as follows:
if (frmMain.objfrm== null)
{
frmMain.objfrm = frmMyForm.Instance;
frmMain.objfrm.ShowInTaskbar = false;
}
frmMain.objfrm.Show();
frmMain.objfrm.BringToFront();
frmMain is the Main Form that has a static variable of the frmMyForm. than in my code whereever i want to use this I just check if it's not null than create it using a static Instance and than give the peoperty.
While closing the form I have the following code:
frmMain.objfrm.Close_this();
The Close_this calls the Close() or Dispose() method.
But when I call that function I get the above exception.
You need to use ShowDialog instead of Show thats the problem. Show dont block the application and the code keeps running.
You are disposing the object when the GUI is creating it (that what the exception said)
Try with this:
if (frmMain.objfrm== null)
{
frmMain.objfrm = frmMyForm.Instance;
frmMain.objfrm.ShowInTaskbar = false;
}
frmMain.objfrm.ShowDialog();
Note the ShowDialog()

How can I get the current exception in a WinForms TraceListener

I am modifying an existing WinForms app which is setup with a custom TraceListener which logs any unhandled errors that occur in the app. It seems to me like the TraceListener gets the message part of the exception (which is what gets logged), but not the other exception information. I would like to be able to get at the exception object (to get the stacktrace and other info).
In ASP.NET, which I am more familiar with, I would call Server.GetLastError to get the most recent exception, but of course that won't work in WinForms.
How can I get the most recent exception?
I assume that you have set an event handler that catches unhandled domain exceptions and thread exceptions. In that delegate you probably call the trace listener to log the exception. Simply issue an extra call to set the exception context.
[STAThread]
private static void Main()
{
// Add the event handler for handling UI thread exceptions
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
// Add the event handler for handling non-UI thread exceptions
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
...
Application.Run(new Form1());
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
MyTraceListener.Instance.ExceptionContext = e;
Trace.WriteLine(e.ToString());
}
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
// similar to above CurrentDomain_UnhandledException
}
...
Trace.Listeners.Add(MyTraceListener.Instance);
...
class MyTraceListener : System.Diagnostics.TraceListener
{
...
public Object ExceptionContext { get; set; }
public static MyTraceListener Instance { get { ... } }
}
On the Write methods in MyTraceListener you can get the exception context and work with that. Remember to sync exception context.

Resources