Pass a parameter to the ApplicationBarIconButton.Click - c#

*Hi everyone,
I'm new in WP7 dev. (i'm used to work on android) and there is a basic thing i don't know how to do.
I create programmatically a list of ApplicationBarIconButton with this:
for (int i=0; i<menus.Count(); i++)
{
ApplicationBarIconButton button = new ApplicationBarIconButton();
button.IconUri = new Uri(menus.ElementAt(i).Element("ImageUrl").Value.Trim(),UriKind.Relative);
button.Text = menus.ElementAt(i).Element("Title").Value.Trim();
button.Click += new EventHandler(button_clicked);
ApplicationBar.Buttons.Add(button);
}
and I want that the button_clicked method could retrieve the i value of the button.
How is it possible?
Thanks

I was beaten to it by #Enigmativity but his answer may still be incorrect. In my experience I've found that you need to clone the iterating i variable, otherwise on the click event, i will be the last value. If his doesn't work try this (again using a lamba function)
for (int i=0; i<menus.Count(); i++){
ApplicationBarIconButton button = new ...
...
var cloned = i;
button.Click += (sender, e) => {
sometTextBlock.Text = String.Format("App Button {0} pressed.", cloned);
};
}
Cheers,
Al.

You could do this:
for (int i=0; i<menus.Count(); i++)
{
ApplicationBarIconButton button = new ApplicationBarIconButton();
button.IconUri = new Uri(menus.ElementAt(i).Element("ImageUrl").Value.Trim(),UriKind.Relative);
button.Text = menus.ElementAt(i).Element("Title").Value.Trim();
var i2 = i; //Thanks to `ajmccall` - I forgot this.
button.Click += (s, e) =>
{
// the variable `i2` is accessible now.
};
ApplicationBar.Buttons.Add(button);
}
Rather than calling a method to handle click you can use a lambda and still get access to i (via local copy i2). You could then call any method passing i2 as a parameter if you need to.

