Add buttons Dynamically at Runtime - c#

I want to add buttons dynamically on c sharp windows form. the no of buttons should be equal the no of records available in data table & i want to display the record whose button is clicked. could anybody help me?

In your case you need to create user control which will represent your item record on UI, create constructor which asspt your item and public event in this user control and add to your container like this.
myPanel.Controls.Add(new ItemRecordUserControl(item));
Probably you will need to use some specific containers instead of regular panel, something like System.Windows.Forms.FlowLayoutPanel.
User control will looks like:
public partial class ItemRecorUserControl : UserControl
{
public event EventHandler<EventArgs> ActionButtonClicked;
public void OnActionButtonClicked(object sender, EventArgs e)
{
if (this.ActionButtonClicked != null)
this.ActionButtonClicked(sender, e);
}
public ItemRecorUserControl()
{
InitializeComponent();
}
public ItemRecorUserControl(ItemRecord item) : this()
{
// fill item data here to controls
}
private void btnAction_Click(object sender, EventArgs e)
{
this.OnActionButtonClicked(sender, e);
}
}

you can add buttons like this :
for (int i = 0; i < YourDataTableItemsCount; i++)
{
Button b = new Button();
b.Left = //Calculate Left
b.Top = //Calculate Top
b.Parent = this;
//Or
this.Controls.Add(b);
}

Related

C# WinForms - Cannot access a control in a handler method

I have a form containing two flow layout panels (FLP), which dynamically have buttons added to them. These buttons are actually a class called tagButton which inherits from Button and I have added a handler in the constructor for the click() method. On click, I want to remove the button from the FLP it is currently in then add it to the other FLP.
Below is a trimmed down version of my code for the tagButton class. Note that the tagButton class is defined inside the of the form class both FLPs are in:
class tagButton : Button
{
public string tag = "";
public bool useTag = false; //tells you which FLP the button is in
public tagButton(String tag, Boolean useTag)
{
this.tag = tag;
this.Text = tag;
this.useTag = useTag;
this.Click += TagButton_Click;
}
private void TagButton_Click(object sender, EventArgs e)
{
tagButton tagButton = (tagButton)sender;
tagButton.useTag = !tagButton.useTag;
if (tagButton.useTag)
{
flowLayoutPanel.Controls.Remove(tagButton);
}
}
}
I'm having problems with the last line:
flowLayoutPanel.Controls.Remove(tagButton);
I can switch it to the following and it works, however there is no way for me to add it to the other FLP. Or at least, not without doing Parent.Parent.Parent.Controls[1]... etc which is clearly a bad idea.
tagButton.Parent.Controls.Remove(tagButton);
I've tried switching different classes and methods to static but nothing I tried worked, the this keyword doesn't seem to work either.
I would recommend having a separate class overriding a parent control that's aware of both FlowLayoutPanels. Then, when your button wants to switch, it can find that custom control in its parents and invoke a custom "switch" function that would move the invoking button from the list it's in to the list it wasn't in.
One of many ways to achieve this outcome is to have MainForm expose a static array of the FlowLayoutPanel candidates as Panels property:
public partial class MainForm : Form
{
public static Control[] Panels { get; private set; }
char _id = (char)64;
public MainForm()
{
InitializeComponent();
Panels = new Control[]{ flowLayoutPanelLeft, flowLayoutPanelRight, };
buttonAddLeft.Click += (sender, e) =>
{
flowLayoutPanelLeft.Controls.Add(new tagButton
{
Height= 50, Width=150,
Name = $"tagButton{++_id}",
Text = $"Button {_id}",
});
};
buttonAddRight.Click += (sender, e) =>
{
flowLayoutPanelRight.Controls.Add(new tagButton
{
Height= 50, Width=150,
Name = $"tagButton{++_id}",
Text = $"Button {_id}",
});
};
}
}
Then, suppose you want to swap between panels when a tagButton gets a right-click (for example).
class tagButton : Button
{
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (MouseButtons.Equals(MouseButtons.Right))
{
Control dest;
if(Parent.Name.Contains("Left"))
{
dest = MainForm.Panels.First(_=>_.Name.Contains("Right"));
}
else
{
dest = MainForm.Panels.First(_ => _.Name.Contains("Left"));
}
Parent.Controls.Remove(this);
dest.Controls.Add(this);
}
}
}

