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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
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 } } |
Jeg ville bare informere deg om at jeg leste hele posten. Og jeg er like klok. 😉