当从"属性"窗口更改值时,我正在尝试重现Font类Nested Property如何能够Invalidate()
它包含Control,而不需要失去对窗体窗口的关注。与更改"大小"属性类似,"窗体"窗口将自动重新绘制为匹配的"字体大小"。
完整的代码在这个后的末尾
-
测试
NestedClass
非常简单,它有两个属性,并使用自定义的ExpandableObjectConverter
。 -
ContainerClass
扩展了Control类。当NestedClass
值发生变化时,它调用Control.Invalidate()
方法。NestedClass
的属性是通过重写Control.OnPaint()
方法绘制的。
加载到设计器时的结果控件:初始绘制
直接从NestedClass
父属性更改值会触发重绘:从父属性更新工作
但是,更改嵌套属性的值并不能重新绘制:从嵌套属性更新不起作用
如果稍后点击Form窗口,则触发Invalidate()
:Form窗口点击后
Font类不需要在设计窗口中额外单击即可重新绘制控件。我们能用这样的自定义类实现同样的行为吗?
作为参考,我已经研究过:
INotifyPropertyChanged
==>这里的问题是PropertyChanged事件仅在首次添加项时由容器控件订阅。如果我关闭Form[Design]窗口并重新打开它,则不会再次订阅该事件- CCD_ 12或CCD_
如果所有其他操作都失败了,显然单击表单设计器窗口可以解决问题,但内置的.NET类可以做到这一点,而自定义类不能。如有任何建议,我们将不胜感激。
代码:
//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);
}
}
有趣的是,经过一天的搜索,没有任何运气,终于发布了我的问题,突然我发现了另一个线程,它用答案讲述了同样的问题-->用答案线程
只是重申一下,显然INotifyPropertyChanged
仍然是我问题的答案。不同的是,我之前订阅了ContainerClass
构造函数上的事件。类似于:
public ContainerClass()
{
NestedClassInstance.PropertyChanged += delegate { this.Invalidate(); };
}
而显然需要做的是在NestedClass
setter主体上订阅或重新订阅该事件。类似于:
public class ContainerClass : Control
{
private NestedClass _nestedClassInstance = new NestedClassInstance();
public NestedClass NestedClassInstance
{
get => _nestedClassInstance;
set
{
if (_nestedClassInstance != null)
_nestedClassInstance.PropertyChanged -= delegate { this.Invalidate(); };
_nestedClassInstance = value;
_nestedClassINstance.PropertyChanged += delegate { this.Invalidate(); };
}
}
}
好了,又一次编码之旅结束了。
EDIT:实际上,我仍在考虑这是否是实际的解决方案,因为如果我们引用Font类元数据,它不一定会对属性更改事件使用INotifyProperty
或任何其他类型的EventHandler。但不管怎样,这确实奏效了。