InvalideOperationException / invoke - c#

the following code is used to trigger invoke (code is reduced, i left out the error handling in this example to make it more clear)
public static void InvokeIfNecessary(this Control control, MethodInvoker methodInvoker)
{
if (control != null && !control.IsDisposed && !control.Disposing)
{
if (control.InvokeRequired)
{
control.Invoke(methodInvoker);
}
else
{
methodInvoker();
}
}
}
Normally it works fine, but sometimes if i call a method of a Form an InvalidOperationException is given. Schematic method to be called
// in a Frm2:
internal void UpdateSomething()
{
List<NO> myObjects = frmMain.NO.GetNOs();
if (null != myObjects)
{
this.InvokeIfNecessary(() =>
{
layoutControlGroup.BeginUpdate(); // DevExpress Layoutcontrolgroup
foreach (NO aObject in myObjects)
{
if(...) // if already a control for the object exist update it.
{
// update
}
else
{
// add item
LayoutControlItem layoutControlItem = new LayoutControlItem();
// create new control
Control control = CreateNewControl(aObject);
layoutControlItem.Control = control;
// do some stuff with visibility and size of control
...
layoutControlGroup.AddItem(layoutControlItem); // <-- And here the InvalidOperationException occurs.
/// The message is (translated
/// InvalidOperationException was not handled by usercode
/// The acces on the Control FrmMain was done from another Thrad then the thread which created it.
...;
}
}
...;
layoutControlGroupCA.EndUpdate();
});
}
}
Well... I must admit that i have a conceptual problem here.
Why is the Exception thrown here?
The Frm2 method creates a new element (In NO there is only a string and a struct with strings and bool). The element is only accessed within the UpdateSomething() method. The layOutControlGroup is a member of Frm2.
So in my opinion only a new Control which shoudl be created in Frm2 Thread should be attached to a Frm2 specific Control.
So why does it insisting in FrmMain? (the main form, which calls the method of the form to inform about an update of items)
P.S. this.InvokeIfRequired <- this is Frm2 actually...

So, as we see, there are always trouble with ( begin) invoke.
Use BeginInvoke instead. is my hint.
Usually it says, that begin invoke on controls executes a method in the same thread where the handle was created on.
But as it seems, the handle of form2 is not been created in the same thread, OR perhaps it is not present yet.
Try to check this and verify this, please.
Ah. by the way, flag exceptions, clr, in visual studio, when they are thrown. This helps to spot the error.

I think that you checked InvokeRequired with a wrong control.
What you needed to check with InvokeRequired is layoutControlGroup, but your extension method checks on form's with the code this.InvokeIfNecessary where this is Frm2.
Also, you invoked layoutControlGroup.BeginUpdate() but layoutControlGroupCA.EndUpdate(), seems not symmetric of the usage.
A correction of the code might be:
internal void UpdateSomething() {
var myObjects=frmMain.NO.GetNOs();
if(null!=myObjects) {
MethodInvoker beginUpdate=() => layoutControlGroup.BeginUpdate();
MethodInvoker endUpdate=() => layoutControlGroup.EndUpdate();
layoutControlGroup.InvokeIfNecessary(beginUpdate);
foreach(NO aObject in myObjects)
if(SomeCondition) {
// update
}
else {
LayoutControlItem layoutControlItem=new LayoutControlItem();
Control control=CreateNewControl(aObject);
layoutControlItem.Control=control;
MethodInvoker addItem=
() => {
layoutControlGroup.AddItem(layoutControlItem);
};
layoutControlGroup.InvokeIfNecessary(addItem);
}
layoutControlGroup.InvokeIfNecessary(endUpdate);
}
}

Related

Find out which winforms controls are accessed from a background thread

