How to find out which button I pressed? - c#

Picture the notification-dropdown menu from Facebook.
I want to implement something similar. When clicking 'Slet', it's supposed to delete that notification from the list.
private void AddNotificationsToPanel(List<Notification> notifications, StackPanel panel)
{
panel.Children.Clear();
foreach (var notification in notifications)
{
//We want every message to have text, a delete button and a postpone button
//So we need a nested stackpanel:
var horizontalStackPanel = new StackPanel();
horizontalStackPanel.Orientation = Orientation.Horizontal;
panel.Children.Add(horizontalStackPanel);
//Display the message:
var text = new TextBlock();
text.Text = notification.Message;
text.Foreground = Brushes.Black;
text.Background = Brushes.White;
text.FontSize = 24;
horizontalStackPanel.Children.Add(text);
//Add a delete button:
var del = new Button();
del.Content = "Slet";
del.FontSize = 24;
del.Command = DeleteNotificationCommand;
horizontalStackPanel.Children.Add(del);
//Add a postpone button:
var postpone = new Button();
postpone.Content = "Udskyd";
postpone.FontSize = 24;
postpone.IsEnabled = false;
horizontalStackPanel.Children.Add(postpone);
}
panel.Children.Add(new Button { Content = "Luk", FontSize = 24, Command = ClosePopupCommand });
}
Basically, I have a vertical stackpanel with x amount of horizontal stackpanels. Each of those have a textbox and two buttons.
How do I know which button I clicked? The buttons are all bound to a delete command, but I'm kind of unsure how these work:
public ICommand DeleteNotificationCommand
{
get{
return new RelayCommand(o => DeleteNotification());
}
}
Which then create this method:
private void DeleteNotification()
{
Notifications.Remove(NotificationForDeletion);
AddNotificationsToPanel(Notifications, Panel);
}
Problem is we don't know which Notification to delete, because I don't know how to see which button was clicked. Any ideas?

You should use CommandParameter property of the button by assigning unique identifier of each notification to it. I'm assuming your notification has an unique integer id:
//Add a delete button:
var del = new Button();
del.Content = "Slet";
del.FontSize = 24;
del.Command = DeleteNotificationCommand;
del.CommandParameter = notification.Id; // <-- unique id
horizontalStackPanel.Children.Add(del);
Then in the DeleteNotification method, you need to specify a parameter for the key.
public ICommand DeleteNotificationCommand
{
get{
return new RelayCommand(DeleteNotification);
}
}
private void DeleteNotification(object parameter)
{
int notificationId = (int)parameter;
var NotificationForDeletion = ...; // <--- Get notification by id
Notifications.Remove(NotificationForDeletion);
AddNotificationsToPanel(Notifications, Panel);
}
Now, in the DeleteNotification you can identify the Notification that is related to the button.

Related

UWP C# How To Handle Event for Dynamically Created Button(s) and Control

