Handle button click event of button list inside UIScrollView Monotouch - c#

I have a UIScrollView and a UIPageControl on my View in my iPhone app. I populate this Scrollview with a list of 10 buttons. The buttons all populate correctly an I can scroll through them perfectly. My question is: How do I wire up a click event to each of those buttons? Each button will perform essentially the same task (Play a sound) however the sound will be different for each of these buttons. The buttons are created programatically by the following method:
private void CreatePanels()
{
scrollView.Scrolled += ScrollViewScrolled;
int count = 10;
RectangleF scrollFrame = scrollView.Frame;
scrollFrame.Width = scrollFrame.Width * count;
scrollView.ContentSize = scrollFrame.Size;
for (int i=0; i<count; i++)
{
float h = 150.0f;
//float w = 50.0f;
float padding = 10.0f;
int n = 25;
var button = UIButton.FromType (UIButtonType.RoundedRect);
button.SetTitle (i.ToString (), UIControlState.Normal);
UIImage img = new UIImage("Images/btntest.png");
button.SetImage(img, UIControlState.Normal);
button.Frame = new RectangleF (
(padding + 40) * (i + 1) + (i * (View.Frame.Width - 100)) ,
padding,
View.Frame.Width - 100,
h);
RectangleF frame = scrollView.Frame;
PointF location = new PointF();
location.X = frame.Width * i;
frame.Location = location;
button.Frame = frame;
scrollView.AddSubview(button);
}
pageControl.Pages = count;
}
private void ScrollViewScrolled (object sender, EventArgs e)
{
double page = Math.Floor(((scrollView.ContentOffset.X - scrollView.Frame.Width) / 2) / scrollView.Frame.Width) + 1;
pageControl.CurrentPage = (int)page;
}
The CreatePanels() method is called within the ViewDidLoad() method which populates the UIScrollView.
How can I link a click event to each of these buttons? I have searched the internet a lot but to no avail.

What about wiring up an anonymous method to each click event in the for loop?
button.TouchUpInside += (s, e) =>
{
//play i.mp3
};
I'm not sure how to actually play a sound file but you could have your sound files named as i.* since that is what your buttons are named.

Related

Don't know how to get index from button list C# winForms

I am writing a program code for creating graphs from graph theory.
Clicking on a grid generates a graph node with the corresponding index. Buttons are generated on the right. I want the nodes to be connected by an edge when the button is clicked. For example, when you click on the button on line 1 and column 2, an edge will be drawn connecting circles 1 and 2.
enter image description here
Button generation code
List<Button> btnList = new List<Button>();
for (int z = 1; z <= count; z++)
{
for (int x = 1; x <= count; x++)
{
Button btn = new Button();
btn.Text = 0.ToString();
btn.Location = new Point(z*30, x*30);
btn.Size = new System.Drawing.Size(30, 30);
btn.BackColor = System.Drawing.Color.White;
btn.MouseClick += new System.Windows.Forms.MouseEventHandler(btnClick);
panel1.Controls.Add(btn);
btnList.Add(btn);
}
}
The code of the event describing the click of the button
public void btnClick(object sender, EventArgs e)
{
Button button = (Button)sender;
button.Text = (int.Parse(button.Text)+1).ToString();
Graphics g = Graphics.FromImage(bmp);
Pen pen = new Pen(Color.Black);
//g.DrawLine(pen, );
pictureBox1.Image = bmp;
}
Maybe the question is very stupid, but I do not understand how to refer to the indexes of the pressed button in the btnClick function.
You have, for every object in C#, a Tag property which is free. You can use it to store the coordinates of your button.
So, insert before you add the btn to the panel1.Controls, something like:
btn.Tag = new Point(z, x);
Then, in the btnClick delegate, to get back your coordinates, you use something like:
Button button = sender as Button;
Point p = button.Tag;
int z = p.X;
int x = p.Y;
You can keep the Button button = (Button)sender; The only difference is that casting can throw an exception but the keyword 'as' can't.
As I am writing this, I see the new comments and your reply. Tag isn't in the delegates like KeyDown but with the properties like Location, Size, Text, etc.
You already store z and x:
btn.Location = new Point(z*30, x*30);
You can do this in the click handler to retrieve them:
int z = button.Location.X / 30;
int x = button.Location.Y / 30;
Thanks for answers.
I add code when generation buttons
btn.Tag = new Point(z, x);
and i use btn.Tag when click on button
Point p = (Point)button.Tag;
int z = p.X;
int x = p.Y;

