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.
精彩评论