in relation to the post UWP C# Add Button Dynamically and Organizing On StackPanel I have additional questions
how do I control these dynamically created button(s)' event? eg. button 1 to turns on LED 1, button 2 to turns on LED 2 etc.
how to selectively remove the button(s) and reorganize the remaining buttons with no empty spaces in between.
Thank you.
Update:
I have a routine to add the client with details such as client IP etc. from the client and to add and display them in a scrollviewer.
How do i link either the clientname or client ip to the dictionary?
private async void AddClientList()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
ClientListUserControl clientListControl = new ClientListUserControl(this, new ClientList(clientName, receiveIP, DateTime.Now, receivePort, receiveService, receiveDEV, receiveSTS, receiveACT));
ClientList_Panel.Children.Add(clientListControl);
clientListControl.updateDisplay();
});
}
You can also use Tag property of Button to pass the parameter. This property is inherited from FrameworkElement, and generally it is used to get or set an arbitrary object value that can be used to store custom information about this object.
Please refer to following code.
private void ButtonCreateNewButton_Click(object sender, RoutedEventArgs e)
{
Button b = new Button();
b.Height = 30;
b.Width = 100;
b.VerticalAlignment = VerticalAlignment.Top;
b.HorizontalAlignment = HorizontalAlignment.Left;
b.Margin = new Thickness(6, 6, 6, 6);
b.Content = "Button " + buttonCounter;
b.Tag = "LED-" + buttonCounter;
b.Click += Button_Click;
....
buttonCounter++;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
var led = btn.Tag;
//use led_name as a parameter here, according with this variable to turn on the LED
TurnOnOffLed(led);
}
To your first question:
To handle this, you should introduce a dictionary, where the button is the key and your value is the client. So you can get the linked client in the ClickHandler.
public Dictionary<Button, object> clientDict = new Dictionary<Button, object>();
Note: Here the type of the client is object, because I don`t know what type you have!
You add the button inside of your AddButton routine. Again: I dont know where you get your client from, so I added the value null. Change this to fulfil your requirements. Then you add another ClickHandler and get the linked client:
b.Click += HandleButtonClick;
clientDict.Add(b, null);
private void HandleButtonClick(object sender, RoutedEventArgs e)
{
//Execute whatever you want from your client:
var client = clientDict[sender as Button];
}
To your second question:
You need to add a RemoveMethod, where you get the column and row of the button, which should be deleted. Afterwards you can manipulate all other buttons column and row property. To avoid, that a new added button is not aligned to the others, you need to change the add-process, to make the position of the new button depending on the number of elements in your dictionary. Here an example how the full code could look like:
public int buttonCounter = 1;
public Dictionary<Button, object> clientDict = new Dictionary<Button, object>();
private void RemoveBtn(Button button)
{
var row = Grid.GetRow(button);
var column = Grid.GetColumn(button);
//Rearange
foreach (var btn in clientDict.Keys)
{
var r = Grid.GetRow(btn);
var c = Grid.GetColumn(btn);
if (c > column || (c == column && r > row))
{
if (r != 0)
{
//Set the row new
Grid.SetRow(btn, r - 1);
}
else
{
//Need to set it to a new column
Grid.SetRow(btn, 3);
Grid.SetColumn(btn, c - 1);
}
}
}
myGrid.Children.Remove(button);
clientDict.Remove(button);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//Create the button
Button b = new Button();
b.Height = 30;
b.Width = 100;
b.VerticalAlignment = VerticalAlignment.Top;
b.HorizontalAlignment = HorizontalAlignment.Left;
b.Margin = new Thickness(20, 20, 0, 0);
b.Content = "Button " + buttonCounter;
b.Click += HandleButtonClick;
clientDict.Add(b, null);
//Calculate the place of the button
int column = (int)(clientDict.Count / 4);
int row = clientDict.Count % 4;
//Check if you need to add a columns
if (row == 0 && myGrid.ColumnDefinitions.Count <= column)
{
ColumnDefinition col = new ColumnDefinition();
col.Width = new GridLength(column, GridUnitType.Auto);
myGrid.ColumnDefinitions.Add(col);
}
//Add the button
myGrid.Children.Add(b);
Grid.SetColumn(b, column);
Grid.SetRow(b, row);
buttonCounter++;
}
private void HandleButtonClick(object sender, RoutedEventArgs e)
{
//Execute whatever you want from you handler:
var client = clientDict[sender as Button];
}
Note: The rearrange process is not performance optimized.

how to create a list of events for a list of buttons c#?

i have a List of Buttons ... but i want to make an events for each button in the list --i have tried this code
ButtonName.Click += (sender, args) =>
{
Point p = new Point(20 * j, 70);
Product[j].Location = p;
Product[j].Width = 200;
Product[j].Height = 250;
this.Controls.Add(Product[j]);
};
the event i want to make is that when click on any button it should show a List Box which is related to this button.
and every List box have its own data .... the only problem i want to get a solution for it is to make event for every button in list
is that possible ??
Update
for (int j = 0; j < Data.BTN_Name.Count; j++)
{
Category[j].Click += (sender, args) =>
{
Point p = new Point(20 * j, 70);
Product[j].Location = p;
Product[j].Width = 200;
Product[j].Height = 250;
this.Controls.Add(Product[j]);
};
It seems to me that you want to do this:
for (int j = 0; j < Data.BTN_Name.Count; j++)
{
Product[j] = new ListBox()
{
Location = new Point(20 * j, 70),
Width = 200,
Height = 250,
Visible = false,
};
this.Controls.Add(Product[j]);
var captured_j = j;
Category[j].Click += (s, ea) => Product[captured_j].Visible = true;
}
You must capture the j variable to use it in the event handler - hence the code var captured_j = j; just before the event handler.
As I can see, you are actualy trying to tie a some button to some listbox. As #TheGeneral said, you can use a Tag property of button, but I don't like this approach (although it has the right to be, it is just a matter of habit). Here is my example:
public class YourForm : Form
{
private IDictionary<Button, ListBox> _listboxes = new Dictionary<Button, ListBox>();
// use this if you create a button and listbox simultaneously
protected void CreateButtonAndList()
{
var listbox = new ListBox();
// initialize listbox as needed
var button = new Button();
// initialize button as needed
button.Click += ButtonClickHandler;
_listboxes.Add(button, listbox);
}
// use this if you create a button for already existing listbox
protected void CreateButtonFor(ListBox listbox)
{
var button = new Button();
// initialize button as needed
button.Click += ButtonClickHandler;
_listboxes.Add(button, listbox);
}
private void ButtonClickHandler(object sender, EventArgs e)
{
var listbox = _listboxes[sender];
// do what you want with listbox
}
}
Also note, that you probably doesn't need both CreateButtonAndList() and CreateButtonFor() methods. You may leave only one that fit your needs.

C# how to make function for all buttons using object sender

I´m using WindowsForm and working with flat design. In the program there are 6 buttons, these buttons are made of a label and a panel. The label controls all the actions that the button can do. when i started writing the program i made one function for each button, now i like to use one function that controls all buttons. I have tried to make that work but I´m stuck and can´t find a way to solve it.
Been looking around at the forum for solutions but i think that i might not know what i´m looking for.
This is what i made so far.
Buttons[] cobra = new Buttons[5];
private class Buttons
{
private bool position;
private string name;
public bool Position
{
get { return position; }
set { position = value; }
}
public string Name
{
get { return name; }
set { name = value; }
}
}
private void SetButtons()
{
cobra[0].Name = "label3";
cobra[0].Position = false;
cobra[1].Name = "label4";
cobra[1].Position = false;
}
private void CheckStatusButtons(object import)
{
for (int i = 0; i < cobra.Length; i++)
{
}
}
private class ToggelFunction
{
private bool hawk;
public bool Hawk
{
get { return hawk; }
set { hawk = value; }
}
}
ToggelFunction tiger = new ToggelFunction();
private void label3_Click(object sender, EventArgs e)
{
if (tiger.Hawk == false)
{
button1.BackColor = Color.PaleGreen;
label3.Text = "ON";
if (myport.IsOpen)
{
send(new byte[] { 16, 128, 32, 16, 1 });
}
tiger.Hawk = true;
return;
}
if (tiger.Hawk == true)
{
button1.BackColor = Color.DarkSeaGreen;
label3.Text = "2";
if (myport.IsOpen)
{
send(new byte[] { 16, 128, 32, 8, 1 });
}
tiger.Hawk = false;
return;
}
}
"label3_Click" this is my function for button 1, all buttons look the same just different variables.
As I found on the forum, you can use object sender to i identify which button that made the click and from there use that in the function to make action.
So all buttons will use this functions, i´m not sure how to compare values in the if statement, if button 1 is click then it should check what values button 1 has.
My idea was to make a class "Buttons" and an array to store all the values of each button, it´s not completed yet. When a button is clicked it checks
with the array what values that button has and compare that in the function depending on what the action is. The first action would be to check if the button is on or off. If it´s off then it enters that if statement and there some actions will happen, change of color and the text, these values also have to be stored in the array i guess.
I have tried to compare the array with object sender, but i get some error saying that you can´t compare bool with object i think.
So i wonder if some one might have a solution or suggestions?
I have made a simple example of what i think you want to do. bare in mind this does not abide by all coding best practises but its not a mess either. You will need to put your safety null checks in.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class ButtonVariables
{
public int value1 { get; set; }
public int value2 { get; set; }
}
Dictionary<string, ButtonVariables> bv = new Dictionary<string, ButtonVariables>();
private void ProcessClick(object sender, EventArgs e)
{
ButtonVariables vars = GetVariables(sender);
//Do stuff with your variable set here
}
private ButtonVariables GetVariables(object sender)
{
ButtonVariables returnValue = new ButtonVariables();
switch (((Button)sender).Name.ToLower())
{
case "buttona":
return bv["A"];
case "buttonb":
return bv["B"];
case "buttonc":
return bv["C"];
default:
break;
}
return null;
}
private void ButtonA_Click(object sender, EventArgs e)
{
ProcessClick(sender, e);
}
private void ButtonB_Click(object sender, EventArgs e)
{
ProcessClick(sender, e);
}
private void ButtonC_Click(object sender, EventArgs e)
{
ProcessClick(sender, e);
}
}
I've basically added two methods to handle your method. One to identify the button and get its related values from a dictionary that you will have to populate with your Buttons class. and one to carry out the logic.
EDIT
As requested in the comments here's an easy (but not the only way) to point your event listeners towards the same method.
Initially you need to set up with one button and double click it or do some other way to create the forms Button_Click() Event Method. At this point an event listener delegate has been added to your Form.Designer.cs File. Open that file and you will see something like this:
//
// ButtonA
//
this.ButtonA.Location = new System.Drawing.Point(12, 12);
this.ButtonA.Name = "ButtonA";
this.ButtonA.Size = new System.Drawing.Size(75, 23);
this.ButtonA.TabIndex = 0;
this.ButtonA.Text = "button1";
this.ButtonA.UseVisualStyleBackColor = true;
this.ButtonA.Click += new System.EventHandler(this.ButtonA_Click);
What you need to do is create your other buttons and add this code in for them with a change to the last line new System.EventHandler(this.ButtonA_Click); This line basically states which method to call when the ButtonA.Click event is invoked. At this point you can add what ever method you want (as long as you name is nicely for good convention). So your example would be this :
//
// ButtonA
//
this.ButtonA.Location = new System.Drawing.Point(12, 12);
this.ButtonA.Name = "ButtonA";
this.ButtonA.Size = new System.Drawing.Size(75, 23);
this.ButtonA.TabIndex = 0;
this.ButtonA.Text = "button1";
this.ButtonA.UseVisualStyleBackColor = true;
this.ButtonA.Click += new System.EventHandler(this.ProcessClick);
//
// ButtonB
//
this.ButtonB.Location = new System.Drawing.Point(12, 12);
this.ButtonB.Name = "ButtonB";
this.ButtonB.Size = new System.Drawing.Size(75, 23);
this.ButtonB.TabIndex = 0;
this.ButtonB.Text = "B";
this.ButtonB.UseVisualStyleBackColor = true;
this.ButtonB.Click += new System.EventHandler(this.ProcessClick);
//
// ButtonC
//
this.ButtonC.Location = new System.Drawing.Point(12, 12);
this.ButtonC.Name = "ButtonC";
this.ButtonC.Size = new System.Drawing.Size(75, 23);
this.ButtonC.TabIndex = 0;
this.ButtonC.Text = "C";
this.ButtonC.UseVisualStyleBackColor = true;
this.ButtonC.Click += new System.EventHandler(this.ProcessClick);
Remember you need to physically create the buttons on the form itself.
Lets say you have three labels and a panel for each label. You can add the event handler to all of them and whenever that event fires the event handler will use that label as the sender. To keep the panel associated with the label, you could add the panel to the label's tag property. Then, in the event handler you can then get the panel from the label.
label1.Click += label_Click;
label2.Click += label_Click;
label3.Click += label_Click;
label1.Tag = panel1;
label2.Tag = panel2;
label3.Tag = panel3;
In the event handler, just cast sender to Label and there you have your label object to do whatever you want with and like I said, the panel is in the Tag property. I did a little refactoring to your code to make it cleaner looking.
private void label_Click(object sender, EventArgs e)
{
// this is what itsme86 was suggesting in the comments
var label = (Label)sender;
var panel = (Panel)label.Tag;
label.BackColor = tiger.Hawk ? Color.DarkSeaGreen : Color.PaleGreen;
label.Text = tiger.Hawk ? "2" : "ON";
if (myport.IsOpen)
send(new byte[] { 16, 128, 32, 8, 1 });
tiger.Hawk = !tiger.Hawk;
}
Let me know if you have any questions about this.

Change background of a button when clicking another button

I am currently experimenting in WPF and just created a UniformGrid with 800 buttons which are created in a for loop. All buttons have their own names and share the same click event.
What I want to do now is the following: I want to click the first button (rect0) to change the color of this button and the next one (rect1).
I am totally stuck right now because everything I write into the click event refers to the button I clicked.
public MainWindow()
{
InitializeComponent();
for (int i = 0; i < 800; i++)
{
Button BTN_rect = new Button()
{
Name = "rect" + i,
Background = Brushes.White,
};
BTN_rect.Click += BTN_rect_Click;
Uniform.Children.Add(BTN_rect);
}
}
private void BTN_rect_Click(object sender, RoutedEventArgs e)
{
Button BTN_rect = sender as Button;
BTN_rect.Background = Brushes.Red;
MessageBox.Show(BTN_rect.Name);
}
There are a load of ways to do this.
I took a shortcut and put just 9 buttons in a stackpanel, otherwise the same.
public MainWindow()
{
InitializeComponent();
for (int i = 0; i < 8; i++)
{
Button BTN_rect = new Button()
{
Name = "rect" + i,
Content =Name,
Tag = i,
Background = Brushes.White,
};
BTN_rect.Click += BTN_rect_Click;
sp.Children.Add(BTN_rect);
}
}
private void BTN_rect_Click(object sender, RoutedEventArgs e)
{
Button current = sender as Button;
current.Background = Brushes.Red;
string targetName = $"rect{((int)current.Tag) + 1}";
Button nextButton = sp.Children.OfType<Button>().Where(x => x.Name == targetName).SingleOrDefault();
nextButton.Background = Brushes.Red;
}
Usually, you'd template data into repeated controls rather than add them in code, btw.

adding content to a button click in wpf

Why cant I do this in wpf?
button1Click.Content = "Hover over me";
or
ToolTip t = new ToolTip();
t.Content = "Something helpful";
button1Click.Tooltip = t;
I populate my widow with populate buttons on initialization I then have a foreach loop that adds buttons like so:
foreach (var routedEventHandler in new RoutedEventHandler[] { button1Click, button2Click, button3_Click })`
Now in this area I apply styles to the buttons all in one go like so:
public void populateButtons()
{
double xPos;
double yPos;
Random ranNum = new Random();
foreach (var routedEventHandler in new RoutedEventHandler[] { button1Click, button2Click, button3_Click })
{
Button foo = new Button();
Style buttonStyle = Window.Resources["CurvedButton"] as Style;
int sizeValue = 100;
foo.Width = sizeValue;
foo.Height = sizeValue;
xPos = ranNum.Next(200);
yPos = ranNum.Next(250);
foo.HorizontalAlignment = HorizontalAlignment.Left;
foo.VerticalAlignment = VerticalAlignment.Top;
foo.Margin = new Thickness(xPos, yPos, 0, 0);
foo.Style = buttonStyle;
foo.Click += routedEventHandler;
LayoutRoot.Children.Add(foo);
}
}
}
}
But when I try this:
private void button3_Click(object sender, RoutedEventArgs e)
{
((Button)sender).ToolTip = t;
}
It only activates when a button is pressed? (Thanks #H.B)
However when I paste the tooltip code in my populate buttons:
Random ranNum = new Random();
foreach (var routedEventHandler in new RoutedEventHandler[] { button1Click, button2Click, button3_Click })
{
Button foo = new Button();
ToolTip t = new ToolTip();
t.Content = "Something helpful";
foo.ToolTip = t;
It works? But the problem is that doing it this way sets all buttons with the same tooltip and or button content! which I dont want, but I cant find a way around it?
So to summarize I can either set all buttons with the same tooltip message or button content within "populateButtons()" or I can only set tooltips and button content when the button has been pressed.
Is there no method possible that can add content to a named button?
Like my initial attempt:
button1Click.Content = "Home Button";
or
button1Click.ToolTip = "Hover over me";
Why on earth cant you set content and tooltips for specific buttons on initialization?
If you want to go this route then you can add a handler for Loaded event:
foo.Click += routedEventHandler;
foo.Loaded += routedEventHandler;
And then you have something like:
void button2Click(object sender, RoutedEventArgs e)
{
if (e.RoutedEvent == FrameworkElement.LoadedEvent)
{
ToolTip t = new ToolTip();
t.Content = "Something helpful";
((Button)sender).ToolTip = t;
return;
}
//Logic for handling button clicks goes here
MessageBox.Show("action 2");
}
You are not assigning the tooltip in the handler, so how should anything happen?
((Button)sender).ToolTip = t;
If I understand correctly, you want to have different "types" of buttons, each one with a different Click handler and a different Tooltip.
If that's the case, You could create different classes deriving from Button (e.g. HelpfulButton, UnhelpfulButton... choose good names please :) ) and in their constructor assign the handler and tooltip.
Then, in the main code, you could loop through the different classes and add instances of them directly to the main canvas.
Another possible option is to create different "template" buttons using commands and copy the properties from them instead of subclassing Button. Something like this:
Button helpfulTemplate = new Button { ToolTip = "blah" };
helpfulTemplate.Command = HelpfulCommand;
Button unhelpfulTemplate = new Button { ToolTip = "bloh" };
unhelpfulTemplate.Command = UnhelpfulCommand;
foreach(var template in new Button[] {helpfulTemplate, unhelpfulTemplate})
{
var newCopy = new Button();
//etc.
newCopy.ToolTip = template.ToolTip;
newCopy.Command = template.Command;
}

Categories

Resources