2012-02-24 2 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.Invoke에서 System.Reflection.RuntimeMethodInfo.CheckConsistency (개체 대상)에 에서 (오브젝트 OBJ, BindingFlags의 invokeAttr 바인더 바인더 [] 파라미터 CultureInfo를 배양 부울 skipVisibilityChecks 개체) System.Reflection.RuntimeMethodInfo.Invoke System.ComponentModel에서 (오브젝트 OBJ, BindingFlags의 invokeAttr 바인더 바인더 개체 [] 매개은 CultureInfo 배양)에서 System.ComponentModel.ReflectEventDescriptor.AddEventHandler
(개체 성분, 대표 값). ReflectPropertyDescriptor.AddValueChanged (Object componen t, 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()

에서 (BindingContext를 newBindingContext 바인딩 바인딩) System.Windows.Forms.BindingContext.UpdateBinding에서 ( 결합 제본)

바인더 대신 바인더가 작동합니다. ext 개체를 사용합니다. ICustomTypeDescriptor 구현에서 뭔가를 놓쳤습니까?

+0

SelectedVersion 속성은 무엇을 반환하며 DynamicClass 클래스 내에 있습니까? 이 문제를 해결할 수 있었습니까? – Martin

답변

0

테스트 코드에서 문제를 재현 해 냈습니다. ExtensionModel에 INotifyPropertyChanged를 구현하지 않으면 작동하는 것을 볼 수 있습니다!

그래서 INotifyPropertyChanged를 구현하는 속성 클래스에서는 작동하지 않는 ICustomTypeDescriptor 구현이 있습니다.

이 방법이 효과적이지만 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 
}