I just posted a question about how to get a delegate to update a textbox on another form. Just when I thought I had the answer using Invoke...this happens. Here is my code:<
I found the InvokeRequired
not reliable, so I simply use
if (!this.IsHandleCreated)
{
this.CreateHandle();
}
It's a runtime error, not a compiler error.
Your Form, "Main", has to be displayed (hence a window handle created) before you can make calls to BeginInvoke or Invoke on it.
What I usually do in these situations is leave it up to the Form to determine if it needs to use a call to BeginInvoke or Invoke. You can test that with a call to InvokeRequired (check MSDN).
So for starters, I'd get rid of the logAddDelegate call in the Loggin class's updateLog method. Just make a straight call to the form to add a log. Like so:
public partial class Main : Form
{
public Main()
{
InitializeComponent();
}
private delegate void AddNewLogMessageEventHandler(string message);
public void AddLogMessage(string message)
{
object[] args = new object[1];
args[0] = message;
if (InvokeRequired)
BeginInvoke(new AddNewLogMessageEventHandler(AddLog), args);
else
Invoke(new AddNewLogMessageEventHandler(AddLog), args);
}
private void AddLog(string message)
{
this.Log.Items.Add(message);
}
}
}
So you can see, the Form itself is in charge of determining if it needs to call the method asynchronously or not.
However, this still won't fix your runtime error, because you're making a call to the form before its been displayed. You can check to see if the form's Handle is null or not, and that will at least allow you to verify whether or not you're dealing with a valid Form.
logAddDelegate(message);
I think you are calling this before the Form_Load event has been raised. Fix your code to wait for the form to load before calling logAddDelegate(...) i.e. before calling Logging.updateLog()
Right, I'm going to start again.
In order to understand what is happening, you need to understand how .NET and Windows relate to one another. .NET runs on Windows and wraps many of the native, Win32 concepts like a window, a listview, an editbox (the Win32 name for a standard textbox). This means that you can have a valid .NET instance of a TextBox or a Form, but not have the underlying Windows version of that item (EditBox, or Window) yet. When HandleCreated is true, the Windows version of the item is created.
Your issue is occurring because something is leading to the logAdd method being called before the Form's Window has been created. This means somewhere during your startup after the Form instance has been instantiated but before the Window handle has been created, something is trying to call logAdd. If you add a breakpoint to logAdd, you should be able to see what is doing that call. What you will find is that the call is being made on the Main instance you create in your logger class and NOT the Main instance that is actually running. As the logger instance never gets shown, the window handle is not created, and so you get your error.
The general way an application runs is to call Application.Run(new Main()) in your startup method, which is usually in the Program class and called Main. You need your logger to point to this instance of main.
There are several ways to get the instance of the form, each with its own caveats, but for simplicity you could expose the instance off the Main class itself. For example:
public partial class Main : Form
{
private static Main mainFormForLogging;
public static Main MainFormForLogging
{
get
{
return mainFormForLogging;
}
}
public Main()
{
InitializeComponent();
if (mainFormForLogging == null)
{
mainFormForLogging = this;
}
}
protected void Dispose(bool disposing)
{
if (disposing)
{
if (this == mainFormForLogging)
{
mainFormForLogging = null;
}
}
base.Dispose(disposing);
}
}
When you get this error, it almost always means that you've attempted to act on a control or form before it was actually created.
In WinForms, GUI elements have two semi-independent lives: as classes in memory and as entities in the operating system. As such, it's possible to reference a control in .net that hasn't actually been created yet. The "handle being created" refers to having a number assigned to the control by the OS to allow programs to manipulate its properties.
In this case, most errors can be eliminated by setting a flag at the end of the form's load event and only attempting to manipulate the form's controls after that flag has been set.
I have solved this in the past using the following method:
private void invokeOnFormThread(MethodInvoker method)
{
if (IsHandleCreated)
Invoke(new EventHandler(delegate { method(); }));
else
method();
}
Call invokeOnFormThread
instead of Invoke. It will only use the form's thread if a handle has already been created, otherwise it will use the caller's thread.