We have built a huge winforms project, already in progress for multiple years.
Sometimes, our users get an exception which looks like this one.
The resolution of this problem seems to be:
don't acces UI components from a background thread
.
But since our project is a very big project with a lot of different threads, we don't succeed in finding all these.
Is there a way to check (with some tool or debugging option) which components are called from a background thread?
To clarify:
I created a sample winforms project with a single Form, containing two Button
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
button1.Text = "Clicked!";
}
private void button2_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
button2.BackColor = Color.Red; //this does not throw an exception
//button2.Text = "Clicked"; //this throws an exception when uncommented
});
}
}
The background color of button2 is set to red when the button is clicked. This happens in a background thread (which is considered bad behavior). However, it doesn't (immediately) throw an exception. I would like a way to detect this as 'bad behavior'. Preferably by scanning my code, but if it's only possible by debugging, (so pausing as soon as a UI component is accessed from a background thread) it's also fine.
I've got 2 recommendations to use together, the first is a Visual Studio Plugin called DebugSingleThread.
You can freeze all the threads and work on one at a time (obviously the non-main-UI threads) and see each threads access to controls. Tedious I know but not so bad with the second method.
The second method is to get the steps in order to reproduce the problem. If you know the steps to reproduce it, it will be easier to see whats causing it. To do this I made this User Action Log project on Github.
It will record every action a user makes, you can read about it here on SO: User Activity Logging, Telemetry (and Variables in Global Exception Handlers).
I'd recommend you also log the Thread ID, then when you have been able to reproduce the problem, go to the end of the log and work out the exact steps. Its not as painful as it seems and its great for getting application telemetry.
You might be able to customise this project, eg trap a DataSource_Completed event or add a dummy DataSource property that sets the real Grids DataSource property and raises an INotifyPropertyChanged event - and if its a non-main thread ID then Debugger.Break();.
My gut feeling is you're changing a control's (eg a grid) data source in a background thread (for that non-freeze feel) and thats causing a problem with synchronisation. This is what happened to the other DevExpress customer who experienced this. Its discussed here in a different thread to the one you referenced.
Is your app set to ignore cross threading intentionally?
Cross-thread operations should be blowing up all the time in winforms. It checks for them like crazy in just about every method. for a starting point check out https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs.
Somewhere in your app, somebody might have put this line of code:
Control.CheckForIllegalCrossThreadCalls = False;
Comment that out and run the app, then follow the exceptions.
(Usually you can fix the problem by wrapping the update in an invoke, e.g., in a worker thread if you see textbox1.text=SomeString; change it to `textbox.invoke(()=>{textbox1.text=SomeString;});.
You may also have to add checking for InvokeRequired, use BeginInvoke to avoid deadlocks, and return values from invoke, those are all separate topics.
this is assuming even a moderate refactor is out of the question which for even a medium sized enterprise app is almost always the case.
Note: it's not possible to guarantee successful discovery of this case thru static analysis (that is, without running the app). unless you can solve the halting problem ... https://cs.stackexchange.com/questions/63403/is-the-halting-problem-decidable-for-pure-programs-on-an-ideal-computer etc...
I did this to search for that specific situation but of course, need to adjust it to your needs, but the purpose of this is to give you at least a possibility.
I called this method SearchForThreads but since it's just an example, you can call it whatever you want.
The main idea here is perhaps adding this Method call to a base class and call it on the constructor, makes it somewhat more flexible.
Then use reflection to invoke this method on all classes deriving from this base, and throw an exception or something if it finds this situation in any class.
There's one pre req, that is the usage of Framework 4.5.
This version of the framework added the CompilerServices attribute that gives us details about the Method's caller.
The documentation for this is here
With it we can open up the source file and dig into it.
What i did was just search for the situation you specified in your question, using rudimentary text search.
But it can give you an insight about how to do this on your solution, since i know very little about your solution, i can only work with the code you put on your post.
public static void SearchForThreads(
[System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
var startKey = "this.Controls.Add(";
var endKey = ")";
List<string> components = new List<string>();
var designerPath = sourceFilePath.Replace(".cs", ".Designer.cs");
if (File.Exists(designerPath))
{
var designerText = File.ReadAllText(designerPath);
var initSearchPos = designerText.IndexOf(startKey) + startKey.Length;
do
{
var endSearchPos = designerText.IndexOf(endKey, initSearchPos);
var componentName = designerText.Substring(initSearchPos, (endSearchPos - initSearchPos));
componentName = componentName.Replace("this.", "");
if (!components.Contains(componentName))
components.Add(componentName);
} while ((initSearchPos = designerText.IndexOf(startKey, initSearchPos) + startKey.Length) > startKey.Length);
}
if (components.Any())
{
var classText = File.ReadAllText(sourceFilePath);
var ThreadPos = classText.IndexOf("Task.Run");
if (ThreadPos > -1)
{
do
{
var endThreadPos = classText.IndexOf("}", ThreadPos);
if (endThreadPos > -1)
{
foreach (var component in components)
{
var search = classText.IndexOf(component, ThreadPos);
if (search > -1 && search < endThreadPos)
{
Console.WriteLine($"Found a call to UI thread component at pos: {search}");
}
}
}
}
while ((ThreadPos = classText.IndexOf("Task.Run", ++ThreadPos)) < classText.Length && ThreadPos > 0);
}
}
}
I hope it helps you out.
You can get the Line number if you split the text so you can output it, but i didn't want to go through the trouble, since i don't know what would work for you.
string[] lines = classText.Replace("\r","").Split('\n');
Try that:
public static void Main(string[] args)
{
// Add the event handler for handling UI thread exceptions to the event.
Application.ThreadException += new ThreadExceptionEventHandler(exception handler);
// Set the unhandled exception mode to force all Windows Forms errors to go through the handler.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException += // add the handler here
// Runs the application.
Application.Run(new ......);
}
Then you can log the message and the call stack and that should give you enough information to fix the issue.
I recommend you update your GUI to handle this situation automatically for your convenience. You instead use a set of inherited controls.
The general principle here is to override the property Set methods in a way to make them Thread Safe. So, in each overridden property, instead of a straight update of the base control, there's a check to see if an invoke is required (meaning we're on a separate thread the the GUI). Then, the Invoke call updates the property on the GUI thread, instead of the secondary thread.
So, if the inherited controls are used, the form code that is trying to update GUI elements from a secondary thread can be left as is.
Here is the textbox and button ones. You would add more of them as needed and add other properties as needed. Rather than putting code on individual forms.
You don't need to go into the designer, you can instead do a find/replace on the designer files only. For example, in ALL designer.cs files, you would replace System.Windows.Forms.TextBox with ThreadSafeControls.TextBoxBackgroundThread and System.Windows.Forms.Button with ThreadSafeControls.ButtonBackgroundThread.
Other controls can be created with the same principle, based on which control types & properties are being updated from the background thread.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ThreadSafeControls
{
class TextBoxBackgroundThread : System.Windows.Forms.TextBox
{
public override string Text
{
get
{
return base.Text;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.Text = value; });
else
base.Text = value;
}
}
public override System.Drawing.Color ForeColor
{
get
{
return base.ForeColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.ForeColor = value; });
else
base.ForeColor = value;
}
}
public override System.Drawing.Color BackColor
{
get
{
return base.BackColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.BackColor = value; });
else
base.BackColor = value;
}
}
}
class ButtonBackgroundThread : System.Windows.Forms.Button
{
public override string Text
{
get
{
return base.Text;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.Text = value; });
else
base.Text = value;
}
}
public override System.Drawing.Color ForeColor
{
get
{
return base.ForeColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.ForeColor = value; });
else
base.ForeColor = value;
}
}
public override System.Drawing.Color BackColor
{
get
{
return base.BackColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.BackColor = value; });
else
base.BackColor = value;
}
}
}
}

ManagementObjectSearcher causes re-entrancy issues for onclick handler

I am having an odd problem with protecting a section of code. My application is a tray app. I create a NotifyIcon inside my class (ApplicationContext). I have assigned a balloon click handler and a double click handler to the NotifyIcon object. there is also a context menu but I am not showing all code. Only important pieces.
public class SysTrayApplicationContext: ApplicationContext
{
private NotifyIcon notifyIcon;
private MainForm afDashBoardForm;
public SysTrayApplicationContext()
{
this.notifyIcon = new NotifyIcon();
this.notifyIcon.BalloonTipClicked += notifyIcon_BalloonTipClicked;
this.notifyIcon.MouseDoubleClick += notifyIcon_MouseDoubleClick;
// ... more code
}
Both handlers launch or create/show my form:
private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
openDashboard();
}
}
private void notifyIcon_BalloonTipClicked(object sender, EventArgs e)
{
openDashboard();
}
private void openDashboard()
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
else
{
log.Debug("Dashboard form does not exist, create it");
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
There is a problem with the above code. Maybe more than 1. Issue: it is possible to display 2 dashboard forms which is not what I want. If user double clicks on tray icon while balloon message is displaying causes a race condition in openDashboard. I can reproduce this easily. So I added a lock around the code in openDashboard code and, to my surprise, that did NOT prevent 2 dashboard forms from displaying. I should not be able to create 2 MainForms. Where am I going wrong here?
here is the updated code with lock statement:
private void openDashboard()
{
lock (dashBoardFormlocker)
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
else
{
log.Debug("Dashboard form does not exist, create it");
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
}
Note: lock object was added to the class and initialized in constructor.
private object dashBoardFormlocker;
UPDATE: Showing more code. this is how code gets started :
static void Main()
{
if (SingleInstance.Start())
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
XmlConfigurator.Configure();
// For a system tray application we don't want to create
// a form, we instead create a new ApplicationContext. The Run method takes
Application.Run(new SysTrayApplicationContext());
SingleInstance.Stop();
SingleInstance.Dispose();
}
}
}
UPDATE 2: Provide more code for clarity
public partial class MainForm : Form
{
public MainForm()
{
log.Trace("MainForm constructor...");
InitializeComponent();
// ... code not shown
this.label_OSVersion.Text = getOSFriendlyName();
// .. more code
}
private string getOSFriendlyName()
{
try
{
string result = string.Empty;
var mgmtObj = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().OfType<ManagementObject>()
select x.GetPropertyValue("Caption")).FirstOrDefault();
result = mgmtObj != null ? mgmtObj.ToString() : string.Empty;
OperatingSystem os = Environment.OSVersion;
String sp = os.ServicePack ?? string.Empty;
return !string.IsNullOrWhiteSpace(result) ? result + sp : "Unknown";
}
catch (System.Exception ex)
{
log.Error("Error trying to get the OS version", ex);
return "Unknown";
}
}
}
The main UI thread must always pump a message loop to support communication from COM components.
So when you do a blocking operation from the UI thread like locking or joining a thread, (EDIT: edited based on Peter Duniho's fix) the UI thread will enter an 'alertable' state, allowing COM to dispatch certain type of messages, which in turn can cause re-entrancy issues like in your scenario.
Look at the answer to this question (Why did entering a lock on a UI thread trigger an OnPaint event?) for a much more accurate explanation.
Looking at the source code of ManagementObjectSearcher.Get there is a lock (inside Initialize), and since you call it from the constructor of your form, it may lead to the second event triggering while the form's constructor has not finished. The assignment to the dashBoardFormlocker variable only happens after the constructor finishes, so that would explain why it was null on the second entry.
The moral of the story is never do blocking operations on the UI thread.
Without a good, minimal, complete code example that reliably reproduces the problem, it's impossible to know for sure what the problem is. But the guess by answerer tzachs seems reasonable. If so, you can fix your problem by changing your method to look like this:
private bool _dashboardOpen;
private void openDashboard()
{
if (_dashboardOpen)
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
}
else
{
log.Debug("Dashboard form does not exist, create it");
_dashboardOpen = true;
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
In that way, any re-entrant attempt to open the window will be detected. Note that you still need the check for null before actually activating; you can't activate a window that hasn't actually finished being created yet. The subsequent call to Show() will take care of activation anyway, so ignoring the activation in the re-entrant case shouldn't matter.

Trouble with raising custom event handling between 2 forms

New to C#. Like the title, I'm having difficulty trying to raise an event. It will eventually then be consumed on another form.
What I'm trying to do is have many instances of a custom user control (my event raising form(s)) that creates a tcp client, connects, and then closes. When this tcp client has an "error", be it a catch exception, I want an event to be raised. I'm forcing the error right now by having my internet turned off to test. My first problem is I can't even get the event to be raised at all. I'll show the event code I'm working with on my custom user control:
public delegate void TaskCompleteEventHandler(object sender, TaskCompleteEventArgs e);
public event TaskCompleteEventHandler TaskComplete;
public class TaskCompleteEventArgs : System.EventArgs
{
// add local member variables to hold text
private string errorString;
// class constructor
public TaskCompleteEventArgs(string ErrorString)
{
this.errorString = ErrorString;
}
// Property
public string ErrorString
{
get
{
return errorString;
}
set
{
errorString = value;
}
}
}
This is my method that processes the exception and ideally would raise the event and allow the host form to print the string and exception accordingly.
private void ErrorLogging(string ex)
{
errorString = String.Format(/*...errorString formatting...*/);
// instance the event args and pass it the errorString value
TaskCompleteEventArgs args = new TaskCompleteEventArgs(errorString);
// raise the event with the updated arguments
TaskComplete(this, args); //----> THIS IS WHERE I GET AN ERROR!! <----
this.Dispose();
}
The error is Object reference not set to an instance of an object.
Here's the Watch screen of my TaskComplete(this, args)
I can't seem to debug this... I'm just not strong enough yet to know what I've done wrong. How is it causing side effects?
I'm sure I'm going to have more issues on my main form when I get this going... Does anyone have a clue what's causing this? Thanks in advance.
EDIT: On my main form:
public Form1()
{
InitializeComponent();
// Start control disabled and subscribe each control the event
foreach (var control in controlList)
{
control.Enabled = false;
control.TaskComplete += new dev_emu_project.dev_emu_widget.TaskCompleteEventHandler(OnTaskComplete);
}
}
List<dev_emu_project.dev_emu_widget> controlList = new List<dev_emu_project.dev_emu_widget>();
public void OnTaskComplete(object sender, dev_emu_project.TaskCompleteEventArgs e)
{
//.... work for processing
}
}
You are getting a NullReferenceException because you're invoking an empty event, meaning no delegate has been registered to it. You need to make sure TaskComplete isn't null before invoking it.
Add a null check before invoking to make sure someone did register to your event:
if (TaskComplete != null)
{
TaskComplete(this, args);
}
From MSDN Event Tutorial:
Invoking an event
Once a class has declared an event, it can treat that event just like a field of the indicated delegate type. The field will either be null, if no client has hooked up a delegate to the event, or else it refers to a delegate that should be called when the event is invoked. Thus, invoking an event is generally done by first checking for null and then calling the event

Threading with WinForms?

In my application I startup a hidden dummyForm that is merely created to keep track of the main UI thread. So If a new form is about to be created InvokeRequired is used on the dummy form to make sure that we are on the main UI thread when creating the new form.
Directly after instantiating my frmStart form I check the frmStart.InvokeRequired and it is set to false so no need for invoke here (the same goes for dummyForm.InvokeRequired).
Then I got a frmMyDialog that will use frmStart as parent/owner something like this:
using(Create frmMyDialog on main UI thread)
{
frmMyDialog.Show(frmStart);
}
This will throw a cross thread exception and the strange thing here is that:
frmMyDialog.InvokeRequired = false
dummyForm.InvokeRequired = false
frmStart.InvokeRequired = true
And this is even when I'm checking that dummyForm.InvokeRequired is false when creating the frmStart?
The frmMyDialog.InvokeRequired should always be the same value as dummyForm.InvokeRequired? What is happening here?
I have checked that the frmStart and dummyForm is not re-created at all after the first instance has been created.
Edit1:
This is how the application starts :
public static void Main(string[] args)
{
_instance = new MyClientMain(parameters);
Application.Run(_instance);
}
The constructor of MyClientMain class will run Setup on a static class called MainControl. MainControler will in the setup method instanciate a dummyform like this :
if (_dummyForm == null)
_dummyForm = new Form();
After this is done a login form will handle a login and this form is multithreaded. When the login is finished the MainController will be called again to instanciate and open the main MDI windo that holds frmStart. To make sure that we are on the same thread the following is done :
public static StartApplication()
{
if (_dummyForm.InvokeRequired)
_dummyForm.Invoke(new MethodInvoker(delegate { OpenMainOrbitWindow(); }));
//Instanciate mainform and frmStart then open mainForm with frmStart as a MDI child
}
There is no multithreading here.
Then when the service goes offline a event will be triggered and I need to popup a frmMyDialog but when using .ShowDialog() it dialog will be placed behind forms so the parent/owner most be found and set like this :
public static Form GetActiveForm()
{
Form activeForm = Form.ActiveForm;
if (activeForm != null)
return activeForm;
if (MainOrbitForm.TopMost)
return MainOrbitForm;
else
{
FormCollection openForms = Application.OpenForms;
for (int i = 0; i < openForms.Count && activeForm == null; ++i)
{
Form openForm = openForms[i];
if (openForm.IsMdiContainer)
return openForm.ActiveMdiChild;
}
}
if (_patientForm != null)
{
if (_patientForm.TopMost)
return _patientForm;
}
return null;
}
public static string ShowOrbitDialogReName()
{
frmMyDialog myDialog;
Form testForm;
//Makes sures that the frmOrbitDialog is created with the same thread as the dummyForm
//InvokeRequired is used for this
using (myDialog = MainController.CreateForm<frmOrbitDialog>())
{
//Settings...
testForm = GetActiveForm();
myDialog.ShowDialog(GetActiveForm(testForm));
}
}
The problem is that
myDialog.InvokeRequired = false
testForm.InvokeRequired = true;
MainController.DummyForm.InvokeRequired = false;
Edit2:
Startup and creates the dummyform :
dummyForm.InvokeRequired = false
Thread.CurrentThread.ManagedThreadId = 9
After success login we create the mainform
_mainForm.InvokeRequired = false
MainControl.DummyForm.InvokeRequired = false
Thread.CurrentThread.ManagedThreadId = 9
Everything looks fine so far. Then a callback is received(WCF) and a event creates a frmMyDialog on the same thread(Invoke is used on the dummyForm) and then the ShowDialog is used :
frmMyCustomDialog.ShowDialog(_mainForm)
This throws a CrossThreadException and this is how it looks like at this point :
_mainForm.InvokeRequired = true
frmMyCustomDialog.InvokeRequired = false
MainControl.DummyForm.InvokeRequired = false
Thread.CurrentThread.ManagedThreadId = 12
Why is the MainControl.DummyForm not true? The ManageThreadId is not 9 but 12?
You should use System.Threading.SynchronizationContext.Current. It was created directly for purposes like this.
You may access it anywhere, after the first form of your app have been created. Judging by your example below, this should not be a problem, since you create a form right o the start of application.
public static void Main(string[] args)
{
_instance = new MyClientMain(parameters);
Application.Run(_instance);
}
Then anywhere, you need the code to be executed on UI thread, you simply use
System.Threading.SynchronizationContext.Current.Send() // To execute your code synchronously
System.Threading.SynchronizationContext.Current.Post() // To execute your code synchronously
Mind, that SynchronizationContext is smartenough to see, that you call it already from UI thread, and then it will simply execute your delegate directly.
And also, remember that you need to create some WinForms form or control before you use SynchronizationContext for the first time, becase when you do this, context will be initialized to appropriate implementation.
There are 3 implementation: Default, that does nothing - just always run code in sync, it stays in the Current until you create WinForms control or WPF control.
Then Current will be populated with either context for Winforms, or context for WPF dispatcher.
This is just off the top of my head, but as Vladimir Perevalov has stated in a different discussion, you have to make your form visible.
If your frmDummy is never shown, then it never has it's window handle created and assigned, and therefore it will always reply False to "InvokeRequired". This would mean that all the code you mean to sync through frmDummy is never actually sent to the initial UI thread, but is always run in the current thread. (which becomes an UI thread of its own for the control it has just created).
The important thing to note is that InvokeRequired tries to determine if the said control's window handle is owned by another thread. It has nothing to do with the constructor.
If you do not want to show the frmDummy, you can call CreateControl right after you instantiate it, to make sure it has it's handle assigned.
I didn't fully understand your question, because your examples don't really show anything about multithreading - however, if you want to create a form where the parent is another form from another thread, you could use this code:
public void CreateShowDialogForm()
{
if (this.InvokeRequired)
{
this.Invoke(new Action(CreateShowDialogForm));
}
else
{
Form frmMyDialog = new Form();
frmMyDialog.Show(this);
}
}
private void Form4_Load(object sender, EventArgs e)
{
Task t = new Task(() => CreateShowDialogForm());
t.Start();
t.ContinueWith(task => true);
}

How to defer execution of an Event on each item in a collection until iteration of collection is complete using delegates and/or events?

Of Note: This is more of a curiosity question than anything else.
Given a List<Window> where each window has an event attached to the Close Event which removes the window from the collection, how could you use delegates / events to defer the execution of the Close Event until the collection has been iterated?
For example:
public class Foo
{
private List<Window> OpenedWindows { get; set; }
public Foo()
{
OpenedWindows = new List<Window>();
}
public void AddWindow( Window win )
{
win.Closed += OnWindowClosed;
OpenedWindows.Add( win );
}
void OnWindowClosed( object sender, EventArgs e )
{
var win = sender as Window;
if( win != null )
{
OpenedWindows.Remove( win );
}
}
void CloseAllWindows()
{
// obviously will not work because we can't
// remove items as we iterate the collection
// (the close event removes the window from the collection)
OpenedWindows.ForEach( x => x.Close() );
// works fine, but would like to know how to do
// this with delegates / events.
while( OpenedWindows.Any() )
{
OpenedWindows[0].Close();
}
}
}
Specifically, within the CloseAllWindows() method, how could you iterate the collection to call the close event, but defer the event being raised until the collection has been completely iterated?
Presumably you're trying to avoid the "Collection was modified" exception in the first case.
Really the only way to "defer" this is to make a copy of the collection, which involves iterating the original collection fully and adding the items to a new collection:
var openedWindowsCopy = new List<Window>(OpenedWindows);
foreach (var window in openedWindowsCopy)
window.Close();
Also, your second method is valid, although typically when you need to clean up instances in an indexed collection such as a List<T>, you do it simply by counting down:
for (int i = OpenedWindows.Count - 1; i >= 0; i--)
OpenedWindows[i].Close();
Personally, I see no reason to complicate the issue with more delegates.

Categories

Resources