Tutorial :Silverlight DataBinding cross thread issue



Question:

I have an Image control with it's source bound to a property on an object(string url to an image). After making a service call, i update the data object with a new URL. The exception is thrown after it leaves my code, after invoking the PropertyChanged event.

The data structure and the service logic are all done in a core dll that has no knowledge of the UI. How do I sync up with the UI thread when i cant access a Dispatcher?

PS: Accessing Application.Current.RootVisual in order to get at a Dispatcher is not a solution because the root visual is on a different thread(causing the exact exception i need to prevent).

PPS: This only is a problem with the image control, binding to any other ui element, the cross thread issue is handled for you.


Solution:1

System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => {...});  

Also look here.


Solution:2

Have you tried implementing INotifyPropertyChanged?


Solution:3

The property getter for RootVisual on the Application class has a thread check which causes that exception. I got around this by storing the root visual's dispatcher in my own property in my App.xaml.cs:

public static Dispatcher RootVisualDispatcher { get; set; }    private void Application_Startup(object sender, StartupEventArgs e)  {      this.RootVisual = new Page();      RootVisualDispatcher = RootVisual.Dispatcher;  }  

If you then call BeginInvoke on App.RootVisualDispatcher rather than Application.Current.RootVisual.Dispatcher you shouldn't get this exception.


Solution:4

I ran into a similar issue to this, but this was in windows forms:

I have a class that has it's own thread, updating statistics about another process, there is a control in my UI that is databound to this object. I was running into cross-thread call issues, here is how I resolved it:

Form m_MainWindow; //Reference to the main window of my application  protected virtual void OnPropertyChanged(string propertyName)  {    if(PropertyChanged != null)      if(m_MainWindow.InvokeRequired)        m_MainWindow.Invoke(          PropertyChanged, this, new PropertyChangedEventArgs(propertyName);      else        PropertyChanged(this, new PropertyChangedEventArgs(propertyName);  }  

This seems to work great, if anyone has suggestions, please let me know.


Solution:5

When ever we want to update UI related items that action should happen in the UI thread else you will get an invalid cross thread access exception

Deployment.Current.Dispatcher.BeginInvoke( () =>    {    UpdateUI(); // DO the actions in the function Update UI    });    public void UpdateUI()    {    //to do :Update UI elements here    }  


Solution:6

The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.

For example, consider a Person object with a property called FirstName. To provide generic property-change notification, the Person type implements the INotifyPropertyChanged interface and raises a PropertyChanged event when FirstName is changed.

For change notification to occur in a binding between a bound client and a data source, your bound type should either:

Implement the INotifyPropertyChanged interface (preferred).

Provide a change event for each property of the bound type.

Do not do both.

Example:

using System;  using System.Collections.Generic;  using System.ComponentModel;  using System.Drawing;  using System.Runtime.CompilerServices;  using System.Windows.Forms;    // Change the namespace to the project name.   namespace TestNotifyPropertyChangedCS  {      // This form demonstrates using a BindingSource to bind       // a list to a DataGridView control. The list does not       // raise change notifications. However the DemoCustomer type        // in the list does.       public partial class Form1 : Form      {          // This button causes the value of a list element to be changed.           private Button changeItemBtn = new Button();            // This DataGridView control displays the contents of the list.           private DataGridView customersDataGridView = new DataGridView();            // This BindingSource binds the list to the DataGridView control.           private BindingSource customersBindingSource = new BindingSource();            public Form1()          {              InitializeComponent();                // Set up the "Change Item" button.              this.changeItemBtn.Text = "Change Item";              this.changeItemBtn.Dock = DockStyle.Bottom;              this.changeItemBtn.Click +=                  new EventHandler(changeItemBtn_Click);              this.Controls.Add(this.changeItemBtn);                // Set up the DataGridView.              customersDataGridView.Dock = DockStyle.Top;              this.Controls.Add(customersDataGridView);                this.Size = new Size(400, 200);          }            private void Form1_Load(object sender, EventArgs e)          {              // Create and populate the list of DemoCustomer objects               // which will supply data to the DataGridView.              BindingList<DemoCustomer> customerList = new BindingList<DemoCustomer>();              customerList.Add(DemoCustomer.CreateNewCustomer());              customerList.Add(DemoCustomer.CreateNewCustomer());              customerList.Add(DemoCustomer.CreateNewCustomer());                // Bind the list to the BindingSource.               this.customersBindingSource.DataSource = customerList;                // Attach the BindingSource to the DataGridView.               this.customersDataGridView.DataSource =                  this.customersBindingSource;            }            // Change the value of the CompanyName property for the first            // item in the list when the "Change Item" button is clicked.          void changeItemBtn_Click(object sender, EventArgs e)          {              // Get a reference to the list from the BindingSource.              BindingList<DemoCustomer> customerList =                  this.customersBindingSource.DataSource as BindingList<DemoCustomer>;                // Change the value of the CompanyName property for the                // first item in the list.              customerList[0].CustomerName = "Tailspin Toys";              customerList[0].PhoneNumber = "(708)555-0150";          }        }        // This is a simple customer class that        // implements the IPropertyChange interface.       public class DemoCustomer : INotifyPropertyChanged      {          // These fields hold the values for the public properties.           private Guid idValue = Guid.NewGuid();          private string customerNameValue = String.Empty;          private string phoneNumberValue = String.Empty;            public event PropertyChangedEventHandler PropertyChanged;            // This method is called by the Set accessor of each property.           // The CallerMemberName attribute that is applied to the optional propertyName           // parameter causes the property name of the caller to be substituted as an argument.           private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")          {              if (PropertyChanged != null)              {                  PropertyChanged(this, new PropertyChangedEventArgs(propertyName));              }          }            // The constructor is private to enforce the factory pattern.           private DemoCustomer()          {              customerNameValue = "Customer";              phoneNumberValue = "(312)555-0100";          }            // This is the public factory method.           public static DemoCustomer CreateNewCustomer()          {              return new DemoCustomer();          }            // This property represents an ID, suitable           // for use as a primary key in a database.           public Guid ID          {              get              {                  return this.idValue;              }          }            public string CustomerName          {              get              {                  return this.customerNameValue;              }                set              {                  if (value != this.customerNameValue)                  {                      this.customerNameValue = value;                      NotifyPropertyChanged();                  }              }          }            public string PhoneNumber          {              get              {                  return this.phoneNumberValue;              }                set              {                  if (value != this.phoneNumberValue)                  {                      this.phoneNumberValue = value;                      NotifyPropertyChanged();                  }              }          }      }  }  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »