Перекрестная проблема Silverlight DataBinding - PullRequest
6 голосов
/ 06 сентября 2008

У меня есть элемент управления Image, источник которого привязан к свойству объекта (строковый URL к изображению). После вызова службы я обновляю объект данных новым URL. Исключение выдается после того, как оно покидает мой код, после вызова события PropertyChanged.

Структура данных и логика обслуживания выполнены в ядре dll, которое не знает интерфейса пользователя. Как мне синхронизировать с потоком пользовательского интерфейса, когда я не могу получить доступ к Dispatcher?

PS: доступ к Application.Current.RootVisual для получения доступа к Dispatcher не является решением, поскольку корневой визуальный элемент находится в другом потоке (что вызывает точное исключение, которое мне нужно предотвратить).

PPS: Это только проблема с контролем изображения, привязкой к любому другому элементу пользовательского интерфейса, проблема с перекрестными потоками решается за вас.

Ответы [ 6 ]

7 голосов
/ 18 декабря 2009
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => {...});

Также смотрите здесь.

1 голос
/ 06 сентября 2008

Вы пытались реализовать INotifyPropertyChanged ?

0 голосов
/ 15 августа 2013

Интерфейс INotifyPropertyChanged используется для уведомления клиентов, обычно привязывающих клиентов, об изменении значения свойства.

Например, рассмотрим объект Person со свойством FirstName. Чтобы предоставить общее уведомление об изменении свойства, тип Person реализует интерфейс INotifyPropertyChanged и вызывает событие PropertyChanged при изменении FirstName.

Чтобы уведомление об изменении происходило в привязке между связанным клиентом и источником данных, тип привязки должен:

Реализация интерфейса INotifyPropertyChanged (предпочтительно).

Укажите событие изменения для каждого свойства связанного типа.

Не делайте обоих.

Пример:

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();
                }
            }
        }
    }
}
0 голосов
/ 05 мая 2013

Когда мы хотим обновить элементы, связанные с пользовательским интерфейсом, действие должно происходить в потоке пользовательского интерфейса, в противном случае вы получите недопустимое исключение доступа к многопоточному потоку

Deployment.Current.Dispatcher.BeginInvoke( () =>

{

UpdateUI(); // DO the actions in the function Update UI

});

public void UpdateUI()

{

//to do :Update UI elements here

}
0 голосов
/ 11 августа 2009

Я столкнулся с подобной проблемой, но это было в формах Windows:

У меня есть класс, у которого есть собственный поток, обновляющий статистику о другом процессе, в моем пользовательском интерфейсе есть элемент управления, связанный с данным объектом. Я столкнулся с проблемами вызова между потоками, вот как я решил это:

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);
}

Кажется, это работает отлично, если у кого-то есть предложения, пожалуйста, дайте мне знать.

0 голосов
/ 01 января 2009

Получатель свойства для RootVisual класса Application имеет проверку потока, которая вызывает это исключение. Я справился с этим, сохранив диспетчер корневого визуала в своем собственном свойстве в файле App.xaml.cs:

public static Dispatcher RootVisualDispatcher { get; set; }

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

Если вы затем вызываете BeginInvoke для App.RootVisualDispatcher, а не для Application.Current.RootVisual.Dispatcher, вы не должны получать это исключение.

...