.NET Windows Forms design time rules - c#

I have an object that starts a thread, opens a file, and waits for input from other classes. As it receives input, it writes it to disk. Basically, it's a thread safe data logging class...
Here's the weird part. When I open a form in the designer (Visual Studio 2008) that uses the object the file gets created. It's obviously running under the design time vhost process...
The odd thing is I've not been able to reproduce the issue in another project. I'm not sure what the rules are for code that gets executed in the designer and code that does not. For example, creating a file in a Windows Forms constructor doesn't actually create the file at design time...
What is the explanation? Is there a reference?

The constructor of a control or form does not get executed when editing that class in the designer (nor does OnLoad get called). I've occasionally used this to set one value in the designer (eg. making its child controls all Visible in the designer) but override some of them to a different default value in the constructor (eg. hiding certain child controls which will only show in certain circumstances, such as an indicator on a status bar).
However, the constructor does get executed if the control is placed as a child on another control or form in the designer. OnLoad gets executed as well. This may be how your logging code was getting accidentally triggered in the designer.
For detecting design vs runtime, an answer to another question has screenshots of some emperical tests showing the values returned by some common approaches. It appears that a child control of a child control (two levels down) of the form or control being edited in the designer sees its own DesignMode == false, so the normal property check will fail to protect code (eg. in the OnLoad method) for controls nested within a control added in the designer. If you were checking DesignMode as one would expect, it could be the nesting which caused it to get around that check. It also always sees DesignMode == false within the constructor.
Also, note that the LicenseManager.UsageMode check only sees DesignTime within the constructor; when OnLoad is called it is within a RunTime LicenseContext. The most complete solution seems to be to check LicenseManager.UsageMode in the constructor of the control or form (or component) and save the setting to a member variable or property which you can check later to avoid running code that should never run in the designer even when nested. There's also another approach in another answer to that other question which accounts for nesting but only works outside the constructor.

You can check the UsageMode of the LicenseManager, to check if the code is in design time or not.
System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime
Here is a quick example:
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace Test
{
public class ComponentClass : Component
{
public ComponentClass()
{
MessageBox.Show("Runtime!");
}
}
}
When this component gets add to your form in the designer, you will immediatly get a message box.
To prevent this you can add a simple if statement to check if the code is not in design time
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace Test
{
public class ComponentClass : Component
{
public ComponentClass()
{
if (LicenseManager.UsageMode != LicenseUsageMode.Designtime)
{
MessageBox.Show("Runtime!");
}
}
}
}
After adding the if statement, the messagebox no longer appears when the component is added to the form via the designer.

Well, since this has been resurrected anyway, here's the function I use to determine whether I'm in design mode:
public static bool IsAnyInDesignMode(Control control){
while(control != null){
if(control.Site != null && control.Site.DesignMode)
return true;
control = control.Parent;
}
return false;
}
This handles the case where the control is a child created by another control. The DesignMode property is only set for controls created by the designer itself.

You could also use this to check if the Visual Studio Designer is running the code:
public static bool DesignMode
{
get { return (System.Diagnostics.Process.GetCurrentProcess().ProcessName == "devenv"); }
}
Then in Form_Load:
if (!DesignMode)
{
// Run code that breaks in Visual Studio Designer (like trying to get a DB connection)
}
However, this is less elegant than using the LicensManager.UsageMode, but it works (until Microsoft changes the name of the process Visual Studio runs under).

There are some things you shouldn't do with the designer. I don't have any hard evidence, but I found that the Windows Forms designer hates it when you take away the default constructor from it. Just go ahead and create new overloads, but leave the empty constructor in place.
Also try to avoid doing Form_Load events in base classes you inherit from.

Related

Why does VS2017 keep losing my derived controls?

