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
NestedClass
is very simple which has two Properties and uses a customExpandableObjectConverter
.The
ContainerClass
extends Control class. It invokesControl.Invalidate()
method when theNestedClass
value is changed. Properties of theNestedClass
are 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.All
orRefreshProperties.Repaint
does 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);
}
}