I have code that dynamically loads (and destroys) user controls of different types onto a form as they are needed. The user control uses properties from its parent to load certain values into controls such as textboxes and check boxes. The issue is that in the line:
activeControl = new userControl1(params...);
my constructor is obviously going to try loading fields that don't exist yet (they belong to the parent form) since it hasn't been added to the parent form until after userControl1 is created.
I tried solving this using the ParentChanged event of the user control so it waits until its parent is set before loading fields. The problem here is when I destroy userControl1 and create a userControl2, the parent changes and it tries to load the fields from the null ParentForm (the exact problem I had originally.
Is there a preferred way to tackle this? Clearly, I could just add something like
if(this.ParentForm == null) { return; }
to the ParentChanged event or something along those lines. But this feels neither elegant nor safe. Am I overlooking some event or preferred way to accomplish this?
While it is not necessarily bad to check for null references and divert the code accordingly (e.g. not try to reference a missing object), the better approach here is to decouple your UserControl classes from their parents.
That is, these UserControl classes should not have any awareness of their parent form types at all. To do otherwise is to negate one of the major benefits of OOP: code reuse. With your current design, even if you get it to work without crashing, you can't ever use these UserControl objects except in the context of this specific parent form type.
You may think today that that's okay, that you'll never want to use them in any other context. But the future is hard to predict. You may decide, for example, that you want a variation on the parent form that should still use these same UserControl objects.
What you should be doing is exposing the UserControl state via public properties, and then allow the parent form to initialize those as necessary.
Here's a very simple example (I'm omitting the Designer-created codeā¦I assume that you can infer from the field names what I mean):
partial class UserControl1 : UserControl
{
/* ... */
public bool IsMyFlagChecked
{
get { return checkBox1.Checked; }
set { checkBox1.Checked = value; }
}
/* ... */
}
partial class ParentForm : Form
{
/* ... */
private void SomeMethodThatAddsUserControl1()
{
UserControl1 uc1 = new UserControl1;
uc1.IsMyFlagChecked =
this.SomeParentFormPropertyUserControl1UsedToReferenceDirectly;
// other initialization for uc1...
Controls.Add(uc1);
}
/* ... */
}
(Note that without a good, minimal, complete code example that illustrates your question, I can't provide a more detailed code example than the above. If this doesn't seem to address your question, please edit it so that it provides more detail, including a better code example).
Related
I am building some applications that use OPC to control some industrial automation. I have decided it might be a good idea to create some custom controls for standard things I will use such as buttons and text boxes. One of my main reasons was that I felt in an ideal pattern the end object i.e. the button is the one who holds information about the OPC item it refers to otherwise in a typical application my Form class gets polluted by tons of variables that are in my mind scoped more global then they should. I started then by using the Tag property of the button but this requires some overhead code that is the same for each instance. I felt like the right thing to do is subclass controls that I would like to use and provide properties to configure each one.
With that context in mind my real question is this. To make it as portable as possible I decided there should a property to define the OPC Group. I declared a property like this
public class OPCButton : Button
{
[Category("OPC")]
public OPCConnectedGroup
{
get { return _OPCGroup; }
set { _OPCGroup = value; }
}
}
This shows up int the property list when I add the control but I am unable to bind this property even though my Form1 contains
public OPCConnectedGroup Connection1 = new OPCConnectedGroup();
I have resolved that the way to solve this is to probably create an additional control like OPCGrp that can be added to a Form then the Controls can reference this. To test I added a property of type Button and sure enough when I added it and browsed to the property it gave me options for all the buttons on the Form. I have no huge problems with this approach I just want to make sure that Im following a prototypical pattern because I will be responsible for maintaining the control library but not always implementing and Im trying to get it down to a 1-2 step process to implement a control.
Thanks
Matt
When defining OPCConnectedGroup you can inherit Component class.
This way if you have a property of type OPCConnectedGroup in your OPCButton, then at design time, you can put instances of OPCConnectedGroup on the form, and then if you choose your OPCButton at designer, that property of type OPCConnectedGroup will show as a drop down list that you can select one of instances that you put on the form for it.
Example:
If I have such MyButton and MyClass:
public class MyButton : Button
{
public MyClass MyClassInstance { get; set; }
}
public class MyClass : Component
{
public string SomeProperty {get;set;}
}
Then you can put some (or one) instance of MyClass on the component tray of the form:
And then if you select MyButton on your form, you can choose one of MyClass instances from in property grid:
How can I use my UI form controls from another class? I want to work with these controls in another class, then call it into my main form. The problem is that the new class I created can't seem to access the label or the textboxes of these controls and I keep getting an error.
How would i solve this issue?
Error Messages:
FirstName_text is inaccessible due to its protection level
First_Name_label is inaccessible due to its protection level
Color does not exist in the current context
public void Checker()
{
//First Name Validation
if (Regex_Static_Class.FirstNameRgex.IsMatch(FirstName_text.Text) == false)
{
First_Name_label.Text = "invalid first name";
Error_Lable.ForColor = Color.Pink;
}
}
I guess First_Name_label is a UI label and it must be accessible form your other class too.
Maybe make a setter method to fill in text into your label.
I would say that you have to set Modifiers property for your controls to public or internal in Forms Designer to access it from another class. Control's instance is protected by default.
That's the answer to your question. Another thing is that it's not the best idea to do that. You shouldn't access form's controls outside of form directly. Form class should encapsulate its controls and expose an interface to change them, for example.
Also Color doesn't exists most probably because you don't have proper using in your another class.
It has nothing to do with conditions (references to conditions removed from the original question).
Is there any good way in Windows Forms Designer to have an array (or other collection) of similar components defined? You can check "GenerateMember", which will give you a unique named member in code to reference, but what if I want that generated member to be one of a list of some sort, so that I can iterate through them easily? The only way I can think of is to create a new list on load, and the manually add all the already-generated members to that list, which is tedious and redundant. I thought about editing the code that generates these components, but it's inside that auto-generated "Do not modify" section, and I'm afraid any change I make there will be overwritten any time I make other changes to the form. Does the Form Designer just not have this functionality?
If I understand your question correctly, you can simply cast "this" (the form) to a "Control", and then examine it's "ControlCollection" property- a collection containing the form's controls. Eg.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var myControlList = ((Control)this).Controls;
}
}
Unless I am missing something...
You could put them inside a Panel and then iterate through its controls.
An application I'm working will have a number of forms with a lot of shared functionality. For instance, each form will have a DataGridView, many of the same buttons, much of the same UI code and so on.
I'd like to implement this by creating a base version of this common form, subclass it for all these very-similar-but-not-quite-the-same child forms, and tack on whatever additional controls and features I need for each of them.
I've already figured out that it helps to make the base form's controls protected because this allows things like anchoring to work propertly. However, I have yet to find a way to automatically make the derived forms the same size as the base form.
Experience tells me there should be a simple way to do this. While it's not much of a problem to just type in the required size by hand for every derived form right after creating it, I'd prefer to make everything as clean, simple, and automatic as possible.
I find it interesting that your derived forms do not automatically inherit the size from their base form, because this should work without you having to do anything about it.
Suspected cause of your problem:
I suspect your problem results from the fact that you're using Visual Studio's Forms Designer to edit the forms. Whenever you've edited a form, Windows Forms Designer generates the required code in the InitializeComponent method of your forms. Among all the generated code are assignments that set a form's size, even if it is identical to the base form's size. Therefore you might have to manually comment out those assignments if you want your derived form to have the same size as the base form, even when you change the base form's size after creating the derived forms. (However, I don't know if that might lead to further problems with the controls' positioning & layouting.)
// Code to be commented out in your derived form's InitializeComponent method:
this.AutoScaleDimensions = new System.Drawing.SizeF(...);
this.ClientSize = new System.Drawing.Size(...);
Once these lines are commented out, the size as set in your base form's InitializeComponent will be used for the derived form.
A workaround solution:
You can do the following so that you don't have to manually comment-out designer-generated code every time you've edited a form:
Create an form derived from your base form; let's call it FrozenBaseForm. You will derive all other forms from this class instead of directly from the base form. Now, in this "intermediate" class, you define a new property ClientSize:
public class FrozenBaseForm : BaseForm
{
new public SizeF ClientSize
{
get { return base.ClientSize; }
set { }
}
}
This will cause all assignments to ClientSize to have no effect at all and therefore preserve the size from the base form. This feels like a hack to tell the truth, but it seems to work. You might have to hide the Size property in the same way btw.
As said, derive your forms from FrozenBaseForm instead of from BaseForm directly:
public class DerivedForm1 : FrozenBaseForm { ... }
public class DerivedForm2 : FrozenBaseForm { ... }
...
Another option (last resort if all else fails):
As a last resort, you could simply forget about the Forms Designer and just define the derived forms manually in the code editor (though I personally would not want to do this):
public class DerivedForm : BaseForm
{
public DerivedForm()
{
// make all necessary changes to the base form:
...
}
}
public partial class derivedForm : baseForm
{
public derivedForm()
{
InitializeComponent();
this.Width = base.Width;
this.Height = base.Height;
}
}
Why not make the BaseForm set the size of itself?
public partial class BaseForm : Form
{
public BaseForm()
{
InitializeComponent();
// you could hardcode these or retrieve these values from a
// config file or something
this.Width = 640;
this.Height = 468;
}
}
Wouldn't this do what you want?
I have a Form and a UserControl. The UserControl has a menu, and the form has a tabstrip (General, Food, Vitamins etc).
In the UserControl, I have the following code: (Form name is frmForm, the tab names in the form are tabGeneral,tabFood, tabVitamins)
frmForm fm=new frmForm();
fm.tabMain.Selected=tabVitamins;
I call these line from the UserControl to capture the tab to get selected on the form, but it does not select the vitamins tab.
Where am I going wrong? I have access specifier as Protected Internal for tabs in the form.
Please advice.
Thanks,
Karthick
When you write new frmForm(), you're creating a completely new instance of frmForm, which is then discarded.
To get the frmForm instance that holds your control, call the FindForm() method and cast to frmForm.
For example:
frmForm myForm = FindForm() as frmForm;
if(myForm != null)
myForm.tabMain.SelectedTab = myForm.tabVitamins;
If the control is on some other form, this code won't do anything.
By the way, Hungarian notation is frowned upon in .Net.
Your form should probably be named something like MainForm.
SLaks has correctly pointed out your fundamental error, and given you a valid example of a way, via a call to the method 'FindForm, to get the Form the UserControl is sited on.
It may be valuable to you to keep in mind that a UserControl (and all Controls) also has a 'Parent property, but, of course, a UserControl could be placed inside another Control on a Form (like your UserControl could be inside a Panel on the Form) : in that case the UserControl's Parent would be the control it's inside on the Form (like, a Panel), not the Form itself, but 'FindForm will do the right thing to get you the Form it's on.
However you are calling a Method every time you use 'FindForm, and "best practice" suggests that what you want to do is to "inject" a reference to the Form into the UserControl at run-time so that it can always access its Form property easily, without calling a 'Method.
In your example, on a practical level, this (calling the Method) may make almost no difference in performance, but, imho, as you get to a place with WinForms and .NET where you might have a UserControl that will need access to its Parent Form very frequently, this will pay off, and it's a better way to structure your code in the long run, for maintenance.
Wes showed you one way you can "embed" (inject) the UserControl's hosting Form : using an overloaded constructor for the UserControl. But that requires you to modify the Designer.cs file in standard WinForms, and I strongly advise you against that, even though it will work. Particularly if you are just "getting your feet on the ground" in .NET, I strongly advise you against modifying it, or anything having to do with the Form's constructor and its internal call to : InitializeComponent();
Also, as you progress with WinForms you are going to meet many situations where you are going to want instances of "objects" (a Control, a Form, an instance of a Class) to contain references to other instances of "objects.
If you can understand and use one simple use of "injection" here, you are going to make progress to make yourself ready to handle more complex .Net programming in the future.
Another way is to put a Public Property in the UserControl that can be set in code from the MainForm. In the UserControl something like :
private frmForm ParentForm;
public frmForm UCParentForm
{
set { ParentForm = value; }
}
So then in your main form's code, perhaps in the Load event like this :
private void frmForm_Load(object sender, EventArgs e)
{
TheUserControl.UCParentForm = this;
}
or when you need to, you set the UserControl's 'ParentForm property once. So you have eliminated using the method 'FindForm().
In this case, if you only want access to a specific control on the UserControl's Parent Form, like a TabControl, you might consider that you want to make the Property you set of type TabControl, rather than Form : the same coding technique shown above can be used in the UserControl :
private TabControl mainFormTabControl;
public TabControl MainFormTabControl
{
set { mainFormTabControl = value; }
}
imho, it is when you are creating UserControls dynamically at run-time, using an overloaded constructor, as Wes suggests, is the best strategy. And using overloaded constructors has many, many others uses in .NET that you'll get into.
good luck !
You should not be creating a new frmForm() inside the user control. You could pass a reference to the frmForm to the user control.
In your user control constructor try something like this.
private frmForm fm;
public YourUserControl(frmForm fm)
{
this.fm = fm;
}
Then you could use.
fm.tabMain.Selected=tabVitamins;
Does that help?