0

I have created a C# User Control in Visual Studio 2019. It has a property called "BoundLayout".

    public Layout BoundLayout
    {
        get
        {
            return _Layout;
        }

        set
        {
            _Layout = value as Layout;
            if (_Layout == null)
            {
                MessageBox.Show("Value submitted is not of type 'LAYOUT'","Invalid Value",MessageBoxButtons.OK,MessageBoxIcon.Error);
            }
            else
            {
                InitializeControl();
            }
        }
    }

If a program attempts to assign an incompatible value to the property an error message is displayed in a MessageBox. This works correctly.

What is very strange is that when ever I BUILD (not RUN) the project this error message is displayed in its modal MessageBox which must be acknowledged before you can return to Visual Studio. This occurs when building in both Debug and Release modes. A break point added to the property set code does not get triggered. The build completes successfully without errors or warnings and I can run the application.

The application, including this User Control operates as intended. I have never encountered this behavior before. Has anyone else?

Compiler 'Build' results

The complete (still in development) code for the User Control:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Dispatcher
{
    public partial class DivisionModuleGrid : UserControl
    {
        private Layout _Layout = null;

        private ObservableListSource<LayoutDivision> _LayoutDivisions;

        private DivisionModulesList _activeDivision = null;

        private int _divisionCount;

        public Layout BoundLayout
        {
            get
            {
                return _Layout;
            }

            set
            {
                _Layout = value as Layout;
                if (_Layout == null)
                {
                    MessageBox.Show("Value submitted is not of type 'LAYOUT'","Invalid Value",MessageBoxButtons.OK,MessageBoxIcon.Error);
                }
                else
                {
                    InitializeControl();
                }
            }
        }

        public DivisionModulesList ActiveDivision
        {
            get
            {
                return _activeDivision;
            }

            set
            {
                _activeDivision = value as DivisionModulesList;

                if (_activeDivision != null)
                {
                    lbl_ActiveDivision.Text = _activeDivision.DivisionName;
                }
                else
                {
                    lbl_ActiveDivision.Text = "-No Active Division-";
                }
            }
        }


        public DivisionModuleGrid()
        {
            InitializeComponent();
        }


        private void InitializeControl()
        {
            _LayoutDivisions = _Layout.LayoutDivisions;
            _divisionCount = _LayoutDivisions.Count;

            tbx_LayoutName.Text = _Layout.LayoutName;

            //  Grid Layout divide into Rows & Columns
            int tlp_rows = _divisionCount / 3;



            TableLayoutPanel tlp = (TableLayoutPanel)(Controls.Find("tlp_DivisionGrid", false)[0]);
            DivisionModulesList dml;

            foreach (LayoutDivision ld in _LayoutDivisions)
            {
                dml = new DivisionModulesList(ld);
                dml.BoundDivision = ld;

                tlp.Controls.Add(dml);

            }


        }

        private void Tlp_DivisionGrid_Paint(object sender, PaintEventArgs e)
        {

        }
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
Ross
  • 78
  • 7
  • 2
    the best way out of the situation would be to figure out how to handle `null` value gracefully (e.g. use `new Layout()` instead). I would say MessageBox.Show() should not be in setter of UserControl property at all - just throw an exception, document it, and it is not your problem anymore. setter is triggered from designer or Properties window, I guess – ASh Dec 04 '19 at 18:59
  • To add to the comment by @ASh, you're trying to do too much here. The `UserControl` is technically another `UI` control, and should behave as such. It's job should be to display data it's given, if it can. Don't try to display error messages. – Sach Dec 04 '19 at 19:03

2 Answers2

2

When creating your own Properties for a UserControl, and you use this UserControl at another place, the Designer is generating Code for this property like:

 yourControl.BoundLayout = null;

Search for it in the Designer.cs file; it will solve the problem until the code is regenerated.

If the designer displays your control it runs your code, and displays your MessageBox at Design-Time (not Build or Runtime). Do avoid this forever put

 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)]
 public Layout BoundLayout

