Property Grid Number formatting
Asked Answered
T

4

10

Is it possible to format numerical properties displayed in PropertyGrid of winforms?

class MyData
{
      public int MyProp {get; set;}
}

And I want it to be displayed in the grid as 1.000.000 for example.

Are there some attributes for this?

Tonguelashing answered 14/5, 2013 at 10:51 Comment(2)
This looks like it should be three properties, i.e. major, minor, revision. If it is possible you will need to write your own formatter that you would use like String.FormatMindoro
The OP uses the European numbering scheme with the thousands separator being . and not , like in the US. So the question does not relate to Version formatting, but numeric formatting.Fionafionna
C
15

You should implement custom type converter for your integer property:

class MyData
{
    [TypeConverter(typeof(CustomNumberTypeConverter))]
    public int MyProp { get; set; }
}

PropertyGrid uses TypeConverter to convert your object type (integer in this case) to string, which it uses to display object value in the grid. During editing, the TypeConverter converts back to your object type from a string.

So, you need to use type converter which should be able to convert integer to string with thousand separators and parse such string back to integer:

public class CustomNumberTypeConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, 
                                        Type sourceType)
    {
        return sourceType == typeof(string);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, 
        CultureInfo culture, object value)
    {            
        if (value is string)
        {
            string s = (string)value;
            return Int32.Parse(s, NumberStyles.AllowThousands, culture);
        }

        return base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context, 
        CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(string))
            return ((int)value).ToString("N0", culture);

        return base.ConvertTo(context, culture, value, destinationType);
    }
}

Result:

propertyGrid.SelectedObject = new MyData { MyProp = 12345678 };

enter image description here

I recommend you to read Getting the Most Out of the .NET Framework PropertyGrid Control MSDN article to understand how PropertyGrid works and how it can be customized.

Crissycrist answered 2/10, 2013 at 15:28 Comment(2)
As an interesting tweak, the argument context.Instance points to the object being displayed, and if any formatting clues (like a format string property) exist you can use them to in ToString().Fionafionna
Note that this example is specific to int, and will not handle, for example, long.Quevedo
C
7

I don't know a way to format the properties directly in the PropertyGrid, but you could do something like

class MyData
{
    [Browsable(false)]
    public int _MyProp { get; set; }

    [Browsable(true)]
    public string MyProp
    {
        get
        {
             return _MyProp.ToString("#,##0");
        }
        set
        {
             _MyProp = int.Parse(value.Replace(".", ""));
        }
    }
}

Only the Browsable(true) property is shown in the PropertyGrid.

Candice answered 2/10, 2013 at 15:29 Comment(1)
This is actually my preferred solution because of the level of customization available.Fionafionna
C
2

I had the same question and came up with a slightly more flexible solution than Sergy's answer. It involves both a TypeConverter and a custom attribute. The TypeConverter is responsible for performing the conversion, and the custom attribute tells the TypeConverter how you want the string formatted.

I'm declaring my example class as follows:

class MyData
{
    [TypeConverter(typeof(FormattedDoubleConverter))]
    [FormattedDoubleFormatString("F3")]
    public double MyProp { get; set; }
}

The type converter is implemented as follows:

class FormattedDoubleConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || sourceType == typeof(double);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(string) || destinationType == typeof(double);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture,
                                       object value)
    {
        if (value is double)
            return value;

        var str = value as string;
        if (str != null)
            return double.Parse(str);

        return null;
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture,
                                     object value, Type destinationType)
    {
        if (destinationType != typeof(string))
            return null;

        if (value is double)
        {
            var property = context.PropertyDescriptor;
            if (property != null)
            {
                // Analyze the property for a second attribute that gives the format string
                var formatStrAttr = property.Attributes.OfType<FormattedDoubleFormatString>().FirstOrDefault();
                if (formatStrAttr != null)
                    return ((double)value).ToString(formatStrAttr.FormatString);
                else
                    return ((double)value).ToString();
            }
        }

        return null;
    }
}

Note that the TypeConverter uses context.PropertyDescriptor to find the FormattedDoubleFormatString attribute which provides the "F3" format string.

The attribute is simple, it just accepts and holds the format string:

[AttributeUsage(AttributeTargets.Property)]
class FormattedDoubleFormatString : Attribute
{
    public string FormatString { get; private set; }

    public FormattedDoubleFormatString(string formatString)
    {
        FormatString = formatString;
    }
}

And there you have it. A solution that is reusable for any format. You could even make it somewhat independent of the type by changing it to convert any type that implements IConvertable, but I'm not going to get that deep into this.

Chalcocite answered 22/3, 2019 at 0:47 Comment(1)
This IS much more flexible! Thanks.Supernova
S
1

I just made a C#10 version from the code provided by John Thoits. Please give him an upvote if you find this useful! 🤗

No functional difference, it just uses switch expressions and the nullability checks.

using System.ComponentModel;
using System.Globalization;

namespace Roche.FOM.UI;

class FormattedDoubleConverter : TypeConverter {
    public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) {
        return sourceType == typeof(string) || sourceType == typeof(double);
    }

    public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) {
        return destinationType == typeof(string) || destinationType == typeof(double);
    }

    public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture,
        object?                                                 value) {
        return value switch {
            double     => value,
            string str => double.Parse(str),
            _          => null
        };
    }

    public override object? ConvertTo(
        ITypeDescriptorContext? context,
        CultureInfo?            culture,
        object?                 value, Type destinationType) {
        switch (value) {
            case double d when context != null && destinationType == typeof(string):
                var property      = context.PropertyDescriptor;
                var formatStrAttr = property.Attributes.OfType<FormattedDoubleFormatString>().FirstOrDefault();
                return formatStrAttr != null
                    ? d.ToString(formatStrAttr.FormatString)
                    : d.ToString(CultureInfo.CurrentCulture);
            default:
                return null;
        }
    }
}
Supernova answered 26/1, 2023 at 22:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.