In .Net WinForms (and WPF for that matter) only the main GUI thread is allowed to make any change to GUI. If you attempt to do so from another thread then Visual Studio MDA will let you know that you made a mistake. This is actually an improvement from VS2003 where nobody said anything, so your application would mysteriously hang every so often.
But still its a pain in the neck to keep jumping between threads all the time. Often you want multiple threads running and updating the form whenever they see fit.
So… I ran Reflector on .Net 4.0 WinForm Form, copied source, then wrote up a small PERL script to create the majority of what you see below.
Basically this is a partial Form class that hides most normal Form methods and replaces them with new ones that automatically invoke GUI thread if required.
It’s a quick hack, but it works… The heart of the whole operation is GUIInvokeMethod(). If you don’t want all the hiding then this could be useful to keep in your form.
You can create similar methods like this for your user control.
The important part:
#region GUI Thread Invoker: GUIInvokeMethod()
private delegate void GUIInvokeMethodDelegate(Action @delegate);
/// <summary>
/// Invoke command with GUI thread. Usage: GUIInvoke(() => FormOrControl.Cmd());
/// </summary>
/// <param name="delegate">Command to execute in form: () => Cmd()</param>
public void GUIInvokeMethod(Action @delegate)
{
// Check if we need to invoke as GUI thread
if (this.InvokeRequired)
{
this.Invoke(new GUIInvokeMethodDelegate(GUIInvokeMethod), @delegate);
return;
}
// Execute
@delegate.Invoke();
}
#endregion
The whole file:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Your.NameSpace // <\-- replace with correct namespace
{
partial class Form1 // <\-- replace with name of your form
{
#region GUI Thread Invoker: GUIInvokeMethod()
private delegate void GUIInvokeMethodDelegate(Action @delegate);
/// <summary>
/// Invoke command with GUI thread. Usage: GUIInvoke(() => FormOrControl.Cmd());
/// </summary>
/// <param name="delegate">Command to execute in form: () => Cmd()</param>
public void GUIInvokeMethod(Action @delegate)
{
// Check if we need to invoke as GUI thread
if (this.InvokeRequired)
{
this.Invoke(new GUIInvokeMethodDelegate(GUIInvokeMethod), @delegate);
return;
}
// Execute
@delegate.Invoke();
}
#endregion
#region GUI Thread Invoked Form: Methods
/// <summary>Automatic GUI Thread Invoke</summary>
public new void Activate() { GUIInvokeMethod(() => base.Activate()); }
/// <summary>Automatic GUI Thread Invoke</summary>
protected new void ActivateMdiChild(Form form) { GUIInvokeMethod(() => base.ActivateMdiChild(form)); }
/// <summary>Automatic GUI Thread Invoke</summary>
public new void AddOwnedForm(Form ownedForm) { GUIInvokeMethod(() => base.AddOwnedForm(ownedForm)); }
/// <summary>Automatic GUI Thread Invoke</summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected new void AdjustFormScrollbars(bool displayScrollbars) { GUIInvokeMethod(() => base.AdjustFormScrollbars(displayScrollbars)); }
/// <summary>Automatic GUI Thread Invoke</summary>
[Obsolete("This method has been deprecated. Use the ApplyAutoScaling method instead. http://go.microsoft.com/fwlink/?linkid=14202"), EditorBrowsable(EditorBrowsableState.Never)]
protected new void ApplyAutoScaling() { GUIInvokeMethod(() => base.ApplyAutoScaling()); }
/// <summary>Automatic GUI Thread Invoke</summary>
protected new void CenterToParent() { GUIInvokeMethod(() => base.CenterToParent()); }
/// <summary>Automatic GUI Thread Invoke</summary>
protected new void CenterToScreen() { GUIInvokeMethod(() => base.CenterToScreen()); }
/// <summary>Automatic GUI Thread Invoke</summary>
public new void Close() { GUIInvokeMethod(() => base.Close()); }
//[EditorBrowsable(EditorBrowsableState.Advanced)]
//protected new Control.ControlCollection CreateControlsInstance() { GUIInvokeMethod(() => base.CreateControlsInstance()); }
/// <summary>Automatic GUI Thread Invoke</summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected new void CreateHandle() { GUIInvokeMethod(() => base.CreateHandle()); }
//[EditorBrowsable(EditorBrowsableState.Advanced), SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
//protected new void DefWndProc(ref Message m) { GUIInvokeMethod(() => base.DefWndProc(ref m)); }
//protected new void Dispose(bool disposing) { GUIInvokeMethod(() => base.Dispose(disposing)); }
//[EditorBrowsable(EditorBrowsableState.Advanced)]
//protected new Rectangle GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified) { GUIInvokeMethod(() => base.GetScaledBounds(bounds, factor, specified)); }
/// <summary>Automatic GUI Thread Invoke</summary>
public new void LayoutMdi(MdiLayout value) { GUIInvokeMethod(() => base.LayoutMdi(value)); }
//[EditorBrowsable(EditorBrowsableState.Advanced)]
//[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
//protected new bool ProcessCmdKey(ref Message msg, Keys keyData) { GUIInvokeMethod(() => base.ProcessCmdKey(ref msg, keyData)); }
//[EditorBrowsable(EditorBrowsableState.Advanced), UIPermission(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)]
//protected new bool ProcessDialogChar(char charCode) { GUIInvokeMethod(() => base.ProcessDialogChar(charCode)); }
//[UIPermission(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)]
//protected new bool ProcessDialogKey(Keys keyData) { GUIInvokeMethod(() => base.ProcessDialogKey(keyData)); }
//[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
//protected new bool ProcessKeyPreview(ref Message m) { GUIInvokeMethod(() => base.ProcessKeyPreview(ref m)); }
//[UIPermission(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)]
//protected new bool ProcessTabKey(bool forward) { GUIInvokeMethod(() => base.ProcessTabKey(forward)); }
/// <summary>Automatic GUI Thread Invoke</summary>
public new void RemoveOwnedForm(Form ownedForm) { GUIInvokeMethod(() => base.RemoveOwnedForm(ownedForm)); }
/// <summary>Automatic GUI Thread Invoke</summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected new void ScaleControl(SizeF factor, BoundsSpecified specified) { GUIInvokeMethod(() => base.ScaleControl(factor, specified)); }
/// <summary>Automatic GUI Thread Invoke</summary>
[EditorBrowsable(EditorBrowsableState.Never)]
protected new void ScaleCore(float x, float y) { GUIInvokeMethod(() => base.ScaleCore(x, y)); }
/// <summary>Automatic GUI Thread Invoke</summary>
protected new void Select(bool directed, bool forward) { GUIInvokeMethod(() => base.Select(directed, forward)); }
/// <summary>Automatic GUI Thread Invoke</summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected new void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { GUIInvokeMethod(() => base.SetBoundsCore(x, y, width, height, specified)); }
/// <summary>Automatic GUI Thread Invoke</summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected new void SetClientSizeCore(int x, int y) { GUIInvokeMethod(() => base.SetClientSizeCore(x, y)); }
/// <summary>Automatic GUI Thread Invoke</summary>
public new void SetDesktopBounds(int x, int y, int width, int height) { GUIInvokeMethod(() => base.SetDesktopBounds(x, y, width, height)); }
/// <summary>Automatic GUI Thread Invoke</summary>
public new void SetDesktopLocation(int x, int y) { GUIInvokeMethod(() => base.SetDesktopLocation(x, y)); }
/// <summary>Automatic GUI Thread Invoke</summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected new void SetVisibleCore(bool value) { GUIInvokeMethod(() => base.SetVisibleCore(value)); }
/// <summary>Automatic GUI Thread Invoke</summary>
public new void Show(IWin32Window owner) { GUIInvokeMethod(() => base.Show(owner)); }
//[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
//public new DialogResult ShowDialog() { GUIInvokeMethod(() => base.ShowDialog()); }
//public new DialogResult ShowDialog(IWin32Window owner) { GUIInvokeMethod(() => base.ShowDialog(owner)); }
//public new string ToString() { GUIInvokeMethod(() => base.ToString()); }
/// <summary>Automatic GUI Thread Invoke</summary>
protected new void UpdateDefaultButton() { GUIInvokeMethod(() => base.UpdateDefaultButton()); }
//[EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]
//public new bool ValidateChildren() { GUIInvokeMethod(() => base.ValidateChildren()); }
//[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
//public new bool ValidateChildren(ValidationConstraints validationConstraints) { GUIInvokeMethod(() => base.ValidateChildren(validationConstraints)); }
//[EditorBrowsable(EditorBrowsableState.Advanced), SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
//protected new void WndProc(ref Message m) { GUIInvokeMethod(() => base.WndProc(ref m)); }
#endregion
#region GUI Thread Invoked Form: Properties
/// <summary>Automatic GUI Thread Invoke</summary>
public new IButtonControl AcceptButton { get { return base.AcceptButton; } set { GUIInvokeMethod(() => base.AcceptButton = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool AllowTransparency { get { return base.AllowTransparency; } set { GUIInvokeMethod(() => base.AllowTransparency = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool AutoScale { get { return base.AutoScale; } set { GUIInvokeMethod(() => base.AutoScale = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new virtual Size AutoScaleBaseSize { get { return base.AutoScaleBaseSize; } set { GUIInvokeMethod(() => base.AutoScaleBaseSize = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool AutoScroll { get { return base.AutoScroll; } set { GUIInvokeMethod(() => base.AutoScroll = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool AutoSize { get { return base.AutoSize; } set { GUIInvokeMethod(() => base.AutoSize = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new AutoSizeMode AutoSizeMode { get { return base.AutoSizeMode; } set { GUIInvokeMethod(() => base.AutoSizeMode = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new AutoValidate AutoValidate { get { return base.AutoValidate; } set { GUIInvokeMethod(() => base.AutoValidate = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new Color BackColor { get { return base.BackColor; } set { GUIInvokeMethod(() => base.BackColor = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new IButtonControl CancelButton { get { return base.CancelButton; } set { GUIInvokeMethod(() => base.CancelButton = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new Size ClientSize { get { return base.ClientSize; } set { GUIInvokeMethod(() => base.ClientSize = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool ControlBox { get { return base.ControlBox; } set { GUIInvokeMethod(() => base.ControlBox = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new Rectangle DesktopBounds { get { return base.DesktopBounds; } set { GUIInvokeMethod(() => base.DesktopBounds = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new Point DesktopLocation { get { return base.DesktopLocation; } set { GUIInvokeMethod(() => base.DesktopLocation = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new DialogResult DialogResult { get { return base.DialogResult; } set { GUIInvokeMethod(() => base.DialogResult = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new FormBorderStyle FormBorderStyle { get { return base.FormBorderStyle; } set { GUIInvokeMethod(() => base.FormBorderStyle = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool HelpButton { get { return base.HelpButton; } set { GUIInvokeMethod(() => base.HelpButton = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new Icon Icon { get { return base.Icon; } set { GUIInvokeMethod(() => base.Icon = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool IsMdiContainer { get { return base.IsMdiContainer; } set { GUIInvokeMethod(() => base.IsMdiContainer = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool KeyPreview { get { return base.KeyPreview; } set { GUIInvokeMethod(() => base.KeyPreview = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new Point Location { get { return base.Location; } set { GUIInvokeMethod(() => base.Location = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new MenuStrip MainMenuStrip { get { return base.MainMenuStrip; } set { GUIInvokeMethod(() => base.MainMenuStrip = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new Padding Margin { get { return base.Margin; } set { GUIInvokeMethod(() => base.Margin = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool MaximizeBox { get { return base.MaximizeBox; } set { GUIInvokeMethod(() => base.MaximizeBox = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
protected new Rectangle MaximizedBounds { get { return base.MaximizedBounds; } set { GUIInvokeMethod(() => base.MaximizedBounds = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new Size MaximumSize { get { return base.MaximumSize; } set { GUIInvokeMethod(() => base.MaximumSize = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new Form MdiParent { get { return base.MdiParent; } set { GUIInvokeMethod(() => base.MdiParent = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new MainMenu Menu { get { return base.Menu; } set { GUIInvokeMethod(() => base.Menu = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool MinimizeBox { get { return base.MinimizeBox; } set { GUIInvokeMethod(() => base.MinimizeBox = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new Size MinimumSize { get { return base.MinimumSize; } set { GUIInvokeMethod(() => base.MinimumSize = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new double Opacity { get { return base.Opacity; } set { GUIInvokeMethod(() => base.Opacity = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new Form Owner { get { return base.Owner; } set { GUIInvokeMethod(() => base.Owner = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new virtual bool RightToLeftLayout { get { return base.RightToLeftLayout; } set { GUIInvokeMethod(() => base.RightToLeftLayout = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool ShowIcon { get { return base.ShowIcon; } set { GUIInvokeMethod(() => base.ShowIcon = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool ShowInTaskbar { get { return base.ShowInTaskbar; } set { GUIInvokeMethod(() => base.ShowInTaskbar = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new Size Size { get { return base.Size; } set { GUIInvokeMethod(() => base.Size = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new SizeGripStyle SizeGripStyle { get { return base.SizeGripStyle; } set { GUIInvokeMethod(() => base.SizeGripStyle = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new FormStartPosition StartPosition { get { return base.StartPosition; } set { GUIInvokeMethod(() => base.StartPosition = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new int TabIndex { get { return base.TabIndex; } set { GUIInvokeMethod(() => base.TabIndex = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool TabStop { get { return base.TabStop; } set { GUIInvokeMethod(() => base.TabStop = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new string Text { get { return base.Text; } set { GUIInvokeMethod(() => base.Text = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool TopLevel { get { return base.TopLevel; } set { GUIInvokeMethod(() => base.TopLevel = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new bool TopMost { get { return base.TopMost; } set { GUIInvokeMethod(() => base.TopMost = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new Color TransparencyKey { get { return base.TransparencyKey; } set { GUIInvokeMethod(() => base.TransparencyKey = value); } }
/// <summary>Automatic GUI Thread Invoke</summary>
public new FormWindowState WindowState { get { return base.WindowState; } set { GUIInvokeMethod(() => base.WindowState = value); } }
#endregion
}
}