开发者

Difficulty with persisting a collection that references an internal property at design time in Winforms and .net

开发者 https://www.devze.com 2022-12-23 02:55 出处:网络
The easiest way to explain this problem is to show you some code: Public Interface IAmAnnoyed End Interface

The easiest way to explain this problem is to show you some code:

Public Interface IAmAnnoyed
End Interface

Public Class IAmAnnoyedCollection
    Inherits ObjectModel.Collection(Of IAmAnnoyed)
End Class

Public Class Anger
    Implements IAmAnnoyed
End Class

Public Class MyButton
    Inherits Button

Private _Annoyance As IAmAnnoyedCollection
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
Public ReadOnly Property Annoyance() As IAmAnnoyedCollection
    Get
        Return _Annoyance
    End Get
End Property

Private _InternalAnger As Anger
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
Public ReadOnly Property InternalAnger() As Anger
    Get
        Return Me._InternalAnger
    End Get
End Property

Public Sub New()
    Me._Annoyance = New IAmAnnoyedCollection
    Me._InternalAnger = New Anger
    Me._Annoyance.Add(Me._InternalAnger)
End Sub

End Class

And this is the code that the designer generates:

Private Sub InitializeComponent()
    Dim Anger1 As Anger = New Anger
    Me.MyButton1 = New MyButton
    '
    'MyButton1
    '
    Me.MyButton1.Annoyance.Add(Anger1)
    // Should be: Me.MyButton1.Annoyance.Add(Me.MyButton1.InternalAnger)
    '
    'Form1
    '
    Me.Controls.Add(Me.MyButton1)

End Sub

I've added a comment to the above to show how the code should have been generated. Now, if I dispense with the interface and just have a collection of Anger, then it persists correctly.

Any ideas?

Update 1

I'm sick of this. This problem was specifically about persisting an interface collection but now on further testing it doesn't work for a normal collection. Here's some even simpler code:

Public Class Anger
End Class

Public Class MyButton
    Inherits Button

Private _Annoyance As List(Of Anger)
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
Public ReadOnly Property Annoyance() As List(Of Anger)
    Get
        Return _Annoyance
    End Get
End Property

Private _InternalAnger As Anger
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
Public ReadOnly Property InternalAnger() As Anger
    Get
        Return Me._InternalAnger
    End Get
End Property

Public Sub New()
    Me._Annoyance = New List(Of Anger)
    Me._InternalAnger = New Anger
    Me._Annoyance.Add(Me._InternalAnger)
End Sub

End Class

The designer screws up the persistence code in the same way as the original problem.

Update 2

I've worked out what is going on. I wondered why sometimes it would work and not others. It boils down to the name that I give to the internal property and the collection.

If I rename the property 'Annoyance' to 'WTF', it will serialize correctly because 'WTF' is, alphabetically, after the name of the collection - 'InternalAnger'.

It looks like the serializer is creating instances of objects alphabetically and needs my internal property to be created by the time it comes to create the collection.

I can fix this with a rename, but that's a hack and I fear t开发者_如何学Gohat writing a custom serializer is a big job - which I've never done before.

Any ideas?

Update 3

I've answered the question with a hack. I'm fairly confident in it unless MS change the way that codedom serializers the designer code.


I'm a bit out of my element here, but shouldn't you have a private setter on that readonly property? I mean, it can't serialize what it can't set, correct?


Could the issue here be that you're adding an item to the collection in the control's constructor, which the designer is then separately serializing?

I think your commented "should have been generated" code is also incorrect. Since the control's constructor adds InternalAnger to the collection, why would you also want to see it in the InitializeComponent?


Take the DesignerSerializationVisibility.Content off the InternalAnger property and it should work as you expect.

For example (C# but the idea is the same)

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
        this.InternalAnger = new Anger();
        this.InternalAnger.SomeValue = 2;
        this.Angers.Add(this.InternalAnger);
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public List<Anger> Angers
    {
        get { return this.list; }
    }

    public Anger InternalAnger { get; set; }

    private List<Anger> list = new List<Anger>();
    private Anger _InternalAnger;
}

public class Anger
{
    public Anger() { }

    public int SomeValue { get; set; }
}

generates:

WindowsFormsApplication1.Anger anger1 = new WindowsFormsApplication1.Anger();
anger1.SomeValue = 2;
this.userControl11.Angers.Add(anger1);
this.userControl11.InternalAnger = anger1;

which is, I think, what you want.


As i said in the OP, the problem boils down to the name that I give to the internal property and the collection.

Without delving into a custom codedom serializer, the simple solution is to make sure the internal property's name is alphabetically before any other property that will reference it.

I do this by retaining the original property name 'InternalProperty', but I disable serialization and refer it to a proxy property, that is cunningly named, and is serialized.

Private _InternalProperty
Public ReadOnly Property InternalProperty
    Get
        Return Me._ProxyInternalProperty 
    End Get
End Property

<Browsable(False), EditorBrowsable(Never), DesignerSerializationVisibility(Content)> _
Public ReadOnly Property _ProxyInternalProperty
    Get
        Return Me._InternalProperty
    End Get
End Property

This is a hack, but its better than renaming my property AInternalProperty. Also, the user will never see _ProxyInternalProperty because it's hidden, and even if they did discover it, there is no danger in referencing it.

0

精彩评论

暂无评论...
验证码 换一张
取 消