UserControl collection not marked as serializable
Asked Answered
F

3

12

I must be missing something really obvious. I'm quite new to C# but have been programming in C/C++ for years, so sorry if it IS something blindingly obvious ;)

[See Edit for newer problems]

I'm trying to create a node containing UserControl. I have the Control appearing in the WinForm designer and I can add nodes to it. However when I try and run the code I get the following error:

Code generation for property 'Nodes' failed. Error was: 'Type App.Node' in Assembly 'App, version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.

and then none of the nodes I added show up.

This is beginning to drive me mad as, as far as I can see, it IS marked as serializable.

The node is defined as follows:

[Serializable]
public class Node : MarshalByRefObject
{
    public Node()
    {
    }

    public Node( String text )
    {
        this.Text       = text;
        this.Checked    = false;
        this.Complete   = false;
    }

    public String       Text        { get; set; }
    public bool         Checked     { get; set; }
    public bool         Complete    { get; set; }
    public bool         Selected    { get; set; }
};

I then define a "Collection" as follows:

[Serializable]
public class NodeCollection : List< Node >
{
    public NodeCollection() :
        base()
    {
    }
};

Both the collection and the node itself have the "Serializable" attribute set as you can see.

The Nodes property mentioned in the error is defined as follows

    private NodeCollection      mNodes  = new NodeCollection();

    [Category( "Behavior" )]
    [Description( "Nodes" )]
    public NodeCollection Nodes
    { 
        get
        {
            return mNodes;
        }
    }

So has anyone got any idea whats I'm doing wrong here?

Edit: In response to Archeg's comments this is my UserControl:

public partial class Control : UserControl
{
    public Control()
    {
        InitializeComponent();
    }

    protected override void OnPaint( PaintEventArgs pe )
    {
        Graphics graph  = pe.Graphics;

        int rowHeight   = Font.Height + 2;

        if ( Nodes != null )
        {
            int yPos    = 0;
            foreach( Node node in this.Nodes )
            {
                // Calculate my various bounding boxes.
                Rectangle nodeBounds    = new Rectangle( Bounds.Left, yPos, Bounds.Width, rowHeight );
                Rectangle lightBounds   = new Rectangle( Bounds.Right - Font.Height, yPos, rowHeight, rowHeight );
                Rectangle spannerBounds = new Rectangle( lightBounds.Left - Font.Height, yPos, rowHeight, rowHeight );
                Rectangle checkBoxBound = new Rectangle( 32, yPos, rowHeight, rowHeight );
                Rectangle textBounds    = new Rectangle( checkBoxBound.Right, yPos, Bounds.Width - (rowHeight * 2) - checkBoxBound.Right, rowHeight );

                // Draw selection box.
                Brush textColour    = Brushes.Black;
                if ( node.Selected )
                {
                    graph.FillRectangle( Brushes.Blue, nodeBounds );
                    textColour      = Brushes.Yellow;
                }

                // Draw node text.
                graph.DrawString( node.Text, Font, textColour, textBounds );

                // Draw Red/Green light
                Image[] lightImages = new Image[] { CompleteLightImage, InCompleteLightImage };
                Image lightImage    = lightImages[node.Complete ? 1 : 0];
                if ( lightImage != null )
                {
                    graph.DrawImage( lightImage, lightBounds );
                }

                // Draw Spanner Icon
                if ( SettingsImage != null )
                {
                    graph.DrawImage( SettingsImage, spannerBounds );
                }
                // Draw check box.
                VisualStyleRenderer renderer    = null;
                VisualStyleElement  ve          = node.Checked ? VisualStyleElement.Button.CheckBox.CheckedPressed : VisualStyleElement.Button.CheckBox.CheckedNormal;
                if (VisualStyleRenderer.IsElementDefined( ve ))
                {
                    renderer = new VisualStyleRenderer( ve );
                }

                if ( renderer != null )
                {
                    renderer.DrawBackground( graph, checkBoxBound );
                }
                else
                {
                    ControlPaint.DrawCheckBox( graph, checkBoxBound, node.Checked ? ButtonState.Checked : ButtonState.Normal );
                }
                yPos    += Font.Height;
            }
        }
    }

    private NodeCollection      mNodes  = new NodeCollection();

    [Category( "Behavior" )]
    [Description( "Nodes" )]
    [DesignerSerializationVisibility( DesignerSerializationVisibility.Content )]
    [MergableProperty( false )]
    [Bindable( false )]
    public NodeCollection Nodes
    { 
        get
        {
            return mNodes;
        }
    }

    public Image CompleteLightImage         { get; set; }
    public Image InCompleteLightImage       { get; set; }
    public Image SettingsImage              { get; set; }
}

I have made some modifications since I originally posted generally relating to the "DesignerSerializationVisibility" attribute which has helped but I am now getting the following build error:

error MSB3103: Invalid Resx file. Could not load type App.Node, App, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null which is used in the .RESX file. Ensure that the necessary references have been added to your project.

Edit 2: Its worth noting that my problems only occur when I add a bunch of Nodes in the designer then I get the above Resx error. If I add the nodes manually from code then it all works as I'd expect ...

Falsetto answered 20/3, 2012 at 14:49 Comment(4)
What control do you use? Is it custom?Erubescence
@Archeg: Yup its totally custom derived directly from UserControlFalsetto
Can you post your UserControl code?Erubescence
@Archeg: Added code and updated slightly with my latest issues ...Falsetto
A
28

I believe that you have this problem because Designer automatically tries to serialize all public UserControl properties. If this property is not needed for your custom UserControl design time support, then you can Add "DesignerSerializationVisibility" attribute:

[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 

or simply omit the get{} and set{} methods of the property and use it as a public field.

Hope it helps!

Acnode answered 26/6, 2012 at 4:25 Comment(0)
E
2

That is very strange. I've reproduced it locally on my note, then moved Node class to another project and it worked. I think it is something with circular dependencies - it tries to find your assembly (in my case it was WindowsFormsApplication1) built, but it cannot as it is building it right now.

Hope that help you, and I'll try to dig further.

Update Another way to solve it: remove [Serialization] attribute from the Node class. In that case you will force VS instead of generating Node contents in resx file, just generate such code:

// Form1.designer.cs:
Node node1 = new Node(); 
node1.Checked = false;
node1.Complete = false;
node1.Selected = false;
node1.Text = null;
this.contr1.Nodes.Add(node1);
Erubescence answered 20/3, 2012 at 15:54 Comment(5)
Added another solution to the problemErubescence
Added an extra edit at the bottom. Basically the problem lies with something going wrong with the designer ... dunno if that helps you at allFalsetto
Yep, seems that VS fails to generate serialization data on the control it is building. Do u mind if I post that as a bug to Microsoft, as I wasn't been able to find some meaningfull explanation?Erubescence
Dunno how I failed to spot that update ... but yeah i really want to be able to use the designer ...Falsetto
If you think it will make any difference, go fer it (IME you need to hold an MS employee down in person to get any use out of bug reports ;))Falsetto
R
0

I solved this making sure that the property class has a parameterless constructor. I have had similar problems when serializing classes to files where the class does not have a parameterless constructor.

    [Serializable]
    public class ChartTitle {
        public Color BackColor { get; set; }
        public string Text { get; set; }

        public ChartTitle() {   // <--- Make sure have parameterless constructor
            BackColor = SystemColors.Control;
            Text = String.Empty;
        }
    }
Russelrussell answered 28/10, 2021 at 5:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.