In my app namespace = DRT, I'm creating control classes (e.g., button, textbox) that derive fron their corresponding Windows control classes, e.g.,
internal abstract class DRT_Button_Abstract : Button
{
....
}
internal class DRT_Button_CancelSearch : DRT_Button_Abstract
{
....
}
internal class DRT_Button_StartSearch : DRT_Button_Abstract
{
....
}
All together I currently have 13 derived classes that derive either from one of my abstracts or from a Windows control class. After a successful build, I see my control classes (e.g., DRT_Button_CancelSearch and DRT_Button_StartSearch) on the Toolbox and I successfully drop them onto my main form. All is fine for a while, but ultimately, I'll go to open the main form.cs [Design] (i.e., the UI designer) and it will show the error The variable '{control property name}' is either undeclared or was never assigned. for some combination of my controls.
When I examine the main form Designer.cs file, the expected code for all the controls is present EXCEPT for the expected new statement. They are not present in the main form Designer.cs file. For example, I expect to see this.drt_Button_CancelSearch = new DRT.DRT_Button_CancelSearch(); but its missing
I've tried ignoring the error, proceeding to the UI designer windows to re-apply the lost controls, but the problem just repeats with the newly applied control
What the heck is going on? Is there a way to recover from this situation?
This is most likely a problem of the Designer not being able to clear/reload its cache. There is not much you can do. In the past I:
closed and reopened all designers that have user controls
put all the controls in a separate project (in the same solution)
put all the controls in a separate solution/Visual Studio instance and set a proper reference to the controls' dll (or even nuget package)
With the first two options I have had varying success. Reopening the designer is not very convenient and doesn't work.
That last option is the best but also the most annoying because every adjustment requires a rebuild of the project and update of the reference/package.
Also make sure that all controls that you create have public default constructors and function well when this constructor is used.

Why can't I access a textbox on another form?

Another beginners question here, coming from Delphi you always have access to another forms controls but in my early days with C# / Visual Studio I am faced with a problem which is proving more difficult than it should be.
I have been getting started by writing a simple notepad style application, I have my main form and a secondary form used to select a line number.
From my main form, I call the goto line number form like so:
private void mnuGoTo_Click(object sender, EventArgs e)
{
Form gotoForm = new GoToForm();
var dialogResult = gotoForm.ShowDialog();
if (dialogResult == DialogResult.OK)
{
// get the text from gotoForm.editLineNumber.Text
MessageBox.Show(gotoForm.editLineNumber.Text); // doesn't work
}
}
As you can see from the commented code I have a TextBox control called editLineNumber which is on my other form (GoToForm).
My problem (and likely a beginner question) is why does editLineNumber not show in the intellisense menu when I type gotoForm.?
How do I access the editLineNumber control from the form GoToForm?
The error message for the // doesn't work commented line is:
Error CS1061 'Form' does not contain a definition for 'editLineNumber'
and no extension method 'editLineNumber' accepting a first argument of
type 'Form' could be found (are you missing a using directive or an
assembly reference?)
Unless I am missing something obvious, why are controls that exist on another form not publically available to all forms? I understand that C# / Visual Studio is different to Delphi but the way Delphi lets you access and see all controls on all forms without any extra works seems more logical to me. Why does C# / Visual Studio hide controls on secondary forms, for what purpose can this be beneficial?
The editLineNumber control is private. You can change it to be public, but that's discouraged.
Instead, create a property in GoToForm that returns the value you want.
public string LineNumber
{
get { return this.editLineNumber.Text; }
}
Now you can just reference your new property:
if (dialogResult == DialogResult.OK)
{
MessageBox.Show(gotoForm.LineNumber);
}
Especially if you're new to C# and WinForms, don't touch designer code with a 10 foot pole. As Grant Winney said, use a property:
public string GetLineNumberText
{
get { return this.editLineNumber.Text; }
}
It should be mentioned that it's important to be aware of the directional nature of forms. That is to say, if I make Form1 and then define Form2 inside of it, you'll want to be careful how you communicate between the two forms. Properties are nearly always a better alternative than accessing form elements directly - it makes the code very difficult to change otherwise. If you, for example, removed editLineNumber from the other form or renamed it, every instance in the parent form would have to be edited. If you use a property, then you only have to change it in one place.

VS 2008 designer and usercontrol

