handle onclick event in picturebox created on runtime - c#

I am working on windows form application in c#.
Firstly i created a table layout on run time and then inserted picture boxes after reading from database. Now i want to write sql query on every tablepanel cell/picturebox click.
Firstly I tried this by inserting picture box by drag and drop and i wrote query in picturebox_onclick function. But now i am creating picture box dynamically on run time.Now how i can handle onclick function of picture boxes that i created on run time.
private void GenerateTable(int columnCount, int rowCount)
{
//Clear out the existing controls, we are generating a new table layout
tableLayoutPanel1.Controls.Clear();
//Clear out the existing row and column styles
tableLayoutPanel1.ColumnStyles.Clear();
tableLayoutPanel1.RowStyles.Clear();
//Now we will generate the table, setting up the row and column counts first
tableLayoutPanel1.ColumnCount = columnCount;
tableLayoutPanel1.RowCount = rowCount;
for (int x = 0; x < columnCount; x++)
{
//First add a column
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
for (int y = 0; y < rowCount; y++)
{
//Next, add a row. Only do this when once, when creating the first column
if (x == 0)
{
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.AutoSize));
}
//Create the control, in this case we will add a button
picturebox cmd = new picturebox();
cmd.image = new bitmap("c:\\images\\abc.png");
tableLayoutPanel1.Controls.Add(cmd, x, y);
}
}
}
Please suggest me simple way as i am new in development. Thanks in advance.

This should do it for you.
PictureBox cmd = new PictureBox();
cmd.Click += cmd_Click;
Then have a new function to handle the click.
void cmd_Click(object sender, EventArgs e)
{
throw new NotImplementedException();
}
To perform unique actions depending on what PictureBox was clicked you may want to give the PictureBox a name you can relate to such as a row number or unique ID of the row.
PictureBox cmd = new PictureBox();
cmd.Click += cmd_Click;
cmd.Name = y.ToString();
void cmd_Click(object sender, EventArgs e)
{
string RowID = ((PictureBox)sender).Name;
}

Related

How to add icons to the listview in windows Forms c#

How to add an icon to the listView in Windows Forms c#? I have found a NuGet called "ObjectListView" but I don't understand how to add items and sub-items to it. By using standard listview I am able to add images to the first column only.
ListViewItem item = new ListViewItem();
item.ImageIndex = 0;
this.listView1.Items.Add(item);
I'm trying to add buttons to remove/restart/etc. for USB devices like this MyProgram
I need those buttons/Images in "remove", "Restart, "Uninstall" columns as subitems.
By using this I can "switch" columns, but still cannot add images/buttons for multiple columns.
listView1.Columns[0].DisplayIndex = 1;
// listView1.Items.Add("tekst3", 3);
// listView1.Items.Add("tekst2", 2);
//listView1.Items.Add("tekst1", 1);
for (int j = 0; j < 10; j++)
{
ListViewItem item = new ListViewItem();
item.ImageIndex = 0;
this.listView1.Items.Add(item);
}
An image list is added with loaded files(3 of them) attached to the list view small/large image list.
You have to assign the image list
listView1.SmallImageList = imageList;
for (int i = 0; i < listView1.Items.Count; i++ )
{
listView1.Items[i].ImageIndex =i;
}
The image list was already assigned: Click
Your code will add image to the first row only. The difference is that I have to click a specific item in the first column.
for (int i = 0; i < listView1.Items.Count; i++)
{
listView1.Items[i].ImageIndex = i;
}
}
This will give the following result:Click1
I need it like this: Click2
EDIT This what you write is not what I mean. I Have found the solution:
private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
// insert buttons to last two columns
if (e.ColumnIndex == 1)
{
e.Graphics.FillRectangle(new SolidBrush(Color.Blue), e.Bounds);
}
else if (e.ColumnIndex == 2)
{
e.DrawBackground();
var imageRect = new Rectangle(e.Bounds.X, e.Bounds.Y, 60, 20);
// e.Graphics.DrawImage(SystemIcons.Information.ToBitmap(), imageRect);
e.Graphics.DrawImage(Resources.Remove_PNG, imageRect);
}
else
{
e.DrawDefault = true;
}
}

Finding index of clicked image in a dynamically generated array of pictureboxes

