[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?
Related
I have a list of a class which I serialize to the designer generated code with the following code:
internal class TargetSettingsConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(InstanceDescriptor) && value is ControlListManager.TargetSettings)
{
ConstructorInfo constructor = typeof(ControlListManager.TargetSettings).GetConstructor(new[] { typeof(object), typeof(string), typeof(DisplayModes), typeof(bool), typeof(int), typeof(int), typeof(int) });
var target = value as ControlListManager.TargetSettings;
var descriptor = new InstanceDescriptor(constructor, new[] { target.Target, target.Text, target.DisplayMode, target.Fade, target.HideTimeout, target.PaddingX, target.PaddingY }, true);
return descriptor;
}
if (culture == null) { culture = CultureInfo.CurrentCulture; }
return base.ConvertTo(context, culture, value, destinationType);
}
}
This works well so far, but what bothers me is that I have to specify the types and values individually.
My first idea was to use GetConstructors() instead. But then to problem of the values still remains. I'm really confused about this problem since I actually write the function without knowing the amount of parameters - or only if it is "hardcoded".
Does anyone have any idea how this can be done better?
Edit: The data class
[TypeConverter(typeof(TargetSettingsConverter))]
public class TargetSettings : IEquatable<TargetSettings>
{
public object Target = new { };
public string Text;
public DisplayModes DisplayMode = DisplayModes.FollowXY;
public bool Fade = true;
public int HideTimeout;
public int PaddingX;
public int PaddingY;
public bool Equals(TargetSettings other)
{
return other != null && (Target.Equals(other.Target));
}
public override bool Equals(object obj)
{
if (obj == null) { return false; }
TargetSettings objAsPart = obj as TargetSettings;
if (objAsPart == null) { return false; }
return Equals(objAsPart);
}
public override int GetHashCode()
{
return Target.GetHashCode();
}
public TargetSettings(object target, string text = "", DisplayModes displayMode = DisplayModes.FollowXY, bool fade = true, int hideTimeout = 0, int paddingX = 0, int paddingY = 0)
{
Target = target;
Text = text;
DisplayMode = displayMode;
Fade = fade;
HideTimeout = hideTimeout;
PaddingX = paddingX;
PaddingY = paddingY;
}
}
Based on your question, you want to make your code more dynamic for the types and values.
Since the types and values you want to convert are all field types. I recommend that you use the reflection to do it.
You can get all the field types as the following:
Type []tarr = typeof(TargetSettings).GetFields().Select(i => i.FieldType).ToArray();
ConstructorInfo constructor = typeof(TargetSettings).GetConstructor(tarr);
You can get all the field values as the following:
object []oarr= typeof(TargetSettings).GetFields().Select(i => i.GetValue(target)).ToArray();
var descriptor = new InstanceDescriptor(constructor, oarr, true);
Full code:
internal class TargetSettingsConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(InstanceDescriptor) && value is TargetSettings)
{
Type []tarr = typeof(TargetSettings).GetFields().Select(i => i.FieldType).ToArray();
ConstructorInfo constructor = typeof(TargetSettings).GetConstructor(tarr);
var target = value as TargetSettings;
object []oarr= typeof(TargetSettings).GetFields().Select(i => i.GetValue(target)).ToArray();
var descriptor = new InstanceDescriptor(constructor, oarr, true);
return descriptor;
}
if (culture == null) { culture = CultureInfo.CurrentCulture; }
return base.ConvertTo(context, culture, value, destinationType);
}
}
[TypeConverter(typeof(TargetSettingsConverter))]
public class TargetSettings : IEquatable<TargetSettings>
{
public object Target = new { };
public string Text;
public DisplayModes DisplayMode = DisplayModes.FollowXY;
public bool Fade = true;
public int HideTimeout;
public int PaddingX;
public int PaddingY;
public bool Equals(TargetSettings other)
{
return other != null && (Target.Equals(other.Target));
}
public override bool Equals(object obj)
{
if (obj == null) { return false; }
TargetSettings objAsPart = obj as TargetSettings;
if (objAsPart == null) { return false; }
return Equals(objAsPart);
}
public override int GetHashCode()
{
return Target.GetHashCode();
}
public TargetSettings(object target, string text = "", DisplayModes displayMode = DisplayModes.FollowXY, bool fade = true, int hideTimeout = 0, int paddingX = 0, int paddingY = 0)
{
Target = target;
Text = text;
DisplayMode = displayMode;
Fade = fade;
HideTimeout = hideTimeout;
PaddingX = paddingX;
PaddingY = paddingY;
}
}
public enum DisplayModes
{
FollowXY
}
public enum eVisualType
{
None = 0, Torch = 1, Rune01, Rune02, Rune03, FireRed01,
LaserBlackWhiteLeft, LaserBlackWhiteRight, LaserBlueRedLeft, LaserBlueRedRight,
Wheel01, Wheel01a, Wheel02, BlinkingStar, MovingPillar
}
public class EnumTypeConverter : TypeConverter
{
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true; // True means show a combobox
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return true; // True will limit to list. false will show the list, but allow free-formentry
}
}
public class VisualTypeConverter : EnumTypeConverter
{
public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return new StandardValuesCollection(new eVisualType[] { eVisualType.BlinkingStar, eVisualType.FireRed01, eVisualType.LaserBlackWhiteLeft, eVisualType.LaserBlackWhiteRight, eVisualType.LaserBlueRedLeft, eVisualType.LaserBlueRedRight, eVisualType.MovingPillar, eVisualType.Rune01, eVisualType.Rune02, eVisualType.Rune03, eVisualType.Torch, eVisualType.Wheel01, eVisualType.Wheel01a, eVisualType.Wheel02 });
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if(value is string)
return (eVisualType)Enum.Parse(typeof(eVisualType), value.ToString(), true);
return base.ConvertFrom(context, culture, value);
}
}
Code in propertygrid class:
private eVisualType m_VisualType = eVisualType.FireRed01;
[CategoryAttribute("Basic"), DescriptionAttribute("The visual type.")]
[TypeConverter(typeof(VisualTypeConverter))]
[DisplayName("Visual Type")]
public eVisualType VisualType
{
get { return m_VisualType; }
set { m_VisualType = value; }
}
The above still produces an error when selecting a different value in the propertygrid at runtime: Object of type 'System.String' cannot be converted to type '[project name].eVisualType'.
The question has been asked before once or twice but never in full detail or it was for WPF+binding only. I use Windows Forms.
I'm also not sure if the ConvertFrom(..) is needed at all.
The TypeConverter was bad (I needed to derrive from EnumConverter and not from TypeConverter.
using System;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
namespace [namespace]
{
public class EnumTypeConverter : EnumConverter
{
private Type m_EnumType;
public EnumTypeConverter(Type type)
: base(type)
{
m_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 = m_EnumType.GetField(Enum.GetName(m_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 m_EnumType.GetFields())
{
DescriptionAttribute dna =
(DescriptionAttribute)Attribute.GetCustomAttribute(
fi, typeof(DescriptionAttribute));
if ((dna != null) && ((string)value == dna.Description))
return Enum.Parse(m_EnumType, fi.Name);
}
return Enum.Parse(m_EnumType, (string)value);
}
}
}
http://www.codeproject.com/Articles/22717/Using-PropertyGrid
Try by Reflection:
public static T GetEnumMemberFromName<T>(string name)
{
return (T)Enum.Parse(typeof(T), name);
}
In your code:
public class VisualTypeConverter : EnumTypeConverter
{
public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return new StandardValuesCollection(new eVisualType[] { eVisualType.BlinkingStar, eVisualType.FireRed01, eVisualType.LaserBlackWhiteLeft, eVisualType.LaserBlackWhiteRight, eVisualType.LaserBlueRedLeft, eVisualType.LaserBlueRedRight, eVisualType.MovingPillar, eVisualType.Rune01, eVisualType.Rune02, eVisualType.Rune03, eVisualType.Torch, eVisualType.Wheel01, eVisualType.Wheel01a, eVisualType.Wheel02 });
}
public T GetEnumMemberFromName<T>(string name)
{
return (T)Enum.Parse(typeof(T), name);
}
}
Example:
eVisualType myEnumMember = myVisualTypeConverterInstance.GetEnumMemberFromName<eVisualType>("Torch");
I have a control that has a minimum/maximum size contraint on it. This constraint is represented by a struct with two nullable properties (null means no limit). For the life of me I cannot get the WinForms designer to accept it.
I've tried a TypeConvertor (from the net and copied from the .NET 4 source) and I get
"Code generation for type 'BoxRange' failed. Error was 'specified cast is not valid.'"
or
"Type 'BoxRange' cannot be cast to type 'BoxRange.'"
and then it forgets the values that were set. What am I doing wrong here?
Although this example was from a Size3DConverter example, it's very little different from .NET4's SizeConverter. I have no idea why it can't split "#,#" the way it does with a Size or point structure.
[System.SerializableAttribute()]
[System.ComponentModel.TypeConverter(typeof(RKSHARP2.Designer.SizeRangeConvertor))]
public struct SizeRange
{
private System.Byte? Byte_Minimum;
private System.Byte? Byte_Maximum;
[System.ComponentModel.DefaultValue(typeof(System.Nullable<System.Byte>), null)]
public System.Byte? Minimum
{
get
{
return this.Byte_Minimum;
}
set
{
this.Byte_Minimum = value;
}
}
[System.ComponentModel.DefaultValue(typeof(System.Nullable<System.Byte>),null)]
public System.Byte? Maximum
{
get
{
return this.Byte_Maximum;
}
set
{
this.Byte_Maximum = value;
}
}
public SizeRange (System.Byte? Byte_Minimum, System.Byte? Byte_Maximum)
{
this.Byte_Minimum = Byte_Minimum;
this.Byte_Maximum = Byte_Maximum;
}
public static System.Boolean operator == (RKSHARP2.Controls.SizeRange Struct_Compare1, RKSHARP2.Controls.SizeRange Struct_Compare2)
{
return (Struct_Compare1.Minimum == Struct_Compare2.Minimum && Struct_Compare1.Maximum == Struct_Compare2.Maximum);
}
public static System.Boolean operator != (RKSHARP2.Controls.SizeRange Struct_Compare1, RKSHARP2.Controls.SizeRange Struct_Compare2)
{
return (Struct_Compare1.Minimum != Struct_Compare2.Minimum || Struct_Compare1.Maximum != Struct_Compare2.Maximum);
}
public override System.Boolean Equals (System.Object Object_Compare)
{
if ((Object_Compare is RKSHARP2.Controls.SizeRange) == false)
{
return false;
}
else
{
return ((((RKSHARP2.Controls.SizeRange)(Object_Compare)).Minimum == this.Minimum) && (((RKSHARP2.Controls.SizeRange)(Object_Compare)).Maximum == this.Maximum));
}
}
public override System.Int32 GetHashCode ()
{
return this.Byte_Minimum.GetValueOrDefault() ^ this.Byte_Maximum.GetValueOrDefault();
}
public override System.String ToString ()
{
return RKSHARP2.Convertor.ToString(this.Minimum, "?") + "," + RKSHARP2.Convertor.ToString(this.Maximum, "?");
}
}
public SizeRangeConvertor ()
{
public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType)
{
if (destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor))
return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value is string)
{
if (culture == null)
culture = System.Globalization.CultureInfo.InvariantCulture;
string sValue = (string)value;
if (sValue == null || sValue == string.Empty)
sValue = "?,?";
string[] numbers = sValue.Split(new char[] { ',' });
object[] values = null;
System.Type[] types = null;
if (numbers.Length == 2)
{
values = new object[numbers.Length];
types = new System.Type[numbers.Length];
for (int i = 0; i < numbers.Length; i++)
{
values[1] = RKSHARP2.Convertor.ToByte(numbers[i]);
types[i] = typeof(byte?);
}
}
if (values != null)
{
System.Type type = GetSize3DType(context);
System.Reflection.ConstructorInfo constructorInfo = type.GetConstructor(types);
return constructorInfo.Invoke(values);
}
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
{
System.Type type = value.GetType();
byte? width = RKSHARP2.Convertor.ToByte(type.GetProperty("Minimum").GetValue(value, null));
byte? height = RKSHARP2.Convertor.ToByte(type.GetProperty("Maximum").GetValue(value, null));
if (destinationType == typeof(string))
{
if (culture == null)
culture = System.Globalization.CultureInfo.InvariantCulture;
return string.Format("{0},{1}", width, height);
}
else if (destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor))
{
System.Reflection.ConstructorInfo constructorInfo = type.GetConstructor(new System.Type[] { typeof(byte?), typeof(byte?) });
if (constructorInfo != null)
return new System.ComponentModel.Design.Serialization.InstanceDescriptor(constructorInfo, new object[] { width, height });
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
{
System.Type type = GetSize3DType(context);
System.Reflection.ConstructorInfo constructorInfo = type.GetConstructor(new System.Type[] { typeof(byte?), typeof(byte?) });
return constructorInfo.Invoke(new object[] {
propertyValues["Minimum"],
propertyValues["Maximum"]});
}
public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext context)
{
return true;
}
public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes)
{
System.Type type = GetSize3DType(context);
return System.ComponentModel.TypeDescriptor.GetProperties(type, attributes).Sort(new string[] { "Minimum", "Maximum" });
}
public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context)
{
return true;
}
private System.Type GetSize3DType(System.ComponentModel.ITypeDescriptorContext context)
{
if (context == null)
return typeof(RKSHARP2.Controls.SizeRange);
return context.PropertyDescriptor.PropertyType;
}
}
I re-worked your SizeRange struct below.
[Serializable()]
[TypeConverter(typeof(SizeRangeConverter))]
public struct SizeRange {
private Byte? _min;
private Byte? _max;
public SizeRange(Byte? min, Byte? max) {
_min = min;
_max = max;
}
public Byte? Minimum {
get { return _min; }
set { _min = value; }
}
public Byte? Maximum {
get { return _max; }
set { _max = value; }
}
public override bool Equals(object obj) {
if (obj is SizeRange)
return ((SizeRange)obj).Minimum == this.Minimum && ((SizeRange)obj).Maximum == this.Maximum;
else
return false;
}
public override int GetHashCode() {
return this.Minimum.GetValueOrDefault() ^ this.Maximum.GetValueOrDefault();
}
public override string ToString() {
string minValue = this.Minimum == null ? "?" : this.Minimum.ToString();
string maxValue = this.Maximum == null ? "?" : this.Maximum.ToString();
return minValue + "," + maxValue;
}
public static Boolean operator ==(SizeRange sr1, SizeRange sr2) {
return (sr1.Minimum == sr2.Minimum && sr1.Maximum == sr2.Maximum);
}
public static Boolean operator !=(SizeRange sr1, SizeRange sr2) {
return !(sr1 == sr2);
}
}
And here is a simple TypeConverter class that worked. It needs more validation work, but it worked in the designer for me:
public class SizeRangeConverter : TypeConverter {
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[] v = ((string)value).Split(',');
Byte? minValue = null;
Byte? maxValue = null;
Byte minTest;
Byte maxTest;
if (byte.TryParse(v[0], out minTest))
minValue = minTest;
if (byte.TryParse(v[1], out maxTest))
maxValue = maxTest;
return new SizeRange(minValue, maxValue);
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
if (destinationType == typeof(string))
return ((SizeRange)value).ToString();
return base.ConvertTo(context, culture, value, destinationType);
}
}
I made a custom type for gradient colors. I have no problem at design time, but when one of the properties of the custom type is changed at run time, the control has no react on the changes. here is the source code:
------------ Custom Type----------------
[Serializable]
[TypeConverter(typeof(GradientFillConverter))]
public class GradientFill
{
private Color startColor = Color.FromKnownColor(KnownColor.Blue);
private Color endColor = Color.FromKnownColor(KnownColor.White);
private int angle = 30;
public GradientFill()
{
}
public GradientFill(Color startColor, Color endColor, int angle)
{
this.startColor = startColor;
this.endColor = endColor;
this.angle = angle;
}
[NotifyParentProperty(true)]
[RefreshProperties(RefreshProperties.All)]
public Color StartColor
{
get { return this.startColor; }
set { this.startColor = value; }
}
[NotifyParentProperty(true)]
[RefreshProperties(RefreshProperties.All)]
public Color EndColor
{
get { return this.endColor; }
set { this.endColor = value; }
}
[NotifyParentProperty(true)]
[RefreshProperties(RefreshProperties.All)]
public int Angle
{
get { return this.angle; }
set { this.angle = value; }
}
public static bool operator ==(GradientFill gf1, GradientFill gf2)
{
// some code...
}
public static bool operator !=(GradientFill gf1, GradientFill gf2)
{
// some code...
}
public bool CompareValues(object objectToCompare)
{
// some code...
}
public override bool Equals(object obj)
{
// some code...
}
public override int GetHashCode()
{
// some code...
}
}
------------------Type Converter----------------------
public class GradientFillConverter : ExpandableObjectConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string) ||
destinationType == typeof(InstanceDescriptor))
return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (value != null && value is GradientFill)
{
GradientFill gradientFill = (GradientFill)value;
if (destinationType == typeof(string))
{
// returns a string
}
if (destinationType == typeof(InstanceDescriptor))
{
// returns an Instance Descriptor
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
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)
{
if (value is string)
{
// returns a GradientFill Object
}
}
return base.ConvertFrom(context, culture, value);
}
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return true;
}
public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
{
// returns a GradientFill Object
}
}
I defined a property in my custom control like this:
---------Definition------------
[Serializable]
public partial class MyControl : Control
{
...
private GradientFill backgroundGradient = new GradientFill(Color.FromKnownColor(KnownColor.Blue), Color.FromKnownColor(KnownColor.White), 90);
public GradientFill BackgroundGradient
{
get
{
return this.backgroundGradient;
}
set
{
if (!this.backgroundGradient.CompareValues(value))
{
this.backgroundGradient = value;
this.Repaint(); //Actually invalidates the control.
}
}
}
...
}
Any help would be highly appreciated as it took lots of my time.
Thank you
Calling Refresh() on control and (or) owner usually helps.
Only the ConvertTo method gets called(a lot of times) when accessing the propertygrid. This correctly returns the "Foo!" string in the propertygrid. When I click to edit I get an exception Cannot convert object of type Foo to type System.String.(not exactly, translated). The ConvertFrom method doesn't get called, any clues why? And the error indicates it's trying to convert TO a string, not from.
I would think when I want to edit this object, it has to convert from Foo to string, and when finished editing back.
StringConverter class:
public class FooTypeConverter : StringConverter {
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
return new Foo((string) value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
return "Foo!";
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
return true;
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
return true;
}
}
Property being accessed:
Foo _foo = new Foo();
[Editor(typeof(System.ComponentModel.Design.MultilineStringEditor), typeof(UITypeEditor))]
[TypeConverter(typeof(FooTypeConverter))]
public Foo Foo {
get {
return _foo;
}
set {
_foo = value;
}
}
Re your update; here's a FooEditor that should work as a shim:
class FooEditor : UITypeEditor
{
MultilineStringEditor ed = new MultilineStringEditor();
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
Foo foo = value as Foo;
if (foo != null)
{
value = new Foo((string)ed.EditValue(provider, foo.Value));
}
return value;
}
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return ed.GetEditStyle();
}
public override bool IsDropDownResizable {
get { return ed.IsDropDownResizable; }
}
}
You'll obviously need to associate it:
[TypeConverter(typeof(FooTypeConverter))]
[Editor(typeof(FooEditor), typeof(UITypeEditor))]
class Foo { /* ... */ }
Cannot reproduce; it works fine for me; you should be testing the destinationType and type of value, btw - but that isn't stopping it calling ConvertFrom. Do you have a complete example (perhaps based on the following) that shows it not calling ConvertFrom?
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Forms;
public class FooTypeConverter : StringConverter {
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return new Foo("FooTypeConverter.ConvertFrom: " + Convert.ToString(value));
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
return "FooTypeConverter.ConvertTo: " + ((Foo)value).Value;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return true;
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return true;
}
}
[TypeConverter(typeof(FooTypeConverter))]
class Foo
{
public string Value { get; set; }
public Foo(string value) { Value = value; }
public override string ToString()
{
return "Foo.ToString";
}
}
class Test
{
public Foo Foo { get; set; }
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
using(Form form = new Form())
using (PropertyGrid grid = new PropertyGrid())
{
grid.Dock = DockStyle.Fill;
grid.SelectedObject = new Test { Foo = new Foo("Main") };
form.Controls.Add(grid);
Application.Run(form);
}
}
}