INotifyPropertyChanged causes a crossflow error

Here is my scenario:

I have a GridControl related to a BindingList. At first I was involved in creating a workflow and directly accessed the BindingList, but this caused a “cross-thread operation”, so I followed the guide here:

http://www.devexpress.com/Support/Center/p/AK2981.aspx

By cloning the original BindingList into a workflow and changing it, I got the desired effect. However, I recently injected INotifyPropertyChanged into an object that is stored in a BindingList, and I started getting an error again.

I assume that the GridView is still listening to INotifyPropertyChanged from the object.

How can i fix this?

My class:

public class Proxy : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
+5
source share
4 answers

- (, ), . , Invoke . , , InvokeRequired.

:

public void ChangeText(string text)
{
   if(this.InvokeRequired)
   {
      this.Invoke(new Action(() => ChangeText(text)));
   }
   else
   {
      label.Text = text;  
   }
}

INotifyPropertyChanged, , ( ), INotifyPropertyChanged. . , ChangeProperty .

Entity. , . , , , , DynamicObject, .

public class NotificationHelper : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private readonly ISynchronizeInvoke invokeDelegate;
    private readonly Entity entity;

    public NotificationHelper(ISynchronizeInvoke invokeDelegate, Entity entity)
    {
       this.invokeDelegate = invokeDelegate;
       this.entity = entity;

       entity.PropertyChanged += OnPropertyChanged;
    }

    public string Name
    {
       get { return entity.Name; }
    }

    private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
        {
           if (invokeDelegate.InvokeRequired)
           {
               invokeDelegate.Invoke(new PropertyChangedEventHandler(OnPropertyChanged),
                                     new[] { sender, e });
               return;
           }
           PropertyChanged(this, e);
        }
     }
 }
+10

TheGateKeeper. . . , ICustomTypeDescriptor. , , .

public class SynchronizedNotifyPropertyChanged<T> : INotifyPropertyChanged, ICustomTypeDescriptor
    where T : INotifyPropertyChanged
{
    private readonly T _source;
    private readonly ISynchronizeInvoke _syncObject;

    public SynchronizedNotifyPropertyChanged(T source, ISynchronizeInvoke syncObject)
    {
        _source = source;
        _syncObject = syncObject;

        _source.PropertyChanged += (sender, args) => OnPropertyChanged(args.PropertyName);
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged == null) return;

        var handler = PropertyChanged;
        _syncObject.BeginInvoke(handler, new object[] { this, new PropertyChangedEventArgs(propertyName) });
    }

    public T Source { get { return _source; }}

    #region ICustomTypeDescriptor
    public AttributeCollection GetAttributes()
    {
        return new AttributeCollection(null);
    }

    public string GetClassName()
    {
        return TypeDescriptor.GetClassName(typeof(T));
    }

    public string GetComponentName()
    {
        return TypeDescriptor.GetComponentName(typeof (T));
    }

    public TypeConverter GetConverter()
    {
        return TypeDescriptor.GetConverter(typeof (T));
    }

    public EventDescriptor GetDefaultEvent()
    {
        return TypeDescriptor.GetDefaultEvent(typeof (T));
    }

    public PropertyDescriptor GetDefaultProperty()
    {
        return TypeDescriptor.GetDefaultProperty(typeof(T));
    }

    public object GetEditor(Type editorBaseType)
    {
        return TypeDescriptor.GetEditor(typeof (T), editorBaseType);
    }

    public EventDescriptorCollection GetEvents()
    {
        return TypeDescriptor.GetEvents(typeof(T));
    }

    public EventDescriptorCollection GetEvents(Attribute[] attributes)
    {
        return TypeDescriptor.GetEvents(typeof (T), attributes);
    }

    public PropertyDescriptorCollection GetProperties()
    {
        return TypeDescriptor.GetProperties(typeof (T));
    }

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        return TypeDescriptor.GetProperties(typeof(T), attributes);
    }

    public object GetPropertyOwner(PropertyDescriptor pd)
    {
        return _source;
    }
    #endregion ICustomTypeDescriptor
}

Ui , - :

    private void CreateBindings()
    {
        if (_model == null) return;

        var threadSafeModel = new SynchronizedNotifyPropertyChanged<MyViewModel>(_model, this);

        directiveLabel.DataBindings.Add("Text", threadSafeModel, "DirectiveText", false, DataSourceUpdateMode.OnPropertyChanged);
    }

MyViewModel "DirectiveText" INotifyPropertyChanged .

+2

- ... . :

, , INotifyPropertyChanged, , .

, , INotifyPropertyChanged, invoke .

:

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            //If the Proxy object is living in a non-UI thread, use invoke
            if (c != null)
            {
                c.BeginInvoke(new Action(() => handler(this, new PropertyChangedEventArgs(name))));
            }
            //Otherwise update directly
            else
            {
                handler(this, new PropertyChangedEventArgs(name));
            }

        }
    }

    //Use this to reference the object on the UI thread when there is need to
    public Control C
    {
        set { c = value; }
    }

, , :

                    prox.c = this;
                    //Logic here
                    prox.c = null;

, -!

+1

I subclassed BindingListto check required Invoke. Thus, my business objects do not have a user interface link.

public class InvokingBindingList<T> : BindingList<T>
{
  public InvokingBindingList(IList<T> list, Control control = null) : base(list)
  {
    this.Control = control;
  }

  public InvokingBindingList(Control control = null)
  {
    this.Control = control;
  }

  public Control Control { get; set; }

  protected override void OnListChanged(ListChangedEventArgs e)
  {
    if (Control?.InvokeRequired == true)
      Control.Invoke(new Action(() => base.OnListChanged(e)));
    else
      base.OnListChanged(e);
  }
}
0
source

All Articles