Is there a way to activate a Button that exists within another class?

I am using C# and Xamarin. I have two separate classes. One class is essentially the user interface and another class is acting as a custom built generic entry for users to input data and search for results by clicking a button.
Main UI Class:
Class MainPage
{
public MainPage
{
Content = new StackLayout
{
Children =
{
new InputClass // This is my custom built user entry class
{
}.Invoke(ic => ic.Clicked += WhenButtonPressedMethod) // The problem is here, I can't figure out how to call the button within the input class to fire a clicked event.
}
}
}
}
public async void WhenButtonPressedMethod (object sender, EventArgs e)
{
// Supposed to do stuff when the button is pressed
}
InputClass:
public class InputClass : Grid
{
public delegate void OnClickedHandler(object sender, EventArgs e);
public event OnClickHandler Clicked;
public InputClass
{
Children.Add(
new Button {}
.Invoke(button => button.Clicked += Button_Clicked)
)
}
private void Button_Clicked(object sender, EventArgs e)
{
Clicked?.Invoke(this, e);
}
}
The "InputClass" is a grid that holds a title text label, an entry and a button that a user can press to submit and search data. The button in this class is what I'm trying to actually access to invoke/cause a click event so that the method in the main UI class can be called. But, when I try to invoke a click event on the "InputClass" I can't access the button inside of it, I can only access "InputClass" itself which is just a grid with no useful event properties.
Any solutions or ideas?
If you are running into the same problem as mentioned here, follow the code on this page and read through the comments, it covers enough to be able to piece it together. My mistake was attaching Invokes to the wrong objects.
Don't know why fluent Invoke didn't work correctly.
Add the event handlers this way:
public MainPage
{
var ic = new InputClass();
ic.Clicked += WhenButtonPressedMethod;
Content = new StackLayout
{
Children = { ic }
}
}
public InputClass
{
var button = new Button;
button.Clicked += Button_Clicked;
Children.Add(button);
}

How to bind an object to the label in c# windows forms

I have labels with footballer's names inside. I want to get footballer's age after clicking on these labels. I do it by this way:
public partial class Form1 : Form
{
Footballer[] team = { /*team initialization*/};
public Form1()
{
InitializeComponent();
}
private void OnLabel_Click(object sender, EventArgs e)
{
for (int i = 0; i < team.Length; i++)
{
if (team[i].Name == this.Text)
{
MessageBox.Show(team[i].Age.ToString());
break;
}
}
}
}
But there is problem. There might be more than one player with the same name. So I want to bind each label with footballer. How can I do this?
For sake of simplicity suppose that you initialize your labels in this mode....
public Form1()
{
InitializeComponent();
int index = 0;
foreach(Label lbl in this.Controls.OfType<Label>())
{
lbl.Text = team[index].Name;
// Make the Tag property reference the Footballer instance
// used to set the label text with the footballer name
lbl.Tag = team[index];
index++;
}
At this point, when you receive the click event, you just need to retrieve the reference from the Tag property and use it directly
private void OnLabel_Click(object sender, EventArgs e)
{
// No loop needed here
Label current = sender as Label;
Footballer player = current.Tag as Footballer;
if(player != null)
MessageBox.Show(player.Age.ToString());
}
}
You should separate out your business logic from your presentation logic. The name is what you presented, but to keep each player/team unique then assign a unique ID.
This is then assigned to the label but hidden from view, so that when the label is clicked the ID is retrieved and you can then do a lookup based on this.
So with Player class like the following:
public class Player
{
public int ID { get;set; }
public string Name { get;set; }
//etc.
}
Then when assigning a Player to label use Label.Tag which is a general purpose field which you can use for anything your want. (Available on all Controls).
label1.Text = MyPlayer.Name;
label1.Tag = MyPlayer.ID;
If I was you, I would also change your Teams to be a List not an array
List<Footballer> team = new List<Footballer>() { /*team initialization*/};
Then you can look up as follows
private void OnLabel_Click(object sender, EventArgs e)
{
Label clickedLabel = (sender as Label);
int id = Convert.ToInt32(clickedLabel.Tag);
Footballer found = team.Find(x => x.Id == id);
MessageBox.Show(found.Age.ToString());
}