I have code that creates a row of 5 pictureboxes at runtime. I have added (I think) the code to add a click event handler to each picturebox as it is created.
int xPos = 95;
for (int index = 0; index < 5; index++)
{
keepImage[index] = new PictureBox();
keepImage[index].Width = 120;
keepImage[index].Height = 41;
keepImage[index].Left = xPos;
keepImage[index].Top = 360;
keepImage[index].Click += new EventHandler(keepImage_Click);
keepImage[index].BackColor = Color.Transparent;
keepImage[index].SizeMode = PictureBoxSizeMode.CenterImage;
this.Controls.Add(keepImage[index]);
xPos += 125;
}
The code works - it creates and displays the pictureboxes. I have been looking on here to find out how to find which one of the pictureboxes is clicked on...
public void keepImage_Click(object sender, EventArgs e)
{
PictureBox index = sender as PictureBox;
// identify which button was clicked and perform necessary actions
Debug.Write(index);
}
This code was taken from a solution found on here, but how do I adapt it for my needs? I have tried but so far, no luck.
At runtime the debug shows System.Windows.Forms.PictureBox, SizeMode: CenterImage but not the actual index.
Thanks for any suggestions.
EDIT
After trying one of the solutions mentioned in the comments, I now get the following error...
You can follow second approach from Get the index of array of picturebox clicked. But there is a typo issue with this answer it should be (sender as PictureBox).
So in your case, you can use Control.Tag property to store index of pictures as:
int xPos = 95;
for (int index = 0; index < 5; index++)
{
//Other codes
keepImage[index].Tag = index; //Set tag from index
this.Controls.Add(keepImage[index]);
xPos += 125;
}
Then click event would be like:
public void keepImage_Click(object sender, EventArgs e)
{
int index = int.Parse((sender as PictureBox).Tag.ToString());
Debug.Write(index);
}

Adding button to DataGridView

I am trying to customize DataGridView class to have a button beneath all rows.
So far I've added a button to a DataGridView.Controls.
The position of this button is calculated on each add/remove row, DataGridView resize and scroll.
This works, however there is a one problem with that. On DataGridView resize or scroll, when bottom edge of the DataGridView is directly below last row, the button is not visible at all or just partially.
Is there a way to make the button always visible?
I've tried setting scrollbar position and FirstDisplayedScrollingRowIndex. This does not work.
Unfortunatelly adding a whole new row isn't possible for this project.
Adding button:
buttonAddRow.Height = 17;
buttonAddRow.Text = "+";
buttonAddRow.FlatStyle = FlatStyle.System;
buttonAddRow.Font = new Font(buttonAddRow.Font.FontFamily, 6.75F);
buttonAddRow.Click += ButtonAddRow_Click;
dataGridView.Controls.Add(buttonAddRow);
And the location:
private void setLocation()
{
if (dataGridView.FirstDisplayedCell != null)
{
int positionY = 0;
positionY += dataGridView.ColumnHeadersHeight;
var visibleRowsCount = dataGridView.DisplayedRowCount(true);
var firstDisplayedRowIndex = dataGridView.FirstDisplayedCell.RowIndex;
var lastvisibleRowIndex = (firstDisplayedRowIndex + visibleRowsCount) - 1;
for (int rowIndex = firstDisplayedRowIndex; rowIndex <= lastvisibleRowIndex; rowIndex++)
{
positionY += dataGridView.Rows[rowIndex].Height;
}
buttonAddRow.Location = new Point(dataGridView.ClientRectangle.X, dataGridView.ClientRectangle.Y + positionY);
buttonAddRow.Visible = true;
}
}
Below is some code that creates a “button Row” I described earlier and adds this button row to the top, bottom and row 4 of the DataGridView. As the picture shows this button is only in the first column. If you want to display the button across all columns then you will have to implement the OnPaint method to adjust this row. However, looking at the picture, if the user scrolls down then the top button row will not be visible, this is where you would have to implement keeping the button row at the top as the user scrolled down. If this is what you are looking for, then what you end up with is a STATIC button that is always displayed at the top of the grid. Again putting this button ABOVE and OUTSIDE the grid would accomplish the same thing, with much less effort.
The code below uses a DataGridView with 3 text columns.
private void Form1_Load(object sender, EventArgs e) {
FillGrid();
InsertButtonRow(0);
InsertButtonRow(4);
InsertButtonRow(dataGridView.Rows.Count -1);
}
private void FillGrid() {
for (int i = 1; i < 15; i++) {
dataGridView.Rows.Add("Row" + i + "C1", "Row" + i + "C2", "Row" + i + "C3");
}
}
private void InsertButtonRow(int rowIndex) {
if (rowIndex >= 0 && rowIndex < dataGridView.Rows.Count) {
DataGridViewButtonCell buttonCell = new DataGridViewButtonCell();
buttonCell.Value = "+";
buttonCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
DataGridViewRow row = (DataGridViewRow)dataGridView.Rows[0].Clone();
row.Cells[0] = buttonCell;
dataGridView.Rows.Insert(rowIndex, row);
}
}