I have created a custom data grid control. I dragged it on windows form and set its properties like column and all & ran the project. It built successfully and I am able to view the grid control on the form.
Now if i try to view that form in designer, I am getting following error..
Object reference not set to an instance of an object.
Instances of this error (1)
1. Hide Call Stack
at Microsoft.VisualStudio.Design.Serialization.CodeDom.XML.CodeDomXmlProcessor.GetMemberTargetObject(XmlElementData xmlElementData, String& member)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.XML.CodeDomXmlProcessor.CreateAssignStatement(XmlElementData xmlElement)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.XML.CodeDomXmlProcessor.XmlElementData.get_CodeDomElement()
at Microsoft.VisualStudio.Design.Serialization.CodeDom.XML.CodeDomXmlProcessor.EndElement(String prefix, String name, String urn)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.XML.CodeDomXmlProcessor.Parse(XmlReader reader)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.XML.CodeDomXmlProcessor.ParseXml(String xmlStream, CodeStatementCollection statementCollection, String fileName, String methodName)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomParser.OnMethodPopulateStatements(Object sender, EventArgs e)
at System.CodeDom.CodeMemberMethod.get_Statements()
at System.ComponentModel.Design.Serialization.TypeCodeDomSerializer.Deserialize(IDesignerSerializationManager manager, CodeTypeDeclaration declaration)
at System.ComponentModel.Design.Serialization.CodeDomDesignerLoader.PerformLoad(IDesignerSerializationManager manager)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomDesignerLoader.PerformLoad(IDesignerSerializationManager serializationManager)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomDesignerLoader.DeferredLoadHandler.Microsoft.VisualStudio.TextManager.Interop.IVsTextBufferDataEvents.OnLoadCompleted(Int32 fReload)
If I ignore the exception, form appears blank with no sign of grid control on it. However I can see the code for the grid in the designer file.
Any pointer on this would be a great help.
I have customized grid for my custom requirements like I have added custom text box n all. I have defined 3 constructors
public GridControl()
public GridControl(IContainer container)
protected GridControl(SerializationInfo info, StreamingContext context)
I have this problem all the time...it sucks.
[Ramble(on)]
Here is what I think I know:
When designing place the control on a form. Build and refresh often..this will let you know what change caused the designer to barf.
Close visual studio all the way an re-open....I cannot tell you how many times I have chased a designer error that was the designer being "stuck".
It is very important that you understand : The designer is really, really stupid...like bag of rocks stupid.
Any public fields or properties of custom object types will almost always cause designer confusion*. I find the following attributes will clear up most of these problems:
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
Public fields or properties without a default constructor will always cause designer confusion. When you drop a user control on a form the designer effectively creates the control..so any public object needs a clear creation path. I have found that the easiest way around this (read hack) is keeping the non-trivial custom classes private and expose public properties as a facade.
-- Did I say restart visual studio because sometimes the designer is "stuck" on an error that doesn't exist ?..I hope I did.
[Ramble(off)]
I hope some of this helps..
*designer confusion: Instead of showing your controls the designer shows you a useless error message that might or might not include the dire warning that it is protecting you from code loss...blah, blah.
It sounds like a NullReferenceException is thrown in your control's default constructor (the one without parameters). Obviously, this exception is only thrown at design time since you say it works at runtime. Do you perform any initialization code in this constructor, like data base calls or similar? Or do you use any instances which might not be available at design time?
It looks like the form-designer is trying to initialize the control. Yet, the property it is trying to initialize may have been removed from the UserControl. There are lots of way to troubleshoot this problem. I recommend you debug the control in design-time. It is the surest way to find the problem. Check out "MSDN Search" for "design-time control debugging" at http://social.msdn.microsoft.com/Search/en-US?query=design-time+control+debugging&ac=8
I had run in to the same error check whether your Windows Form class inherits from System.Windows.Forms.Form class as in Form1:Form Hope dis helps !!!!

IExtenderProvider and WinForms designer file

