2012-02-24 62 views
3

我在我的應用程序中實現了ICustomTypeDescriptor,以便能夠在運行時定義自定義屬性。我的基本實現看起來像:ICustomTypeDescriptor包裝對象

public class DynamicClass <T> : ICustomTypeDescriptor 
{ 
    private readonly T _object; 

    public DynamicClass(T trackedObject) 
    { 
     _object = trackedObject; 
    } 

    // Collection to code add dynamic properties 
    public KeyedCollection<string, DynamicProperty> Properties 
    { 
     get; 
     private set; 
    } 

    // ICustomTypeDescriptor implementation 
    public AttributeCollection GetAttributes() 
    { 
     return TypeDescriptor.GetAttributes(_object, true); 
    } 

    public string GetClassName() 
    { 
     return TypeDescriptor.GetClassName(_object, true); 
    } 

    public string GetComponentName() 
    { 
     return TypeDescriptor.GetComponentName(_object, true); 
    } 

    public TypeConverter GetConverter() 
    { 
     return TypeDescriptor.GetConverter(_object, true); 
    } 

    public EventDescriptor GetDefaultEvent() 
    { 
     return TypeDescriptor.GetDefaultEvent(_object, true); 
    } 

    public PropertyDescriptor GetDefaultProperty() 
    { 
     return TypeDescriptor.GetDefaultProperty(_object, true); 
    } 

    public object GetEditor(Type editorBaseType) 
    { 
     throw new NotImplementedException(); 
    } 

    public EventDescriptorCollection GetEvents() 
    { 
     return TypeDescriptor.GetEvents(_object, true); 
    } 

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

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() 
    { 
     return TypeDescriptor.GetProperties(_object, true); 
    } 

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

    public object GetPropertyOwner(PropertyDescriptor pd) 
    { 
     return _object; 
    } 
} 

的問題是,現在,當我的對象綁定到使用DynamicClass粘合劑的文本框它不工作了。

我用這樣的:

DynamicClass<ExtensionModel> binder = new DynamicClass<ExtensionModel>(ext); 
_versionLabel.DataBindings.Add("Text", binder, "SelectedVersion", false, DataSourceUpdateMode.OnPropertyChanged); 

,我得到異常:「對象不匹配目標類型」

對象與目標類型不匹配。

在System.Reflection.RuntimeMethodInfo.CheckConsistency(對象 靶)在System.Reflection.RuntimeMethodInfo.Invoke在 (對象OBJ, 的BindingFlags invokeAttr,粘結劑粘結劑,對象[]參數, CultureInfo的文化,布爾skipVisibilityChecks) System.Reflection.RuntimeMethodInfo.Invoke在 System.ComponentModel(對象OBJ,的BindingFlags invokeAttr,粘結劑粘結劑,對象[]參數,CultureInfo的培養物)
在System.ComponentModel.ReflectEventDescriptor.AddEventHandler(對象 成分,代表值)。 ReflectPropertyDescriptor.AddValueChanged(Object componen噸,事件處理程序的處理程序)處 System.Windows.Forms.ListManagerBindingsCollection.AddCore在 System.Windows.Forms.Binding.SetListManager(BindingManagerBase bindingManagerBase System.Windows.Forms.BindToObject.CheckBinding())(綁定 數據綁定)在System.Windows.Forms.BindingsCollection.Add在 System.Windows.Forms.Control.UpdateBindings()

(綁定 結合)在 System.Windows.Forms.BindingContext.UpdateBinding(的BindingContext newBindingContext,裝訂裝訂)

該綁定工作,如果不是活頁夾,我使用ext對象。我在ICustomTypeDescriptor實現中遺漏了什麼?

+0

SelectedVersion屬性返回並位於DynamicClass類中的是什麼?你有沒有設法解決這個問題? – Martin 2012-03-29 21:08:44

回答

0

我已經設法在我的測試代碼中重現您的問題。我可以看到,如果你沒有在ExtensionModel上實現INotifyPropertyChanged,那麼它的工作原理!

所以你的ICustomTypeDescriptor的實現有些東西不能與實現INotifyPropertyChanged的屬性類一起工作。

這會起作用,但如果您取消註釋INotifyPropertyChange,它將會中斷。

public class BindingExample 
{ 
    public void Shows_Binding_To_A_Label_With_DynamicClass() 
    { 
     Form frm = new Form(); 
     Label _versionLabel = new Label(); 
     frm.Controls.Add(_versionLabel); 

     ExtensionModel ext = new ExtensionModel() { SelectedVersion = "DynamicClass Example" }; 
     DynamicClass<ExtensionModel> binder = new DynamicClass<ExtensionModel>(ext); 

     _versionLabel.DataBindings.Add("Text", binder, "SelectedVersion", false, DataSourceUpdateMode.OnPropertyChanged); 

     frm.ShowDialog(); 
    } 
} 

