Contacting a dynamically created control - c#

I have created a textBox control on run-time for my winform application. The control appears just find once the form loads up, and works great too. However, I have just run into a problem as I realize I do not know how to write the code to write to a dynamically created control.
Let's assume I have created a button (named "Button1") on design time. In Button1's click event, (Button1_Click), I would like to write the word "Hello" to a textBox control that won't be created until the application is executed. Some code below:
C# Code:
// Create the textBox control
TextBox new_textBox = null;
int x = 10;
int y = 10;
int xWidth = 300;
int yHeight = 200;
new_textBox = new TextBox();
new_textBox.Text = controlText;
new_textBox.Name = "textBox" + controlName;
new_textBox.Size = new System.Drawing.Size(xWidth - 10, yHeight - 10);
new_textBox.Location = new Point(x, y);
new_textBox.BringToFront();
new_textBox.Multiline = true;
new_textBox.BorderStyle = BorderStyle.None;
// Add the textBox control to the form
this.Controls.Add(new_textBox);
The Problem:
From Button1_Click event, I cannot get in contact with a control that has not even been created yet. Thus, Visual Studio will throw an obvious error that the control does not exist (because it doesn't).
So, is there some way to dynamically call a control, and more
specifically, a textBox control?
Thank you for any help on the matter,
Evan

Declare the new_textBox at class scope. Then the compiler can access it. For example:
class MyForm
{
TextBox new_textBox;
void InitializeTextBox()
{
new_textBox = new TextBox();
// initialization code here
// Add it to the form
this.Controls.Add(new_textBox);
}
void Button1_Click(...)
{
new_textBox.Text = "clicked";
}

You can make the new_textBox a class member (member of the form). You can again assign it a value and add to the forms controls later dynamically.
It would be a good practice to check if is null in the buttonClick event, though.

Related

Dynamically Generate Groupboxes

I'm working on an inventory program and have finished the main functionality as a command line console app. I am now working on a version for winforms. I want to enable it to dynamically generate a Groupbox that holds some textboxes. I'd rather not design 50+ lines of multiple textboxes. Keep in mind I'm rather new to programming, having started with C# a year ago. I know next to nothing on Winforms.
I've tried to use dynamic item = new Groupbox();as a similar method allowed generation of objects at runtime. In the command line app, the way it works is that based on information given, a certain amount of objects are passed into the list _AllItems. I was thinking of generating the Groupboxes by using:
private void InitializeGroupBox()
{
foreach (Product product in Product._AllItems)
{
dynamic Item = new GroupBox();
}
}
But I have the feeling I'm nowhere near the correct method. Thanks to anybody who helps.
You will need to learn a bit more, but here is what I usually do to achieve what you asked.
internal class DynamicForm : Form
{
private FlowLayoutPanel mFlowLayoutPanel;
public DynamicForm()
{
mFlowLayoutPanel = new FlowLayoutPanel();
mFlowLayoutPanel.Dock = DockStyle.Fill;
// Add to this Form
this.Controls.Add(mFlowLayoutPanel);
InitializeGroupBox();
}
private void InitializeGroupBox()
{
mFlowLayoutPanel.SuspendLayout(); // Performance
for (int i = 1; i <= 20; i++) {
var groupBox = new GroupBox();
groupBox.Text = "GroupBox #" + i;
groupBox.Size = new Size(200, 50);
var textBox = new TextBox();
textBox.Dock = DockStyle.Fill;
// Add the TextBox to GroupBox
groupBox.Controls.Add(textBox);
// Add to this Form
mFlowLayoutPanel.Controls.Add(groupBox);
}
mFlowLayoutPanel.ResumeLayout(); // after suspend, resume!
}
}

How can I programmatically make a control added to a form, public?

For exampple
Form1 frm1 = new Form1();
TextBox tb = new TextBox();
frm1.Controls.Add(tb);
now I can't say frm1.tb because tb is not public.
If i'd drawn the textbox then I could go to the properties window and set the modifier to public. So, I know how to do it in the GUI.
But how can I do it programmatically?
Added Clarification
Some have suggested alternatives to frm.tb, that wasn't what I was looking for.
I'll elaborate.
Consider this winforms program. It has two forms. Form1 and FormX
This is FormX
Form1 has just this code in its load
FormX frmx = new FormX();
frmx.drawnTextBox.Text = "blah"; //works
TextBox programmaticallyMadeTextbox = new TextBox();
frmx.Controls.Add(programmaticallyMadeTextbox);
frmx.Show();
frmx.programmaticallyMadeTextbox.Text = "asdf"; // does not compile
// can't say frm1.programmaticallyMadeTextbox.Text="asdf"
// why not?
// I suppose because programmaticallyMadeTextbox is not public
// how can I make it public like my drawnTextBox is public?
It is meant to add a textbox to FormX, and set the textbox's text property the same way I can do with the drawnTextBox.
The reason why I can do that with drawnTextBox, is that I set the modifier property to public. I'd like to somehow do that with the textbox I made programmatically.
Usually I do this when adding the controls dynamically, give them a (proper)name and find the control from the collection when it is needed.
TextBox textbox = new TextBox();
// other properties
textbox.Name = "newtextbox"; // Any unique name
form1.Controls.Add(textbox);
Now you could access the elements.
var textbox = Controls.OfType(TextBox)().FirstOrDefault(x=>x.Name == "newtextbox");
if(textbox != null)
{
// access element.
}
You could add this line to the Form1 class in the Form1.cs file.
public TextBox tb;
and change your example to
Form1 frm1 = new Form1();
frm1.tb = new TextBox();
frm1.Controls.Add(frm1.tb);
or maybe you could get access to your TextBox using frm1.Controls.OfType<TextBox>().Single() or frm1.Controls.OfType<TextBox>().First() or frm1.Controls.OfType<TextBox>().Last().

C# Can't change labels and button properties from a dialogbox

I have my main form and a dialogbox which is called from main. In my main form I have a label and a button that which properties I can't change. I'm using Visual Studio 2015, not sure if there is a bug regarding this. I also made sure my label and button are set to public to modify.
Code: (this is from the dialog box, this has a list box the function is triggered at selectindexchange)
else if ((short)lbDiscountTypes.SelectedValue == 2) //Senior
{
frm_Main main = new frm_Main();
main.VAT = false;
main.labelStatus.Text = "NON-VAT (SENIOR)";
main.labelStatus.BackColor = System.Drawing.Color.IndianRed;
main.labelStatus.ForeColor = System.Drawing.Color.WhiteSmoke;
main.btnNonVat.Enabled = false;
main.btnNonVat.BackColor = System.Drawing.Color.SlateGray;
main.btnNonVat.ForeColor = System.Drawing.Color.Navy;
main.labelVatAmount.Text = 0.00m.ToString();
main.Dispose();
//INQUIRE DISCOUNT TYPES
var Discount = GC.CSHR_DiscountTypes.Where(Filter => Filter.DiscountCode == (short)lbDiscountTypes.SelectedValue);
decimal DP = 0.00m;
foreach (var item in Discount)
{
DP = item.DiscountPercentage;
}
foreach (var item in GC.CSHR_SORepo
.Where(Filter => Filter.Machine == MACHINE
&& Filter.SalesOrderNum == SALESORDERNUM
&& Filter.First_SRP == Filter.IMFSRP))
{
item.DiscountAmount = (item.SoldSRP * DP) / 100;
item.TotalAmount = (item.Quantity * item.SoldSRP) - item.DiscountAmount;
item.VATableSalesOnTotalAmount = (item.Quantity * item.SoldSRP) - item.DiscountAmount;
item.VATRate = 0.00m;
GC.SaveChanges();
}
Close();
}
The code below //INQUIRE DISCOUNT TYPES works well but not the one on top.
I've used debug mode to check if the lines are not being skipped over and they aren't.
You should pay attention to:
You are creating a new instance of your main form that you don't need (while it is open behind the dialog), so you need to get it not create a new instance
You are disposing the main form you created. main.Dispose();
In fact you are creating a new instance of main form and assigning values to those controls and then dispose it. While and instance of yor main form that you expect to see changes on it, is open and untouched behind your dialog.
To set value of those controls you can do one of these ways:
Option 1
Make your labelStatus and btnNonVat public. Open your main form in designer and select labelStatus and btnNonVat and in property grid, set Modifier to public. Then write this code:
//var main = Application.OpenForms.OfType<frm_Main>().FirstOrDefault();
var main = (frm_Main)Application.OpenForms["frm_Main"];
main.labelStatus.Text = "NON-VAT (SENIOR)";
main.labelStatus.BackColor = System.Drawing.Color.IndianRed;
main.labelStatus.ForeColor = System.Drawing.Color.WhiteSmoke;
main.btnNonVat.Enabled = false;
main.btnNonVat.BackColor = System.Drawing.Color.SlateGray;
main.btnNonVat.ForeColor = System.Drawing.Color.Navy;
main.labelVatAmount.Text = 0.00m.ToString();
Option 2
Pass an instance of your frm_Main to your dialog and work with it.
Option 3
After closing the dialog, use values from dialog and set values of your main form
Looks like you are trying to create new form using frm_Main main = new frm_Main(); syntax. All you need to do is get the instance of your current form.
var _currentMainForm= Application.OpenForms[0];
or if you have given name to your form
var _currentMainForm = Application.OpenForms["MainFormName"];
Once you get the reference you can perform all your label updates.
The code on top creates a new form, changes the labels and then disposes the form.
I think you should change the labels of the existing form.
Like in the other answer said you are setting properties of controls into a new Form object and not in the form where you come from.
You should pass the form object into the parameters of the dialog, something like:
void myDialog(frm_Main callingForm)
{
callingForm.Textbox1.Text = "abc";
}
And call it from you main form like this
...
myDialog(this);

Use controls that are created at runtime

I am creating a textbox and a checkbox at runtime:
TextBox tb = new TextBox();
tb.Name = "txtPassword";
tb.PasswordChar = '*';
CheckBox cb = new CheckBox();
cb.Text = "Show Password";
cb.Name = "cbShowPassword";
cb.CheckedChanged += new EventHandler(cbShowPassword_CheckedChanged);
And I want to mask or unmask the password according to the checkbox:
private void cbShowPassword_CheckedChanged(object sender, EventArgs e)
{
txtPassword.PasswordChar = cbShowPassword.Checked ? '\0' : '*';
}
The problem is, it doesn't recognize txtPassword and cbShowPassword under cbShowPassword_CheckedChanged, since it is created in the code.
How can I make it work?
As it stands, you use a local variable tb in the method in which you instantiate the control. You can use that variable only in the method that instantiates the control. The fact that you gave the control a name does not mean that there is a variable defined named txtPassword.
You could continue this way, and dynamically look the control up from any other methods that wish to refer to it. However, that makes life harder than it needs to be. What you really want is a variable that refers to the control.
So, create a private member field of your class named txtPassword. Create the control like this:
txtPassword = new TextBox();
txtPassword.PasswordChar = '*';
....
To be really clear, txtPassword is a private member of your class, not a local variable.
Then you will be able to refer to it from other methods. Is there is a possibility that it might not have been created, test txtPassword against null.
Obviously you use the same technique for any other dynamically created controls.
I think you are mixing something up.
Did you add the controls to the parent form/controls?
Does the event fire ? (put a break point in there)
Try to use them as members instead of the name property and access this.cb and this.tb
You could use your form to find any Child controls that matches your newly created textboxes and Checkboxes.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.controls(v=vs.110).aspx
Else you could set a reference to this objects in a property on the Form.

FindName returning null

I'm writing a simple tic tac toe game for school. The assignment is in C++, but the teacher has given me permission to use C# and WPF as a challenge. I've gotten all the game logic finished and the form mostly complete, but I've run into a wall. I'm currently using a Label to indicate who's turn it is, and I want to change it when a player makes a valid move. According to Applications = Code + Markup, I should be able to use the FindName method of the Window class. However, it keeps returning null. Here's the code:
public TicTacToeGame()
{
Title = "TicTacToe";
SizeToContent = SizeToContent.WidthAndHeight;
ResizeMode = ResizeMode.NoResize;
UniformGrid playingField = new UniformGrid();
playingField.Width = 300;
playingField.Height = 300;
playingField.Margin = new Thickness(20);
Label statusDisplay = new Label();
statusDisplay.Content = "X goes first";
statusDisplay.FontSize = 24;
statusDisplay.Name = "StatusDisplay"; // This is the name of the control
statusDisplay.HorizontalAlignment = HorizontalAlignment.Center;
statusDisplay.Margin = new Thickness(20);
StackPanel layout = new StackPanel();
layout.Children.Add(playingField);
layout.Children.Add(statusDisplay);
Content = layout;
for (int i = 0; i < 9; i++)
{
Button currentButton = new Button();
currentButton.Name = "Space" + i.ToString();
currentButton.FontSize = 32;
currentButton.Click += OnPlayLocationClick;
playingField.Children.Add(currentButton);
}
game = new TicTacToe.GameCore();
}
void OnPlayLocationClick(object sender, RoutedEventArgs args)
{
Button clickedButton = args.Source as Button;
int iButtonNumber = Int32.Parse(clickedButton.Name.Substring(5,1));
int iXPosition = iButtonNumber % 3,
iYPosition = iButtonNumber / 3;
if (game.MoveIsValid(iXPosition, iYPosition) &&
game.Status() == TicTacToe.GameCore.GameStatus.StillGoing)
{
clickedButton.Content =
game.getCurrentPlayer() == TicTacToe.GameCore.Player.X ? "X" : "O";
game.MakeMoveAndChangeTurns(iXPosition, iYPosition);
// And this is where I'm getting it so I can use it.
Label statusDisplay = FindName("StatusDisplay") as Label;
statusDisplay.Content = "It is " +
(game.getCurrentPlayer() == TicTacToe.GameCore.Player.X ? "X" : "O") +
"'s turn";
}
}
What's going on here? I'm using the same name in both places, but FindName can't find it. I've tried using Snoop to see the hierarchy, but the form doesn't show up in the list of applications to choose from. I searched on StackOverflow and found I should be able to use VisualTreeHelper class, but I don't understand how to use it.
Any ideas?
FindName operates on the XAML namescope of the calling control. In your case, since the control is created entirely within code, that XAML namescope is empty -- and that's why FindName fails. See this page:
Any additions to the element tree after initial loading and processing must call the appropriate implementation of RegisterName for the class that defines the XAML namescope. Otherwise, the added object cannot be referenced by name through methods such as FindName. Merely setting a Name property (or x:Name Attribute) does not register that name into any XAML namescope.
The easiest way to fix your problem is to store a reference to your StatusDisplay label in the class as a private member. Or, if you want to learn how to use the VisualTreeHelper class, there's a code snippet at the bottom of this page that walks the visual tree to find the matching element.
(Edited: Of course, it's less work to call RegisterName than to use the VisualTreeHelper, if you don't want to store a reference to the label.)
I'd recommend reading the first link in its entirety if you plan on using WPF/Silverlight in any depth. Useful information to have.
You have to create a new NameScope for your window:
NameScope.SetNameScope(this, new NameScope());
Then you register name of your label with the window:
RegisterName(statusDisplay.Name, statusDisplay);
So this seems to be all you need to do to make FindName() work.

Categories

Resources