UI线程访问外部控件

时间:2011-07-04 09:09:02

标签: .net multithreading ui-thread

我有一个Session对象来管理对象状态(类似于nhibernate会话)。此会话侦听来自外部源的事件,该事件可能需要更新内部会话状态。我们已经尝试在会话中实现锁定,以确保访问的数据是连贯的,但是有很多边缘情况。

相反,将这些事件编组到与使用会话(即UI线程)相同的线程上可能更容易。通常,这是使用Control.Invoke()完成的,但由于这是一个数据对象,因此无法访问Control

这是一种合理的方法吗?如何在更新会话状态之前将此事件发送到UI线程?我可以使用Dispatcher并在创建会话时捕获当前线程的调度程序吗?

2 个答案:

答案 0 :(得分:1)

我会让业务对象触发一个事件被视图(UI)捕获并对该事件处理程序进行编组,因此您可以在该位置使用Control来了解是否需要调用或不:

public static class ControlExtentions
    {
        public delegate void InvokeHandler();
        public static bool SafeInvoke(this Control control, InvokeHandler handler)
        {
            if (control.InvokeRequired)
            {
                try
                {
                    control.Invoke(handler);
                }
                finally { }
                return false;
            }
            else
                handler.Invoke();
            return true;
        }

    }

如果你使用WPF,你可以从CaliburnMicro获取灵感:

public static class Execute
    {
        private static Action<System.Action> executor = action => action();

        /// <summary>
        /// Initializes the framework using the current dispatcher.
        /// </summary>
        public static void InitializeWithDispatcher()
        {
#if SILVERLIGHT
            var dispatcher = Deployment.Current.Dispatcher;

            executor = action => {
                if(dispatcher.CheckAccess())
                    action();
                else {
                    var waitHandle = new ManualResetEvent(false);
                    Exception exception = null;
                    dispatcher.BeginInvoke(() => {
                        try {
                            action();
                        }
                        catch(Exception ex) {
                            exception = ex;
                        }
                        waitHandle.Set();
                    });
                    waitHandle.WaitOne();
                    if(exception != null)
                        throw new TargetInvocationException("An error occurred while dispatching a call to the UI Thread", exception);
                }
            };
#else
            var dispatcher = Dispatcher.CurrentDispatcher;

            executor = action => {
                if(dispatcher.CheckAccess())
                    action();
                else dispatcher.Invoke(action);
            };
#endif

        }

        /// <summary>
        /// Resets the executor to use a non-dispatcher-based action executor.
        /// </summary>
        public static void ResetWithoutDispatcher() {
            executor = action => action();
        }

        /// <summary>
        /// Executes the action on the UI thread.
        /// </summary>
        /// <param name="action">The action to execute.</param>
        public static void OnUIThread(this System.Action action) {
            executor(action);
        }
    }

答案 1 :(得分:1)

我认为锁定通常非常简单,可能是您的最佳选择。正确实现你想要的东西可能要困难得多(当然也不是没有“繁琐的边缘情况”)。

你可以做的是使用.Net 4中的BlockingCollection<T>的现有实现,并在单线程中从它中出列。不幸的是,该线程将被阻止,因此您无法使用UI线程。