C# NestedClass 在设计模式下扩展了 属性 到 Refresh/Invalidate 控件

C# NestedClass Expanded Property to Refresh/Invalidate Control in Design Mode

我正在尝试重现字体 class 嵌套 属性 如何能够 Invalidate() 当值从属性 window 更改时它包含控件,而不需要失去对表格 window 的关注。 就像如果您更改大小 属性,表单 window 将自动重绘为匹配的字体大小。

完整代码在文末post

  1. 测试NestedClass非常简单,它有两个属性并使用自定义ExpandableObjectConverter

  2. ContainerClass 扩展了控件 class。它在 NestedClass 值更改时调用 Control.Invalidate() 方法。 NestedClass 的属性是通过覆盖 Control.OnPaint() 方法绘制的。

加载到设计器时生成的控件:Initial Draw

直接从 NestedClass 父 属性 更改值会触发重绘:Updating from the parent property works

但是从嵌套 属性 更改值不会重绘:Updating from the nested properties doesn't work

如果稍后单击表单 window,将触发 Invalidate():After Form window clicked

字体 class 不需要在设计中额外单击 window 即可重绘控件。我们可以用这样的自定义 class 实现同样的行为吗?

作为参考,我已经研究过:

  1. INotifyPropertyChanged ==> 这里的问题是 属性Changed 事件仅在首次添加项目时由容器控件订阅。如果我关闭 Form[Design] window 并重新打开它,将不会再次订阅该事件。
  2. RefreshProperties.AllRefreshProperties.Repaint 似乎没有做任何事情。

如果一切都失败了,显然单击表单设计器 window 将解决问题,但令我烦恼的是内置的 .NET class 可以做到这一点,但是自定义 class 不能。非常感谢任何建议。

代码:

//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(); };
        }
    }
}

给你。又一次编程之旅结束。

编辑: 实际上我还在想这是否是实际的解决方案,因为如果我们指的是 Font class 元数据,它不一定使用 INotifyProperty 或任何其他类型的 EventHandler 作为 属性 改变了事件。但无论如何,这确实有效。