This project has moved. For the latest updates, please go here.

Why modifying an expandable object doesn't not refresh the content in a PropertyGrid?

Apr 6, 2012 at 6:52 PM

Hi there, in the PropertyGrid I set an object which contains an ExpandableObject. This one is Rang<T> class:

 

public class Range<T> : NotificationObject
{ 
private T _minValue;  
public T MinValue 
{ 
get { return _minValue; }  
set {
 if (!_minValue.Equals(value )) 
{
_minValue =value ; 
RaisePropertyChanged(() => MinValue);
}
}
}
 
private T _maxValue; 
public T MaxValue 
{
get { return _maxValue; } 
set {
 if (!_maxValue.Equals(value )) 
{
_maxValue =value ; 
RaisePropertyChanged(() => MaxValue);
}
}
}
 
public Range() { }
public Range(T min, T max) 
{
this .MinValue = min; 
this .MaxValue = max; 
}
 
public override string ToString() 
{
return string.Format("{0},{1}", this.MinValue, this .MaxValue); 
}
}

 

 

In the expandable object shows the values Min and Max thanks to ToString(), and when I modify its properties, does not refresh this ToString().

Does you know a way to solve this?

Apr 8, 2012 at 1:05 PM

Hi,

i have the same problem and need a solution to use this property grid instead of winforms property grid.

I am interested in your NotificationObject class and also the implementation of RaisePropertyChanged(() => .........);

Is it about using the INotifyPropertyChanged interface? Could you show me your imoplementation?

Coordinator
Apr 11, 2012 at 3:33 PM

The default editor that is being used for your Range<T> object is a TextBlock.  This TextBlock will call your object's ToString() method.  Unfortunately, it doesn't know it is linked to your Min and Max properties.  Even if you implement INotifyPropertyChanged, ToString() won't be called again.

 

If you create your own editor for your ExpandableObject, you may be able to specify how your editor should display its content.  The following code gives you the general idea:

public class MinMaxEditor : Xceed.Wpf.Toolkit.PropertyGrid.Editors.ITypeEditor
{
  public FrameworkElement ResolveEditor( Xceed.Wpf.Toolkit.PropertyGrid.PropertyItem propertyItem )
  {
    TextBlock textBlock = new TextBlock();
          
    var minBinding = new Binding( "Value.Min" );
    minBinding.Source = propertyItem;
    minBinding.ValidatesOnExceptions = true;
    minBinding.ValidatesOnDataErrors = true;
    minBinding.Mode = BindingMode.OneWay;

    var maxBinding = new Binding( "Value.Max" );
    maxBinding.Source = propertyItem;
    maxBinding.ValidatesOnExceptions = true;
    maxBinding.ValidatesOnDataErrors = true;
    maxBinding.Mode = BindingMode.OneWay;

    var binding = new MultiBinding();
    binding.Converter = new MinMaxEditorConverter();
    binding.ConverterParameter = "{0},{1}";
    binding.ValidatesOnExceptions = true;
    binding.ValidatesOnDataErrors = true;
    binding.Mode = BindingMode.OneWay;
    binding.Bindings.Add( minBinding );
    binding.Bindings.Add( maxBinding );

    BindingOperations.SetBinding( textBlock, TextBlock.TextProperty, binding );

    return textBlock;
  }
}

public sealed class MinMaxEditorConverter : IMultiValueConverter
{
  public object Convert( object[] values, Type targetType, object parameter, CultureInfo culture )
  {
    return string.Format( ( string )parameter, values );
  }

  public object[] ConvertBack( object value, Type[] targetTypes, object parameter, CultureInfo culture )
  {
    throw new NotSupportedException();
  }
}

 

To use this editor instead of the default editor, you will have to set the EditorAttribute on your ExpandableObject property like this:

[ExpandableObject]
[Editor( typeof( MinMaxEditor ), typeof( MinMaxEditor ) )]
public Range<T> MyProperty
{
  get
  {
    ...
  }
  set
  {
    ...
  }
}

 

I hope this will help you out.

Jan 24, 2015 at 12:33 PM
I also need this functionality. It seems clear to me that an unexpanded object should definitely describe it's current state (and not it's initial state) otherwise the user looking at an unexpanded property would be reading the wrong thing. This could be deadly if this control is used in a life or death situation (e.g. imagine reading the unexpanded property "Airplane Storm Avoidance: On"). So it seems to me to be fundamental and I would hope that the authors of this (excellent) control might consider adding this as a standard feature - rather than leaving it as an exercise for the rest of the world to do.

I would suggest that the PropertyGrid could easily check if an expandable object implements the INotifyPropertyChanged interface and if so, it could hook up to the PropertyChanged event and refresh the display by re-calling the ToString method every time a property is changed (any property).

Alternatively, if you want to be more specific, how about providing a more advanced ExpandableObject attribute, for example:
ExpandableObject(params string[] propertyNames)

Used like this:
[ExpandableObject("Min", "Max")]

This new constructor for the ExpandableObject attribute could be used to tell the property grid to refresh the expandable object only when the "Min" or "Max" properties have changed.

That's my 2 cents.
Thanks anyway.
Jan 24, 2015 at 4:59 PM
Edited Jan 24, 2015 at 5:47 PM
Here is my version which does not use binding but instead will use the INotifyPropertyChanged interface of the object being displayed in the property grid to keep the ToString display updated.
public class NotifiedToStringDisplay : ITypeEditor
{
    // ---------------------------------------------------------------------------
    #region Private Properties

    /// <summary>
    /// Get or set the TextBlock used to display the state of the property item.
    /// </summary>
    protected TextBlock DisplayBlock
    {
        get
        {
            if (_DisplayBlock == null)
            {
                _DisplayBlock = new TextBlock();
                _DisplayBlock.Foreground = Brushes.DimGray;
            }

            return _DisplayBlock;
        }
    }
    private TextBlock _DisplayBlock = null;

    /// <summary>
    /// Get or set the current object being displayed.
    /// </summary>
    protected INotifyPropertyChanged CurrentObject
    {
        get { return _CurrentObject; }
        set { _CurrentObject = value; }
    }
    private INotifyPropertyChanged _CurrentObject = null;

    #endregion


    // ---------------------------------------------------------------------------
    #region ITypeEditor Implementation

    /// <summary>
    /// Returns the framework element that is to be used as the editor.
    /// </summary>
    /// <param name="propertyItem">The item being edited.</param>
    /// <returns>A TextBlock that represents the item being edited.</returns>
    public FrameworkElement ResolveEditor(PropertyItem propertyItem)
    {
        if (CurrentObject != null)
        {
            CurrentObject.PropertyChanged -= CurrentObject_PropertyChanged;
        }

        CurrentObject = propertyItem.Value as INotifyPropertyChanged;

        if (CurrentObject != null)
        {
            CurrentObject.PropertyChanged += CurrentObject_PropertyChanged;
        }

        DisplayBlock.Text = propertyItem.Value.ToString();
        return DisplayBlock;
    }

    /// <summary>
    /// Updates the TextBlock being used to represent the property grid item.
    /// </summary>
    /// <param name="sender">The sender of this event.</param>
    /// <param name="e">The event arguments.</param>
    private void CurrentObject_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        // Avoid problems when properties are updated from non-UI threads
        // by using an action and calling the TextBlock's dispatcher.

        Action<object> updateText = delegate(object obj)
        {
            DisplayBlock.Text = obj.ToString();
        };

        DisplayBlock.Dispatcher.BeginInvoke(updateText, CurrentObject);
    }

    #endregion
}
Developer
Jan 26, 2015 at 8:55 PM