Passing data between usercontrols c#

It's known that there are some solutions similar to this one, but I can't solve my problem with them.
I have two user controls:
The first one makes a Report object.
The second one shows it.
I have a main Form that links both controls.
These two controls are created in a DLL, and are added to the
main form like this:
//ADDS THE FIRST CONTROL TO THE PANEL CONTROL
myDll.controlUserReport userControlA = new myDll.controlUserReport();
panelControl1.Controls.Add(userControlA);
userControlA.Dock = DockStyle.Left;
//ADDS THE SECOND CONTROL TO THE PANEL CONTROL
myDll.controlDocViewer userControlB = new myDll.controlDocViewer();
panelControl1.Controls.Add(userControlB);
userControlB.Dock = DockStyle.Fill;
How can I pass the Report object, which is created in the first control controlUserReport when I click over a button, to the other user control controlDocViewer to show it?
You should use events for this. In UserControlA declare the event:
//Declare EventHandler outside of class
public delegate void MyEventHandler(object source, Report r);
public class UserControlA
{
public event MyEventHandler OnShowReport;
private void btnShowReport_Click(object sender, Report r)
{
OnShowReport?.Invoke(this, this.Report);
}
}
In UserControlB subscribe to the event and show the report:
public class UserControlB
{
// Do it in Form_Load or so ...
private void init()
{
userControlA.OnShowReport += userControlA_OnShowReport;
}
private void userControlA_OnShowReport(object sender, Report r)
{
// Show the report
this.ShowReport(r);
}
}
the post above is good except init() method should not be in ControlB, but in the parent form, something like this:
public class frmMain : Form
{
private void frmMain_Load(object sender, EventArgs e)
{
// subscribe/glue
userControlA.OnShowReport += userControlB.OnShowReport;
userControlB.OnShowReport += userControlA.OnShowReport;
}
public class UserControlA
{
public event EventHandlerNodeCopy OnDataCopy;
public TreeNode NodeCopied { get; set; }
private void some_method(string z, TreeNode trn)
{
OnDataCopy?.Invoke(this, trn);
...
}
public void frmJsTree_OnDataCopy(object source, TreeNode tn)
{
NodeCopied = tn;
}
public class UserControlB
{
public event EventHandlerNodeCopy OnDataCopy;
public TreeNode NodeCopied { get; set; }
private void another_method(int i, TreeNode trn)
{
OnDataCopy?.Invoke(this, trn);
...
}
public void frmJsTree_OnDataCopy(object source, TreeNode tn)
{
NodeCopied = tn;
}
enter code here
Another approach is using BehaviorSubject (requires System.Reactive). Once the data is added in the BehaviorSubject, all places that subscribe can see the info. A very basic example:
Create a class to represent your data. Ex:
DataService.cs
public static BehaviorSubject<YourDataType> MyAwesomeData { get; } = new BehaviorSubject<YourDataType> (null);
In You UserControlA (maybe in the clickEvent from the button) or whatever:
private void btnShowReport_Click(object sender, MouseButtonEventArgs e)
{
// Do some stuffs to prepare the data...
YourDataType myDataReportPrepared = null; // something;
// Here you update your DataInfo on BehaviorSubject
DataService.MyAwesomeData.OnNext(myDataReportPrepared);
}
Finally every place in your solution that uses subscribe in that Subject will listen to that data, like for example in your UserControlB:
// This will trigger every time MyAwesomeData.OnNext() is executed.
DataService.MyAwesomeData.Subscribe(item =>
{
if (item != null){
// Do something with it...Like populate some Datagrid...
}
});

How can I take items from a listbox and display them in a listview in another form?

I have a listbox full of items for my order.
I want to take all of the items inside my listbox and transfer them into my listview.
Then I want to take my listview and display it in another form (my messagebox).
My new listview:
private void CustomerInfo_Click(object sender, EventArgs e)
{
ListViewItem customers = new ListViewItem(fullName.Text);
customers.SubItems.Add(totalcount.ToString());
customers.SubItems.Add(total.ToString());
customers.SubItems.Add(Address.Text);
customers.SubItems.Add(telephone.Text);
for (int i = 0; i < OrderlistBox.Items.Count; i++)
{
customers.SubItems.Add(OrderlistBox.Items[i].ToString());
}
Customers.Items.Add(customers);
//CLEAR ALL FIELDS
OrderlistBox.Items.Clear();
fullName.Text = "";
Address.Text = "";
telephone.Text = "";
totalDue.Text = "";
totalItems.Text = "";
}
My contextMenuStrip, so when I click on the customer I can get its info (name, address, order, etc.):
private void customerInformationToolStripMenuItem_Click(object sender, EventArgs e)
{
if (Customers.SelectedItems.Count != 0)
{
var myformmessagedialog = new MessageBoxForm
{
name = Customers.SelectedItems[0].SubItems[0].Text,
address = Customers.SelectedItems[0].SubItems[3].Text,
telephone = Customers.SelectedItems[0].SubItems[4].Text,
};
myformmessagedialog.ShowDialog();
}
}
My new form, the messagebox where I will display all the info for the client:
public partial class MessageBoxForm : Form
{
public MessageBoxForm()
{
InitializeComponent();
}
public string name;
public string address;
public string telephone;
public ListViewItem order = new ListViewItem();
private void MessageBoxForm_Load(object sender, EventArgs e)
{
lblName.Text = name;
lbladdress.Text = address;
lbltelephone.Text = telephone;
orderListView.Items.Add(order);
}
}
I'm sorry if this seems confusing but I'm just looking for help to go in the right direction. Any help is appreciated.
One way to do this is to put the data that you want to display in some sort of ViewModel, basically a class or set of classes that has the data that you want to display. Then the main form can display it, and you can pass a reference to that ViewModel to the message box and it can display it as well.
In general you want to avoid any kind of code that directly ties controls from different forms together.
The easiest way based on your current setup is to simply pass your list view data across to your MessageBoxForm e.g.
public partial class MessageBoxForm : Form
{
...
public void LoadListView(ListViewItemCollection items)
{
orderListView.Clear();
orderListView.AddRange(items);
}
}
....
private void customerInformationToolStripMenuItem_Click(object sender, EventArgs e)
{
if (Customers.SelectedItems.Count != 0)
{
var myformmessagedialog = new MessageBoxForm
{
name = Customers.SelectedItems[0].SubItems[0].Text,
address = Customers.SelectedItems[0].SubItems[3].Text,
telephone = Customers.SelectedItems[0].SubItems[4].Text,
};
myformmessagedialog.LoadListView(Customers.Items);
myformmessagedialog.ShowDialog();
}
}
Basic answer is you don't.
You maintain a collection of items (whatever they are).
You display them in a list box.
You display them in a list view.
If you want say select some from the list box and only move them to the list view.
Then you use the listbox selection to find them in your collections of items, create a list of selected ones then passs that to the form with the listview to display.
Don't use UI controls to store your data and try really hard to never make one form's UI directly dependant on another.
I'm guessing what you'd need (and I could have misunderstood what you are looking for) is a new method in you MessageBoxForm to pass in your Customers object:
private void customerInformationToolStripMenuItem_Click(object sender, EventArgs e)
{
if (Customers.SelectedItems.Count != 0)
{
var myformmessagedialog = new MessageBoxForm;
myformmessagedialog.Customers = Customers;
if (myformmessagedialog.ShowDialog() == DialogResult.OK)
{
Customers = myformmessagedialog.Customers;
}
}
}
If so, simply modify your class to be something like this:
public partial class MessageBoxForm : Form
{
public MessageBoxForm()
{
InitializeComponent();
}
private void MessageBoxForm_Load(object sender, EventArgs e)
{
if (Customers != null)
{
// add your code here to add your Customers as needed
}
}
public Customers Customers { get; set; }
}
To access anything from the parent form you need to pass it to the child form so
myformmessagedialog.ShowDialog();
becomes
myformmessagedialog dialog = new myformmessagedialg(this);
dialog.ShowDialog();
and your class constructor becomes this:
public MessageBoxForm(myformmessagedialog parent){
name=parent.fullName.Text;
address=parent.address.Text;
...etc...
InitializeComponent();
}
Though it might be better to just pass in the name, address, etc rather than the whole form, this way is nice for while you are changing things because you have one less place to change to add another variable to pass.

Categories