ASP.NET How do I check ever row in the gridview? - c#

I want to add value into a Grid view via dropdownlist with a button.
I want the ddTN.SelectedItem.value in the Grid view to be unique. No duplication
How do I check every row for the ddTN.SelectedItem.value before adding a new ddTN.SelectedItem.value into the Grid view?
This are the codes that I have and it keep comparing the value with the first value in the gridview. Not the others.
I don't want to use a checkbox and such. All the example I found required using checkbox.
protected void Insert(object sender, EventArgs e)
{
int i = 0;
var p = 1;
DataControlFieldCell cell = GridView1.Rows[i].Cells[p] as DataControlFieldCell;
if (cell.Text != ddTN.SelectedItem.Value)
{
dt.Rows.Add(ddTN.SelectedValue, ddDuration.SelectedValue);
ViewState["Customers"] = dt;
this.BindGrid();
label.Text = "";
p++;
}
else
{
label.Text = "Exercise already inserted";
}
}

It looks like you were intending on looping over the items in the grid but you have forgotten the looping mechanism. In your code, it always only checks the first item because i is initialized to 0 and never changes.
Try using a looping mechanism like a for loop or a while loop. Or, if you know the items in the grid from the beginning, perhaps use a hash table for quickly checking if the selected item already exists.
Keep trying, you are almost there!

Related

Alternative to DataGridView DataBindingComplete Event

As stated here the DataBindingComplete event for a DataGridView is fired whenever the contents of the data source change, or a property such as DataSource changes. This results in the method being called multiple times.
I am currently using the DataBindingComplete event to do some visual formatting to my form. For example, I make the text in the first column (column 0) appear as Row Headers and then hide that column (see code below).
private void grdComponents_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
foreach (DataGridViewRow row in grdComponents.Rows)
{
row.HeaderCell.Value = row.Cells[0].Value.ToString();
}
grdComponents.Columns[0].Visible = false;
// do more stuff...
}
It is unnecessary to execute this code more than once, and I am looking to put it into a place where that can happen. Unfortunately it didn't work when I added the snippet to the end of my form's Load method (after I set the DataSource of my DataGridView), nor did it work in the DataSourceChanged event.
Yes, you can use DataSourceChanged event, but be aware, that it occurs only when data source is changed. Additionally, DataBindingComplete offers you information why it has happend - through e.ListChangedType:
Reset = 0,// Much of the list has changed. Any listening controls should refresh all their data from the list.
ItemAdded = 1,// An item added to the list
ItemDeleted = 2,// An item deleted from the list.
ItemMoved = 3,// An item moved within the list.
ItemChanged = 4,// An item changed in the list.
PropertyDescriptorAdded = 5,// A System.ComponentModel.PropertyDescriptor was added, which changed the schema.
PropertyDescriptorDeleted = 6,// A System.ComponentModel.PropertyDescriptor was deleted, which changed the schema.
PropertyDescriptorChanged = 7// A System.ComponentModel.PropertyDescriptor was changed, which changed the schema.
According to this answer:
https://social.msdn.microsoft.com/forums/windows/en-us/50c4f46d-c3b8-4da7-b08f-a751dca12afd/databindingcomplete-event-is-been-called-twice
the whole thing happens because you don't have DataMember property set in your dataGridView. And you can set it only if you want to set particular table from database which is set as your DataSource of dataGridView. Other way - throws an exception.
The simplest way will be just to execute this code once:
Add a flag like Boolean isDataGridFormatted in your form.
And check it like
private void grdComponents_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
if (this.isDataGridFormatted )
return;
foreach (DataGridViewRow row in grdComponents.Rows)
{
row.HeaderCell.Value = row.Cells[0].Value.ToString();
}
grdComponents.Columns[0].Visible = false;
// do more stuff...
this.isDataGridFormatted = false;
}
A bit better will be to prepare your DataGridView during the form construction. As I understand your columns won't change during the course of your program but you don't want to initialize everything manually. You could load some dummy one-item(one-row) data during the initialization:
private void Initialize_DataGridView()
{
// Add dummy data to generate the columns
this.dataGridView_Items.DataContext = new Item[]{ new Item {Id = 5, Value = 6}};
// Make your formatting
foreach (DataGridViewRow row in grdComponents.Rows)
{
row.HeaderCell.Value = row.Cells[0].Value.ToString();
}
grdComponents.Columns[0].Visible = false;
// Reset the dummy data
this.dataGridView_Items.DataContext = null; // Or new Item[]{};
}
...
public MyForm()
{
Initialize();
this.Initialize_DataGridView();
}
I am not sure that exactly such code will work with dataGridView but it is close enough.
Of course an event would have been a nearly ideal solution but there's hardly any that deals with successful autogeneration of columns http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview_events(v=vs.110).aspx except the AutoGenerateColumnChanged but that is not what we need.
While it is possible to use the ColumnAdded - it will probably execute only once foreach of the autogenerated column, the actual implementation could become an overkill and will be even less direct than already mentioned approaches.
If you will have some time and desire you could create your own DataGridView derived class, take Boolean isDataGridFormatted from your form and implement all the initialization(or event hooking) inside the custom DataGridView.

