Developer Cookies Blog

Thread Extensions for Invoking WinForm-Controls

Yes, I know WinForms is an outdated task of Microsoft and Wpf is the future. But most of the developer have honor to maintain WinForm project. WinForm controls have the boring feature that it is not possible to access from a foreign thread context to set the WinForm properties. If you do that you get a friendly exception and the application terminates, which is not a wanted function for the customers. To reach a stable application an Invoke-Call might solve this issue. For that I wrote a tread extension class which allows Invoked calls in several ways. Some code parts are from the web and some I developed by myself. For those which are still on WinForms this code part might help to synchronize data with controls.

    /// <summary>
    ///   CThreadExtensions class
    /// </summary>
    public static class CThreadExtensions
    {
        #region Nested types

        private delegate void SetPropertyValueCallback<ControlType, PropertyType>(
            ControlType control, string propertyName, PropertyType value) where ControlType : Control;

        #endregion

        #region Delegates

        /// <summary>
        ///   Func
        /// </summary>
        public delegate void Func();

        #endregion

        #region Methods

        /// <summary>
        ///   Thread safe control changes in WinForms with Invoke.
        ///   Example:
        ///     _control.ExecuteThreadSafe(ThreadSafeVoid);
        /// </summary>
        /// <param name="control"> </param>
        /// <param name="action"> </param>
        public static void ExecuteThreadSafe(this Control control, Action action)
        {
            if (control.InvokeRequired)
            {
                control.Invoke(action);
            }
            else
            {
                action.Invoke();
            }
        }

        /// <summary>
        ///   Thread safe control changes in WinForms with Invoke.
        ///   Example:
        ///     CThreadExtensions.Invoke(_control, new DelegateThreadSafeVoid(ThreadSaveVoid));
        /// </summary>
        /// <param name="controlToInvokeOn"> </param>
        /// <param name="code"> </param>
        public static void Invoke(this Control controlToInvokeOn, Func code)
        {
            if (controlToInvokeOn.InvokeRequired)
            {
                controlToInvokeOn.Invoke(code);
            }
            else
            {
                code();
            }
        }

        /// <summary>
        ///   Thread safe control changes in WinForms with Invoke, using the Invoke
        ///   method with arguments.
        ///   Example:
        ///     CThreadExtensions.Invoke(_control, new DelegateThreadSafeVoid(ThreadSaveVoid),
        ///                              new object[] { param1, param2, param3 });
        /// </summary>
        /// <param name="controlToInvokeOn"> </param>
        /// <param name="method"> </param>
        /// <param name="args"> </param>
        public static void Invoke(this Control controlToInvokeOn, Delegate method, params object[] args)
        {
            if (controlToInvokeOn.InvokeRequired)
            {
                if (controlToInvokeOn.IsHandleCreated)
                {
                    controlToInvokeOn.Invoke(method, args);
                }
            }
            else
            {
                method.DynamicInvoke(args);
            }
        }

        /// <summary>
        ///   Thread safe control changes in WinForms with Invoke
        ///   Example:
        ///     _control.invoke(() => ThreadSafeVoid);
        /// </summary>
        /// <typeparam name="T"> </typeparam>
        /// <typeparam name="TResult"> </typeparam>
        /// <param name="controlToInvokeOn"> </param>
        /// <param name="code"> </param>
        /// <returns> </returns>
        public static TResult Invoke<T, TResult>(this T controlToInvokeOn, Func<TResult> code) where T : Control
        {
            if (controlToInvokeOn.InvokeRequired)
            {
                return (TResult) controlToInvokeOn.Invoke(code);
            }
            else
            {
                return code();
            }
        }

        /// <summary>
        ///   Thread safe control changes in WinForms with Invoke
        ///   Example:
        ///     CThreadExtensions.SetPropertyThreadSafe(_control, "Text", "Hello world");
        /// </summary>
        /// <typeparam name="ControlType"> </typeparam>
        /// <typeparam name="PropertyType"> </typeparam>
        /// <param name="control"> </param>
        /// <param name="propertyName"> </param>
        /// <param name="value"> </param>
        public static void SetPropertyThreadSafe<ControlType, PropertyType>(ControlType control, string propertyName,
                                                                            PropertyType value)
            where ControlType : Control
        {
            try
            {
                if (control.InvokeRequired)
                {
                    SetPropertyValueCallback<ControlType, PropertyType> cb = SetPropertyThreadSafe;
                    control.Invoke(cb, new object[] {control, propertyName, value});
                }
                else
                {
                    PropertyInfo property = control.GetType().GetProperty(propertyName);
                    property.SetValue(control, value, null);
                }
            }
            catch (Exception)
            {
            }
        }

        #endregion
    }

 

Related Articles