1. 程式人生 > >NotifyPropertyChanged執行緒與介面繫結非同步更新

NotifyPropertyChanged執行緒與介面繫結非同步更新

         為了解決NotifyPorpertyChanged的執行緒與介面繫結非同步更新衝突問題,所以檢視相關資料後,將自己的NotifyPropertyChanged作了改進。

1.問題起因

        在開發C#應用程式,通常後使用到介面控制元件特定屬性(Enable | Text)與指定的類成員繫結。但如果指定類成員變數線上程中和介面需非同步更新時,會出現異常情況(介面顯示異常)。

以下程式碼未作非同步更新,示例如下:  

  public class TubeConsumedRecord : INotifyPropertyChanged
    {

        /// <summary>
        /// The lock tube
        /// </summary>
        private object lockTube;
        /// <summary>
        /// The available count
        /// </summary>
        private int availableCount;
        /// <summary>
        /// 在屬性值更改時發生。
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Gets or sets the available tube count.
        /// </summary>
        /// <value>The available tube count.</value>
        public int AvailableTubeCount
        {
            get
            {
                return availableCount;
            }

            set
            {
                if (this.availableCount == value)
                {
                    return;
                }
                this.availableCount = value;
                OnPropertyChanged(new PropertyChangedEventArgs("AvailableTubeCount"));
            }
        }
        /// <summary>
        /// Handles the <see cref="E:PropertyChanged" /> event.
        /// </summary>
        /// <param name="e">The <see cref="PropertyChangedEventArgs"/> instance containing the event data.</param>
        private void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, e);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TubeConsumedRecord"/> class.
        /// </summary>
        public TubeConsumedRecord()
        {
            this.lockTube = new object();
        }

    }

2. 解決方法

為了解決屬性繫結的介面和執行緒非同步更新時衝突問題,對INotifyPropertyChanged作了相應的改造。

改造後的類AsynNotifyPropertyChanged, 直接繼承使用。

設定非同步上下文類為SynchronizationContextProvider,需要使用者設定介面的非同步上下文,在創造主窗體Main()中新增。

 SynchronizationContextProvider.CreateSynchronizationContext(WindowsFormsSynchronizationContext.Current);

AsynNotifyPropertyChanged程式碼如下:

  public class AsynNotifyPropertyChanged : INotifyPropertyChanged
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="AsyncBindingData"/> class.
        /// </summary>
        public AsynNotifyPropertyChanged()
        {
        }

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Called when [property changed].
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        public void OnPropertyChanged(string propertyName)
        {
            
            if (this.PropertyChanged == null||string.IsNullOrEmpty(propertyName))
            {
                return;
            }
        
            if (SynchronizationContextProvider.UISynchronizationContext != null)
            {
                SynchronizationContextProvider.UISynchronizationContext.Send(new SendOrPostCallback(AysnPropertyChanged), propertyName);
            }
            else
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private void AysnPropertyChanged(object state)
        {
            string propertyName = state.ToString();
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }


        public void OnPropertyChanged(object sender, PropertyChangedEventHandler propertyChanged, string propertyName)
        {
            if (propertyChanged == null || string.IsNullOrEmpty(propertyName))
            {
                return;
            }

            if (SynchronizationContextProvider.UISynchronizationContext != null)
            {
                SynchronizationContextProvider.UISynchronizationContext.Send(new SendOrPostCallback(AysnPropertyChanged), propertyName);
            }
            else
            {
                propertyChanged(sender, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

SynchronizationContextProvider類程式碼如下:

  public  class SynchronizationContextProvider
    {
        /// <summary>
        /// The current
        /// </summary>
        private static SynchronizationContext current;

        /// <summary>
        /// Gets the UI synchronization context.
        /// </summary>
        /// <value>The UI synchronization context.</value>
        internal static SynchronizationContext UISynchronizationContext
        {
            get
            {
                if(SynchronizationContext.Current==null)
                {
                    SynchronizationContext.SetSynchronizationContext(current);
                }

                return current; 
            }
        }

        /// <summary>
        /// Creates the synchronization context.
        /// </summary>
        /// <param name="context">The context.</param>
        public static void CreateSynchronizationContext(SynchronizationContext context)
        {
            current = context;
        }
    }