Issue using counters in datagridview c# - c#

I have datagridview table. Whenever a user selects row and click 'Update Counter' button, counter value changes to 1 at top corner of window.
Now if a user selects the same row, the counter is updated incrementally which I don't want. I don't want counter to update on same row selection.
My code -
Int32 selectedRowCount = dataGridView1.Rows.GetRowCount(DataGridViewElementStates.Selected);
for (int i = 0; i < selectedRowCount; i++)
{
// need to insert condition so that counter is not updated when same row is selected again.
Counter.Text = Convert.ToString(i + 1);
}
}
I new to C# development, please provide changes.

Without seeing the rest of the code, or really understanding what you are doing (is this list going to change, what is the counter, can you identify the selected row easily?), all I can really suggest is maintaining a list of selected rows and checking that before updating the counter.
For example, in the for each:
if (selectedRows.Contains(rowIdentifier)) continue;//if you want to keep looping, otherwise break;

You may use the Tag property of the DataGridViewRow to identify rows already selected.
foreach (DataGridViewRow r in dataGridView1.SelectedRows)
if (r.Tag!=null) { Counter.Text=int.Parse(Counter.Text)+1 ; r.Tag=True ; }

I would add a condition statement that checks to see if the variable is dirty or has already been updated. This sounds similar to a concurrency issue or checking concurrency, I'm sure if you searched for ways to resolve concurrency issues the logic used would be similar. I agree with #johnc
I would utilize the onclick event then disable the ability to click the same row again.
OR use some other unique property of the row and check against it.
if(row.uniqueProperty == -1)
{
Counter.Text = Convert.ToString(i + 1);
}

Basically, I created a SQL table storing all values from selected row. If selected row value is something already there in the table, the counter doesn't get updated while the counter gets updated when unique row is selected.

Related

How to keep virtualmode datagridview from calling cellvalueneeded while updating data in the background in C#

I have a datagridview with VirtualMode = true that I have also implemented drag/drop to enable the user to reorder rows within the datagridview. My issue is even though I am using SuspendLayout/ResumeLayout, the datagridview is still calling CellValueNeeded in the middle of processing causing my program to crash.
Within the DragDrop event, I have the following code where 'dragRow' is the source row and 'row' is the destination of the drag/drop event.
gridview.SuspendLayout();
try
{
// copy dragged row
DataGridViewRow rowCopy = gridview.Rows[dragRow];
DataValue dataCopy = dataList[dragRow];
// remove dragged row
dataList.RemoveAt(dragRow);
gridview.Rows.RemoveAt(dragRow);
// insert row
dataList.Insert(row, dataCopy);
gridview.Rows.Insert(row, rowCopy);
// move selection to moved row
gridview.CurrentCell = gridview[gridview.CurrentCell.ColumnIndex, row];
}
finally { gridview.ResumeLayout(true); }
Before the drag/drop is initiated, my program detects that the user selected the last row. I have designed the last row to always be empty for reasons I am not going to get into here. Usually if the user selects the last row, then it initiates the drag/drop with only the DragDropEffects.Copy option enabled. If I detect the second to last row is also empty, then I switch the row being dragged to the second to last row to enable the user to move the blank row (as the last row is not movable). The issue is during the DragDrop event between where the row is removed from my data list to where it is inserted in the new location the datagridview calls its CellValueNeeded event causing my program to crash on an out of range exception as it tries to read something from my data list that is not there.
I have also seen this issue in relation to tool tips being displayed. If the user hovers the mouse within the row/cell they just dragged, then the tool tip displayed is for the wrong row as if the CellToolTipTextNeeded event was raised for the wrong cell and not updated after the ResumeLayout.
Is there something I'm missing that I should be doing to let the datagridview know I'm updating its data source while in virtualmode?
For reference, the following CellValueNeeded handler is example of where IndexOutOfRangeException is being thrown due to gridview trying to read from row that no longer exists in dataList after line dataList.RemoveAt(dragRow); in above code.
private void gridview_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
switch (e.ColumnIndex)
{
case 2: // Name
e.Value = dataList[e.RowIndex].Name;
break;
case 3: // Value
e.Value = dataList[e.RowIndex].Value;
break;
}
}
You ask two questions:
First:
Q: "How to keep virtualmode datagridview from calling cellvalueneeded while updating data in the background in C#?"
A: My modeling shows that this line is what explicitly causes CellValueNeeded to be called:
gridview.CurrentCell = gridview[gridview.CurrentCell.ColumnIndex, row];
Wrapping it in SuspendLayout does not change the fact. If you want to avoid CellValueNeeded being called in this method, then remove this line and call it elsewhere.
Second
Q: "Is there something I'm missing that I should be doing to let the datagridview know I'm updating its data source while in virtualmode?"
A: (Short Answer) No.
According to my modeling, your code will work without throwing exceptions if:
It takes into account that CellValueNeeded will be called if the control redraws for any reason at any time whether caused by your application or some other window activity or mouse state change (which included any mouse motion whatsoever over the control).
It maintains these three values in sync at all times, updating immediately if a row is removed or inserted:
The RowCount of the DGV
The Count of the data source
The offset of 1 needed for RowCount if-and-when the AllowUserToAddRows property is true.
You address a bug in your code: That you are trying to manipulate the removal and insertions of rows using int index values. This approach is fraught with danger anyway, but particularly in VirtualMode because there is no binding between those index values and the source list containing the DataValue objects. In a drag drop operation coded in the manner you show, these index values become unreliable (i.e might-or-might-not-work) the moment you insert or remove a list item.
Please try substituting this for the first code block in your post and let me know if it fixes your problem.
try
{
// We need to bank the actual objects here.
DataValue
draggedItem = dataList[dragRowIndex],
dropTarget = dataList[dropRowIndex];
// From here on out, anything index-based is doomed to
// spradically fail because we're changing the list by
// removing one or more items from it. There is no
// binding between the two (you gave that up when you
// set VirtualMode = true)
dataList.RemoveAt(dragRowIndex); // Remove the dragged item(s)
SynchronizeCounts();
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// CRITICAL:
// So at what index is the drop target now?
int correctDropRowIndex = dataList.IndexOf(dropTarget);
// In many cases it's not the same as dropRowIndex!!
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
dataList.Insert(correctDropRowIndex, draggedItem);
SynchronizeCounts();
// move selection to moved row
int safeColumnIndex = gridview.CurrentCell == null ? 0 : gridview.CurrentCell.ColumnIndex;
int newIndexOfDroppedItem = dataList.IndexOf(draggedItem);
#if false
// Setting CurrentCell is guaranteed to make calls
// to CellValueChanged. You will have to do it elsewhere
// if you don't want that to happen in this here code block.
gridview.CurrentCell =
gridview
[
columnIndex: safeColumnIndex,
rowIndex: newIndexOfDroppedItem
];
#endif
}
catch (Exception e)
{
Debug.Assert(false, e.Message);
}
... where ...
private void SynchronizeCounts()
{
gridview.RowCount = dataList.Count;
if (gridview.AllowUserToAddRows)
{
gridview.RowCount++;
}
}
Our GitHub has a DataGridView VirtualMode example with a lot of diagnostic capability. If you'd like to do more code analysis of your DGV you're welcome to Clone or Download our Visual Studio solution.