All rows selected on clicked

I'm building a application in C# Winforms and using a Datagridview.
My problem is that when I click on a row, all rows are selected (without intention) and the application crashes (as the values inserted from the grid view selected row into labels are not intact (too many values...).
The correct behavior I'm looking for is the selection of on row only.
I thought the problem might be in the selectionMode (I used the value "RowHeaderSelect"), but I changed it, and the problem persisted, so it isn't it.
Do you have an idea what might be the problem ??
Not relying on much code, really, but here it is:
private void dgvCustomersList_MouseClick(object sender, MouseEventArgs e)
{
{
customerFunctions ChoosenRow = new customerFunctions(); //empty
DataGridViewRow dr = dgvCustomersList.SelectedRows[0];
ChoosenRow.CfirstName = dr.Cells[1].Value.ToString();
ChoosenRow.ClastName = dr.Cells[2].Value.ToString();
ChoosenRow.Caddress = dr.Cells[3].Value.ToString();
ChoosenRow.CcreditNumber = int.Parse(dr.Cells[7].Value.ToString());
ChoosenRow.CpersonalID = int.Parse(dr.Cells[5].Value.ToString());
}
}

Hiding a GridView ID column

Seems to be a common issue.
I'm trying to hide a column of my GridView. I have read that simply setting the column to 'visible = false' will not work as I'm auto-generating my data.
Currently my code stands as so:
protected void Page_Load(object sender, EventArgs e)
{
bind();
if (GridView1.Columns.Count > 0)
GridView1.Columns[0].Visible = false;
else
{
GridView1.HeaderRow.Cells[0].Visible = false;
foreach (GridViewRow gvr in GridView1.Rows)
{
gvr.Cells[1].Visible = false;
}
}
}
The 'if' statement will not trigger as as said I am auto-generating the data. With the above loop, I can hide the header text of the column but want to hide the whole column with the ability to still be able to access the hidden data.
How about just doing this later in the control's life cycle (when the Columns collection has been populated):
protected void GridView1_PreRender(object sender, EventArgs e)
{
if (GridView1.Columns.Count > 0)
GridView1.Columns[0].Visible = false;
else
{
GridView1.HeaderRow.Cells[0].Visible = false;
foreach (GridViewRow gvr in GridView1.Rows)
{
gvr.Cells[1].Visible = false;
}
}
}
Note: you would need to add OnPreRender="GridView1_PreRender" to your GridView markup.
Why not use the GridView.DataKeyNames and GridView.DataKeys properties to store the ID and then retrieve it with the rowIndex later? This also will keep the column from being autogenerated.
DataKeyNames
DataKeys
This is a limitation with auto-generating columns. You give up a lot of control over HOW the columns are displayed for the convenience of not pre-defining them.
Bite the bullet now and define your columns. If this is the ONLY customization you need to make, you may be all right, but 90+% of the time I end up defining columns to customize how they are displayed.
Otherwise you're going to end up with several of these "tweaks" that are prone to breaking under various circumstances (e.g. the order of the columns changes in the data source).
Ah... so the problem is the Columns is not predefined.
Perhaps you could use Linq to select all the columns you want to display:
gvTest.DataSource = (from d in dataSource
select new
{
ColumnA = d.ColumnA...
}
Alternatively, you could hide the columns with JavaScript;

Dropdownlist first value display "choose"

I want that my dropdownlist display first value: "-choose car-"
I succeed at this way:
protected void ddl1_DataBound(object sender, EventArgs e)
{
Convert.ToInt32(ddl1.SelectedValue);
ddl1.Items.Insert(0, new ListItem("-Choose car-", "-Choose car-" ));
}
and that's ok,the "-choose-" is in the first place but the problem now is that if I have values,for example,the dropdownlist show like that:
-Choose car-
Subaro
Fiat
Honda
The first value that display when I'm enter to the site is the Subaro,and to see the -choose car- the user need to open the dropdownlist and then he will see the -choose car- at the first place.How can I do that from the start,from the page load - the -choose car- will display at the ddl from the page load.Where I wrong at the code ?
I tried the itemlist with AppendDataBoundItems = "true" but I got an error, and when I succeed,the problem is the same like I said before.
You were on the right track with using the AppendDataBoundItems property, it should be set to true if you're databinding the list.
Your markup should look like this
<asp:DropDownList runat="server" ID="ddl1" AppendDataBoundItems="true">
<asp:ListItem Text="-Choose car-" />
</asp:DropDownList>
and your code behind probably already looks something like this
ddl1.DataSource = [your datasource goes here];
ddl1.DataBind();
This will place the Choose car text as the first option in the drop-down list and append the rest of the options below it.
Now for the more interesting part of why you were seeing the behavior you were seeing (first item not being selected). If you look at the implementation of SelectedIndex using a tool like JustDecompile (not affiliated with Telerik, just happen to use their tool) you'll see code that looks like this:
public int SelectedIndex
{
get
{
int num = 0;
num++;
while (num < this.Items.Count)
{
if (this.Items[num].Selected)
{
return num;
}
}
return -1;
}
set
{
// stuff you don't care about
this.ClearSelection();
if (value >= 0)
{
this.Items[value].Selected = true;
}
// more stuff you don't care about
}
}
As you can see, the index isn't stored anywhere, it's computed every time based on which item has the Selected property set to true. When you set the SelectedIndex to 0 in the markup and databind your datasource, it will select the 0th item in that list, in your case Subaro. When you insert a new item at the beginning of the list, Subaro is still marked as the selected item, which is why when the page loads, you see that selected and not Choose car. If you want to mark Choose car as the selected item using code, you will have to do it after you data databind your dropdown. Please note, this is just an implementation detail of how DropdownList works. It could change in future version of ASP.NET so do not write code that relies on it working this way.
Make sure that you bind the data source and insert you "-choose care-" item first before selected he first item
make sure when you insert your 1st item "-Choose car-" you make it once not on each PostBack. Check if not IsPostBack to add the 1st item.
EDIT:
Example:
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
ddl1.Items.Insert(0, new ListItem("-Choose car-", "-Choose car-" ));
}
ddl1.SelectedIndex = 0;
}
You should do ddl1.Items.Insert(0, new ListItem("-Choose car-", "-Choose car-")); first, and than ddl1.SelectedIndex = 0
private void FillCar()
{
DataTable dt = GetCar();
ddl1.Items.Clear();
ddl1.DataSource = dt;
ddl1.DataTextField = "carName"; // field name in the database
ddl1.DataValueField = "CarNum"; // field name in the database
ddl1.DataBind();
ListItem li = new ListItem();
li.Text = "--Choose car--";
li.Value = "-1";
ddl1.Items.Insert(0, li);
ddl1.SelectedIndex = 0;
}
I use method like this and call it in the page load in if (!IsPostBack){}.

