How do I format/Alter displayed value in property grid - c#

Am trying to work with a Winform Property Grid, and I am not able to format displayed value (mind too strongly tied to wpf now)
So what I want is, there is a drop down in property grid that has its own UITypeEditor, this editor shows values such as
1 - On
2 - Off
3 - Unknown
so the property on that listens to propertyGrid changes is int and for some odd reasons I cant change it to string, so like in wpf can I have a converter sort of thing that converts 1 into 1- On and 1-On to 1 ?
how can I decorate my property or property grid to be this intelligent ?
My property looks like
[LocalizedCategory("Limits", typeof(Api.Properties.Resources))]
[LocalizedDisplayName("Maximum", typeof(Api.Properties.Resources))]
[LocalizedDescription("Maximum", typeof(Api.Properties.Resources))]
[Editor(typeof(TextConversionTypeEditor), typeof(UITypeEditor))]
public int CriticalMaximum
{
get; set;
}
Can I make my property grid display more information than just an int ?

If you can use an Enum as type of your property, then it shows available values in drop-down, otherwise you can create a TypeConverter to provide values for drop-down. To do so you can use either of these options:
Use TypeConverter of an Enum for your int Property
If values are limited and known at design-time, In this case although the property is int, you can use the converter of an Enum for your property, without overriding anything:
public class MyObject
{
[TypeConverter(typeof(MyTypeConverter))]
public int MyProperty { get; set; }
}
public class MyTypeConverter : EnumConverter
{
public MyTypeConverter() : base(typeof(MyValues)) { }
}
public enum MyValues
{
On = 1,
Off,
Unknown
}
Create your own TypeConverter which supports standard values
If you can not have an enum and your standard values are generated at run-time, you can create such TypeConverter:
public class MyTypeConverter : TypeConverter
{
Dictionary<int, string> values;
public MyTypeConverter()
{
values = new Dictionary<int, string> { { 1, "1 - On" }, { 2, "2 - Off" }, { 3, "3 - Unknown" } };
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string)) return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value != null && values.ContainsValue(value.ToString()))
return values.Where(x => x.Value == value.ToString()).FirstOrDefault().Key;
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string) && value != null && value.GetType() == typeof(int))
return values[(int)value];
return base.ConvertTo(context, culture, value, destinationType);
}
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return new StandardValuesCollection(values.Keys);
}
}

Related

Custom localized BooleanConverter

I am trying to implement localized BooleanConverter. Everything works well so far, but when you double click on the property next message is being shown:
"Object of type 'System.String' cannot be converted to type 'System.Boolean'."
I suppose the problem is in method CreateInstance of TypeConverter which has that boolean property.
public class BoolTypeConverter : BooleanConverter
{
private readonly string[] values = { Resources.BoolTypeConverter_False, Resources.BoolTypeConverter_True };
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string) && value != null)
{
var valueType = value.GetType();
if (valueType == typeof(bool))
{
return values[(bool)value ? 1 : 0];
}
else if (valueType == typeof(string))
{
return value;
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var stringValue = value as string;
if (stringValue != null)
{
if (values[0] == stringValue)
{
return true;
}
if (values[1] == stringValue)
{
return false;
}
}
return base.ConvertFrom(context, culture, value);
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return new StandardValuesCollection(values);
}
}
The main problem of your code is you are overriding GetStandardValues incorrectly.
In fact you don't need to override GetStandardValues, just remove it and you will get expected result, that acts like original boolean converter while showing your desired strings:
When overriding GetStandardValues you should return a list of supported values of the type that you are creating converter for, then using the ConvertTo you provide string representation values and using ConvertFrom, provide a way to converting the type from string values.

c# - Custom type Convert.ToString

I'm having trouble when rendering a text box using #Html.TextBoxFor with a custom type I've created. My custom type looks like this:
public class Encrypted<T>
{
private readonly Lazy<T> _decrypted;
private readonly Lazy<string> _encrypted;
public static implicit operator Encrypted<T>(T value)
{
return new Encrypted<T>(value);
}
public static implicit operator string(Encrypted<T> value)
{
return value._encrypted.Value;
}
...
}
Then on my model, I have:
public class ExampleModel
{
public Encrypted<string> Name { get; set; }
}
If I manually populate the value in my controller action:
public ActionResult Index()
{
var model = new ExampleModel
{
Name = "Example Name";
};
return View(model);
}
Then on my view I have the standard #Html.TextBoxFor(m => m.Name). However, when that renders, the value of my text box gets set to: Services.Encrypted`1[System.String]`
Presumably this is because I'm using a custom type and the compiler doesn't know how to convert my type to a string value.
I've tried using a custom TypeConverter:
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
var encrypted = value as IEncrypted;
if (encrypted != null)
{
return encrypted.DecryptedValue();
}
}
return null;
}
Then on my Encrypted model I added:
[TypeConverter(typeof(EncryptedTypeConveter))]
However it doesn't seem to be using the custom TypeConverter. Does anyone know how I can resolve this?
You need to override ToString().

