none
c# WPF 在事件發生時 , 丟出 "發出呼叫的執行緒必須是 STA,因為許多 UI 元件都這樣要求。" RRS feed

  • 問題

  • 小弟目前在套用MVVM patten 到 WPF上

    目前Model 上的屬性 , 在事件發生時改變給控制項 , 已經避免 "目前執行緒被占用的問題"

    但是如果在事件發生時 , 例如增加TextBlock 到UniformGrid 中 以下是View程式片段

    public test_View()
    {
       InitializeComponent();
    
       this.Model = viewModel;
       this.Model.View = this;
       NetworkChange.NetworkAddressChanged += new   NetworkAddressChangedEventHandler(NetworkChange_NetworkAddressChanged);
    }
    
    void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
    {
       AddStation();
    }
    public test_ViewModel Model
    {
       get
       {
         return this.DataContext as test_ViewModel;
       }
       set
       {
         this.DataContext = value;
       }
    }
    public void AddStation()
    {
       StackPanel sp = new StackPanel { HorizontalAlignment = HorizontalAlignment.Left, Orientation = Orientation.Horizontal };
    
       sp.Children.Add(new TextBox { Width = 45, Margin = new Thickness(40, 4, 4, 4) });
    
       UniformGrid.Children.Add(sp);
    }

     如果是以按鍵 綁定 AddStation 去觸發增加TextBlock , 是沒有問題

    若是以網路事件啟動, 就會發生 "發出呼叫的執行緒必須是 STA,因為許多 UI 元件都這樣要求。"

    我找到方法 http://www.eggheadcafe.com/software/aspnet/31268816/the-calling-thread-must-be-sta.aspx

    是用委派處理

    那請問除了委派之外 , MVVM有其他方式在處理這類事件動態改變控制像的解決方式

    感謝解答

    2010年10月15日 上午 06:51