DataGridView throwing "InvalidOperationException: Operation is not valid..." when adding a row

I want an OpenFileDialog to come up when a user clicks on a cell, then display the result in the cell.
It all works, except that the DataGridView displays an extra row, for adding values to the list it's bound to. The row shows up if dataGridView.AllowUserToAddNewRows == true, which is what I want. What I don't want is for the application to crash when that row is edited programatically; instead, it should do exactly what it would do if the user had edited that row manually (add the new row to the underlying list, push another empty row onto the grid for adding values).
I read about SendKeys.Send(), which should make the DataGridView behave exactly as though the user had typed the value in; however, it does not work either. Here is what I am trying:
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
dataGridView1.CurrentCell = cell;
//simply doing a cell.Value = etc. will cause the program to crash
cell.ReadOnly = false;
dataGridView1.Columns[cell.ColumnIndex].ReadOnly = false;
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
dataGridView1.BeginEdit(true);
SendKeys.Send(openFileDialog1.FileName + "{Enter}");
dataGridView1.EndEdit();
cell.ReadOnly = true;
dataGridView1.Columns[cell.ColumnIndex].ReadOnly = true;
}
//I would expect the FileName would be in the cell now, and a new empty
//row tacked onto the end of the DataGridView, but it's not; the DataGridView
//is not changed at all.
I found a workaround on this page, though I don't know why it works
public MyForm()
{
InitializeComponent();
//Create a BindingSource, set its DataSource to my list,
//set the DataGrid's DataSource to the BindindingSource...
_bindingSource.AddingNew += OnAddingNewToBindingSource;
}
private void OnAddingNewToBindingSource(object sender, AddingNewEventArgs e)
{
if(dataGridView1.Rows.Count == _bindingSource.Count)
{
_bindingSource.RemoveAt(_bindingSource.Count - 1);
}
}
I'm getting very sick of spending so much time dealing with Visual Studio bugs...
I was having the same problem when trying to programattically edit cells with a binding source.
""Operation is not valid due to the current state of the object"
Which operation? What State? So helpful.
My code seem to work fine except when editing the last row in the grid.
Turns out the key is DataGridView.NotifiyCurrentCelldirty(true)
The correct sequence for programatically editing a cell, so it works the same as if the user did it.
(A new empty row appears when changing a cell in the last row) is something like this:
1) Make the cell to edit the current cell (do what ever you need to the current currentcell, first
like calling endEdit if it is in edit mode.)
2) Call DataGridview.BeginEdit(false)
3) Call DataGridView.NotifyCurrentCellDirty(true)
4) Modify the value.
5) Call DataGridView.EndEdit()
And you'll want to do something for the RowValidating and RowValidated events.
One of my routines for updating a cell value looks like this:
This is from a method in my class derived from DataGridView.
You could do the same thing from the containing form, calling
through a DataGridView instance, because the methods are public.
Here the calls are using an impliciit 'this.'
private void EnterTime()
{
if (CurrentRow == null) return;
SaveCurrentCell(); // Calls EndEdit() if CurrentCell.IsInEditMode
DataGridViewCell previous = CurrentCell;
CurrentCell = CurrentRow.Cells[CatchForm.TimeColumn];
BeginEdit(false);
NotifyCurrentCellDirty(true);
CurrentCell.Value = DateTime.Now;
EndEdit();
CurrentCell = previous;
}
I’m not sure why a separate call is needed.
Why doesn’t BeginEdit, or actually modifying the cell value, cause the right
things to happen?
And if you move the NotifyCurrentCellDirty call to after you actually modify the cell,
it doesn’t behave correctly either. All very annoying.
This is old, but I am running VS2010 and just come across this issue. I have a DataGridView bound to a List<T> using a BindingList<T>. I have a drag n' drop event on my DataGridView and it would throw this exception after deleting all rows from the DGV (except for the last blank one which one cannot delete) and then adding new rows to the DGV in the DragDrop handler via the BindingList<T>. This exception was not thrown if I simply added rows manually editing individual cells.
One solution I read said to handle the BindingList<T>.AddNew event, but I found that this event did not fire when calling BindingList<T>.Add() within the DragDrop event handler (I'm not sure why). I solved the issue by adding
if(bindingList.Count == 0)
bindingList.RemoveAt(0)
inside of the DragDrop event handler before adding new objects to bindingList. It seemed that adding an object to the bindingList failed when the only "object" in the bindingList was the one associated to the final blank row. The point of a BindingList<T> is to allow the developer to work with it instead of the DGV directly, but it seems doing so can cause problems in border cases.
The relationship between DGV rows and BindingList<T> rows seems to be a bit of a gray area. I have not spent much time investigating this, but it is not clear to me what is the state of the "object" in the BindingList<T> associated to the final (empty) row of the DGV. However, it does seem like the "object" at the end is only instantiated "correctly" when you interact with the final row directly (not via a DataSource).
Try this:
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
int row = e.RowIndex;
int clmn = e.ColumnIndex;
if(e.RowIndex == dataGridView1.Rows.Count- 1)
dataGridView1.Rows.Add();
dataGridView1.Rows[row].Cells[clmn].Value = openFileDialog1.FileName;
}
EDIT
I didn't notice that you are binding your datagridview :(
Ok, to solve it: use binding source, set its DataSource property to your list, then set the data source of the data grid view to this binding source. Now, the code should look like so:
public partial class frmTestDataGridView : Form
{
BindingSource bindingSource1 = new BindingSource();
List<string> datasource = new List<string>();
public frmTestDataGridView()
{
InitializeComponent();
datasource.Add("item1");
datasource.Add("item2");
datasource.Add("item3");
bindingSource1.DataSource = datasource;
dataGridView1.DataSource = bindingSource1;
}
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
int row = e.RowIndex;
int clmn = e.ColumnIndex;
if (e.RowIndex == dataGridView1.Rows.Count - 1)
{
bindingSource1.Add("");
}
dataGridView1.Rows[row].Cells[clmn].Value = openFileDialog1.FileName;
}
}
}
Remember to use Row.BeginEdit() and Row.EndEdit() if you get this error while editing a value in a row, using DataGrid or GridEX from Janus (in my case). The sample code that Darrel Lee posted here (https://stackoverflow.com/a/9143590/1278771) remind me to use these instructions that I forgot to use and this solved the problem for me.

Categories