RadPropertyGrid binding issue with ExpandoObject property whose value is null

7 posts, 0 answers
  1. Lauren
    Lauren avatar
    4 posts
    Member since:
    Jan 2014

    Posted 13 Jan 2014 Link to this post

    I have a situation where I'm setting the RadPropertyGrid's Item to an ExpandoObject instance whose properties have been defined at run-time. I'm listening for the PropertyChanged event of the ExpandoObject to tell when a property has been updated.

    I'm using the AutoGeneratingPropertyDefinition event to catch properties with specific names and assign an EditorDataTemplate that uses a RadNumericUpDown control. The problem is that the property value (let's just use the name "Prop1") is set by a nullable double, so sometimes Prop1 has a value, and sometimes its null. If Prop1 has a numeric value everything works great with the property editor, and I can change the field for Prop1 and it will change its corresponding value in the ExpandoObject perfectly.

    However if Prop1 is set to null, its field shows up in the PropertyGrid but when I change the value I don't get any PropertyChanged event from the ExpandoObject. The event fires if I set a value to Prop1 in code-behind, but not if I try to change it from the PropertyGrid. This leads me to believe there is some sort of binding issue with the property grid and ExpandoObject properties with null values.

    Any suggestions?
  2. Yoan
    Admin
    Yoan avatar
    1070 posts

    Posted 16 Jan 2014 Link to this post

    Hello Lauren,

    Unfortunately with the supplied information I cannot figure out what is going on. Would it be possible to share with us more details on your exact implementation? It would be great if you can send me a small sample with dummy data in order to investigate the issue locally.

    Thank you in advance.


    Regards,
    Yoan
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WPF.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
  3. UI for WPF is Visual Studio 2017 Ready
  4. Lauren
    Lauren avatar
    4 posts
    Member since:
    Jan 2014

    Posted 16 Jan 2014 Link to this post

    Yoan,

    Thank you for your reply. I actually found another way around my issue. Instead of using an ExpandoObject, I created my own class that inherits DynamicObject and implements ICustomTypeDescriptor and INotifyPropertyChanged. I also created a custom PropertyDescriptor class implementation as well. That way I keep track of what type a property is "supposed" to be even if it is null (which ExpandoObject currently does not do).

    Thanks again!
    - Lauren
  5. Yoan
    Admin
    Yoan avatar
    1070 posts

    Posted 20 Jan 2014 Link to this post

    Hi Lauren,

    I am glad to hear that you have resolved the problem by yourself. 

    Please, don't hesitate to ask if you need any further help.

    Regards,
    Yoan
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WPF.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
  6. Chris
    Chris avatar
    18 posts
    Member since:
    Sep 2014

    Posted 06 Nov 2015 in reply to Lauren Link to this post

    Lauren,

         Would you mind sharing your solution. I'm trying to accomplish the same thing. Thank you.

  7. Ivan Ivanov
    Admin
    Ivan Ivanov avatar
    1128 posts

    Posted 11 Nov 2015 Link to this post

    Hi,

    I prepared a sample project with RadPropertyGrid with dynamic properties. You can use it as a starting point if you have any further questions.

    Regards,
    Ivan Ivanov
    Telerik
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  8. Chris
    Chris avatar
    18 posts
    Member since:
    Sep 2014

    Posted 11 Nov 2015 in reply to Ivan Ivanov Link to this post

    Thank you Ivan.

     

    For anyone else that is pursuing this approach, here is the code that is working for me.

     

    private sealed class MyDynObject : DynamicObject, ICustomTypeDescriptor, INotifyPropertyChanged
      {
        private class OrderedProperty
        {
          public IProperty Property { get; set; }
          public int Order { get; set; }
        }
     
        private readonly Dictionary<string, OrderedProperty> dynamicProperties
          = new Dictionary<string, OrderedProperty>();
     
        public MyDynObject(ReadOnlyObservableCollection<IProperty> properties)
        {
          for (var i = 0; i < properties.Count; i++)
          {
            var property = properties[i];
            dynamicProperties.Add(property.Name, new OrderedProperty()
            {
              Property = property,
              Order = i
            });
            PropertyChangedEventManager.AddHandler(property, Property_PropertyChanged, nameof(IProperty.Value));
          }
        }
     
        private void Property_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
          if (nameof(IProperty.Value).Equals(e.PropertyName))
          {
            OnPropertyChanged(((IProperty)sender).Name);
          }
        }
     
        public override IEnumerable<string> GetDynamicMemberNames()
        {
          return dynamicProperties.Keys;
        }
     
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
          if (dynamicProperties.ContainsKey(binder.Name))
          {
            result = dynamicProperties[binder.Name].Property.Value;
            return true;
          }
          result = null;
          return false;
        }
     
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
          if (dynamicProperties.ContainsKey(binder.Name))
          {
            dynamicProperties[binder.Name].Property.Value = value;
            OnPropertyChanged(binder.Name);
            return true;
          }
          return false;
        }
     
        #region Implementation of INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
     
        private void OnPropertyChanged(string propertyName)
        {
          if (PropertyChanged == null)
          {
            return;
          }
     
          var eventArgs = new PropertyChangedEventArgs(propertyName);
          PropertyChanged(this, eventArgs);
        }
        #endregion
     
        #region Implementation of ICustomTypeDescriptor
        public AttributeCollection GetAttributes()
        {
          throw new NotImplementedException();
        }
     
        public string GetClassName()
        {
          return GetType().Name;
        }
     
        public string GetComponentName()
        {
          throw new NotImplementedException();
        }
     
        public TypeConverter GetConverter()
        {
          throw new NotImplementedException();
        }
     
        public EventDescriptor GetDefaultEvent()
        {
          throw new NotImplementedException();
        }
     
        public PropertyDescriptor GetDefaultProperty()
        {
          throw new NotImplementedException();
        }
     
        public object GetEditor(Type editorBaseType)
        {
          throw new NotImplementedException();
        }
     
        public EventDescriptorCollection GetEvents()
        {
          throw new NotImplementedException();
        }
     
        public EventDescriptorCollection GetEvents(Attribute[] attributes)
        {
          throw new NotImplementedException();
        }
     
        public PropertyDescriptorCollection GetProperties()
        {
          var properties = dynamicProperties
              .Select(pair => new DynamicPropertyDescriptor(this,
                  pair.Key, pair.Value.Property.ValueType, GetAttributes(pair.Value)));
          return new PropertyDescriptorCollection(properties.Cast<PropertyDescriptor>().ToArray());
        }
     
        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
          throw new NotImplementedException();
        }
     
        public object GetPropertyOwner(PropertyDescriptor pd)
        {
          throw new NotImplementedException();
        }
     
        private Attribute[] GetAttributes(OrderedProperty orderedProperty)
        {
          var attributes = new Attribute[2];
          attributes[0] = new DisplayAttribute
          {
            Name = orderedProperty.Property.Name,
            Description = orderedProperty.Property.Description,
            GroupName = orderedProperty.Property.GroupName,
            Order = orderedProperty.Order
          };
          attributes[1] = new ReadOnlyAttribute(orderedProperty.Property.IsReadOnly);
          return attributes;
        }
        #endregion
     
     
     
     
        private class DynamicPropertyDescriptor : PropertyDescriptor
        {
          private readonly MyDynObject model;
     
          public DynamicPropertyDescriptor(MyDynObject model,
              string propertyName, Type propertyType, Attribute[] propertyAttributes)
              : base(propertyName, propertyAttributes)
          {
            this.model = model;
            PropertyType = propertyType;
          }
     
          public override bool CanResetValue(object component)
          {
            return true;
          }
     
          public override object GetValue(object component)
          {
            return model.dynamicProperties[Name].Property.Value;
          }
     
          public override void ResetValue(object component)
          {
          }
     
          public override void SetValue(object component, object value)
          {
            model.dynamicProperties[Name].Property.Value = value;
          }
     
          public override bool ShouldSerializeValue(object component)
          {
            return false;
          }
     
          public override Type ComponentType => typeof(MyDynObject);
     
          public override bool IsReadOnly => model.dynamicProperties[Name].Property.IsReadOnly;
     
          public override Type PropertyType { get; }
        }
      }

     

    public interface IProperty : INotifyPropertyChanged
      {
        string Name { get; }
     
        object Value { get; set; }
     
        Type ValueType { get; }
     
        string Description { get; }
     
        bool IsReadOnly { get; }
     
        string GroupName { get; }
      }

     

Back to Top
UI for WPF is Visual Studio 2017 Ready