Thursday, February 23, 2012

Unauthorized access exception when value set for property using Secondary Thread - Silverlight

Thinking about threading in Silverlight 4, we choose between the two options
  1. Background worker
  2. Managing thread manually
Most of the time-consuming operations will be handled using the Background worker without blocking the UI. It ultimately executes in a separate thread. The main functions and events to know in Background worker are DoWork, RunWorkerCompleted and RunWorkerAsync. As the name specifies we need to add the event handler for DoWork event for doing the background operation. Add the handler for RunWorkerCompleted to receive the operation completed notification.  RunWorkerAsync method is to start the operation. The main thing to note is that we should not access directly access the UI objects from the DoWork event.

Also there are times where we need to manage the threads manually. Since Silverlight runs in a Single threaded apartment (STA) application model and the default is the UI thread, accessing the UI objects from other thread leads to cross thread exception. Knowing this fact I worked with a secondary thread and updated a property in my ViewModel. In that scenario I got an exception and digging into the inner exception found out the cross thread exception. For understanding I've created a sample scenario that raises exception.

Snippet 1

public partial class ThreadTest : UserControl, INotifyPropertyChanged
{        
    private string textValue;
    public string TextValue
    {
        get { return textValue; }
        set
        {
            textValue = value;
            NotifyPropertyChanged("TextValue");
        }
    }

    public ThreadTest()
    {
        InitializeComponent();

        InitControls();
    }

    private void InitControls()
    {
        this.DataContext = this;
    }

    private void Direct_Click(object sender, RoutedEventArgs e)
    {
        TextValue = "direct text......";
    }

    private void UsingTask_Click(object sender, RoutedEventArgs e)
    {
        Thread secondaryThread = new Thread(SecondaryThreadWorker);
        secondaryThread.Start();
        
    }

    private void SecondaryThreadWorker()
    {
        TextValue = "thread text.......";            
    }


    #region INotifyPropertyChanged Members

    protected void NotifyPropertyChanged(string propertyName)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

Then examining the cause, realized that the specific property raises an property changed notification . Immediately recalled the excellent article from Pete Brown in his 10rem.net blog.

The following implementation does the trick.

Snippet 2 (Just replace the INotifyPropertyChanged implementation)

#region INotifyPropertyChanged Members

protected void NotifyPropertyChanged(string propertyName)
{
    if (PropertyChanged != null)
    {
        if (Deployment.Current.Dispatcher.CheckAccess())
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        else
        {
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            });
        }
    }
}

public event PropertyChangedEventHandler PropertyChanged;

#endregion


When I typed "Deployment.Current.Dispatcher.", it didn't show up the "CheckAccess()" method in the intellisense. But I verified it in the object which shows the existence of the method. So I manually typed the  "CheckAccess()" method name and got complied without error.

References
http://10rem.net/blog/2012/01/10/threading-considerations-for-binding-and-change-notification-in-silverlight-5




1 comment:

  1. Thank you a lot. I`ve saved a week.

    ReplyDelete

Creative Commons License
This work by Tito is licensed under a Creative Commons Attribution 3.0 Unported License.