DateGridview with checkbox column

In my datagridview there is checkbox column. Based on the values in the database want to check the checkbox in the datagridview during runtime and display it to the user.
for (int i = 0; i < supName.Count; i++)
{
foreach (DataGridViewRow row in dataGridView2.Rows)
{
int supId = Convert.ToInt32(row.Cells["supplierId"].Value.ToString());
if (supId == supName[i])
{
row.Cells["selectSupplier"].Value = true;
}
}
}
For one Item there can be multiple suppliers. When adding a new Item to the database, all the existing suppliers are displayed in a datagridview. In this datagridview there is a checkbox column which allows user to select relevant suppliers.
When retrieving information about an item I want to check the checkboxes of the suppliers user has selected for that particular item in the, above mentioned datagridview (datagridview with all the existing suppliers).
Above is the code that I have used to check the checkbox but it the checkbox is not selected.
checkbox column name is "selectSupplier".
Thank You
Your nested loop checks a row if supName list contains the row's supplierId. First issue I found was that if you don't have "AllowUserToAddRows" set to false, your inner loop will try to parse a blank row to integer and throw an exception. This stresses the importance of try-catch blocks and proper error handling.
Your method is also quite inefficient, order[n*m]. Ultimately, most of us will recommend using data binding (https://msdn.microsoft.com/en-us/library/fbk67b6z%28v=vs.90%29.aspx). You can "link" your database table directly to your DataGridView and the supplier ID and check come for free. You will likely need a simple LINQ command (https://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b) to perform the supplier "exists" verification.
If you don't want to or cannot use data binding, you can perform the same result with just your inner loop and use supName.Contains(supId) for order[m].

c# DataGridViewButtonColumn auto click

i have a DataGridView in my program that has a DataGridViewButtonColumn.
When a user clicks on that button, it shows more detail on another panel.
I am having trouble trying to do the following:
Click the "send" button on an ordering screen, that will send the order to the database, and the DataGridView will automatically update, but the user still will need to click on the corresponding column button on the row to view more detail.
What i want to do is that when the user clicks send, it will automatically find the row that corresponds to the orderID, and automatically click that button.
this is what i have:
foreach(DataGridViewRow row in OutOrderListGridView.Rows){
string compareID = row.Cells[0].Value.ToString();
if (compareID == OrderID)
{
row.Cells[10].Selected = true; <-- here i want to performClick() on the columnbutton(cell[10]) on the specific row.
}
}
Thank you.
I am not 100% sure i quite understand what you are after, but if i am correct you want to do the following provided the CellClick event is handling whatever you are trying to ultimately achieve.
Instead of 'performing' the click ( PerformClick() ) as such you can raise the event manually with known values... This should work for you but is slightly untested in your particular application given unknown variables. Code below also had to be slightly reworked to suit.
for (int i = 0; i < OutOrderListGridView.RowCount; i++)
{
if (OutOrderListGridView[0, i].Value.ToString() == OrderID)
{
OutOrderListGridView_CellClick(OutOrderListGridView, new DataGridViewCellEventArgs(10, i));
break;
}
}
The foreach loop had to be changed to a for loop in order to make use of the current index (position of the loop) that a foreach loop does not provide.
The string comparison did not need to be two stepped so was condensed accordingly.
I have also added a break to the loop as i would think that once you found your match, it would be pointless to iterate through the remaining rows.

How to select Row without key

There are many ways to select a row programmatically e.g. by if we know row index, or if we know the Primary-key-like column (column that contain unique value).
Now, when I'm creating my custom control, I wonder is there another way to select row using another way?
Let's say I backup the selected record in each OnSelectionChanged event using variable named BackupRow.
then when I sort the data using columnheader click, in OnSorted I want to select the grid using the BackupRow.
Is something like that possible?
Thank you.
No other method.. For solution to your problem you could.
Backup indices(in an integer list or array), no need to backup complete rows.. if you do need you do it as well. But you need to Backup indices any case.. And select rows form sorted grid on the base of those (backup) indices
if your data count never change and you can exactly know sorting algorithm its possible but not trustful for the true value..
as a pseudo-example : You have rows A-B-C-D and current is B..
if rows sorted ascendant (A to Z or 0 to 100) and then you sorted descendant(Z to A or 100 to 0) then its simply :
suppose your,
total rows = 45;
current row index for ascendant sort = 30;
sort descendant();
your current's new index = (total row - ascendant sorted row index)-1;
//-1 is for index [0] correction
your current's new index = 45-30 -1;
your current's new index = 14;
And an alternative way :
implement your own sort algorithm and create a delegate / pointer - i can't imagine which can be better right now-
then while each sort operation investigate your selected row and select again..
but as i said these are nost trustful ways..and noone never prefer / suggest..
Another Opinion :
Your question is not clear for me..this opinion is for clearer the possible misunderstood..
if you want "when user selects a row or sorts from-on a column value, the complete row cells get highlighted automatically- shown to user as Selected"
then its under datagridview's SelectionMode Property..
dataGridView1.SelectionMode = FullRowSelect;

Datagridview and checkboxlist

I have a question. Let's say i have a checkboxlist with the ingredieints for a cake and i choose not just one but more ingredients (3-4). I want to save this in datagridview. Can datagridview show more than one ingredient or is this not possible ?
You see i'm making an application for bakeries and for ordering cakes you will choose the type of cake and adding(toppings) and the number of cakes you want. Then I would like to save this to datagridview, or show this in dgv and then when I would finish with ordering i would just click on send button in dgv and all the orders i made would be sent to an email. I hope you understand what i want.
Even in its simplest uses a DataGridView is a 2-dimesional container; a 'grid', so you can have as many rows and columns as you want.
If you want to display them all in row you should first try to estimate the maximum number of ingredients. Then you add this number of columns to the target DGV.
Then add a row and fill its column cells with the ingredients.
In case you come across a cake with even more ingredients that would't be a problem either as you can always add more columns.
Empty cells are no problem, just make sure to always check for null values.
By the way, cells are quite powerful and you can have a tag for each single cell, so you can add as many data of any complexity to the cell beyond its mere value!
Here is some simple sample code:
while (dataGridView1.ColumnCount < checkedListBox1.CheckedItems.Count)
dataGridView1.Columns.Add("", "");
int newRow = dataGridView1.Rows.Add();
int item = 0;
foreach ( object o in checkedListBox1.CheckedItems)
{
dataGridView1[item, newRow].Value = o.ToString();
item ++;
}

Categories