PrintPage Event stays in endless loop while printing Chart Control

I've found some issues on this but nothing that would help me in my logic. I have following code (showing not all because it would be too much):
public int AreaCounter { get; set; } = 0;
public PrintDocument pd { get; set; }
public void PrintCharts(DataTable dt)
{
foreach (DataRow row in dt.Rows)
{
//after a couple of rows - here is code for creating
//a new chartArea and binding the points to the series
//plus binding that series to the area
if(// two chartAreas have been created with each one having a chart)
{
PrintChartControl();
}
AreaCounter++;
}
}
private void PrintChartControl()
{
pd.PrintPage += pd_PrintPage;
pd.Print();
}
private void pd_PrintPage(object sender, PrintPageEventArgs e)
{
if (AreaCounter < 11) //12 ChartAreas have to be created
e.HasMorePages = true;
else
e.HasMorePages = false;
var myRec = e.MarginBounds;
Container.chart1.Printing.PrintPaint(e.Graphics, myRec);
}
Now what I want to do:
I loop through a DataTable. Every few lines I create a new ChartArea, binding some values from this lines to a Series and bind this to the ChartArea. I'm actually drawing to ChartAreas to my Chart Control. Every two ChartAreas I print the Control, clean it, and draw the next two ChartAreas (there are 12 at the end - so 6 times drawing to the Chart Control). This works but I want to achieve the following:
I want to add a new page for every Chart Control to my print event so that I have 6 pages at the end and then print this to one file (pdf). Somehow it gets into an infinitive loop in the printPage Event because of the e.HasMorePages property. How does this work with the chart?
Thank you very much!
You better have the PrintDocument class drive your data, instead of you trying to drive the PrintDocument. The PrintDocument class will raise a PrintPage event, it is our task to provide the data for that single page. The data goes onto the Graphics instance provided in the PrintPageEventArgs. If we want to print another page we make sure HasMorePages is true, the printdocument instance will call PrintPage again and we provide the data for the next page etc. When we have no more data and don't want more pages to be printed, we set HasMorePagesto false.
Based on your code I created the following example that I expect to be applicable to your case.
I assumed the Rows in your DataTable are the main source to determine if and how many pages you need to print. If it is not based on that, you can create an Enumerator for a different collection/array, the mechanisms remain the same.
// the reference to the enumerator for the DataRows
IEnumerator rows;
private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
{
var dataTable = Load();
rows = dataTable.Rows.GetEnumerator();
}
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
// no rows, no glory
if (rows == null) return;
// keep track of stuff
var chartsOnthisPage = 0;
var yPos = 20f;
// loop for what needs to go on this page, atm it prints 2 charts
// as long as there are rows
// the enumerator is moved to the next record ...
while ((e.HasMorePages = rows.MoveNext()))
{
// ... get hold of that datarow
var currentRow = (DataRow)rows.Current;
// print
e.Graphics.DrawString(currentRow[0].ToString(), Font, Brushes.Black, 0, yPos);
// keep track where we are
yPos = yPos + 40;
// do what ever is need to print the chart fro this row
GenerateChart(currentRow);
// print the chart
chart1.Printing.PrintPaint(e.Graphics, new Rectangle(0, (int)yPos, 200, 200));
// position tracking
yPos = yPos + 200;
// optionaly break here if we reached the end of the page
// keep track
chartsOnthisPage++;
if (chartsOnthisPage > 1) break; // HasMorePages is set
}
}
private void printDocument1_EndPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
{
// clean up;
rows = null;
}
I have two helper methods, one to create a DataTable with rows and one to generate some interesting chart.
private DataTable Load()
{
var dt = new DataTable();
dt.Columns.Add("chart");
for(int r=0; r<10; r++)
{
var rw = dt.NewRow();
rw[0] = Guid.NewGuid().ToString("N");
dt.Rows.Add(rw);
}
return dt;
}
Random rnd = new Random();
private void GenerateChart(DataRow row)
{
chart1.Series.Clear();
chart1.Series.Add("data");
var mx = rnd.Next(rnd.Next(10) + 3);
for (int x = 0; x < mx; x++)
{
chart1.Series[0].Points.Add(x, rnd.Next(100));
}
}
When I run this with a preview control this is what I get:

Disabling a dynamic button

