I have a PropertyGrid with Expandable Properties. It looks like this:
http://i.imgur.com/2FhVV41.jpg
It displays the Value and the Measurement Unit. Now, if the user writes in a double value in the root row, the Value property will change, everything else stays unmodified.
I want that if the user clicks in the root row, its text ("5 EUR" on the picture) would change to show only the Value ("5"). But if the user doesn't click in, the text would stay unmodified ("5 EUR").
Now my ExpandableObjectConverter looks like this (only the relevant part):
ObjectProperty oldOP;
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(ObjectProperty))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
ObjectProperty op = (ObjectProperty)value;
oldOP = op;
return op.ValueProp.ToString() + " " + op.MUProp.ToString();
}
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)
{
ObjectProperty op;
double newValue;
if (double.TryParse((string)value, out newValue))
{
op = oldOP;
op.ValueProp = newValue;
return op;
}
else
{
op = oldOP;
return op;
}
}
Related
This question is self-answered. If you have better answer post it.
Specially when dealing with NULL value in TypeConverters if you have better
approach post it
When creating properties like Strings, DateTime, etc.
It appeared well and serialized.
But I need to create a property of Type type.
Actually creating a Type property but it is un-modifiable.
Is there a converter or such task to allow that?
public class MyClass
{
public Type MyType{get;set;}
}
From picture Its grayed. and read-only.
First, Many special thanks to #Reza Aghaei for his awesome answers Thanks to
him much.
"Specified cast is not valid" error in TypeConverter.ConvertTo
How to create TypeConverter which accepts multiple values and serialized to WinForms Form.designer.cs?
TypeConverter Implemented as following:
public class DataTypeConverter : TypeConverter
{
private const string NullString = "<NULL>";
private Type[] types = new Type[] { null, typeof(byte), typeof(short), typeof(int), typeof(long), typeof(decimal), typeof(string), typeof(DateTime), typeof(bool) };
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, CultureInfo culture, object value)
{
if (value.ToString() == NullString) return null;
var result = types.FirstOrDefault(x => x?.ToString() == value.ToString());
if (result != null)
return result;
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
if (value == null)
return NullString;
return value.ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) => new StandardValuesCollection(types);
public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true;
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => true;
}
Result
I have a control that has a complex browsable property, it shows a list of all the controls of that type in the form but I want to allow to copy and paste the name of the control in the combobox.
I used a TypeConverter, but the items of the combobox disappear.
Is there another way of doing that?
Or how I return the default list of items in the GetStandardValues function?
I tried to return the base function, but it doesn't work.
public class SubflowchartConverter : TypeConverter
{
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
FlowChart flowchart = context.Instance as FlowChart;
if (flowchart != null)
return true;
else
return base.GetStandardValuesSupported(context);
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return false;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return base.GetStandardValues(context);
}
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, CultureInfo culture, object value)
{
if (value is string)
{
string flowchartName = value.ToString();
if (flowchartName == "(none)")
return null;
FlowChart flowchart = context.Instance as FlowChart;
if (flowchart.Parent.Controls.ContainsKey(flowchartName))
{
return flowchart.Parent.Controls[flowchartName];
}
}
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
if (value == null)
return "(none)";
FlowChart flowchart = value as FlowChart;
if (flowchart != null)
return flowchart.Name;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
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.
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
}
}
[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?