I'm trying to reproduce how Font class Nested Property is able to Invalidate() it's containing Control when the values are changed from the Properties window, without a need to lost focus on the form window.
Like if you change the Size property, the Form window will be automatically redrawn with a matching Font Size.
The complete code is at the end of this post
The test
NestedClassis very simple which has two Properties and uses a customExpandableObjectConverter.The
ContainerClassextends Control class. It invokesControl.Invalidate()method when theNestedClassvalue is changed. Properties of theNestedClassare drawn by overridingControl.OnPaint()method.
Resulting Control when loaded to the Designer : Initial Draw
Changing the values directly from the NestedClass parent property triggers the redraw : Updating from the parent property works
But changing the values from the nested property, does not redraw : Updating from the nested properties doesn't work
If later the Form window is clicked, Invalidate() is triggered : After Form window clicked
Font class does not require extra click in the design window in order to redraw the control. Can we achieve this same behavior with a custom class like this?
For reference I've already looked into :
INotifyPropertyChanged==> Problem here is that the PropertyChanged event is only subscribed by the container Control when the item is first added. If I close the Form[Design] window and reopens it, the event will not be subscribed again.RefreshProperties.AllorRefreshProperties.Repaintdoes not seems to do anything.
If all else fails, obviously clicking on the form designer window will solve the problem, but it bugs me that a built-in .NET class can do this, but a custom class cannot. Any suggestions are greatly appreciated.
The Code :
//The Nested Class
[TypeConverter(typeof(NestedClassTypeConverter))]
public class NestedClass
{
[NotifyParentProperty(true)]
public string CountryName { get; set; } = "Japan";
[NotifyParentProperty(true)]
public string Capital { get; set; } = "Tokyo";
}
//The Container Class
public class ContainerClass : Control
{
private NestedClass _country = new NestedClass();
[Category("_Data")]
public NestedClass Country
{
get => _country;
set
{
_country = value;
this.Invalidate();
}
}
protected override Size DefaultSize => new Size(100, 100);
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (Brush b = new SolidBrush(Color.Black))
{
e.Graphics.DrawString(Country.CountryName, this.Font, b, new PointF(10, 10));
e.Graphics.DrawString(Country.Capital, this.Font, b, new PointF(10, 50));
}
}
}
//TypeConverter for that fancy Expandable properties
public class NestedClassTypeConverter : ExpandableObjectConverter
{
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(',');
return new NestedClass
{
CountryName = v[0],
Capital = v[1]
};
}
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(NestedClass))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
return ((NestedClass)value).CountryName + "," + ((NestedClass)value).Capital;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}