Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UserControl collection not marked as serializable

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 ...

like image 790
Goz Avatar asked Mar 20 '12 14:03

Goz


2 Answers

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!

like image 177
Rzassar Avatar answered Oct 16 '22 06:10

Rzassar


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);
like image 27
Archeg Avatar answered Oct 16 '22 08:10

Archeg