1

Just stumbled upon propertyGrid and its awesome! However, i have one task that i cant find how to do with it:

I have a class which has a type. Based on type, it has different properties available. I keep it in one class (not multiple inherited classes) for simplicity sake (there are ten types but they mostly have the same properties).

For example, i have a class MapObject which can have string type equal "player" or "enemy". For "enemy", property "name" is used, but for "player" it is blank (not used).

When i select two objects, one of which is of type "player" and other of type "enemy", i want property "name" to only "count" for the "enemy". So, i want propertyGrid to show the name of the object that has type="enemy", and when it (name property) is changed in Grid, only assign it to the object of type "enemy".

Is this possible to do?

Istrebitel
  • 2,963
  • 6
  • 34
  • 49
  • So just to clarify, the class changes depending on the data in some of the properties? – Matt Mar 28 '12 at 17:19
  • I'd like it to behave as if the object that doesnt use the property just wasnt selected for all purposes of propertyGrid object. – Istrebitel Mar 28 '12 at 18:17
  • 1
    Lets make a very simple example. ClassA has three properties i,x,y and if i=1 then x is used and y isnt used, if i =2 then x and y are used. Lets assume o1.i=2 o1.y=1, o2.i=2 o2.y=2, o3.i=1. If i select o1 and o2, propertyGrid will show blank for y property since o1 has different y property than o2. Thats okay. But i want it to show o1.y property if i select o1 and o3, and i want it to apply changes to the y property only to o1 in this case – Istrebitel Mar 28 '12 at 18:19

3 Answers3

1

Depending on whose PropertyGrid you are using, toggling the Browsable attribute may do what you want. See my answer here for how to do that at runtime.

If, like me, you are using the Xceed PropertyGrid then only changing the Browsable attribute at runtime once the control has loaded doesn't do anything. Instead, I also had to modify the PropertyDefinitions collection. Here is an extension method for doing that:

    /// <summary>
    /// Show (or hide) a property within the property grid.
    /// Note: if you want to initially hide the property, it may be
    /// easiest to set the Browable attribute of the property to false.
    /// </summary>
    /// <param name="pg">The PropertyGrid on which to operate</param>
    /// <param name="property">The name of the property to show or hide</param>
    /// <param name="show">Set to true to show and false to hide</param>
    public static void ShowProperty(this PropertyGrid pg, string property, bool show)
    {
        int foundAt = -1;
        for (int i=0; i < pg.PropertyDefinitions.Count; ++i)
        {
            var prop = pg.PropertyDefinitions[i];
            if (prop.Name == property)
            {
                foundAt = i;
                break;
            }
        }
        if (foundAt == -1)
        {
            if (show)
            {
                pg.PropertyDefinitions.Add(
                    new Xceed.Wpf.Toolkit.PropertyGrid.PropertyDefinition()
                    {
                        Name = property,
                    }
                );
            }
        }
        else
        {
            if (!show)
            {
                pg.PropertyDefinitions.RemoveAt(foundAt);
            }
        }
    }

In case the above does not work for you, the following may work better and is simpler anyway. It also doesn't use deprecated properties like the code above did...

    public static void ShowProperty(this PropertyGrid pg, string property, bool show)
    {
        for (int i = 0; i < pg.Properties.Count; ++i)
        {
            PropertyItem prop = pg.Properties[i] as PropertyItem;

            if (prop.PropertyName == property)
            {
                prop.Visibility = show ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
                break;
            }
        }
    }
John Cummings
  • 1,949
  • 3
  • 22
  • 38
0

I made it with my own custom attribute:

public class VisibilityAttribute : Attribute
{
    public bool IsVisible { get; set; }

    public VisibilityAttribute(bool isVisible)
    {
        IsVisible = isVisible;
    }
}

Then my data model:

 public abstract class BaseSettings: INotifyPropertyChanged
 {
    public void SetVisibilityProperty(string propertyName, bool isVisible)
    {
        var theDescriptor = TypeDescriptor.GetProperties(this.GetType())[propertyName];

        var theDescriptorVisibilityAttribute = (VisibilityAttribute)theDescriptor.Attributes[typeof(VisibilityAttribute)];
        if (theDescriptorVisibilityAttribute == null) return;

        theDescriptorVisibilityAttribute.IsVisible = isVisible;
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

public class ThrottleSettings : BaseSettings
{
    private ThrottleType _throttleType = ThrottleType.SmartThrottle;
    [PropertyOrder(1)]
    public ThrottleType ThrottleType
    {
        get
        {
            return _throttleType;
        }
        set
        {
            if (value == ThrottleType.FullThrottle)
            {
                SetVisibilityProperty(nameof(FullThrottleTimeInMilliseconds), true);
                SetVisibilityProperty(nameof(ThumbnailThrottleTimeInMilliseconds), false);
            }
            else if (value == ThrottleType.SmartThrottle)
            {
                SetVisibilityProperty(nameof(FullThrottleTimeInMilliseconds), false);
                SetVisibilityProperty(nameof(ThumbnailThrottleTimeInMilliseconds), true);
            }
            else if (value == ThrottleType.NoThrottle)
            {
                SetVisibilityProperty(nameof(FullThrottleTimeInMilliseconds), false);
                SetVisibilityProperty(nameof(ThumbnailThrottleTimeInMilliseconds), false);
            }

            _throttleType = value;
        }
    }

    private int _fullThrottleTime = 100;
    [PropertyOrder(2)]
    [Visibility(false)]
    [Description("Specifies full throttle time (in milliseconds).")]
    public int FullThrottleTimeInMilliseconds
    {
        get
        {
            return _fullThrottleTime;
        }
        set
        {
            if (value < 0) return;
            _fullThrottleTime = value;
        }
    }

    private int _thumbnailThrottleTime = 0;
    [PropertyOrder(3)]
    [Visibility(true)]
    [Description("Specifies thumbnail throttle time (in milliseconds).")]
    public int ThumbnailThrottleTimeInMilliseconds
    {
        get
        {
            return _thumbnailThrottleTime;
        }
        set
        {
            if (value < 0) return;
            _thumbnailThrottleTime = value;
        }
    }
}

Finally I subscribed to 'propertyGrid_PropertyValueChanged' event and call there my method:

private void _propertyGrid_PropertyValueChanged(object sender, PropertyValueChangedEventArgs e)
    {
        RefreshVisibility(_propertyGrid.Properties);
    }

Here is method itself:

private void RefreshVisibility(IList properties)
    {
        foreach (PropertyItem property in properties)
        {
            var visibilityAttribute = GetVisibilityAttribute(property);
            if (visibilityAttribute != null)
            {
                if (visibilityAttribute.IsVisible)
                    property.Visibility = Visibility.Visible;
                else
                    property.Visibility = Visibility.Collapsed;
            }

            RefreshVisibility(property.Properties);
        }
    }
uzrgm
  • 375
  • 3
  • 8
0

This is a design pattern known as the state pattern. It is pretty easy to implement and you do not need property grids. http://www.dofactory.com/Patterns/PatternState.aspx

Isaac T
  • 16
  • 1
  • I studied it and there is a problem. Since this is a class being property of a class, it will show as such in propertyGrid - nested in a kind of "folder" which is not at all desired. Is there a way around it? – Istrebitel Apr 03 '12 at 08:00