所有回覆

  • 參考一下這篇文章
    http://www.codeproject.com/Articles/51457/Synchronous-Invocation-of-Delegates-with-the-Silve.aspx

    雖然內容是針對Silverlight但對於WPF其實也大同小異.
    不外乎就是透過
    1.Dispatcher機制,不過必須Dispatcher屬於UI,在非UI部分需要另外處理.
    2.透過SynchronizationContext,這是比較好的做法SynchronizationContext類別的目的就是記錄當前Thread的執行狀態,把SynchronizationContext當作參數傳遞給其他Thread後,騎他的Thread就能靠SynchronizationContext切換回原來的Thread的狀態做處理.
    底下程式碼可套用於Silverlight與WPF,是這篇文章的修改版本.

    透過底下這種方式就能隨時將目前程式碼狀態切為主UI Thread

     UISynchronizationContext.Instance.InvokeSynchronously(
    () => {
    //這區塊的程式碼都是在UI Thread執行.
    });
    

     

    public interface ISynchronizationContext
    	{
    		/// <summary>
    		/// Invokes the specified callback asynchronously.
    		/// Method returns immediately upon queuing the request.
    		/// </summary>
    		/// <param name="callback">The delegate to invoke.</param>
    		/// <param name="state">The state to pass to the invocation.</param>
    		void InvokeWithoutBlocking(SendOrPostCallback callback, object state);
    
    		/// <summary>
    		/// Invokes the specified callback asynchronously.
    		/// Method returns immediately upon queuing the request.
    		/// </summary>
    		/// <param name="action">The delegate to invoke.</param>
    		void InvokeWithoutBlocking(Action action);
    
    		/// <summary>
    		/// Invokes the specified callback synchronously.
    		/// Method blocks until the specified callback completes.
    		/// </summary>
    		/// <param name="callback">The delegate to invoke.</param>
    		/// <param name="state">The state to pass to the invocation.</param>
    		void InvokeAndBlockUntilCompletion(SendOrPostCallback callback, object state);
    
    		/// <summary>
    		/// Invokes the specified callback synchronously.
    		/// Method blocks until the specified callback completes.
    		/// </summary>
    		/// <param name="action">The delegate to invoke.</param>
    		void InvokeAndBlockUntilCompletion(Action action);
    
    		/// <summary>
    		/// Gets a value indicating whether invocation is required.
    		/// That is, it determines whether the call was made from a thread other 
    		/// than the one that the current instance was created on.
    		/// </summary>
    		/// <value><c>true</c> if the calling thread was not the thread that the current instance 
    		/// was initialized on; otherwise, <c>false</c>.</value>
    		bool InvokeRequired { get; }
    
    		/// <summary>
    		/// Initializes this instance.
    		/// </summary>
    		void Initialize();
    
    		/// <summary>
    		/// Initializes this instance using the specified dispatcher.
    		/// </summary>
    		/// <param name="dispatcher">The dispatcher used to provide synchronization.</param>
    		void Initialize(Dispatcher dispatcher);
    	}
    
    	public partial class UISynchronizationContext : ISynchronizationContext
    	{    
    		DispatcherSynchronizationContext context;
    		Dispatcher dispatcher;
    
    		#region Singleton implementation
    
    		static readonly UISynchronizationContext instance = new UISynchronizationContext();
    
    		/// <summary>
    		/// Gets the singleton instance.
    		/// </summary>
    		/// <value>The singleton instance.</value>
    		public static ISynchronizationContext Instance
    		{
    			get
    			{        
    				return instance;
    			}
    		}
    
    		UISynchronizationContext()
    		{
    			/* Intentionally left blank. */
    		}
    
    		#endregion
    
    		public void Initialize()
    		{
    			EnsureInitialized();
    		}
    
    		readonly object initializationLock = new object();
    
    		void EnsureInitialized()
    		{
    			if (dispatcher != null && context != null)
    			{
    				return;
    			}
    
    			lock (initializationLock)
    			{
    				if (dispatcher != null && context != null)
    				{
    					return;
    				}
    
    				try
    				{
    #if SILVERLIGHT
    					dispatcher = System.Windows.Deployment.Current.Dispatcher;
    #else
    					dispatcher = Dispatcher.CurrentDispatcher;
    #endif
    					context = new DispatcherSynchronizationContext(dispatcher);
    				}
    				catch (InvalidOperationException)
    				{
    					/* TODO: Make localizable resource. */
    					throw new Exception("Initialised called from non-UI thread.");
    				}
    			}
    		}
    
    		public void Initialize(Dispatcher dispatcher)
    		{
    			ArgumentValidator.AssertNotNull(dispatcher, "dispatcher");
    			lock (initializationLock)
    			{
    				this.dispatcher = dispatcher;
    				context = new DispatcherSynchronizationContext(dispatcher);
    			}
    		}
    
    		public void InvokeWithoutBlocking(SendOrPostCallback callback, object state)
    		{
    			ArgumentValidator.AssertNotNull(callback, "callback");
    			EnsureInitialized();
    
    			context.Post(callback, state);
    		}
    
    		public void InvokeWithoutBlocking(Action action)
    		{
    			ArgumentValidator.AssertNotNull(action, "action");
    			EnsureInitialized();
    
    			context.Post(state => action(), null);
    			//			if (dispatcher.CheckAccess())
    			//			{
    			//				action();
    			//			}
    			//			else
    			//			{
    			//				dispatcher.BeginInvoke(action);
    			//			}
    		}
    
    		public void InvokeAndBlockUntilCompletion(SendOrPostCallback callback, object state)
    		{
    			ArgumentValidator.AssertNotNull(callback, "callback");
    			EnsureInitialized();
    
    			context.Send(callback, state);
    		}
    
    		public void InvokeAndBlockUntilCompletion(Action action)
    		{
    			ArgumentValidator.AssertNotNull(action, "action");
    			EnsureInitialized();
    
    			if (dispatcher.CheckAccess())
    			{
    				action();
    			}
    			else
    			{
    				context.Send(delegate { action(); }, null);
    			}
    		}
    
    		public bool InvokeRequired
    		{
    			get
    			{
    				EnsureInitialized();
    				return !dispatcher.CheckAccess();
    			}
    		}
    	}
    
    
    
    	public static class ArgumentValidator
    	{
    		/// <summary>
    		/// Ensures the specified value is not null.
    		/// </summary>
    		/// <typeparam name="T">The type of the value.</typeparam>
    		/// <param name="value">The value to test.</param>
    		/// <param name="parameterName">Name of the parameter.</param>
    		/// <returns>The specified value.</returns>
    		/// <exception cref="ArgumentNullException">Occurs if the specified value 
    		/// is <code>null</code>.</exception>
    		public static T AssertNotNull<T>(T value, string parameterName) where T : class
    		{
    			if (value == null)
    			{
    				throw new ArgumentNullException(parameterName);
    			}
    
    			return value;
    		}
    
    		/// <summary>
    		/// Ensures the specified value is not <code>null</code> or empty (a zero length string).
    		/// </summary>
    		/// <param name="value">The value to test.</param>
    		/// <param name="parameterName">Name of the parameter.</param>
    		/// <returns>The specified value.</returns>
    		/// <exception cref="ArgumentNullException">Occurs if the specified value 
    		/// is <code>null</code> or empty (a zero length string).</exception>
    		public static string AssertNotNullOrEmpty(string value, string parameterName)
    		{
    			if (value == null)
    			{
    				throw new ArgumentNullException(parameterName);
    			}
    
    			if (value.Length < 1)
    			{
    				throw new ArgumentException("Parameter should not be an empty string.", parameterName); /* TODO: Make localizable resource. */
    			}
    
    			return value;
    		}
    
    		/// <summary>
    		/// Ensures the specified value is not <code>null</code> 
    		/// and that it is of the specified type.
    		/// </summary>
    		/// <param name="value">The value to test.</param> 
    		/// <param name="parameterName">The name of the parameter.</param>
    		/// <returns>The value to test.</returns>
    		/// <exception cref="ArgumentNullException">Occurs if the specified value 
    		/// is <code>null</code> or of type not assignable from the specified type.</exception>
    		/// <example>
    		/// public DoSomething(object message)
    		/// {
    		/// 	this.message = ArgumentValidator.AssertNotNullAndOfType&lt;string&gt;(message, "message");	
    		/// }
    		/// </example>
    		public static T AssertNotNullAndOfType<T>(object value, string parameterName) where T : class
    		{
    			if (value == null)
    			{
    				throw new ArgumentNullException(parameterName);
    			}
    			var result = value as T;
    			if (result == null)
    			{
    				throw new ArgumentException(string.Format(
    					"Expected argument of type " + typeof(T) + ", but was " + value.GetType(), typeof(T), value.GetType()),
    					parameterName);
    			}
    			return result;
    		}
    
    		/* TODO: [DV] Comment. */
    		public static int AssertGreaterThan(int value, int greaterThan, string parameterName)
    		{
    			if (value < greaterThan)
    			{
    				throw new ArgumentOutOfRangeException("Parameter should be greater than " + greaterThan, parameterName); /* TODO: Make localizable resource. */
    			}
    			return value;
    		}
    
    		/* TODO: [DV] Comment. */
    		public static double AssertGreaterThan(double value, double mustBeGreaterThan, string parameterName)
    		{
    			if (value < mustBeGreaterThan)
    			{
    				throw new ArgumentOutOfRangeException("Parameter should be greater than " + mustBeGreaterThan, parameterName); /* TODO: Make localizable resource. */
    			}
    			return value;
    		}
    
    		/* TODO: [DV] Comment. */
    		public static double AssertLessThan(double value, double mustBeLessThan, string parameterName)
    		{
    			if (value > mustBeLessThan)
    			{
    				throw new ArgumentOutOfRangeException("Parameter should be less than " + mustBeLessThan, parameterName); /* TODO: Make localizable resource. */
    			}
    			return value;
    		}
    
    		/* TODO: [DV] Comment. */
    		public static long AssertGreaterThan(long value, long mustBeGreaterThan, string parameterName)
    		{
    			if (value < mustBeGreaterThan)
    			{
    				throw new ArgumentOutOfRangeException("Parameter should be greater than " + mustBeGreaterThan, parameterName); /* TODO: Make localizable resource. */
    			}
    			return value;
    		}
    	}
    
    2010年10月15日 上午 07:13