What is the 'OnDockChanged' event handler?

I'm trying to design a button that has the DockChanged EH that knows the height & width of its paren't container (panel) and adjusts its metrics accordingly. E.g. button has text and icon, but if button width is in a panel that is too small, it will just show its icon and disable the text, etc.
Here's the method i'm trying to invoke the method with:
InnerPanel.DockChanged += HandleDockChanged;
Here's the method i'm trying to invoke the method on:
private void HandleDockChanged(EventArgs args)
{
//sorry for magic numbers
if (Parent.Height < Height || Parent.Width < Width)
{
TextLabel.Enabled = false;
InnerPanel.Width = ButtonWidth;
InnerPanel.Height = ButtonHeight;
Padding = new Padding(1);
}else
{
TextLabel.Enabled = true;
InnerPanel.Width = ButtonWidth * 2;
InnerPanel.Height = ButtonWidth * 2;
}
}

I am having trouble with windows form assignment puzzle slide

I am trying to set the array for all 9 buttons to move around to the blank tile.
for example
this: to this
1 2 3 1 2 3
4 5 6 4 5 6
7 8 7 8
The teacher claims she, "Don’t forget that you will need to cast sender to a Button" this is so I can be able to swap the tiles with the blank tile as shown above. How do I do that?
Then when I reset everything back to its original location in the reset event handler, "I get Object reference not set to an instance of an object." Do you know how I fix that error? This is because it says the same syntax error when I try to cast sender to a Button.
Here is the following code that I hope you can fix.
public partial class frmSlide : Form
{
/*
* frmSlide -constructor
*
*/
Button[,] arr = new Button[3, 3];
public frmSlide()
{
InitializeComponent();
}
/*
* allNumberButton_Click - click event handler for allNumberButton
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void frmSlide_Load(object sender, EventArgs e)
{
Button b;
b = new Button();
b.Location = new Point(79, 104);
b.Width = 40;
b.Height = 40;
b.Text = "";
b.TextAlign = ContentAlignment.MiddleCenter;
b.Font = new Font(FontFamily.GenericSerif, (float)15.0);
b.Click += new System.EventHandler(this.allNumberButton_Click);
this.Controls.Add(b);
}
private void InitTwoDim(Button[,] arr)
{
for (int i = 0; i < arr.GetLength(0); i++)
{
for (int j = 0; j < arr.GetLength(1); j++)
{
Button b;
b = new Button();
b.Location = new Point(79, 104);
b.Width = 40;
b.Height = 40;
b.Text = "1";
b.TextAlign = ContentAlignment.MiddleCenter;
b.Font = new Font(FontFamily.GenericSerif, (float)15.0);
b.Click += new System.EventHandler(this.allNumberButton_Click);
this.Controls.Add(b);
arr[i, j] = b;
}
}
}
private void allNumberButton_Click(object sender, EventArgs e)
// this is when I tried to cast button to sender to swap text but nothing happened
{
Button btn8 = (Button)sender;
Button btnBlank = (Button)sender;
string tmp;
tmp = btn8.Text;
btn8.Text = btnBlank.Text;
btnBlank.Text = tmp;
}
/*
* btnReset_Click - click event handler
* .text
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btnReset_Click(object sender, EventArgs e)
{
//This is where I tried to reset the button at its original location but got an error about'
//Object reference not set to an instance of an object.'
arr[0, 0].Text = "1";
arr[0, 1].Text = "2";
arr[0, 2].Text = "3";
arr[0, 3].Text = "4";
arr[1, 1].Text = "5";
arr[1, 2].Text = "6";
arr[1, 3].Text = "7";
arr[2, 1].Text = "8";
arr[2, 2].Text = " ";
}
/*
* btn1_Click -click event handler
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btn1_Click(object sender, EventArgs e)
{
}
/*
* btn8_click -click event handler
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btn8_Click(object sender, EventArgs e)
{
btn8.Text = btnBlank.Text;
btnBlank.Text = btn8.Text;
}
/*
* btnExit_Click -click event handler
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btnExit_Click(object sender, EventArgs e)
{
this.Close();
}
/*
* frmSlide_Load- Load event handler
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btnStart_Click(object sender, EventArgs e)
{
}
}
Can you help me try to swap the text with the blank tile and reset all tiles back to its location? This is because im basically trying to create a slide puzzle using a two dimensional array for the 9 buttons being arranged with the blank button. This is for c#.
If you run your application in Debug (F5) (rather, with an attached debugger), you will generally see and be prompted on which line in your source is giving you the exception. (i.e. uses debug symbols to essentially map back to your source code)
The line arr[0,0].Text = 1 (and every other reference to arr[<column>, <row>] usage in that scope) is going to throw an object reference exception if there are no instances of Button in arr.
Note that the line Button[,] arr = new Button[3, 3]; dimension the array (arr) by defining the length and type of that 2 Dimension array of type Button, but it doesn't actually create\construct button instances.
So, to resolve this object reference exception, you need to ensure those button instances exist before attempting to use the .Text property.
You can do that by calling to InitTwoDim and passing arr in your form load event handler: InitTwoDim(this.arr); But, this in this.arr is unnecessary code, and passing the argument is also unnecessary.
The following code will refactor that method call, and move it to the class constructor.
public frmSlide()
{
InitializeComponent();
InitializeSliderButtons();
}
void InitializeSliderButtons() {
int i = 1;
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++, i++) {
arr[c, r] = GetNewButton(i.ToString(), c * defaultButtonWidth, r * defaultButtonHeight);
this.Controls.Add(arr[c, r]);
}
}
}
int defaultButtonWidth = 40;
int defaultButtonHeight = 40;
Button GetNewButton(string btnText, int offsetX, int offsetY) {
var b = new Button()
{
Location = new Point(79 + offsetX, 104 + offsetY),
Width = defaultButtonWidth,
Height = defaultButtonHeight,
Text = btnText,
TextAlign = ContentAlignment.MiddleCenter,
Font = new Font(FontFamily.GenericSerif, (float)15.0)
};
b.Click += new System.EventHandler(this.allNumberButton_Click);
return b;
}
Noteworthy
The click event handler is assumed to be the same for all buttons here.
a 2D array is also 0 based indexed array. Currently, your reset function is attempting to index with 3 (like arr[0,3]) that will cause a different exception (out of range).
These Button instances should be added to the Form and their Button.Location assign, or Point instance, should be handled; I'm defining and including offsetX and offsetY as arguments on the helper to ensure the buttons do not stack at the same location.
I defined defaultButtonWidth and defaultButtonHeight for maintainability. They should be defined with static readonly keywords (you should do that); but, I didn't want to introduce concepts you would have to explain on.

c# Dynamically creating an EventHandler in a loop

I am trying to dynamically create an array of images on a form. THe images are rendering okay, but I cannot work out how to create the eventHandlers.
pictureBox.Click += new System.EventHandler(this.pictureBox);
This line is causing be problems.
private void frmBorderlessMain_Load(object sender, EventArgs e)
{
int top = 10;
int left = 10;
int width = 200;
int height = 150;
var uutNames = MyObject.GetNames().ToArray();
var uutImages = MyObject.GetImages().ToArray();
for (int i = 0; i < uutNames.Length; i++)
{
System.Windows.Forms.PictureBox pictureBox = new System.Windows.Forms.PictureBox();
Image newImage = Image.FromFile(uutImages[i]+".png");
pictureBox.Image = ((System.Drawing.Image)(newImage));
pictureBox.Location = new System.Drawing.Point(left, top);
pictureBox.Name = "pictureBox"+i;
pictureBox.Size = new System.Drawing.Size(200, 150);
pictureBox.SizeMode =
System.Windows.Forms.PictureBoxSizeMode.Zoom;
pictureBox.TabIndex = i;
pictureBox.TabStop = false;
pictureBox.Click += new System.EventHandler(this.pictureBox);
this.tabPage1.Controls.Add(pictureBox);
left += width + 10;
if ( (i > 0 ) && (i % 3) == 0)
{
top += height + 10;
left = 10;
}
}
}
Additional Info:
Upon clicking the image I would like a new form to open displaying details about the entity that was clicked. It will be the same form, but will be loaded with data depending on which image was clicked on.
pictureBox.Click += new System.EventHandler(this.pictureBox);
That is not how event handlers are used.
Lambda:
pictureBox.Click += (sender, eventArgs) => Console.WriteLine("HELP I WAS CLICKED OMG");
Or with a function:
pictureBox.Click += PictureBox_Click;
....
private void PictureBox_Click(object sender, EventArgs e)
{
Console.WriteLine("Stop clicking me >:(");
}

Panel with auto scroll redraw

I have form that programmatically add a panel in it;
For each task coming in to my program, I add a label and
progress bar to panel which have 30 pixel Y added to previous Y position.
But when panel get scrolls sometimes when want to scroll down,
positions multiplied and not in their exact position.
Remember, I checked and written Y positions to console and saw that Y position is ok, but panel does not show it correctly
I think problem is for panel draw method,
but don't know how to fix it.
Problem is that task 26 should come just after 25 but not in correct position, despite of console I've seen position is correct.
code to add controls:
private static void AddTaskControls(int taskId)
{
Label lbl = new Label();
lbl = new Label();
lbl.Name = "lbl" + taskId.ToString();
lbl.Text = "Task " + taskId.ToString() + ":";
lbl.Width = 57;
lbl.Height = 13;
lbl.Location = new Point(0, 25 + (taskId) * 30);
ProgressBar pr = new ProgressBar();
pr = new ProgressBar();
pr.Name = "pr" + taskId.ToString();
pr.Width = 180;
pr.Height = 23;
pr.Location = new Point(50, 20 + (taskId) * 30);
Label lbl2 = new Label();
lbl2.Name = "lbl" + taskId.ToString() + "List";
lbl2.Text = "Starting " + taskId.ToString() + "";
lbl2.Width = 200;
lbl2.Height = 13;
lbl2.Location = new Point(230, 25 + (taskId) * 30);
//Console.WriteLine(lbl2.Location.Y + "taskid:"+taskId);
if (panel1.InvokeRequired)
{
AddTaskControllsCallback t = new AddTaskControllsCallback(AddTaskControls);
panel1.BeginInvoke(t, new object[] { taskId });
}
else
{
panel1.Controls.Add(lbl);
panel1.Controls.Add(pr);
panel1.Controls.Add(lbl2);
pr.BringToFront();
}
}
This has nothing to do with threading. When adding controls the current scroll position is used to re-calculate the effective Location. Weird? Yes..
So the reason probably is that your Panel scrolls down while you are adding new controls. I don't see where it happens but here is a tiny test to demonstrate the problem:
Looks familiar, right?
I enforce the scrolling in the code but in your case the reason should be similar, as should be the fix..
private void button20_Click(object sender, EventArgs e)
{
// lets add a few buttons..
for (int i = 0; i < 20; i++)
{
Button btn = new Button();
btn.Top = i * 25;
btn.Parent = panel2;
}
// ..now let's enforce a scoll amount of 111 pixels:
panel2.AutoScrollPosition = new Point(panel2.AutoScrollPosition.X,
-panel2.AutoScrollPosition.Y + 111);
// ..and add a few more buttons..
for (int i = 20; i < 40; i++)
{
Button btn = new Button();
btn.Top = i * 25; // not good enough!
// btn.Top = i * 25 + panel2.AutoScrollPosition.Y; // this will fix the problem!
btn.Parent = panel2;
}
}
So your code needs to set the Locations like this:
lbl.Location = new Point(0, 25 + (taskId) * 30 + panel1.AutoScrollPosition.Y);
..
pr.Location = new Point(50, 20 + (taskId) * 30 + panel1.AutoScrollPosition.Y));
..
lbl2.Location = new Point(230, 25 + (taskId) * 30 + panel1.AutoScrollPosition.Y));
(Or you find out how the scrolling happens and prevent it.)
I've found that it is a lot easier to add a second panel inside the scrollable panel, add the controls to that second panel, and increase the size of the second panel. Any problems with scrolling position do not apply then.
So create a form, add a button, add a panel panel1 with AutoScroll = true;, and add another panel panel2 inside the first panel (with location 0, 0).
private void button1_Click(object sender, EventArgs e)
{
// lets add a few buttons..
for (int i = 0; i < 25; i++)
{
Button btn = new Button();
btn.Top = i * 25;
panel2.Controls.Add(btn); // Add to panel2 instead of panel1
}
// Recalculate size of the panel in the scrollable panel
panel2.Height = panel2.Controls.Cast<Control>().Max(x => x.Top + x.Size.Height);
panel2.Width = panel2.Controls.Cast<Control>().Max(x => x.Left + x.Size.Width);
}

Categories