C# 3.5 Winforms...
So I’ve recently discovered the IExtenderProvider and how it can be used to extend controls with additional properties.
In a prototype project that I setup i put a break point on the ‘set’ method for my extendee property and as the form loads I could see the ‘set’ method executing for every control on the form; which is exactly what I wanted. Following the successful prototype I implemented the extender component into my main project. All the forms in my project inherit from a base form which I’ve added my extender component to. On the base form I set the modifier of the extender component to public so that its accessible by the form inheriting this base form.
Doing the same thing before i added a break point on the ‘set’ method for my extendee property but the method doesn’t execute for the controls in the form (but only for the controls in the base form). HELP!
I should probably add at this point that i’ve source controlled my forms and so most of them are checked-in (ie lock from modification). For the forms that I’ve checked out and modified the provider property; I’ve noticed in the designer file that all controls have an additional statement which calls the ‘set’ method of the provider property.
this.MyProvider1.SetMyProperty(this.txtTextBox1, false);
Am I right in thinking that for the extender component to work it has to physically modify the designer file or should it be able to cope with locked files and therefore call the set method dynamically? I guess if it does have to modify the designer file then this isn’t a problem for new forms or forms that get modified after the extender component was added to the project – but it would be problem when you have 101 forms all locked by source-safe...
I’d appreciate any thoughts...
At what point does the extender provider (IExtenderProvider) extend the 'type' (in my case a winforms control) that the extender was intended for; at design time or at run time?
The designer is responsible for showing you the properties of the extender in the property editor
Method bool CanExtend(object) from the IExtenderProvider interface
Am I right in thinking that for the extender component to work it has to physically modify the designer file or should it be able to cope with locked files and therefore call the set method dynamically?
It has to physically modify the designer file, and write the extended properties there
I guess if it does have to modify the designer file then this isn’t a problem for new forms or forms that get modified after the extender component was added to the project – but it would be problem when you have 101 forms all locked by source-safe...
This is is not a problem for new forms, and not for old forms.
If you want to set some extended properties, open the old form and set the extended properties (a check out of the file is necessary)
This really does confirm my suspicions, many thanks. But this does leave a problem in that the components are only extended if some physical change is made to the old form.
I was trying to hijack the Set property method to also add and remove an event handler to the component (if the component was a control). Image the property is a Boolean and when set to false it adds the event handle and therefore the default behaviour (setting to true doesn’t add and event handler)
To cut a long story short the controls which were part of newly added forms automatically have an event handler added even without me explicitly setting the property to false but the designer file of the old forms never modifier so the event handler wasn’t added.
As some background, I was trying to add a global event handler for all controls
Global event handler for all controls for User Help
The theme here is to add context help to my forms here’s example of the extender ( the event handler is added as part of the end initialiser)
public partial class HelpProvider : Component, IExtenderProvider, ISupportInitialize
... other code of the extender omitted ...
#region ISupportInitialize Members
public void BeginInit()
{
// do nothing
}
public void EndInit()
{
if (DesignMode)
return;
foreach (Component item in _disableOnlineHelp)
{
if (item == null)
continue;
if (GetDisableOnlineHelp(item)) // developer has decide to set property to TRUE
continue;
Control control = item as Control;
if (control != null)
continue;
control.HelpRequested += new HelpEventHandler(HelpProvider_HelpRequested);
_toolTip.SetToolTip(control, GetHelpText(control));
}
}
#endregion
#region DisableOnlineHelp Provider Property
public virtual bool GetDisableOnlineHelp(Component component)
{
object flag = _disableOnlineHelp[component];
if (flag == null)
return false;
return (bool)flag;
}
public virtual void SetDisableOnlineHelp(Component component, bool value)
{
_disableOnlineHelp[component] = value;
}
#endregion
One issue might be the foreach loop in the EndInit method:
Control control = item as Control;
if (control != null)
continue;
If the item is, in fact, a Control, you get out of the loop before executing this code:
control.HelpRequested += new HelpEventHandle(HelpProvider_HelpRequested);
_toolTip.SetToolTip(control, GetHelpText(control));
so you never add the Event Handler or the ToolTip, to any Control. Oops :)
Thanks,
John

How to create a derived ComboBox with pre-bound datasource that is designer friendly?

I'd like to create a derived control from System.Windows.Forms.ComboBox that is bound to a list of objects that I retrieve from the database. Idea is other developers can just drop this control on their form without having to worry about the datasource, binding, unless they want to.
I have tried to extend combobox and then set the DataSource, DisplayMember, and ValueMember in the constructor.
public class CustomComboBox : ComboBox
{
public CustomComboBox()
{
this.DataSource = MyDAL.GetItems(); // Returns List<MyItem>
this.DisplayMember = "Name";
this.ValueMember = "ItemID";
}
}
Works when I run, but throws a lot of errors in Visual Studio's once it's added to any form. The error I get is:
"Code generation for property 'Items' failed. Error was: 'Object reference not set to an instance of an object"
What's the correct way to accomplish this (C#, Winforms, .NET 2.0+)?
The problem is that the designer actually does some compilation and execution in a slightly different context than normally running the program does.
In the constructor, you can wrap your code in:
if (!DesignMode)
{
//Do this stuff
}
That will tell the designer to not run any of your initialization code while it is being designed.
DesignMode property doesn't work in a constructor. From googling for a while, found this LicenseManager thing.
if (LicenseManager.UsageMode != LicenseUsageMode.Designtime)
{
// Do your database/IO/remote call
}
However LicenseManager only works in constructors. For eventhandlers use DesignMode.
Source: http://dotnetfacts.blogspot.com/2009/01/identifying-run-time-and-design-mode.html
Another reference: http://weblogs.asp.net/fmarguerie/archive/2005/03/23/395658.aspx
My usual comment here - DesignMode is not reliable in any situation other than if the control is placed directly on a design surface - i.e. if the control is placed on another control, DesignMode is not true even if you are in design mode. I have found NO reliable way to tell if you are in design mode - especially with inherited controls. Even variants using Site are not reliable if the control is inherited from a non-visual control (e.g. Common Dialog).
See http://keyofdflat.livejournal.com/5407.html (make sure to read the last comment).

Categories