Hi I have a small winforms program that will soon develop into something more. The program has 2 panels panel1 and panel2 these panels are populated dynamically with some form controls. the first panel is populated with combo-boxes and the second with a grid of buttons. What I want to achieve is to be able to disable the right button depending on what the user selects from the combobox. Each column of the grid represent a day of the week and the combobox will be used to disable the wanted day by selecting it from the list if you like.
To do this statically is straight forward, however my program will soon expand so that it can handle a large database so that's why I am doing this dynamically. Basically this is where I'm stuck at the moment I want to simply disable the right button.
Below is the interface that i have so far:
And this is my code if any help:
public Form1()
{
InitializeComponent();
}
Button[] btn = new Button[2];
ComboBox[] cmb = new ComboBox[1];
private void Form1_Load(object sender, EventArgs e)
{
placeRows();
}
public void createColumns(int s)
{
for (int i = 0; i < btn.Length; ++i)
{
btn[i] = new Button();
btn[i].SetBounds(40 * i, s, 35, 35);
btn[i].Text = Convert.ToString(i);
panel1.Controls.Add(btn[i]);
}
for (int i = 0; i < cmb.Length; ++i)
{
cmb[i] = new ComboBox();
cmb[i].SelectedIndexChanged += new EventHandler(cmb_SelectedIndexChanged);
cmb[i].Text = "Disable";
cmb[i].Items.Add("Monday");
cmb[i].Items.Add("Tuesday");
cmb[i].SetBounds(40 * i, s, 70, 70);
panel2.Controls.Add(cmb[i]);
}
}
void cmb_SelectedIndexChanged(object sender, EventArgs e)
{
ComboBox senderCmb = (ComboBox)sender;
if (senderCmb.SelectedIndex == 1)
{
//MessageBox.Show("Tuesday");
btn[1].Enabled = false;
}
}
public void placeRows()
{
for (int i = 0; i < 80; i = i + 40)
{
createColumns(i);
}
}
}
Alternative 1
Every control has a Tag property.
You can set the Tag property of your buttons to represent the column they are in.
When a selection is made in the combo box, simply search through all buttons, and enable or disable the button based on whether each button's Tag property matches the selected text in the combo box.
Alternative 2
Create a
Dictionary<string, List<Button>> buttonMap;
where the key is the value representing the column ("Tuesday") and the value is a list of buttons with that tag. When creating the buttons initially, also populate that dictionary.
If you go with Alternative 2, you'll have to remember the previously selected value of the checkbox so you can re-enable buttons that are no longer disabled.
If you have lots of buttons, you may find that Alternative 2 is noticeably faster.
UPDATE
Here's a complete working sample of Alternative 1.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
const int ROWS = 2;
const int COLS = 2;
Button[,] btn = new Button[ROWS,COLS];
ComboBox[] cmb = new ComboBox[ROWS];
private void Form1_Load(object sender, EventArgs e)
{
placeRows();
}
private readonly string[] cbTexts = new string[] { "Monday", "Tuesday" };
public void createColumns(int rowIndex)
{
int s = rowIndex * 40;
// Your original code kept overwriting btn[i] for each column. You need a 2-D array
// indexed by the row and column
for (int colIndex = 0; colIndex < COLS; colIndex++)
{
btn[rowIndex, colIndex] = new Button();
btn[rowIndex, colIndex].SetBounds(40 * colIndex, s, 35, 35);
btn[rowIndex, colIndex].Text = Convert.ToString(colIndex);
btn[rowIndex, colIndex].Tag = cbTexts[colIndex];
panel1.Controls.Add(btn[rowIndex, colIndex]);
}
cmb[rowIndex] = new ComboBox();
cmb[rowIndex].SelectedIndexChanged += new EventHandler(cmb_SelectedIndexChanged);
cmb[rowIndex].Text = "Disable";
foreach (string cbText in cbTexts)
{
cmb[rowIndex].Items.Add(cbText);
}
cmb[rowIndex].SetBounds(40, s, 70, 70);
cmb[rowIndex].Tag = rowIndex; // Store the row index so we know which buttons to affect
panel2.Controls.Add(cmb[rowIndex]);
}
void cmb_SelectedIndexChanged(object sender, EventArgs e)
{
ComboBox senderCmb = (ComboBox)sender;
int row = (int)senderCmb.Tag;
for (int col = 0; col < COLS; col++)
{
Button b = btn[row, col];
// These three lines can be combined to one. I broke it out
// just to highlight what is happening.
string text = ((string)b.Tag);
bool match = text == senderCmb.SelectedItem.ToString();
b.Enabled = match;
}
}
public void placeRows()
{
for (int rowIndex = 0; rowIndex < 2; rowIndex++)
{
createColumns(rowIndex);
}
}
}

Categories