We just started learning winforms and we have to make a mock GUI. With a contextMenuStrip I can make new labels which look like folders.
private void makeFolder(int x, int y)
{
Label lbl_folder = new Label();
lbl_folder.Location = new Point(x, y);
lbl_folder.Width = 75;
lbl_folder.Height = 75;
lbl_folder.BackColor = Color.Transparent;
lbl_folder.Image = Properties.Resources.folder;
lbl_folder.Text = "New Folder" + folderindex;
lbl_folder.TextAlign = ContentAlignment.BottomCenter;
canvas.Controls.Add(lbl_folder);
folderindex++;
lbl_folder.ContextMenuStrip = folderMenuStrip; //so I can use another contextMenuStrip on them
}
With another contextMenuStrip used on these folders, I would like for example to delete the right-clicked folder but I can't reach lbl_folder.
//I would like to do something like this
private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
{
lbl_folder.Hide();
}
The name lbl_folder doesn't exists in the current context. How can I fix this?
You declared lbl_folder in local scope (within a method) so you can only use that variable within it. You have two options:
Declare the variable at form scope. So move the line Label lbl_folder = new Label(); outside of makeFolder.
private Label lbl_folder = null;
private void makeFolder(int x, int y)
{
lbl_folder = new Label();
Inside of deleteToolStripMenuItem_Click, retrieve a new reference to the same control, via the form's ControlCollection, e.g.
var lbl = this.Controls.Find("NameOfControl", true)[0] as Label;
or
var lbl = this.Controls.Find("NameOfControl", true).OfType<Label>().Single();
...depending how comfortable you are with LINQ.
You might also be able to get it from canvas more efficiently, depending how you have canvas scoped.
var lbl = canvas.Controls.Find("NameOfControl")[0] as Label.
Since you are creating labels dynamically and attaching the context menu to each label, I suspect what you really want is the delete menu item to reference the label you are right clicking.
In that case, you can use the SourceControl of the ContextMenuStrip to provide that:
private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
{
((Label)folderMenuStrip.SourceControl).Visible = false;
}
Its an issue about the scopes of your labels. If they were declared in your makeFolder method they you cannot be accessed from outside the method.
If you don't want to use the FindControls method of the Container. Then, you can keep references to your labels created in a hashtableor dictionary which is declared as instance property in the form itself.
Then, in your makeFolder add the label to it.
lbl_folder.Name = folderindex;
hashtable.Add(lbl_folder.Name, lbl_folder);
From anywhere in the form you can get the label from you hashtable and modify its properties.
private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
{
var menuItem = sender as ToolStripMenuItem;
var menu = menuItem.GetCurrentParent() as ContextMenuStrip;
var label = menu.SourceControl as Label;
label.Visible = false;
}
You can also do this inside the makeFolder method:
lbl_folder.Click += (s, e) => lbl_folder.Hide();
I understand that this isn't the event that you're trying to respond to, but it should help you to understand how it can be done.
So your method would look like:
private void makeFolder(int x, int y)
{
Label lbl_folder = new Label();
lbl_folder.Location = new Point(x, y);
lbl_folder.Width = 75;
lbl_folder.Height = 75;
lbl_folder.BackColor = Color.Transparent;
lbl_folder.Image = Properties.Resources.folder;
lbl_folder.Text = "New Folder" + folderindex;
lbl_folder.TextAlign = ContentAlignment.BottomCenter;
lbl_folder.Click += (s, e) => lbl_folder.Hide();
canvas.Controls.Add(lbl_folder);
folderindex++;
lbl_folder.ContextMenuStrip = folderMenuStrip;
}
This works no matter how many time you call this method and how many folder are created.
Related
I wish to load multiple groupboxes in the windows form application using a button_click event.
A groupbox should appear in the form each time the button is clicked.
Expected output.
I am having trouble making the location of the groupbox dynamic, as the second groupbox should be some distance away from the first groupbox. I thought of manually calculating the coordinates and using an array of points for the location, but I feel that there should be a better a way to go about it.
I have defined 'int count=0' variable to count the number of times the button is clicked. Based on that I am naming the new groupbox. But I think there is some problem in the logic used in the count++ line. It is not going after 1. Therefore I am only getting one groupbox "groupBox1". Nothing happens when I click the button again.
I appreciate your help.
Thank you
int count=0;
private GroupBox GetGroupBox(int a)
{
GroupBox groupBox = new GroupBox();
groupBox.Text = "groupBox"+(a.ToString());
groupBox.Width= 200;
groupBox.Height= 200;
groupBox.Location = new Point(50,400);
return groupBox;
}
private void button1_Click(object sender, EventArgs e)
{
count++;
this.Controls.Add(GetGroupBox(count));
}
Your question states these objectives:
Dynamically add a GroupBox based on an event (like button click).
Assign the new GroupBox location.
Pad the location with "some distance away".
You say you "feel that there should be a better a way to go about it" and there is!
Try experimenting with a FlowLayoutPanel which handles all three of these by its nature.
Here's the code I used to add and remove instances of CustomGroupBox. This is a UserControl that I added to my project, but this will work with any type of control.)
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
numericUpDownGroupboxes.ValueChanged += onGroupBoxCountChanged;
foreach (var radio in Controls.OfType<RadioButton>())
{
radio.CheckedChanged += onFlowLayoutDirectionChanged;
}
}
When the numeric up-down changes, compare the expected number of groupboxes to the current count. Alternatively, you can continue to use a button click and go straight to flowLayoutPanel.Controls.Add(...).
private void onGroupBoxCountChanged(object sender, EventArgs e)
{
// Need an int for comparison.
int
countIs = flowLayoutPanel.Controls.OfType<CustomGroupBox>().Count(),
countShouldBe = (int)numericUpDownGroupboxes.Value;
switch(countIs.CompareTo(countShouldBe))
{
case -1:
flowLayoutPanel.Controls.Add(
new CustomGroupBox
{
Name = $"groupBox{countShouldBe}",
Text = $"GroupBox {countShouldBe}",
Size = new Size(300, 150),
Margin = new Padding(10),
BackColor = Color.White,
});
break;
case 1:
Control last = flowLayoutPanel.Controls.OfType<CustomGroupBox>().Last();
flowLayoutPanel.Controls.Remove(last);
break;
}
}
The direction of the flow can also be specified.
private void onFlowLayoutDirectionChanged(object sender, EventArgs e)
{
if(radioButtonHorizontal.Checked)
{
flowLayoutPanel.FlowDirection = FlowDirection.LeftToRight;
}
else
{
flowLayoutPanel.FlowDirection = FlowDirection.TopDown;
}
}
}
Since you want to create boxes from left to right you should adjust Left: say, 1st box should have Left = 50, 2nd Left = 270, 3d Left = 490 etc.
Code:
const int deltaX = 20;
...
//TODO: check do you really want Top = 400, not, say, 20?
groupBox.Location = new Point(50 + (a - 1) * (groupBox.Width + deltaX), 400);
...
Simplified implementation can be
int count = 0;
// Let's rename the method: we actually create GroupBox, not get existing
private GroupBox CreateGroupBox(int index) => new GroupBox() {
Text = $"groupBox{index}",
Size = new Size(200, 200),
Location = new Point(50 + (index - 1) * (20 + 200), 400),
Parent = this, // Instead of Controls.Add()
};
private void button1_Click(object sender, EventArgs e) {
CreateGroupBox(++count);
}
hello everyone i'm new to c# and wpf programming and
i'm trying to create a dynamic menu where i have + and - buttons which affect a text box which represents quantity. so in a grid i call a class called productcard which i call in a page to fill the grid with the products.
now the problem is how can i use the click event inside of the product card class in my page where i have multiple cards.
class productcard
{
Button plus = new Button();
Button minus= new Button();
public TextBox qtyl = new TextBox();
Grid z = new Grid();
public int left;
public int top;
GroupBox yy;
public GroupBox XX { get { return this.yy; } set { this.yy = value; } }
public productcard(int left , int top )
{
this.left = left;
this.top = top;
Thickness margin = new Thickness(left, top, 0, 0);
Thickness bmar = new Thickness(0, 0, 0, 0);
plus.Height = 30;
plus.Width = 40;
plus.VerticalAlignment = VerticalAlignment.Bottom;
plus.HorizontalAlignment = HorizontalAlignment.Right;
plus.Content = "+";
plus.HorizontalContentAlignment = HorizontalAlignment.Center;
// - button
minus.Height = 30;
minus.Width = 40;
minus.VerticalAlignment = VerticalAlignment.Bottom;
minus.HorizontalAlignment = HorizontalAlignment.Left;
minus.Content = "-";
minus.HorizontalContentAlignment = HorizontalAlignment.Center;
// add the button to the grid
z.Children.Add(plus);
z.Children.Add(minus);
// creat text box
qtyl = new TextBox();
qtyl.Height = 30;
qtyl.Width = 30;
qtyl.Background = Brushes.White;
qtyl.VerticalAlignment = VerticalAlignment.Bottom;
qtyl.HorizontalAlignment = HorizontalAlignment.Center;
qtyl.Text = "0";
// add text box to the grid inside the group box
z.Children.Add(qtyl);
// creat group box
GroupBox yy = new GroupBox();
yy.Margin = margin;
yy.VerticalAlignment = VerticalAlignment.Top;
yy.HorizontalAlignment = HorizontalAlignment.Left;
yy.Content = z;
yy.Height = 150;
yy.Width = 150;
XX = yy;
// insert group box in the produc grid
}
public void plus_Click(object sender, EventArgs e)
{
// this.plus.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
MessageBox.Show(" + has been cliked");
int result=Convert.ToInt32(qtyl.Text)+1;
qtyl.Text = result.ToString();
}
private void minus_Click(object sender, EventArgs e)
{
int result = Convert.ToInt32(qtyl.Text) - 1;
qtyl.Text = result.ToString();
}
}
You can make a handler for your button like this:
Button myButton=new Button();
myButton.Click += delegate(object sender, RoutedEventArgs e) {
//handle event
};
I hope this helps.
Reza is correct in how to write more code for a Button generated in code.
However, I would give you a word of warning that this is not proper WPF usage of MVVM and you might be putting yourself down a path for trouble later.
I would suggest having your view's buttons bind to an ICommand that can be defined in a ViewModel that will handle doing the logic for your text update.
As you mentioned, you're having different view controls represent the data based on your button press. You're currently surviving as the view's are directly updating each other (THIS IS BAD).
The moment you want to represent this data in other views, say you want your button to update 5 labels in 3 different layouts in 2 windows, you're going to have unmaintainable references in your views.
If you have the ViewModel get a command from your view bound to the button, you can have the command logic update the property in the ViewModel that multiple views can be bound to and update them all at the same time via INotifyPropertyChanged.
Not to mention, ICommand can also let you disable buttons cleanly from being clicked.
Consider taking an hour to check out this tutorial to see the separation of View and ViewModel. What you're doing now looks like it's setting you up for a world of hurt later...
I am trying to remove textboxes and labels one by one by pressing a button.
I have a list of textboxes called inputTextBoxes.
Here is the code for adding :
private void onClickAdd(object sender, EventArgs e)
{
inputTextBoxes = new List<TextBox>();
Label label1 = new Label();
label1.Name = "label1";
label1.Text = "w" + i;
label1.Location = new System.Drawing.Point(5, 10 + (20 * i));
label1.Size = new System.Drawing.Size(30, 20);
this.Controls.Add(label1);
TextBox text1 = new TextBox();
text1.Name = "text1";
text1.Location = new System.Drawing.Point(35, 10 + (20 * i));
text1.Size = new System.Drawing.Size(25, 20);
inputTextBoxes.Add(text1);
this.Controls.Add(text1);
i++;
}
For removing I am trying this :
private void onClickRemove(object sender, EventArgs e)
{
foreach(TextBox text1 in inputTextBoxes)
{
this.Controls.Remove(text1);
}
}
But it removes only the last textbox added,clicking againg on the button doesn't do anything.
You are constantly creating a new list in your OnClickAdd() method:
inputTextBoxes = new List<TextBox>();
Try to check if the inputTextBoxes is null and only then do this line of code. Otherwise, just let the rest of the code run.
Also, remember about clearing the inputTextBoxes list after the onClickRemove() method finishes removing textboxes/labels.
You want to remove only one TextBox at a time, why do you need a foreach loop? just grab the last or first TextBox and if it is not null remove it from the Controls:
private void onClickRemove(object sender, EventArgs e)
{
var textBoxToRemove = inputTextBoxes.LastOrDefault();
// or
// var textBoxToRemove = inputTextBoxes.FirstOrDefault();
if (textBoxToRemove != null)
{
this.Controls.Remove(textBoxToRemove);
inputTextBoxes.Remove(textBoxToRemove);
}
}
Make sure you remove it from inputTextBoxes also so the next time you will ask to remove a TextBox it will not try to remove it again and go on to the next one.
Edit
#Piotr Nowak has pointed one more problem you have, you allocate a new list for inputTextBox every time you add a new TextBox, you should allocate the list only once when you create your class.
Remove this from onClickAdd method:
inputTextBoxes = new List<TextBox>();
And use this when you declare the list as a field it your class:
private readonly inputTextBoxes = new List<TextBox>();
I have a method that is returning a collection of Labels although i am able to set an properties of label but i want to define the paint event of label so that i can draw the items there in some format.
public List<Label> drawLabel()
{
lstLable = new List<Label>();
foreach (cOrderItem item in currOrder.OrderItems)
{
_lbl = new Label();
_lbl.Width = 200;// (int)CanvasWidth;
_lbl.BackColor = Color.AliceBlue;
_lbl.Text = item.ProductInfo.ProductDesc;
_lbl.Height = 20;
_lbl.Dock = DockStyle.Top;
_lbl.Paint()////this is the event i want to define for drawign purpose.
lstLable.Add(_lbl);
}
return lstLable;
}
I am returning this collection to a form where i am taking each label and adding to a panel.
It's not quite clear if you mean winforms, wpf or webforms, but in winforms just use the Control.Paint-event as any other event:
public List<Label> drawLabel()
{
lstLable = new List<Label>();
foreach (cOrderItem item in currOrder.OrderItems)
{
_lbl = new Label();
_lbl.Width = 200;// (int)CanvasWidth;
_lbl.BackColor = Color.AliceBlue;
_lbl.Text = item.ProductInfo.ProductDesc;
_lbl.Height = 20;
_lbl.Dock = DockStyle.Top;
//this is the event i want to define for drawign purpose.
_lbl.Paint += new PaintEventHandler(LblOnPaint);
lstLable.Add(_lbl);
}
return lstLable;
}
// The Paint event method
private void LblOnPaint(object sender, PaintEventArgs e)
{
// Example code:
var label = (Label)sender;
// Create a local version of the graphics object for the label.
Graphics g = e.Graphics;
// Draw a string on the label.
g.DrawString(label.Text, new Font("Arial", 10), Brushes.Blue, new Point(1, 1));
}
You can subscribe to the paint event of Label class
_lbl.Paint+=yourCustomPaintMethod;
use ObservableCollection instead of List
You can also learn to use Reactive Extensions.
I would subclass the Label control and override the OnPaint Method.
I'm trying to create components on the fly, so, I know how to make this, but, how can I access this component on the fly?
For example:
public Form1
{
Label label1 = new Label();
label1.AutoSize = true;
label1.Location = new System.Drawing.Point(e.X, e.Y);
label1.Name = string.Format("label{0}", labelsCount.ToString());
label1.Size = new System.Drawing.Size(35, 13);
label1.TabIndex = 2;
label1.Text = string.Format("Label -> {0}", labelsCount.ToString());
label1.Click += new System.EventHandler(this.label1_Click);
this.Controls.Add(label1);
label1.BringToFront();
label1.Show();
labelsCount++;
}
When I click on the label, I want to get the label's information (like position, text and name)
How I can do this? Or, what is the best way to do this?
And, to access the component based on position of the panel, inside of form, how I can do this?
Sender of event is your lablel. Simply cast sender object to Label type:
void label1_Click(object sender, EventArgs e)
{
Label label = (Label)sender;
// use
// label.Name
// label.Location
}