on all your properties, if you intend to modify it by code only and not within the designer, especially if null is an invalid value.

When using your UserControl again at another place it will not create this BoundLayout=null again any more, but for existing references to your UserControl, you have to remove this line manually.

Holger
  • 2,446
  • 1
  • 14
  • 13
0

General Advice: In general, you should throw an exception rather than showing a MessageBox. But it has nothing to do with the problem.

You have defined the initial value of the property as null. This means, when you drop an instance of the control on the form, it will serialize the null assignment as well and generates a code like this:

userControl1.Name = "userControl1";
userControl1.Size = new Size( 100, 100);
userControl1.SomeProperty = null;
...

To solve the problem, you can use either of the following options:

  • Prevent designer from serializing the value if it's null.
  • Disable the validation at design time.
  • Prevent designer from serializing the value always. (as also proposed by Holger)

Example 1 - Prevent designer from serializing the value if it's null

You can set the default value of the property as null using DefaultValue attribute. Then it will not be serialized by designer when the value of the property is null when you drop the control on the form or when assign null value to the property at design time.

private SomeType someProperty = null;
[DefaultValue(null)]
public SomeType SomeProperty 
{
    get { return someProperty; }
    set
    {
        if (value == null)
            MessageBox.Show("Why null????");
        else
            someProperty = value;
    }
}

Example 2 - Disable the validation at design time

You can check if the control is in DesignMode then stop validation:

private SomeType someProperty = null;
public SomeType SomeProperty 
{
    get { return someProperty; }
    set
    {
        if (value == null && !DesignMode)
            MessageBox.Show("Why null????");
        else
            someProperty = value;
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Note: The DesignMode property is not valid inside the constructor, it's set after the constructor has finished. But the usual designer pattern is to call InitializeComponent in the constructor. It's good in eventhandlers or eventhandler-overrides like OnLoad(). – Holger Dec 04 '19 at 20:07
  • If you apply both of your things and avoid the Designer-generated assignment of null, than yes. Cause it's not called in InitializeComponent(). Microsoft should have made this DesignMode property static, cause it has to have the same value for all Controls, by definition. But they set it one by one. – Holger Dec 04 '19 at 20:16
  • @Holger You don't need to apply both. One is enough. To make it clearer, I edited the answer. – Reza Aghaei Dec 04 '19 at 20:20
  • @Holger Your point about `DesignMode` in constructor is correct, but you for your information, designer doesn't call the `InitializeComponent` It just parses that method. Look at [my answer here](https://stackoverflow.com/a/32299687/3110834) and see the example which is full of errors but designer works well. – Reza Aghaei Dec 04 '19 at 20:22
  • OK, tried it and you are right. But this is only valid for the currently designed control itself. A referenced control - and the base class control are actually compiled and executed. So his Layout.InitialiedComponent is really executed with DesignMode =false on DesignTime. So your "works in this case" was right but a little mysterious/unspecific. – Holger Dec 04 '19 at 21:39
  • @Holger As I have already mentioned in the linked post, the code in the constructor of the base class will execute. This is how the windows forms designer works. It creates an instance of the base class which you are designing (it means it runs the constructor of its base), then parses the InitializeComponent and setup the controls. – Reza Aghaei Dec 04 '19 at 21:43
  • 1
    I already confirmed, you are right. I didn't say anything contracdicting. – Holger Dec 04 '19 at 21:57
  • @Ross I've noticed you have never upvoted a post. Do you mind to learn about it? At first place it will help future readers to identify good answers faster, and second, it will be like a token of appreciation and will add reputation score to answerers. To learn about it, take a [tour]. JFYI You can upvote a post by click on up arrow near the post. It's not compulsory at all, but is recommended. Upvoting is not limited to answers of your questions, whenever you find a useful post in the stackoverflow which helps you, you can upvote it. – Reza Aghaei Feb 01 '20 at 18:14