【问题标题】:Is there a way to use "ShouldSerialize[PropertyName]" on a Property of a Property?有没有办法在属性的属性上使用“ShouldSerialize [PropertyName]”?
【发布时间】:2016-03-29 09:34:36
【问题描述】:

我面临的问题是,当我有一个具有这样的默认值的属性的类时:

public class Border {
    public Border() {
        InitializeAdornment();
    }

    [DefaultValue(true)]
    public bool Visible {
        get;
        set;
    }

    private void InitializeAdornment() {
        Visible = true;
        // Some initialization code here that don't do anything with the property...
    }
}

然后将其用作带有DesignerSerializationVisibility.Content 属性的UserControl 的属性,如下所示:

public class Label : HitAbleControl, IText, IBorder {
    public Label() {
        InitializeAdornments();
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public Border Border {
        get {
            return __border;
        }
        set {
            __border = value;
        }
    }

    private Border __border;

    private void InitializeAdornments() {
        Border = new Border {
            Visible = false
        };
        // Some initialization code here that don't do anything with the property...
    }
}

如果我使用 Visual Studio 设计器设置 label1.Border.Visible = true,它不会被序列化,但在这种情况下,它的默认值应该是 false 而不是 true。

我知道我可以将Border 类更改为始终序列化,或者检查默认值是真还是假,但我想知道是否有类似ShouldSerialize[PropertyName] 的属性此序列化问题的属性或其他解决方法,无需更改序列化程序。

我搜索了几个小时试图解决这个问题,但没有求助于 CodeDomSerializer,或者至少理解了为什么视觉工作室设计师不序列化这个问题。

任何帮助将不胜感激,谢谢!

【问题讨论】:

  • 你真的需要Border 属性可以公开设置吗?
  • @Ivan Stoev 在我的原始代码中,Label 类实现了 INotifyPropertyChanged,为简单起见,我将其更改为仅在此处发布支持字段
  • INPC 不是必需的,我的问题是它是否可以从外部设置。通常当你有类实例时,你在构造函数内部设置它们,并且只让它们的属性可以从外部设置。我问是因为它是必不可少的。
  • 用户应该能够将其设置为另一个 Border 实例(即来自另一个 UI 主题),而无需复制每个 Border 属性值,但无论如何,我尝试在没有设置器的情况下对其进行序列化并且没有成功。 VS 一直认为它在 Visible 上有默认值
  • 当然不能。我之前的问题只是想知道如何进行。很快,我们需要以某种方式从外部为Border 类提供默认值,没有别的办法。

标签: c# winforms visual-studio serialization windows-forms-designer


【解决方案1】:

首先让我声明没有ShouldSerialize[PropertyName] 或其他标准机制来控制嵌套对象属性的行为。

您所要求的可以完成,但需要在包含的类中添加一些额外的状态。如果您不提供公共设置器,它可能会更简单,因此您可以传递某种所有者接口或从Border 类调用的东西。由于这似乎不是一个选项,因此需要一些额外的工作来克隆 setter 中传递的值。

关键是要有一个默认值作为类属性,而不是静态常量。请注意,通过设计器或代码设置值都没有关系,序列化程序使用DefaultValueAttribute 来确定值是否需要序列化。顺便说一句,最简单的方法可能是删除该属性,添加一个名为ShouldSerializeVisiblebool 方法并始终返回true。这是一个更高级的实现:

[TypeConverter(typeof(ExpandableObjectConverter))]
public class Border
{
    public Border()
    {
        InitializeAdornment();
    }

    public Border Clone() { return (Border)MemberwiseClone(); }

    public bool Visible { get; set; }

    private bool ShouldSerializeVisible() { return Visible != DefaultVisible; }
    private void ResetVisible() { Visible = DefaultVisible; }

    [Browsable(false)]
    [RefreshProperties(RefreshProperties.All)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public bool DefaultVisible { get; set; }

    private void InitializeAdornment()
    {
        Visible = DefaultVisible = true;
        // Some initialization code here that don't do anything with the property...
    }
}

注意DefaultValueAttribute 被删除并替换为私有ShouldSerialize[PropertyName]Reset[PropertyName] 方法加上DefaultVisible 实例成员。

Label 类中的属性将是:

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Border Border
{
    get { return __border; }
    set
    {
        if (value != null)
        {
            value = value.Clone();
            value.DefaultVisible = false;
        }
        __border = value;
    }
}

缺点是传递的Border实例必须克隆到setter中。

【讨论】:

  • IMO,设计器总是序列化属性的缺点比克隆实例并有另一个属性来告诉默认值要考虑的缺点更容易接受,所以我最终使用了private bool ShouldSerializeVisible() => true; .我的小希望是以某种方式以声明的方式告诉设计师,但正如你所说,没有。所以我将标记为答案,正如你所说的唯一可能的方法。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2013-04-21
  • 2011-06-02
  • 2019-06-11
  • 2013-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-10
相关资源
最近更新 更多