An integrated way of achieving this is through the use of commanding in MVVM frameworks.
Granted with Application bar buttons / menu items it is a bit more tricky but far more flexible than to manipulate the UI Elements on the page.
Look in to MVVM light (http://mvvmlight.codeplex.com) or further with the likes of Calburn.Micro (http://caliburnmicro.codeplex.com/)
For application bar data binding you will need to google further (lost the link at the mo)

Related

Is it possible to turn a button into a variable?

I have a code in C# with 26 button in it and I want them to all be disabled until the user does something, but I dont want to copy/paste button1.Enable = false; Button2.Enable = false...
So is there a way to do something like this :
for (int i = 1; i < 26; i++)
{
button+i.Enable = false;
}
Thanks for your help.
You probably want to iterate buttons directly.
foreach(var button in this.Controls.OfType<Button>())
{
button.Enable = false;
}
Using either of Controls.Find or Controls.OfType<Button>() only works at run-time. I prefer a statically checkable compile-time approach. Then if you delete a button on the form the code won't compile. Helps give you that type-safe feeling.
I do it this way. Start with a field to hold the buttons in an array:
private Button[] _buttons = null;
Set the array once in your initialization code:
_buttons = new[] { button1, button2, button26 };
Then when you need to do something with the buttons you just do this:
foreach (var button in _buttons)
{
button.Enable = false;
}
You can use Form.Controls.Find()
for (int i = 1; i < 26; i++)
{
this.Controls.Find("button" + i, searchAllChildren: false).Enable = false;
}
The code assumes your buttons are named button1 through button26.
It's not possible directly like you are, but that doesn't make sense anyway.

Why can't asp.net dynamic button fire in loop?

So I found this very puzzling problem which involves dynamic buttons.
Here is my method that creates the buttons:
private void CreateButtons()
{
//Button outside loop works
Button selectItem = new Button();
selectItem.Text = "Hello World";
selectItem.ID = "btn";
selectItem.Click += selectItem_Click;
PlaceHolder1.Controls.Add(selectItem);
int ItemCounter = 0;
for (int i = 0; i < BillDate.Count; i++)
{ //Button inside loop doesnt work
ItemCounter++;
Button selectItem = new Button();
selectItem.Text = "Hello World";
selectItem.ID = "btn-" + ItemCounter.ToString();
selectItem.Click += selectItem_Click;
PlaceHolder1.Controls.Add(selectItem);
}
}
Now here is the problem,
the button that is created outside the loop works fine (event handler selectItem_Click only redirects page).
Why does the button not work inside the loop and why does it work outside the loop?
All of the buttons in your loop have the same ID, since you're not incrementing ItemCounter. While you could just use i instead, you don't appear to be using the ID at all, so you're better off just not setting it in the first place.
Also keep in mind that on the post back the buttons need to be created and added to the page in the PreInit event in order for the event handler to be able to run.
Dynamically creating controls, particularly controls that have handlers on subsequent postbacks, can be quite tricky. It's not uncommon at all to need data from the request to be able to generate the controls, but to need the controls to be generated before the request is processed by ASP for the events to fire. It's dramatically easier to create a template that you bind your data to, using something like a GridView or a Repeater instead, as it will be able to properly handle re-creating the controls before the request is processed while still allowing you to have a dynamic number of instances of the template.
**Use the below code it will work.**
private void CreateButtons()
{
//Button outside loop works
Button selectItem = new Button();
selectItem.Text = "Hello World";
selectItem.ID = "btn";
selectItem.Click += selectItem_Click;
PlaceHolder1.Controls.Add(selectItem);
int ItemCounter = 0;
for (int i = 0; i < BillDate.Count; i++)
{ //Button inside loop doesnt work
Button selectItem = new Button();
selectItem.Text = "Hello World";
selectItem.ID = "btn-" + ItemCounter.ToString();
selectItem.Click += selectItem_Click;
ItemCounter++;
PlaceHolder1.Controls.Add(selectItem);
}
}

C# Dynamically added button click is throwing argument out of bounds exception

Basically I am trying to create an attachment window utilizing keeping everything in lists for easy use later. So, every time the form loads it goes through everything in the list and creates both labels and buttons for them. There is no errors until I click my button. If I click any of the X buttons, I get an argument out of bounds exception on the click += line. What's interesting is why its being called? The click isn't supposed to add another event handler to itself. Its also interesting that on click the indicie is one greater than the total count so how its even executing that line is beside me considering it should never iterate higher that its max count. Any help would be greatly appreciated.
ArrayList attachmentFiles;
ArrayList attachmentNames;
public Attachments(ArrayList attachments, ArrayList attachmentFileNames)
{
InitializeComponent();
attachmentFiles = attachments;
attachmentNames = attachmentFileNames;
}
private void Attachments_Load(object sender, EventArgs e)
{
ScrollBar vScrollBar1 = new VScrollBar();
ScrollBar hScrollBar1 = new HScrollBar();
vScrollBar1.Dock = DockStyle.Right;
hScrollBar1.Dock = DockStyle.Bottom;
vScrollBar1.Scroll += (sender2, e2) => { pnl_Attachments.VerticalScroll.Value = vScrollBar1.Value; };
hScrollBar1.Scroll += (sender3, e4) => { pnl_Attachments.HorizontalScroll.Value = hScrollBar1.Value; };
pnl_Attachments.Controls.Add(hScrollBar1);
pnl_Attachments.Controls.Add(vScrollBar1);
Label fileName;
for (int i = 0; i < attachmentNames.Count; i++)
{
fileName = new Label();
fileName.AutoSize = true;
fileName.Text = attachmentNames[i].ToString();
fileName.Top = (i + 1) * 22;
pnl_Attachments.Controls.Add(fileName);
Button btn_RemoveAttachment = new Button();
btn_RemoveAttachment.Text = "X";
btn_RemoveAttachment.Tag = i;
btn_RemoveAttachment.Click += new System.EventHandler((s, e3) => removeAttachment(s, e3, attachmentFiles[i].ToString(), attachmentNames[i].ToString()));
btn_RemoveAttachment.Top = (i + 1) * 22;
btn_RemoveAttachment.Left = fileName.Right + 22;
pnl_Attachments.Controls.Add(btn_RemoveAttachment);
}
}
private void removeAttachment(object sender, EventArgs e, string file, string fileName)
{
attachmentNames.Remove(fileName);
attachmentFiles.Remove(file);
pnl_Attachments.Controls.Clear();
this.Close();
}
In my test, attachmentFiles had a count of 3 and attachmentNames had a count of 3. On form load, there are no issues. But, on button click I get an exception because somehow its trying to add another click listener to a button with i = 3 (a.k.a a 4th element)
The problem is not with the event subscription, but with the event handler execution.
You are running into this problem because a closure is created for the event handler, but the value i is modified by the for loop. The last value of i will be 1 + attachmentNames.Count and this will be the value used by each invocation of the event handler.
For more detail as to why this happens you can check out the question and answer here: Access to Modified Closure.
To resolve the problem, you can assign i to another variable:
var currentAttachmentIndex = i;
btn_RemoveAttachment.Click += new System.EventHandler((s, e3) => {
removeAttachment(s,
e3,
attachmentFiles[currentAttachmentIndex].ToString(),
attachmentNames[currentAttachmentIndex].ToString())
});
Or you can use the value you already captured in the Tag property of the btn_RemoveAttachment control.
btn_RemoveAttachment.Click += new System.EventHandler((s, e3) => {
var senderButton = (Button)s;
var currentAttachmentIndex = (int)senderButton.Tag;
removeAttachment(s,
e3,
attachmentFiles[currentAttachmentIndex].ToString(),
attachmentNames[currentAttachmentIndex].ToString())
});
Keep in mind, though, if you are removing items from the List, the indexes will not be valid. Understanding how closures work, however, should help you solve that problem if it arises (it looks like you close the form anyway after the first removal).
Presumably, the attachmentFiles[i] is what is causing the out of bounds exception, perhaps attachmentFiles has fewer elements than attachmentNames?
Why not set a breakpoint on that line and check what is causing the out of bounds exception?
I get an argument out of bounds exception on the click += line. What's interesting is why its being called? The click isn't supposed to add another event handler to itself
It looks like the exception is not thrown at the event subscription (+=) but at the lambda function declared in that same line
Its also interesting that on click the indicie is one greater than the total count so how its even executing that line is beside me considering it should never iterate higher that its max count. Any help would be greatly appreciated
The value of i is fixed when you assign the lambda to the event. The indexes at the attachmentFiles change as you remove elements, but the indexes used by the lambda to access it don't. Let's try an example.
let's assume we have an array with 4 attchements (index:attachment))
[0:att0, 1:att1, 2:att2, 3:att3]
And 4 buttons that execute this lambdas
[removeAt(0), removeAt(1), removeAt(2), removeAt(3)]
We click the second button and it correctly removes the second attachment from array, now we have:
[0:att0, 1:att2, 2:att3]
[removeAt(0), removeAt(1), removeAt(2), removeAt(3)]
Now we click the fourth button. It tries to remove the attachment with index 3 and the out of bounds exception is thrown because that index doesn't exist anymore (and even if it existed, it might not point to the right attachment!)
Another approach would be to modify your 'removeAttachment' method, and use that as your event handler for all buttons.
An example of this would be:
for (int i = 0; i < attachmentNames.Count; i++)
{
var lbl_FileName = new Label
{
AutoSize = true,
Name = "Label" + i, // Give this a name so we can find it later
Text = attachmentNames[i],
Top = (i + 1) * 22
};
var btn_RemoveAttachment = new Button
{
Text = "X",
Tag = i,
Top = (i + 1) * 22,
Left = lbl_FileName.Right + 22
};
btn_RemoveAttachment.Click += removeAttachment;
pnl_Attachments.Controls.Add(lbl_FileName);
pnl_Attachments.Controls.Add(btn_RemoveAttachment);
}
Then you can modify your removeAttachment method to be an EventHandler, and to detect the button and associated label using the sender As Button and Button.Tag property:
private void removeAttachment(object sender, EventArgs e)
{
// Get associated Label and Button controls
var thisButton = sender as Button;
var index = Convert.ToInt32(thisButton.Tag);
var thisLabel = (Label) Controls.Find("NameLabel" + index, true).First();
// Remove the files
int itemIndex = attachmentNames.IndexOf(thisLabel.Text);
attachmentNames.RemoveAt(itemIndex);
attachmentFiles.RemoveAt(itemIndex);
// Disable controls and strikethrough the text
thisButton.Enabled = false;
thisLabel.Font = new Font(thisLabel.Font, FontStyle.Strikeout);
thisLabel.Enabled = false;
}

Adding menu items during runtime in C#

I'm still working on getting a menu to display all of the input devices on a computer- pardon my third question in something that is probably very simple.
Here's the code:
List<MenuItem> inputDevice = new List<MenuItem>();
MenuItem myMenuItemInputDevices = new MenuItem("&Input Devices");
sgFileMenu.MenuItems.Add(myMenuItemInputDevice);
for (int i = 0; i < DeviceCount; i++)
{
inputDeviceMenu.Add(new MenuItem(inputName[i]));
myMenuItemInputDevices.MenuItems.Add(inputDeviceMenu[i]);
myMenuItemInputDevices.Click += new System.EventHandler(this.myMenuItemInputDeviceClick);
}
This seems to work just fine, the menu items are added, everything is good, but clicks on the dropdown list are not working. I've done other work with menus, and clicks in other code are working correctly. I tried putting
myMenuItemInputDevices.Click += new System.EventHandler(this.myMenuItemInputDeviceClick);
outside of the {}, just in case that was the right way to do it, but that didn't help.
What am I missing?
You want this
List<MenuItem> inputDevice = new List<MenuItem>();
MenuItem myMenuItemInputDevices = new MenuItem("&Input Devices");
sgFileMenu.MenuItems.Add(myMenuItemInputDevice);
for (int i = 0; i < DeviceCount; i++)
{
inputDeviceMenu.Add(new MenuItem(inputName[i]));
inputDeviceMenu[i].Click += new System.EventHandler(this.myMenuItemInputDeviceClick);
myMenuItemInputDevices.MenuItems.Add(inputDeviceMenu[i]);
myMenuItemInputDevices.Click += new System.EventHandler(this.myMenuItemInputDeviceClick);
}
EDIT: It is pretty obvious that the Menu Items that you are trying to add does not have any Click event method hooked up.
inputDeviceMenu.Add(new MenuItem(inputName[i]));
You are just adding them.

Creating Windows Form controls during execution

I'm sure there's a way to do it, I just haven't been able to work it out for myself and searching the site hasn't shown me what I need to know. Maybe I'm just using the wrong keywords.
I am trying to add controls to a form during execution. I would like to create new controls for the number displayed in a numericUpDown. E.g. if the user inputs 3, 3 controls should be created.
Is it something in Form.ActiveForm.* ?
Thanks.
Instead of "elements", I think you are referring to "controls".
The general way is:
TextBox textBox = new TextBox();
textBox.Location = Some Point on your form or container.
this.Controls.Add(textBox);
For your extra numbers, just do that in a loop:
int topValue = 0;
for (int i = 0; i < numericUpDown1.Value; i++) {
TextBox textbox = new TextBox();
textBox.Location = new Point(0, topValue);
this.Controls.Add(textBox);
topValue += textBox.Height + 2;
}
Do you mean something as simple as this?
numericUpDown1.Maximum = int.Parse(textBox1.Text);
If not, please elaborate.

Categories