I have a DataGridView with a column of the type DataGridViewTextBoxColumn. I want the user to be able to copy column data from Excel and paste it into the DataGridView. I'm capturing Ctrl+V in the key down event so I can parse the text and place it in the correct cells.
The problem I have is that after I assign values to the affected cells, the entire clipboard text is pasted in the active cell. For example, if 1\r\n2\r\n3 is pasted, I can place the 2 and 3 in their respective cells but 1\r\n2\r\n3 ends up in the active cell.
My code in the dgvCell_KeyDown event for reference
private void dgvCell_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.V && e.Control)
{
string text = Clipboard.GetText();
string[] data = text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
int col = dgv.CurrentCell.ColumnIndex;
int row = dgv.CurrentCell.RowIndex;
if (data.Length <= dgv.Rows.Count - row)
{
for (int i = 0; i < data.Length; i++)
{
dgv.Rows[row + i].Cells[col].Value = data[i];
}
}
}
}
Is there an event that fires when a paste occurs or is there some way I can cancel the paste from the Key down event?
Right now, I have a hack in place where I'm setting a bool true in the key down event and then calling e.Cancel = true; in the key press event to stop the paste from happening; there has got to be a better way!
Related
So, I have a data grid view in my C# Win Form application, the cells are editable by default, now I want whenever something is typed in those cell the end value should be treated as Upper Case First, so which means If any user types:
*string => String
example => Example
another => Another
chaRactEr => ChaRactEr*
I can do this in my code in the Cell Value Changed event, but when I do this in the Cell Value Changed event and set the value of that cell as the formatted string (Which is required from end-user) the event gets triggered twice. I can't let this happened since there is a database functionality triggering in this event.
I have tried capturing the cell value in other event like Cell Leave, Cell Enter and other events, but never can I capture it.
So I need to know, If there is a any property or characteristic of the Data Grid View in C#.NET which would make the first character of the value as upper case?
Any alternate suggestion to this would also be really helpful.
You can use this code:
bool bchange = false;
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (bchange == false)
{
bchange = true;
String oritext = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();
String newtext= oritext.First().ToString().ToUpper() + oritext.Substring (1);
//Update Database
//Update cell
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = newtext;
}
else
{
bchange = false;
}
}
DataGridView has an event 'CellFormatting'. You can go for something like this:
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.Value != null)
{
string str = (string)e.Value;
str = char.ToUpper(str[0]) + str.Substring(1);
e.Value = str;
}
}
I can only guess you may be neglecting that when the code “changes” the value in the “cell” that called the CellValueChanged event, will obviously “fire” the CellValueChanged event again when its value is changed to have an upper case string!
To avoid this circular reference, simply turn the event “off” (before you change the cells value), change the cells value… the event will not fire… then turn the event back “on" after the value has changed.
Example; Below checks to see if the cell changed is in column 0, changes the string in the cell to make the first character an upper case character. The code utilizes a text box on the form that will contain text indicating when the CellValueChanged event is fired. If the code runs with the commented code as posted, the text box will contain two (2) entries every time a cell value in column one changes. UN-commenting the two lines of code will show the text box entry will have only one (1) entry. Sandwich the line of code that “changes” the cells value between the line of code that turns the event “off” and the line of code that turns it back “on”. Hope this makes sense.
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
if (e.RowIndex >= 0 && e.ColumnIndex >= 0) {
if (e.ColumnIndex == 0 && dataGridView1.Rows[e.RowIndex].Cells[0].Value != null) {
string currentData = dataGridView1.Rows[e.RowIndex].Cells[0].Value.ToString();
string newData = char.ToUpper(currentData[0]) + currentData.Substring(1);
//dataGridView1.CellValueChanged -= new DataGridViewCellEventHandler(dataGridView1_CellValueChanged);
dataGridView1.Rows[e.RowIndex].Cells[0].Value = newData;
//dataGridView1.CellValueChanged += new DataGridViewCellEventHandler(dataGridView1_CellValueChanged);
textBox3.Text += "CellValueChanged fired!" + Environment.NewLine;
}
}
}
I want a Delete button at the end of each row of DataGridView and by clicking that I want to remove the desired row from the binding list which is data source of my grid.
But I can't seem to do it I have created a button object in product class and instantiated it with the unique id to remove that object from list. but button is not showing in the row.
There are TextBoxes in the form and users can enter text, and when they press Add button, a new object of product is instantiated with the provided fields and then it is added to the BindingList.
Finally this list is bound to the DataGridView and details are shown in the grid. (I have done this part).
and at last by clicking save button the list is saved in the DB.
public class Product{
public string Brand { get; set; }
public int ProductPrice { get; set; }
public int Quantity { get; set; }
public product(string brand,int productPrice, int quantity){
this.Brand = brand;
this.ProductPrice = productPrice;
this.Quantity = quantity;
}
}
public partial class MainForm: Form{
.....
BindingList<Product> lProd = new BindingList<Product>();
private void btnAddProduct_Click(object sender, EventArgs e){
string Brand = txtProBrand.Text;
int Price = Convert.ToInt32(txtPrice.Text);
int Quantity = Convert.ToInt32(txtQuantity.Text);
Product pro = new Product(Brand, Price, Quantity);
lProd.Add(pro);
dataGridView1.DataSource = null;
dataGridView1.DataSource = lProd;
}
.....
}
To show a button on DataGridView rows, you should add a DataGridViewButtonColumn to columns of your grid. Here is some common tasks which you should know when using button column:
Add Button Column to DataGridView
Show Image on Button
Set Text of Button
Handle Click Event of Button
Add Button Column to DataGridView
To show a button on each row of your grid, you can add a DataGridViewButtonColumn to columns of your grid programmatically or using designer:
var deleteButton=new DataGridViewButtonColumn();
deleteButton.Name="dataGridViewDeleteButton";
deleteButton.HeaderText="Delete";
deleteButton.Text="Delete";
deleteButton.UseColumnTextForButtonValue=true;
this.dataGridView1.Columns.Add(deleteButton);
Show Image on Button
If you prefer to draw image on button, you should have an image in a resource and then handle CellPainting event of your grid:
void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.RowIndex == dataGridView1.NewRowIndex || e.RowIndex < 0)
return;
if (e.ColumnIndex == dataGridView1.Columns["dataGridViewDeleteButton"].Index)
{
var image = Properties.Resources.DeleteImage; //An image
e.Paint(e.CellBounds, DataGridViewPaintParts.All);
var x = e.CellBounds.Left + (e.CellBounds.Width - image.Width) / 2;
var y = e.CellBounds.Top + (e.CellBounds.Height - image.Height) / 2;
e.Graphics.DrawImage(image, new Point(x, y));
e.Handled = true;
}
}
Set Text of Button
You can use either of these options:
You can set Text property of your DataGridViewButtonColumn and also set its UseColumnTextForButtonValue to true, this way the text will display on each cells of that column.
deleteButton.Text="Delete";
deleteButton.UseColumnTextForButtonValue=true;
Also you can use Value property of cell:
this.dataGridView1.Rows[1].Cells[0].Value = "Some Text";
Also as another option, you can handle CellFormatting event of your grid. This way may be useful when you want to set different texts for buttons.
void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
//If this is header row or new row, do nothing
if (e.RowIndex < 0 || e.RowIndex == this.dataGridView1.NewRowIndex)
return;
//If formatting your desired column, set the value
if (e.ColumnIndex=this.dataGridView1.Columns["dataGridViewDeleteButton"].Index)
{
e.Value = "Delete";
}
}
Handle Click Event of Button
To hanlde clicks on button, you can handle CellClick or CellContentClick event of your grid. Both events fires by click and by pressing Space key.
void dataGridView_CellClick(object sender, DataGridViewCellEventArgs e)
{
//if click is on new row or header row
if( e.RowIndex == dataGridView1.NewRowIndex || e.RowIndex < 0)
return;
//Check if click is on specific column
if( e.ColumnIndex == dataGridView1.Columns["dataGridViewDeleteButton"].Index)
{
//Put some logic here, for example to remove row from your binding list.
//yourBindingList.RemoveAt(e.RowIndex);
// Or
// var data = (Product)dataGridView1.Rows[e.RowIndex].DataBoundItem;
// do something
}
}
Get data of the record on Click event
You have e.RowIndex, then you can get the data behind the row:
var data = (Product)dataGridView1.Rows[e.RowIndex].DataBoundItem;
// then you can get data.Id, data.Name, data.Price, ...
You need to cast it to the data type of the recore, for example let's say Product.
If the data binding has been setup to use a DataTable, the the type to cast is DataRowView.
You can also use dataGridView1.Rows[e.RowIndex].Cells[some cell index].Value to get value of a specific cell, however DataBoundItem makes more sense.
Note
As mentioned by Ivan in comments, when you use BindingList you don't need to set datasource of grid to null and back to binding list with every change. The BindingList itself reflects changes to your DataGridView.
i want to check if obtained marks are not greater than total marks in a gridview while entering data in runtime. what is the best way to implement that?
the logic i have developed is something like this:
if(DG_Result .Rows .Count >0)
{
for(int x=0;x<DG_Result .Rows .Count ;x++)
{
if(DG_Result .Rows [x].Cells ["DGTotal"].Value !="" & DG_Result .Rows [x].Cells ["DGObt"].Value !="")
{
if(Convert .ToInt32(DG_Result .Rows [x].Cells ["DGObt"].Value)>Convert .ToInt32(DG_Result .Rows [x].Cells ["DGTotal"].Value ))
{
DG_Result.Rows[x].Cells["DGObt"].Value = "";
MessageBox.Show("Obtained Marks Cannot be greater than Total Marks");
}
}
}
}
but i am not sure which event shall i use for this. i used timer but its not working. any suggestions? Cheers
Attach an event handler to your datagridview. Imaging it is called dataGridView1:
this.dataGridView1.CellValidating += new
DataGridViewCellValidatingEventHandler(dataGridView1_CellValidating);
Then write the event handler where you will have your validation logic:
private void dataGridView1_CellValidating(object sender,
DataGridViewCellValidatingEventArgs e)
{
string headerText =
dataGridView1.Columns[e.ColumnIndex].HeaderText;
// Abort validation if cell is not in the target column.
// The target column is the column you want to validate
if (!headerText.Equals("TargetColumn")) return;
// Confirm that the cell is not empty.
if (string.IsNullOrEmpty(e.FormattedValue.ToString()))
{
dataGridView1.Rows[e.RowIndex].ErrorText =
"Your error message goes here.";
e.Cancel = true;
}
}
You can read more about validation here.
I'm trying to add a clickable image/button to a datagridview button column.
The image/button will be an icon for play or stop. If the user clicks the play button a service on the system is started, if the user clicks the stop button a service is stopped.
I already have written functions for starting and stopping the service. What I'm having difficulty with is getting the button/image to show up in the datagrid and making it clickable.
Here's what I have for code:
this.dgrdServices.RowPrePaint +=new DataGridViewRowPrePaintEventHandler(dgv_RowPrePaint);
this.dgrdServices.Rows.Add();
this.dgrdServices.Rows[0].Cells[0].Value = Image.FromFile(#"C:\users\brad\desktop\green-dot.gif");
this.dgrdServices.Rows[0].Cells[1].Value = "MyServer";
this.dgrdServices.Rows[0].Cells[2].Value = "MyService";
this.dgrdServices.Rows[0].Cells[3].Value = "Started";
this.dgrdServices.Rows[0].Cells[4].Value = new DataGridViewButtonCell();
this.dgrdServices.Rows[0].Cells[5].Value = "Uninstall";
I can't work out if it would be better to use a button which is an image or an just an image that's clickable. I also can't get a button to show up correctly.
Thanks
Brad
Show Image On Button
You can add a DataGridViewButtonColumn, then handle CellPainting event of the grid and check if the event is raised for your button column, then draw an image on it. At the end of event, don't forget to set e.Handled = true;.
In the below code I suppose you have an image resource like someImage:
private void grid_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.RowIndex < 0)
return;
//I supposed your button column is at index 0
if (e.ColumnIndex == 0)
{
e.Paint(e.CellBounds, DataGridViewPaintParts.All);
var w = Properties.Resources.SomeImage.Width;
var h = Properties.Resources.SomeImage.Height;
var x = e.CellBounds.Left + (e.CellBounds.Width - w) / 2;
var y = e.CellBounds.Top + (e.CellBounds.Height - h) / 2;
e.Graphics.DrawImage(someImage, new Rectangle(x, y, w, h));
e.Handled = true;
}
}
Show Image Without Button
To show a single image on all rows including new row, you can set the Image property of DataGridViewImageColumn. This way the image will be shown in that column on for all rows:
dataGridView1.Columns.Add(new DataGridViewImageColumn(){
Image = someImage, Name = "someName", HeaderText = "Some Text"
});
Also if you may want to have different images for cells, you can set the formatted value of DataGridViewImageColumn in CellFormatting event:
void grid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.RowIndex < 0)
return;
//I supposed the image column is at index 1
if (e.ColumnIndex == 1)
e.Value = someImage;
}
You also can set Image property of DataGridViewImageColumn to an image, but the image will not show on new row.
Handle Click
To handle Click on image/button you can handle CellClick or CellContentClick event:
void grid_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0)
return;
//I suposed you want to handle the event for column at index 1
if (e.ColumnIndex == 1)
MessageBox.Show("Clicked!");
}
If you handled CellContentClick you should exactly click on image when you are using image column.
Screenshot
Here is the result. First column is a button column showing an image and second column is a normal image column set to show a single image:
Important Note
In above examples I assume you have an image in someImage member
variable:
Image someImage = Properties.Resources.SomeImage
Make sure you dispose someImage on disposal of the form and avoid using Properties.Resources.SomeImage directly everywhere you need.
I am getting an error in row validating when I press the escape key. Instead of deleting the text from the datagridview row i.e I added text in a new row. I don't want to save that row and I don't want to close the form. Simply, when I press the Escape key from the keyboard the text should be deleted. I am using this code in datagridviewrow validating event.
if (string.IsNullOrEmpty(dgTests.CurrentRow.Cells["Column1"].Value.ToString()) && !string.IsNullOrEmpty(dgTests.CurrentRow.Cells["Column2"].Value.ToString()))
{
dgTests.Rows[e.RowIndex].ErrorText = "Column1 should not be empty";
e.Cancel = true;
}
When Press escape Key in DataGridView I want to visible it false
in datagridview properties KeyPress write below code
if (Keys.Escape.ToString() == "Escape")
gvExpireDate.Visible = false;
private void DatagridView_KeyPress(object sender, KeyPressEventArgs e)
{
//MessageBox.Show(Keys.Escape.ToString());
if (Keys.Escape.ToString() == "Escape")
gvExpireDate.Visible = false;
//if(e.KeyChar == Keys.Escape)
}