I have a hopefully simple question, for which I could not google up any solution:
I would like to add labels buttons textboxes at runtime, which i can in the constructor of my Form, but I cannot access them outside of the constructor.
Something like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Label changeMe = new Label();
changeMe.AutoSize = true;
changeMe.Left = 50; changeMe.Top = 50;
changeMe.Text = "Change this text from other function";
changeMe.IsAccessible = true;
//changeMe.Name = "changeMe";
this.Controls.Add(changeMe);
}
private void btn_changeLabelText_Click(object sender, EventArgs e)
{
//Would like to achieve this:
//changeMe.Text = "You changed me !! ";
//But I found only this solution:
//Label l; l = (Label)this.Controls.Find("changeMe", true)[0]; l.Text = "You changed Me";
}
}
The solution I commented out is the only one I found, but I can't believe there isn't any better one then that. Is there a way to make my controls public for example? Whats a good way of solving this problem?
(The number of controls vary on every time I call my Dialogbox I am trying to design)
Thanks
EDIT --------------------------
After accepting Adil's answer, I stayed with the following solution, which I only find better as the originally outcommented this.Control.Find way, because I also want to have "n" Textboxes, and in thie way I can easily loop through them and read inputs.
public partial class Form1 : Form
{
public struct labels { public Label lbl; public int id; }
List<labels> lbls = new List<labels>();
public Form1()
{
InitializeComponent();
Label changeMe = new Label();
changeMe.AutoSize = true;
changeMe.Left = 50; changeMe.Top = 50;
changeMe.Text = "Change this text from other function";
changeMe.IsAccessible = true;
this.Controls.Add(changeMe);
labels newlabel = new labels();
newlabel.id = 137; newlabel.lbl = changeMe;
lbls.Add(newlabel);
}
private void btn_changeLabelText_Click(object sender, EventArgs e)
{
lbls.Find(i => i.id == 137).lbl.Text = "You changed me";
}
}
You declared the label inside constructor that makes it accessible only in constructor, ceclare label out side constructor at class scope as class member.
Label changeMe = new Label();
public Form1()
{
InitializeComponent();
Label changeMe = new Label();
changeMe.AutoSize = true;
changeMe.Left = 50; changeMe.Top = 50;
changeMe.Text = "Change this text from other function";
changeMe.IsAccessible = true;
//changeMe.Name = "changeMe";
this.Controls.Add(changeMe);
}
Related
I have made a Custom TextBox that is a panel with a TextBox inside.
What i'm trying to do is [CustomTextBox name].SelectAll(); where it
returns [TextBox nameInsideCustomControl].SelectAll();
[...] is just a name.
How can i implement it?
I have tried:
[Category("Custom")]
public void SelectAll
{
get { textBox1.SelectAll(); }
}
But i don't think it's right.
Answer:
public void SelectAll() => textBox1.SelectAll();. It's a method, not a property. –
dr.null
You have a CustomTextBox that inherits from Panel and you want to be able to select all text in the TextBox that it contains.
To achieve this objective, first make sure that your CustomTextBox exposes a pubic method named SelectAll that forwards the command to the private TextBox it contains. But in addition to that you will either need to make sure that the internal TextBox receives focus or set the HideSelection property of the internal TextBox to false. Otherwise you may still not see the selection highlighted.
Custom Text Box example
public partial class CustomTextBox : Panel
{
public void SelectAll()
{
textBox1.SelectAll();
// Ensure text box gets the focus.
textBox1.Focus();
}
public void LoadTestData() =>
textBox1.Text = "Don't forget to give Focus to the TextBox!";
public CustomTextBox()
{
BackColor= Color.Azure;
label1 = new System.Windows.Forms.Label();
textBox1 = new System.Windows.Forms.TextBox();
#region I N I T I A L I Z E
SuspendLayout();
label1.AutoSize = true;
label1.Location = new System.Drawing.Point(12, 17);
label1.Size = new System.Drawing.Size(143, 25);
label1.TabIndex = 0;
label1.Text = "Custom Text Box";
textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
textBox1.Location = new System.Drawing.Point(190, 17);
textBox1.Multiline = true;
textBox1.Size = new System.Drawing.Size(206, 126);
textBox1.TabIndex = 1;
textBox1.HideSelection = false;
Controls.Add(textBox1);
Controls.Add(label1);
Name = "CustomTextBox";
Size = new System.Drawing.Size(424, 157);
ResumeLayout(false);
PerformLayout();
#endregion I N I T I A L I Z E
}
private Label label1;
private TextBox textBox1;
}
Minimal Test Code
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
customTextBoxPanel.LoadTestData();
buttonSelectAllTest.Click += onClickSelectAllTest;
}
private void onClickSelectAllTest(object? sender, EventArgs e)
{
customTextBoxPanel.SelectAll();
}
}
simple question here:
I have a problem with one of my labels.
My problem is that the (!!!) label corresponding to the textbox "Datei Name" is being blocked by said textbox. Is there a setting that I have missed that puts said label behind/in front of the textbox?
How the (this part of the) program works:
When something is incorrectly entered in one of the textboxes a label containing (!!!) and a Messagebox show up which tell the user where the error is located.
As i mentioned in the comment to the question, i'd use ErrorProvider instead of spending time to write code to manipulate controls on the form.
Here is MSDN example: How to: Display Error Icons for Form Validation with the Windows Forms ErrorProvider Component
How it works?
When the form is NOT valid, you'll see:
When you move cursor over icon, you'll see error details:
If you've got LinqPad, you can use this script to check how it works:
void Main()
{
MyForm mf = new MyForm();
mf.Show();
}
// Define other methods and classes here
public class MyForm: Form
{
private TextBox TxtDateiName = null;
private TextBox TxtDateiNr = null;
private Label Label1 = null;
private Label Label2 = null;
private Button BtnValidate = null;
Dictionary<Control, ErrorProvider> ValidatedControls = null;
public MyForm()
{
Initialize();
}
private void Initialize()
{
this.Size = new Size(250, 180);
this.MinimizeBox = false;
this.MaximizeBox = false;
this.Text = "ErrorProvider Example";
Label1 = new Label(){Text="Datei Name:", Location =new Point(10,12), AutoSize = true};
Label2 = new Label(){Text="Datei Nr:", Location =new Point(10,42), AutoSize = true};
TxtDateiName = new TextBox(){Name="TxtDateiName", Size= new Size(140,24), Location =new Point(78,10)};
TxtDateiNr = new TextBox(){Name="TxtDateiNr", Size= new Size(140,24), Location =new Point(78,42)};
BtnValidate = new Button(){Size = new Size(220, 48), Text = "Validate", Location = new Point(10, 78) };
BtnValidate.Click += BtnValidate_Click;
this.Controls.Add(Label1);
this.Controls.Add(Label2);
this.Controls.Add(TxtDateiName);
this.Controls.Add(TxtDateiNr);
this.Controls.Add(BtnValidate);
ValidatedControls = new Dictionary<Control, ErrorProvider>();
ValidatedControls.Add(TxtDateiName, new ErrorProvider(this));
ValidatedControls.Add(TxtDateiNr, new ErrorProvider(this));
ValidatedControls[TxtDateiName].SetIconAlignment(TxtDateiName, ErrorIconAlignment.MiddleRight);
ValidatedControls[TxtDateiName].SetIconPadding (TxtDateiName, 2);
ValidatedControls[TxtDateiName].BlinkRate = 1000;
ValidatedControls[TxtDateiName].BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.AlwaysBlink;
ValidatedControls[TxtDateiNr].SetIconAlignment(TxtDateiName, ErrorIconAlignment.MiddleRight);
ValidatedControls[TxtDateiNr].SetIconPadding (TxtDateiName, 2);
ValidatedControls[TxtDateiNr].BlinkRate = 1000;
ValidatedControls[TxtDateiNr].BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.AlwaysBlink;
}
private void BtnValidate_Click(object sender, EventArgs e)
{
bool ans = true;
foreach(var k in ValidatedControls.Keys)
{
switch(k.Name)
{
case "TxtDateiName":
ans = IsValidName(((TextBox)k).Text);
ValidatedControls[k].SetError(k, ans ? "" : "Name is wrong!");
break;
case "TxtDateiNr":
ans = IsValidNumber(((TextBox)k).Text);
ValidatedControls[k].SetError(k, ans ? "" : "Number is wrong!");
break;
}
}
ans = ValidatedControls.All(kvp=>kvp.Value.GetError(kvp.Key)=="");
MessageBox.Show(ans ? "Form is valid!" : "Form contains errors!", "Information");
if(ans) this.Close();
}
private bool IsValidName(string dName)
{
return Regex.IsMatch(dName, #"^(\w{3,})$");
}
private bool IsValidNumber(string dNumber)
{
return Regex.IsMatch(dNumber, #"^(\d{3,5})$");
}
}
Do not forget to add references (F4) to: System.Windows.Forms.dll
and then add the following namespaces:
System.Drawing
System.Windows.Forms
Final note:
This is just an example. So, the code is not optimized.
Im trying to make an app that adds shopping items with the price(just for education purpose). I want to add a button as well so that I can delete this item if I want too. this is what Ive done:
class ShopItem
{
string name;
decimal price;
int labelNumber;
public ShopItem(string name, decimal price, int labelNumber)
{
this.name = name;
this.price = price;
this.labelNumber = labelNumber;
}
public string GetName()
{
return name;
}
public decimal GetPrice()
{
return price;
}
public int GetLabelNumber()
{
return labelNumber;
}
}
Then in my main class:
public partial class Form1 : Form
{
List<ShopItem> shopItems = new List<ShopItem>();
List<Button> buttons = new List<Button>();
List<Label> labelsList = new List<Label>();
int shopItemNumber;
int top = 50;
int left = 50;
public Form1()
{
InitializeComponent();
}
private void Button1_Click(object sender, EventArgs e)
{
this.shopItemNumber = shopItems.Count;
label3.Text = "";
label3.Text = shopItemNumber.ToString();
shopItems.Add(new ShopItem(textBox1.Text, Convert.ToDecimal(textBox2.Text), shopItemNumber));
AddLabel();
AddAButton();
}
void AddAButton()
{
Button newButton = new Button();
buttons.Add(newButton);
newButton.Left = left + 100;
newButton.Top = top;
newButton.Text = "DELETE";
panel1.Controls.Add(newButton);
top += 40;
}
void AddLabel()
{
Label newLabel = new Label();
labelsList.Add(newLabel);
newLabel.Left = left;
newLabel.Top = top;
newLabel.Text = shopItems[shopItemNumber].GetName() + " " + shopItems[shopItemNumber].GetPrice();
panel1.Controls.Add(newLabel);
}
}
Im adding new objects and then checking the list count so that I know each item number. Now I dont know how to make buttons to remove each item. I would also like to know what is the best practice for this type of tasks? I dont want to use database as its too complicated for now, just make it in runtime? How would you, advanced guys, would make it?
I managed to do something like this based on your answers:
public partial class Form1 : Form
{
List<ShopItem> shopItems = new List<ShopItem>();
List<Button> buttons = new List<Button>();
List<Label> labelsList = new List<Label>();
int shopItemID;
int top = 50;
int left = 50;
public Form1()
{
InitializeComponent();
shopItemID = 0;
}
private void Button1_Click(object sender, EventArgs e)
{
int shopItemID = this.shopItemID;
label3.Text = "";
label3.Text = shopItemID.ToString();
decimal price;
if(Decimal.TryParse(textBox2.Text.Replace(",", "."), out price))
{
shopItems.Add(new ShopItem(textBox1.Text, price, shopItemID));
label3.Text = price.ToString();
AddLabel();
AddAButton();
this.shopItemID++;
}
else
{
label3.Text = "Wrong price format provided";
}
}
void AddAButton()
{
Button newButton = new Button();
buttons.Add(newButton);
newButton.Left = left + 100;
newButton.Top = top;
newButton.Text = "DELETE";
panel1.Controls.Add(newButton);
top += 40;
newButton.Tag = this.shopItemID;
newButton.Click += DeleteClicked;
}
void DeleteClicked(object sender, EventArgs e)
{
Button button = (Button)sender;
int itemNumber = (int)button.Tag;
ShopItem item = shopItems[itemNumber];
button.Dispose();
labelsList[itemNumber].Dispose();
}
void AddLabel()
{
Label newLabel = new Label();
labelsList.Add(newLabel);
newLabel.Left = left;
newLabel.Top = top;
newLabel.Text = shopItems[shopItemID].GetName() + " " + shopItems[shopItemID].GetPrice();
newLabel.Tag = this.shopItemID;
panel1.Controls.Add(newLabel);
}
}
It's working as I wanted it. The question is: The way I did it was according to the right coding practice?
The other question is: I would like to add placement functionality so that if some item is deleted other items go up the way. Should I use some position list? Can All of those items be contained within a ShopItem class? I mean, can I add button, label and other stuff withing this class so they all are created at the same time and becomes one object?
First thing you'll want to do is start using IDs instead of the count. The reason is, let's say you have 5 items (0 through 4) and you delete one. Now when you add another new one, it will get #4 because the count is 4 again (before adding the new one to the list), which means you'll have two #4s.
What you'll want to do instead is something like look for the highest existing number and add 1 to it:
this.shopItemNumber = shopItems.Any() ? shopItems.Max(item => item.labelNumber) + 1 : 0;
Now for the Delete button, just pass the item's number to the AddAButton() method:
AddAButton(shopItemNumber);
...and change the method's signature accordingly:
void AddAButton(int itemNumber)
And then in the AddAButton() method, add that number to the Tag property (you'll see why below):
newButton.Tag = itemNumber;
You'll also need to subscribe to the button's Click event:
newButton.Click += DeleteClicked;
Then create the DeleteClicked() method. This is where you'll make use of that Tag property:
void DeleteClicked(object sender, EventArgs e)
{
Button button = (Button)sender;
int itemNumber = (int)button.Tag;
ShopItem item = shopItems.FirstOrDefault(item => item.labelNumber = itemNumber);
if (item != null)
shopItems.Remove(item);
}
You'll also want to clean up the controls. Make sure to Dispose() them after removing them.
You just have to identify what Label belongs to the button.
Make AddLabel return the label it just created:
Label AddLabel()
{
Label newLabel = new Label();
labelsList.Add(newLabel);
newLabel.Left = left;
newLabel.Top = top;
newLabel.Text = shopItems[shopItemNumber].GetName() + " " + shopItems[shopItemNumber].GetPrice();
panel1.Controls.Add(newLabel);
return newLabel;
}
Pass the label to the new button, and add code to remove it. Store the current shopItem in a variable, so you can access it from the removal code.
void AddAButton(Label newLabel)
{
Button newButton = new Button();
buttons.Add(newButton);
newButton.Left = left + 100;
newButton.Top = top;
newButton.Text = "DELETE";
panel1.Controls.Add(newButton);
top += 40;
// Store the current shopItem in the context (for the lambda)
var shopItem = shopItems[shopItemNumber]
newButton.Click += (s, e) => {
panel1.Controls.Remove(newLabel);
panel1.Controls.Remove(newButton);
newLabel.Dispose();
newButton.Dispose();
shopItems.Remove(shopItem);
}
}
Since controls are IDisposable, it is advised to call Dispose after removing them.
I've run into a bit of a wall and I don't know how I've managed to stuff it up. I'm trying to have multiple panels on my application in C# and each slides in and out from the menu along the side. I've written a separate slide class:
class Slide
{
Panel pane;
Button btn;
bool hidden;
Timer t;
const int maxWidth = 315;
public Slide(Panel p, Button b)
{
this.pane = p;
this.btn = b;
hidden = true;
btn.Click += new EventHandler(btnClick);
t = new Timer();
t.Interval = 15;
t.Tick += new EventHandler(timeTick);
}
private void timeTick(object sender, EventArgs e)
{
if(hidden)
{
SlidingPane(+10);
}
else
{
SlidingPane(-10);
}
}
private void btnClick(object sender, EventArgs e)
{
t.Start();
}
private void SlidingPane(int i)
{
pane.Width += i;
if(pane.Width >= maxWidth || pane.Width <= 0)
{
t.Stop();
hidden = !hidden;
}
}
}
And I've initialised the panels as follows:
Slide menuP, calendarP, peopleP, taskP, settingsP;
public Form1()
{
InitializeComponent();
ButtonColours();
InitialisePanes();
}
private void InitialisePanes()
{
menuP = new Slide(menuPane, menuButton);
calendarP = new Slide(calendarPane, calendarButton);
peopleP = new Slide(peoplePane, peopleButton);
taskP = new Slide(taskPane, toDoButton);
settingsP = new Slide(settingsPane, settingsButton);
}
And here's the Form designer code for the working panel:
this.menuPane.BackColor = System.Drawing.Color.SlateGray;
this.menuPane.Controls.Add(this.peoplePane);
this.menuPane.Dock = System.Windows.Forms.DockStyle.Left;
this.menuPane.Location = new System.Drawing.Point(67, 0);
this.menuPane.Name = "menuPane";
this.menuPane.Size = new System.Drawing.Size(0, 652);
this.menuPane.TabIndex = 2;
And the others are exactly the same. Eg:
this.peoplePane.BackColor = System.Drawing.Color.SlateGray;
this.peoplePane.Controls.Add(this.calendarPane);
this.peoplePane.Dock = System.Windows.Forms.DockStyle.Left;
this.peoplePane.Location = new System.Drawing.Point(67, 0);
this.peoplePane.Name = "peoplePane";
this.peoplePane.Size = new System.Drawing.Size(0, 652);
this.peoplePane.TabIndex = 2;
I've started up my application and I click on the menuButton, it works. Slides in and out beautifully. I click on the others and....nothing happens.
Can anyone see why this is happening? Everything I'm looking at tells me that it should be working.
To make sure all panes are correctly aligned with each other and (not) nested you could use code like this:
foreach( Control ctl in new[] { peoplePane, calendarPane, taskPane, settingsPane })
{
ctl.Parent = menuPane.Parent;
ctl.Location = menuPane.Location;
}
It assumes that menuPane is at the right spot and makes all others sit right on top without nesting them.
The code you posted contained incorrect nesting and moving panels to the same spot with the mouse will also create (in this case unwanted) nesting. Moving with the keyboard avoids it but is tedious.
hello creating a custom object may be a widely published topic, but my lack of coding skills proves problematic in actually implementing what i'm trying to do.
in a nutshell i'm adding controls at runtime in a flowpanelLayout. right now it's just listboxes, that code is all working fine. i would like a way to label the listboxes that are getting added, i can't think of a better way to do this than to use a text label. i was thinking it would be slick to create some sort of custom control (if possible) which is a listbox and a textlabel like one above the other or something. this way i can add the new custom control in my current code and assign the listbox attributes and label text, etc all in one motion.
this is what i was thinking, maybe there's even a better way to do this.
my current listview creation code:
public void addListView()
{
ListView newListView = new ListView();
newListView.AllowDrop = true;
newListView.DragDrop += listView_DragDrop;
newListView.DragEnter += listView_DragEnter;
newListView.MouseDoubleClick += listView_MouseDoubleClick;
newListView.MouseDown += listView_MouseDown;
newListView.DragOver += listView_DragOver;
newListView.Width = 200;
newListView.Height = 200;
newListView.View = View.Tile;
newListView.MultiSelect = false;
flowPanel.Controls.Add(newListView);
numWO++;
numberofWOLabel.Text = numWO.ToString();
}
maybe the actual best answer is simply to also add a textlabel here and define some set coordinates to put it. let me know what you think.
if a custom control is the way to go, please provide some resource or example for me - i'd appreciate it.
Here is a custom user control that can do that:
You just need to set TitleLabelText to set the title.
[Category("Custom User Controls")]
public class ListBoxWithTitle : ListBox
{
private Label titleLabel;
public ListBoxWithTitle()
{
this.SizeChanged +=new EventHandler(SizeSet);
this.LocationChanged +=new EventHandler(LocationSet);
this.ParentChanged += new EventHandler(ParentSet);
}
public string TitleLabelText
{
get;
set;
}
//Ensures the Size, Location and Parent have been set before adding text
bool isSizeSet = false;
bool isLocationSet = false;
bool isParentSet = false;
private void SizeSet(object sender, EventArgs e)
{
isSizeSet = true;
if (isSizeSet && isLocationSet && isParentSet)
{
PositionLabel();
}
}
private void LocationSet(object sender, EventArgs e)
{
isLocationSet = true;
if (isSizeSet && isLocationSet && isParentSet)
{
PositionLabel();
}
}
private void ParentSet(object sender, EventArgs e)
{
isParentSet = true;
if (isSizeSet && isLocationSet && isParentSet)
{
PositionLabel();
}
}
private void PositionLabel()
{
//Initializes text label
titleLabel = new Label();
//Positions the text 10 pixels below the Listbox.
titleLabel.Location = new Point(this.Location.X, this.Location.Y + this.Size.Height + 10);
titleLabel.AutoSize = true;
titleLabel.Text = TitleLabelText;
this.Parent.Controls.Add(titleLabel);
}
}
Example use:
public Form1()
{
InitializeComponent();
ListBoxWithTitle newitem = new ListBoxWithTitle();
newitem.Size = new Size(200, 200);
newitem.Location = new Point(20, 20);
newitem.TitleLabelText = "Test";
this.Controls.Add(newitem);
}