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
}