Let a ComboBox to show a modified text as the value input instead of the actual value

I'm trying to make a property that will show a different text in its value input each time the user selected an item. But my problem with the values is that they're strings with underscores and lowercase first letters, for example: "naval_tech_school". So I need the ComboBox to display a different value text that will look like this "Naval Tech School" instead.
But the value should remain "naval_tech_school" if trying to access it.
If you just want to change the value (without a special editor) back and forth between the two formats, you just need a custom TypeConverter. Declare the property like this:
public class MyClass
{
...
[TypeConverter(typeof(MyStringConverter))]
public string MyProp { get; set; }
...
}
And here is a sample TypeConverter:
public class MyStringConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
string svalue = value as string;
if (svalue != null)
return RemoveSpaceAndLowerFirst(svalue);
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
string svalue = value as string;
if (svalue != null)
return RemoveUnderscoreAndUpperFirst(svalue);
return base.ConvertTo(context, culture, value, destinationType);
}
private static string RemoveSpaceAndLowerFirst(string s)
{
// do your format conversion here
}
private static string RemoveUnderscoreAndUpperFirst(string s)
{
// do your format conversion here
}
}

Property Grid > how to refresh the main property

[TypeConverter(typeof(BrokerageConverter))]
[DescriptionAttribute("Brokerage Details")]
[PropertyGridInitialExpanded(true)]
[RefreshProperties(RefreshProperties.Repaint)]
public class Brokerage
{
private Decimal _Amt = Decimal.Zero; private string _currency = "";
public Brokerage() { }
public Brokerage(Decimal broAmount, string broCurrency) { Amount = broAmount; Currency = broCurrency; }
[ReadOnly(false)]
public Decimal Amount
{
get { return _Amt; }
set { _Amt = value; }
}
[ReadOnly(true)]
public string Currency
{
get { return _currency; }
set { _currency = value; }
}
//public override string ToString() { return _Amt.ToString() + " - " + _currency; }
}
public class BrokerageConverter : ExpandableObjectConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, System.Type destinationType)
{
if (destinationType == typeof(Brokerage))
return true;
return base.CanConvertTo(context, destinationType);
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type t)
{
if (t == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, t);
}
// Overrides the ConvertFrom method of TypeConverter.
public override object ConvertFrom(ITypeDescriptorContext context,CultureInfo culture, object value)
{
if (value is string)
{
string[] v = ((string)value).Split(new char[] { '-' });
return new Brokerage(Decimal.Parse(v[0]), v[1]);
}
return base.ConvertFrom(context, culture, value);
}
// Overrides the ConvertTo method of TypeConverter.
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(System.String) && value is Brokerage)
{
Brokerage b = (Brokerage)value;
return b.Amount.ToString() + " - " + b.Currency.ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
Now when I change the Amount, the Buyer Bro is not updating automatically. How to achieve it? Let me know, if i have to provide some additional info
Adding the attribute "[NotifyParentProperty(true)]" will do the trick:
[ReadOnly(false)]
[NotifyParentProperty(true)]
public Decimal Amount
{
get { return _Amt; }
set { _Amt = value; }
}
Make sure to add
using System.ComponentModel;
Now when you change Amount and loose the focus of this distinct property,
the parent property will automatically be updated.
Cheers!
I had the same issue in the past.
I did put [RefreshProperties(RefreshProperties.Repaint)] or RefreshProperties.All, then I implemented INotifyPropertyChanged on my target objects, but I never managed to get the automatic mechanism it to work correctly.
Apparently, I am not alone.
I ended using the .Refresh() method on the PropertyGrid. Now it works all the time.
var b = new Brokerage(10, "EUR");
this.propertyGrid1.SelectedObject = b;
...
b.Amount = 20;
this.propertyGrid1.Refresh();
Did you try adding,
[RefreshProperties(RefreshProperties.Repaint)]
to the 2 properties inside your Brokerage class?

Edit the display name of enumeration members in a PropertyGrid

I have a property grid that I am using for users to be able to configure objects for any plugin that is written to be used in my application. I would like to be able to tell developers writing plugins to use the ComponentModel Attributes for their members like so:
[CategoryAttribute("On Screen Display Settings"),
DescriptionAttribute("Whether or not to show the session timer."),
DisplayName("Show Session Timer")]
public bool ShowTimer
{
get;
set;
}
This works great. Now I would like for the members of an enumeration to be able to be edited as well. i.e.
public enum Resolution_ : byte
{
DCIF,
CIF,
QCIF,
[DisplayName("4CIF")]
CIF4,
[DisplayName("2CIF")]
CIF2
}
So that they are displayed in the PropertyGrid's list like so:
DCIF
CIF
QCIF
CIF4
CIF2
Along with any Descriptions and Display names they may have with them.
It seems that I can only do this with properties, events, and methods. Does anyone know how I can do this for an enumeration?
You will have to make an EnumConverter class and decorate your property with a TypeConverter attribute in order to do this.
See this Using PropertyGrid in .NET, it's a fun example:
Imagine that you want more than two items in list. The boolean type is not enough; you need to set Description attributes with a name for every element in enum.
enum DrinkDoses {
[Description("Half of litre")]
litre,
[Description("One litre")]
oneLitre,
[Description("Two litres")]
twoLitre,
[Description("Three litres")]
threeLitres,
[Description("Four litres")]
fourLitres,
[Description("Death dose, five litres")]
fiveLitres
}
In another class you need to utilize the type EnumConverter.
class DrinkDosesConverter : EnumConverter {
private Type enumType;
public DrinkDosesConverter(Type type) : base(type) {
enumType = type;
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destType) {
return destType == typeof(string);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture,
object value, Type destType) {
FieldInfo fi = enumType.GetField(Enum.GetName(enumType, value));
DescriptionAttribute dna = (DescriptionAttribute)Attribute.GetCustomAttribute(fi,
typeof(DescriptionAttribute));
if (dna != null)
return dna.Description;
else
return value.ToString();
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type srcType) {
return srcType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture,
object value) {
foreach (FieldInfo fi in enumType.GetFields()) {
DescriptionAttribute dna = (DescriptionAttribute)Attribute.GetCustomAttribute(fi,
typeof(DescriptionAttribute));
if ((dna != null) && ((string)value == dna.Description))
return Enum.Parse(enumType, fi.Name);
}
return Enum.Parse(enumType, (string)value);
}
}
Third, you need set the attribute TypeConverter for displaying the property.
class DrinkerDoses {
DrinkDoses doses;
[DisplayName("Doses")]
[Description("Drinker doses")]
[Category("Alcoholics drinking")]
[TypeConverter(typeof(DrinkDosesConverter))]
public DrinkDoses Doses {
get { return doses; }
set { doses = value; }
}
int dataInt;
public int DataInt {
get { return dataInt; }
set { dataInt = value; }
}
}
You can attach a custom TypeConverter implementation to the property whose type is your enumeration and override the GetStandardValuesSupported and GetStandardValues to return a custom list of items to show in the drop-down list in the PropertyGrid. You can then override ConvertFrom/ConvertTo methods to handle converting values to/from your enumeration type.
You may also want to override GetStandardValuesExclusive and have it return "true" so the user can't type anything into the property value.
So, something like this:
public class MyTypeConverter : TypeConverter
{
//Override GetStandardValuesExclusive,
//GetStandardValues and GetStandardValuesSupported
}
public class SomeClass
{
[TypeConverter(typeof(MyTypeConverter))]
public Resolution SomePropertry
{
...
}
}
In your implementation of GetStandardValues/ConvertFrom/ConvertTo you could then use Reflection to pull out the DisplayNameAttribute (or DescriptionAttribute, which may be more suited to this task) attributes of the various enum members to show that text instead of hard-coding the list of items to show.
The answer I gave here Has a working example of this. Here is the specific code from that example that you want:
/// <summary>
/// This attribute is used to represent a string value
/// for a value in an enum.
/// </summary>
public class StringValueAttribute : Attribute {
#region Properties
/// <summary>
/// Holds the stringvalue for a value in an enum.
/// </summary>
public string StringValue { get; protected set; }
#endregion
#region Constructor
/// <summary>
/// Constructor used to init a StringValue Attribute
/// </summary>
/// <param name="value"></param>
public StringValueAttribute(string value) {
this.StringValue = value;
}
#endregion
}
public static class MyExtension
{
public static string GetStringValue(this Enum value)
{
// Get the type
Type type = value.GetType();
// Get fieldinfo for this type
FieldInfo fieldInfo = type.GetField(value.ToString());
// Get the stringvalue attributes
StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(
typeof(StringValueAttribute), false) as StringValueAttribute[];
// Return the first if there was a match.
return attribs.Length > 0 ? attribs[0].StringValue : null;
}
public static String[] GetEnumNames(Type t)
{
Array enumValueArray= Enum.GetValues(t);
string[] enumStrings = new String[enumValueArray.Length];
for(int i = 0; i< enumValueArray.Length; ++i)
{
enumStrings[i] = GetStringValue((test)enumValueArray.GetValue(i));
}
return enumStrings;
}
}
enum test
{
[StringValue("test ONE")]
test1,
[StringValue("test TWO")]
test2
}
This also seems to work:
[AttributeUsage(AttributeTargets.Field)]
public class EnumDisplayNameAttribute : System.ComponentModel.DisplayNameAttribute
{
public EnumDisplayNameAttribute(string data) : base(data) { }
}
public enum Resolution_ : byte
{
DCIF,
CIF,
QCIF,
[EnumDisplayName("4CIF")]
CIF4,
[EnumDisplayName("2CIF")]
CIF2
}
Components looking for a DisplayName attribute via Reflection will find one, and as far as I can tell this works. Is there a reason why this might be a bad idea?

Categories

Resources