This may seem totally unreasonable to ask, but I have been designing a multi-panel, real device simulator, that has many different screens and my current approach is to add all the screen objects from the code only and dispose them when I switch to another screen.
I have some fixed objects, that are the real device buttons that are already defined and in place. The thing is, I am separating each panel construction in methods, for example: buildLogin(), buildMainScreen(), etc, and I need to edit some of the screen objects from those methods, like changing the color of an enabled function label to green if enabled or white if disabled.
My question is: would it be possible to declare an object from a method that would be accessible in the whole class, like if it were defined in the variable declaration section? It would be something like the GLOBAL in PHP.
I can't declare it on top of everything like they would always be because when I dispose the objects, I can't "re-create" them, because of parenting, or re-using a disposed object or something...
[EDIT] Sample code:
public partial class frmMain : Form
{
//I could as well do this:
//Button button1 = new Button();
public frmMain()
{
buildLogin();
}
private void buildLogin()
{
Panel panel1 = new Panel();
Controls.Add(panel1);
//But then, there is no way to do this:
// if (button1.IsDisposed == true) //because of the panel, or smthing
Button button1 = new Button();
panel1.Controls.Add(button1);
button1.Click += (s, f) => { panel1.Dispose(); buildMainMenu(); };
}
private void buildMainMenu()
{
Panel panel2 = new Panel();
Controls.Add(panel2);
Button button2 = new Button();
panel2.Controls.Add(button2);
}
//This was created from the Designer and is class-scoped
private void btn_Frame_TSK1_Click(object sender, EventArgs e)
{
//Here, I have no access to the objets I've created programatically.
//button1.Text = "Text changed!";
}
}
If you want to make sure things are always completely dynamic and always done in the code behind, you may want to look at searching for the controls you've created in the Controls collection.
For example, make sure to give button1 an ID value (button1.ID="textUpdatedButton") that will identify it uniquely from other controls you create. Then use FindControl or search on the Controls collection to find the control with the ID you want to locate in your event handler.
//This was created from the Designer and is class-scoped
private void btn_Frame_TSK1_Click(object sender, EventArgs e)
{
Control control = this.FindControl("textUpdatedButton");
if(control != null && control is Button){
Button button1 = (Button)control;
button1.Text = "Text changed!";
}
}
Alternatively, to make things look more like a variable, you can use a Property to hide the control finding (as mentioned previously):
private Button Button1 {
get { return (Button)this.FindControl("textUpdatedButton"); }
}
//This was created from the Designer and is class-scoped
private void btn_Frame_TSK1_Click(object sender, EventArgs e)
{
if(this.Button1 != null){
this.Button1.Text = "Text changed!";
}
}
The actual implementation will vary with how you build up your controls, but essentially this approach can let you build everything dynamically in your code behind if you need to do it that way. Just remember to use identifiers to let you find things later.
Define your object at the class level, as Static. This way it will be accessible from all methods of all instances of the class(disposing an instance will not affect it).
Related
I apologize if this has been addressed already, but I could not find a case that fit my exact situation. So here goes...
I have a MainForm that contains a toolStrip1 docked to the left that functions as a vertical navigation bar. I have a panel (pnlMain) filling up the remainder of the form. I want to use pnlMain to display different forms which are made up of win form classes. Right now, I can click on the labels/buttons on toolStrip1 to display different forms within pnlMain.
private void tsLblCustomers_Click(object sender, EventArgs e)
{
hidePanels();
CustomerReport cr = new CustomerReport();
cr.TopLevel = false;
cr.AutoScroll = true;
cr.BackColor = Color.White;
pnlMain.Controls.Add(cr);
cr.Show();
}
What I want to do now is display additional forms within pnlMain by clicking on a button on another form rather than a label/button on toolStrip1. Some of my forms are as follows: CustomerReport, AddCustomer, EmployeeReport, AddEmployee. The Report forms are linked to my tool strip buttons. The Add forms are linked to buttons on the Reports forms. I tried several things including the following:
1) On CustomerReport, I tried creating an instance of MainForm, then I'll create an instance of AddCustomer, and then add that instance to the panel on MainForm.
2) I also tried creating a method in MainForm to create the instance of AddCustomer, and then call that method from the Add button on CustomerReport. Even though the code was the same as the toolstrip buttons on MainForm, it did not work.
I tried different variations of hiding forms, showing forms, clearing the panel, setting Visible to true or false, and I can't get it to work right. In some cases, I've managed to hide the CustomerReport, but AddCustomer will not come up. At some point I think I created a NEW instance of MainForm and my code wasn't impacting the original form that is already open. I'm just lost. Should I be using a different design? Originally I set up my application to just hide one form then show the other but I read that that is a 'terrible design'.
This sounds very similar to this thread here: Creating Form Inside the Form
You'd want to look into MDI.
Although it sounds like you're aiming for one cohesive interactive window. Otherwise, if you just want separate windows to popup, you can create properties within that other form and read them after returning a DialogResult. I'm not sure why this would be bad design without knowing more about the context of the program.
//Optionally do a hide(); here.
AddCustomer customer = new AddCustomer();
DialogResult result = customer.ShowDialog();
if(result == DialogResult.OK)
{
var name = customer.Name;
//More properties or whatever here.
}
//The properties would still be accessible here, too.
I ended up keeping the toolstrip nav bar on the left side of the primary window, and I created a panel in the main part of the window. All forms are displayed in the panel. Each time one of the label options in the nav bar is clicked on, the current form is cleared off the panel and the active form is displayed.
private void tsLblCustomers_Click(object sender, EventArgs e)
{
pnlMain.Controls.Clear();
CustomerReport cr = new CustomerReport();
cr.TopLevel = false;
cr.AutoScroll = true;
cr.BackColor = Color.White;
pnlMain.Controls.Add(cr);
cr.Show();
}
private void tsLblEmployees_Click(object sender, EventArgs e)
{
pnlMain.Controls.Clear();
EmployeeReport emp = new EmployeeReport();
emp.TopLevel = false;
emp.AutoScroll = true;
emp.BackColor = Color.White;
pnlMain.Controls.Add(emp);
emp.Show();
}
private void tsLblVendors_Click(object sender, EventArgs e)
{
pnlMain.Controls.Clear();
VendorReport vend = new VendorReport();
vend.TopLevel = false;
vend.AutoScroll = true;
vend.BackColor = Color.White;
pnlMain.Controls.Add(vend);
vend.Show();
}
private void MainForm_Load(object sender, EventArgs e)
{
WelcomeForm welcome = new WelcomeForm();
welcome.TopLevel = false;
welcome.AutoScroll = true;
welcome.BackColor = Color.White;
pnlMain.Controls.Add(welcome);
welcome.Show();
}
I have the following code in my Form1 class:
private void pictureBox1_Click(object sender, EventArgs e)
{
if (radioButtonActor.Checked)
{
MouseEventArgs me = (MouseEventArgs)e;
Actor actor = new Actor(textBox1.Text, me.X, me.Y);
actor.DrawActor(pictureBox1.CreateGraphics());
}
else if (radioButtonUseCase.Checked)
{
MouseEventArgs me = (MouseEventArgs)e;
UseCase usecase = new UseCase(textBox1.Text, me.X, me.Y);
usecase.DrawUseCase(pictureBox1.CreateGraphics());
}
}
The DrawActor and DrawUseCase methods are just lines of graphics drawing in classes called Actor and UseCase. The UI features a picturebox and some radiobuttons. Depending on which radiobutton is checked a different method is run, drawing a different picture.
Instead of using this if statement inside the Form1 class, which isn't very maintainable, how would I another class to check which radio button is checked? I'd like to do something like this:
Click on paint form >
Run method from a new class called editor >
Method checks to see which radio button is checked > If statement here decides which method to run next.
All advice is appreciated! Thank you.
I believe you want to re-use this logic somewhere else in your code.
So you can create a class like this..
static class MyDrawer
{
public static void ChecknDraw(RadioButton radioButtonActor,RadioButton radioButtonUseCase, TextBox textBox1, PictureBox pictureBox1, EventArgs e)
{
if (radioButtonActor.Checked)
{
MouseEventArgs me = (MouseEventArgs)e;
Actor actor = new Actor(textBox1.Text, me.X, me.Y);
actor.DrawActor(pictureBox1.CreateGraphics());
}
else if (radioButtonUseCase.Checked)
{
MouseEventArgs me = (MouseEventArgs)e;
UseCase usecase = new UseCase(textBox1.Text, me.X, me.Y);
usecase.DrawUseCase(pictureBox1.CreateGraphics());
}
}
}
However, After seeing your comment about using it in ASP.NET, This will not serve your purpose as the controls (TextBox, PictureBox etc ) are totally different you will not have a graphics object there nor mouse pointers.
Even this can be managed by creating the class with final values rather than passing control objects. Interesting part would be your implementation of DrawActor Method
I have two forms.
1st one is frmStudentDetails. It has a datagrid
2nd one is frmStudentRegistration. It has some text boxes and Add button
When user enter some information and press "Add" button, I want to add those to the datagrid one by one
For accomplish that 1st I created following method in frmStudentDetails
public void AddRecord(string StID, string Name)
{
DataGridViewRow row = (DataGridViewRow)dgvStDetails.Rows[0].Clone();
row.Cells[0].Value = StID;
row.Cells[1].Value = Name;
dgvStDetails.Rows.Add(row);
}
I called it on frmStudentRegistration form's add button -->
private void btnAdd_Click(object sender, EventArgs e)
{
frmStudentDetailsForm frm = new frmStudentDetailsForm();
frm.AddRecord(txtStudentID.Text, txtStName.Text);
frm.ShowDialog();
}
Then the problem is, itz generating new forms to show every new record.
But i want to add all records in one form.
please somebody help me for that
In your "frmStudentRegistration" Class, add a "public frmStudentDetailsForm StudentDetailsForm { get; set; };" Property Declaration at the Class level.
Set it equal to the Instance of your "frmStudentDetailsForm" Class. There are several ways you can do so (i.e. in "frmStudentRegistration" Class's custom Constructor or its "Load" Event Handler), but for a novice, I would recommend just setting it after you Instantiate your "frmStudentRegistration" Class and before you call that Instance Variable's "Show" Method. NOTE: If you call "frmStudentRegistration"'s "ShowDialog" Method, any updates to other Forms (i.e. "frmStudentDetailsForm") won't show on screen until you exit "ShowDialog" or you explicity call the other Form's "Show" or "ShowDialog" Methods.
On a side note, I'm curious as to why you're calling "frmStudentDetailsForm"'s "ShowDialog" Method from "frmStudentRegistration". In my experience, either: a) the grid's Form would call "ShowDialog" on the add-item's Form with the "Add" option (i.e. via Button, Context Menu item, Insert Key and/or Enter Key (after filling in a new template Row)) being on the grid's Form or b) both Forms would remain open at the same time (via Modeless "Show" Method Calls) with the "Add" option on the add-item's form. Btw, in my experience your "frmStudentRegistration" Form would be called the "frmStudentDetailsForm" and your "frmStudentDetailsForm" would be called something like "frmStudentsForm", "frmStudentsListForm" or "frmStudentSummariesForm", etc. Also, btw, the .NET naming convention would be "var studentDetailsForm = new StudentDetailsForm()" (i.e. suffix vs. prefix/both and no abbrev.). Actually, if were up to me, it would be "var StudentDetailsFrmObj = new StudentDetailsFrmCls()". ;)
Ex.
In "frmStudentRegistration":
public frmStudentDetailsForm StudentDetailsForm { get; set; }
public void main ()
{
var studentRegistration = new frmStudentRegistration();
var studentDetailsForm = new frmStudentDetailsForm();
studentRegistration.StudentDetailsForm = studentDetailsForm;
studentRegistration.Show();
}
In "frmStudentRegistration":
private void btnAdd_Click(object sender, EventArgs e)
{
StudentDetailsForm.AddRecord(txtStudentID.Text, txtStName.Text);
StudentDetailsForm.ShowDialog();
}
I've been trying so hard to show the tab pages, actually hide the tab pages is working but show i tried everything but nothing successfully here's the code:
this is how i call the form that the user puts his password:
private void adminToolStripMenuItem1_Click(object sender, EventArgs e)
{
Password pss = new Password();
pss.Show();
}
this is the button that have to add the Tab-Page:
private void btnGo_Click(object sender, EventArgs e)
{
if(String.IsNullOrWhiteSpace(txtGo.Text))
{
lblErro.Text = "Type the password.";
return;
}
if(txtGo.Text != Properties.Settings.Default.Pass)
{
lblErro.Text = "Incorrect password.";
return;
}
else
{
this.Close();
MainForm main = new MainForm();
main.operatorToolStripMenuItem1.Checked = false;
main.adminToolStripMenuItem1.Checked = true;
main.accountPermissionsAdm();
}
}
this is the function i created to the account permissions when the user is admin:
public void accountPermissionsAdm()
{
if (!settings)
tabControl_Config.TabPages.Add(tabPage_Report);
if (!pathLoss)
tabControl_Config.TabPages.Add(tabPage_PathLoss);
if (!instrument)
tabControl_Config.TabPages.Add(tabPage_Instrument);
if (!parameters)
mainTabControl.TabPages.Add(tabPage_DefineParameters);
if (!scenario)
mainTabControl.TabPages.Add(tabPage_RunScenario);
if (!customer)
mainTabControl.TabPages.Add(tabPage_CustomerId);
IntPtr h = this.tabControl_Config.Handle;
tabControl_Config.TabPages.Insert(3, tabPage_RunCondition);
}
UPDATE - after reading your edited question with more code:
Few potential problems:
In btnGo_Click you are calling this.Close(); which returns, so the rest of the code in this else statement is unreachable.
Even if you will not call this.Close();, the code has no practical meaning, as you are creating a new instance of MainForm but you didn't show it (either by calling main.Show() or main.ShowDialog()).
Not sure if that's the flow, but it looks like Password is a child of your main form (MainForm). If that's the case, you should change the behavior so that, for example (there are other approaches) Password will return some value (boolean for example) that the main form needs to be changed (insertion of TabPage etc.). The change then must be performed on the main form and not from other forms.
Please follow these guidelines and make the changes, or consider of a new design, as it seems very broken to me.
Original answer (how to hide/show tab page)
I'm afraid you can't hide TabPage. You will have to remove and add it again. For example, like this:
Hide:
this.tabControl1.TabPages.Remove(tabPage1);
Show:
this.tabControl1.TabPages.Add(tabPage1);
The reason is that the Hide() function will have no effect in this situation, according to MSDN:
TabPage controls are constrained by their container, so some of the
properties inherited from the Control base class will have no effect,
including Top, Height, Left, Width, Show, and Hide.
I'm trying to mimic the web functionality of having a label over a textbox that shows the type of value the textbox should contain. I can add the events individually but I'm wondering if there is a way to add 'Behavior' to a set of controls.
Please see example code:
private void labelFirstName_Click(object sender, EventArgs e)
{
HideLabelFocusTextBox(labelFirstName, textBoxFirstName);
}
private void HideLabelFocusTextBox(Label LabelToHide, TextBox TextBoxToShow)
{
LabelToHide.Visible = false;
TextBoxToShow.Focus();
}
private void textBoxFirstName_Leave(object sender, EventArgs e)
{
if (String.IsNullOrEmpty(textBoxFirstName.Text))
labelFirstName.Visible = true;
}
private void textBoxFirstName_Enter(object sender, EventArgs e)
{
labelFirstName.Visible = false;
}
You could subclass the text box control (write your own that inherits a textbox)
Btw, i have thought about this and i would take another approach:
i would override the text box's paint handler and when the textbox contains no information, draw an info string into it.
Something like:
using System;
using System.Windows.Forms;
using System.Drawing;
class MyTextBox : TextBox
{
public MyTextBox()
{
SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
if (string.IsNullOrEmpty(this.Text))
{
e.Graphics.DrawString("My info string...", this.Font, System.Drawing.Brushes.Gray, new System.Drawing.PointF(0, 0));
}
else
{
e.Graphics.DrawString(Text, this.Font, new SolidBrush(this.ForeColor) , new System.Drawing.PointF(0, 0));
}
}
protected override void OnTextChanged(EventArgs e)
{
Invalidate();
base.OnTextChanged(e);
}
}
Tie Behaviour
You can tie the feature/behaviour closer to the TextBox control by using extension methods. This simple solution might make it feel more tightly knit:
// NOTE: first parameter "this TextBox thisText"- these are all extension methods.
static public void AssignLabel(this TextBox thisText, Label companionLabel) {
thisText.Tag = companionLabel;
// HOOK UP EVENT AT THIS POINT, WHEN LABEL IS ASSIGNED (.NET 3.x)
thisText.Leave += (Object sender, EventArgs e) => {
LeaveMe(thisText); // Invoke method below.
};
}
static public void FocusText(this TextBox thisText) {
if (! ReferenceEquals(null, thisText.Tag))
(Label)thisText.Tag).Visible = false;
thisText.Focus();
}
static public void LeaveMe(this TextBox thisText) {
if (String.IsNullOrEmpty(thisText.Text))
((Label)thisText.Tag).Visible = true;
}
//etc.
and then use your textbox instances like so:
Label overlay1 = new Label(); // Place these appropriately
Label overlay2 = new Label(); // on top of the text boxes.
Label overlay3 = new Label();
TextBox myTextbox1 = new TextBox();
TextBox myTextbox2 = new TextBox();
TextBox myTextbox3 = new TextBox();
// Note: Calling our extension methods directly on the textboxes.
myTextbox1.AssignLabel(overlay1);
myTextbox1.FocusText();
myTextbox1.LeaveMe();
myTextbox2.AssignLabel(overlay2);
myTextbox2.FocusText();
myTextbox2.LeaveMe();
myTextbox3.AssignLabel(overlay3);
myTextbox3.FocusText();
myTextbox3.LeaveMe();
//etc...
How it Works
The code is cleaner and applies to all TextBoxes you instantiate.
It relies on the the .Tag property of the TextBox class to store a Label reference into (so each TextBox knows its label), and also extension methods introduced with .NET 3.x which allow us to "attach" methods onto the TextBox class itself to tie your behaviour directly to it.
I took your code and produced almost the same thing with tweaks to turn it into extension methods, and to associate a Label with your Textbox.
Variation
If you want to attach the same method to other controls (and not just the text box) then extend the base Control class itself like:
static public void LeaveMe(this Control thisControl) { //...
You could always create a user control that does this. Put both the TextBox and the Label inside the control and code up the logic inside the user control. That way every instance of that control will behave the same.
Another option might be to use an Extender Provider. These basically let you add a behavior to any control (though these can be limited if I remember right) at design time. The ToolTip is an example of an Extender Provider that is already floating around in the framework. I used these quite a bit once upon a time to do things like add support for getting the text values of controls from a resource file.
I would subclass the regular textbox and add properties which allow you to either find the associated label or set a reference to the associated label directly.
Typically in winform projects, I subclass all controls before adding them to my forms, so I can add common functionality very easily without having to change forms in the future.