public class ExtensionModel// : INotifyPropertyChanged 
{ 
    string selectedVersion; 
    public string SelectedVersion 
    { 
     get { return selectedVersion; } 
     set 
     { 
      selectedVersion = value; 
      onPropertyChanged("SelectedVersion"); 
     } 
    } 

    void onPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
    public event PropertyChangedEventHandler PropertyChanged; 
} 

我想要這樣做,所以我會繼續玩它。

0

你必須用自定義的包裝原始描述符。以下是代碼:

#region IBindingProxy 

public interface IBindingProxy 
{ 
    void CheckAndNotify(); 
} 

#endregion 

public class BindingProxy : CustomTypeDescriptor, INotifyPropertyChanged, IBindingProxy 
{ 
    #region Static Constructor 

    private static readonly IDictionary<Type, PropertyDescriptorCollection> propertyCache; 

    static BindingProxy() 
    { 
     propertyCache = new Dictionary<Type, PropertyDescriptorCollection>(); 
    } 

    private static PropertyDescriptorCollection GetTypeProperties(Type type) 
    { 
     lock (propertyCache) 
     { 
      PropertyDescriptorCollection properties; 
      if (!propertyCache.TryGetValue(type, out properties)) 
      { 
       var list = new List<PropertyDescriptor>(); 
       GetTypeProperties(list, type); 
       properties = new PropertyDescriptorCollection(list.ToArray()); 
       propertyCache.Add(type, properties); 
      } 
      return properties; 
     } 
    } 

    private static void GetTypeProperties(ICollection<PropertyDescriptor> list, Type type) 
    { 
     foreach (var @interface in type.GetInterfaces()) 
     { 
      GetTypeProperties(list, @interface); 
     } 
     foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(type)) 
     { 
      list.Add(new ProxyPropertyDescriptor(property)); 
     } 
    } 

    #endregion 

    private readonly PropertyDescriptorCollection properties; 
    private readonly object instance; 

    public event PropertyChangedEventHandler PropertyChanged; 

    public BindingProxy(object instance) 
    { 
     this.instance = instance; 

     properties = instance == null 
      ? PropertyDescriptorCollection .Empty 
      : GetTypeProperties(instance.GetType()); 
    } 

    public void CheckAndNotify() 
    { 
     OnPropertyChanged(null); 
    } 

    protected virtual void OnPropertyChanged(string propertyName) 
    { 
     var handler = PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

    public object Instance 
    { 
     get { return instance; } 
    } 

    public override PropertyDescriptorCollection GetProperties() 
    { 
     return GetProperties(null); 
    } 

    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) 
    { 
     return properties; 
    } 

    public override Object GetPropertyOwner(PropertyDescriptor property) 
    { 
     return this; 
    } 

    #region ProxyPropertyDescriptor 

    private class ProxyPropertyDescriptor : PropertyDescriptor 
    { 
     private readonly PropertyDescriptor property; 

     public ProxyPropertyDescriptor(PropertyDescriptor property) : base(property) 
     { 
      this.property = property; 
     } 

     //public override string DisplayName 
     //{ 
     // get { return property.DisplayName; } 
     //} 

     //public override string Description 
     //{ 
     // get { return property.Description; } 
     //} 

     //public override string Category 
     //{ 
     // get { return property.Category; } 
     //} 

     //public override TypeConverter Converter 
     //{ 
     // get { return converter; } 
     //} 

     public override bool IsReadOnly 
     { 
      get { return property.IsReadOnly; } 
     } 

     public override void ResetValue(object component) 
     { 
     } 

     public override bool CanResetValue(object component) 
     { 
      return false; 
     } 

     public override bool ShouldSerializeValue(object component) 
     { 
      return false; 
     } 

     public override Type ComponentType 
     { 
      get { return property.ComponentType; } 
     } 

     public override Type PropertyType 
     { 
      get { return property.PropertyType; } 
     } 

     public override object GetValue(object component) 
     { 
      return property.GetValue(((BindingProxy)component).instance); 
     } 

     public override void SetValue(object component, object value) 
     { 
      var instance = ((BindingProxy)component).instance; 
      property.SetValue(instance, value); 
      OnValueChanged(instance, EventArgs.Empty); 
     } 
    } 

    #endregion 
}