So I have found that one can get the bound datatable row for a datagridview row, however, I am seeking to do the opposite. I have the following working code that sorts the datatable rows into an array sorted by a date field. I am then performing some logic to fill a value based on how much quantity is available. When the quantity available is less than the quantity required, I want to color the datagrid row to let the user know of the situation. How can I refer to the correct datagridview row at this point?
Update For Clarity : This is a winform using a datagridview. The code shown is on a "Match All" button. Its purpose is to match a Qty to Transfer value from a Qty Required cell up to an available quantity for the row's Item.
Dictionary<string, float> availableQtys = new Dictionary<string, float>();
DataView view = new DataView(dtStaged);
DataTable distinctItems = view.ToTable(true, "Item");
for (int i = 0; i < distinctItems.Rows.Count; i++)
{
availableQtys[distinctItems.Rows[i].Field<string>(0)] =
Controller.Instance.GetAvailableQty(distinctItems.Rows[i].Field<string>(0), ddl_SourceLocation.SelectedValue.ToString());
}
DataRow[] rowList = dtStaged.Select("", "Req Ship ASC");
foreach (DataRow row in rowList)
{
if (ddl_SourceLocation.SelectedValue.ToString().Trim() == row["Dest Site"].ToString().Trim())
continue;
if (availableQtys[row["Item"].ToString()] < Convert.ToSingle(row["Qty Required"]))
{
row["Qty to Transfer"] = availableQtys[row["Item"].ToString()];
availableQtys[row["Item"].ToString()] = 0.0f;
warnUser = true;
// HERE I want to set the color of the matching data grid row
}
// some other stuff
}
As far as I understand, you use WinForms. And you probably use DataGridView to display your data.
The best solution for you is to create custom cell template.
For instance you can inherit DataGridViewTextBoxCell, and override OnPaint method.
There are 'rowIndex', and 'value' among the parameters of OnPaint method.
This way your template will be able to consider data bound item and paint the cell accordingly.
Another solution is to manually update rows color every time your data changed.
Here is an example.
Related
I have a DataTable with some integer values (assume 0 => 'open', 1 => 'proceeding', 2 => 'free' etc.) and in a dgv I want to allow the user to change that value, but with a combobox and with string values. So I created to test this a simple winform app
public Form1()
{
InitializeComponent();
DataTable dt = new DataTable();
dt.Columns.Add("test");
dt.Rows.Add(1);
dt.Rows.Add(2);
DataTable source = new DataTable();
source.Columns.AddRange(new DataColumn[] { new DataColumn("Value", typeof(int)), new DataColumn("Display", typeof(string)) });
source.Rows.Add(0, "zero");
source.Rows.Add(1, "one");
source.Rows.Add(2, "two");
dataGridView1.DataSource = dt;
var testTextColumn = new DataGridViewComboBoxColumn();
testTextColumn.HeaderText = "Text";
testTextColumn.Name = "testText";
testTextColumn.DataSource = source;
testTextColumn.DisplayMember = "Display";
testTextColumn.ValueMember = "Value";
dataGridView1.Columns.Add(testTextColumn);
}
So far so good, I thought I could simply make the test column invisible and only have the testText column visible (in the final app), but how does one combine the to values, i.e. when I change something in the cb update the value of the datatable? I could do it by changeEvents, but that seems rather impractical. Is there some sort of databinding?
There are three (3) things wrong in your posted code to achieve what you describe.
1-The line of code…
dt.Columns.Add("test");
… is defaulting to a string value. Therefore, the combo box would throw a DataError when you try to bind the “Value” column from the source table. So, you need to specify the int type column in the data. Like…
dt.Columns.Add("test", typeof(int));
2- Before the code set the grids DataSource the code needs to specify that we do NOT want the grid to AutoGenerateColumns. Otherwise, we will end up with two columns. In addition, this grid property is NOT a displayed property in the “Designer.” You will need to set this property BEFORE you add the data source and you need to set this property in your code. Something like…
dataGridView1.AutoGenerateColumns = false;
3- When the code creates the combo box column, it never identifies “which” column in the grids DataSource we want to bind the combo box column to. That is the purpose of the columns DataPropertyName. So, you need to add this line of code in the combo box definition…
testTextColumn.DataPropertyName = "test";
Making these changes, should display only the combo box column.
creates a cell change event, then takes the number of the row you changed and you have access to the columns with the indexes ...
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (dataGridView1.CurrentRow != null)
{
DataGridViewRow dgvRow = dataGridView1.CurrentRow;
// do what u want here like
int teste = Convert.ToInt32(dgvRow.Cells[1].Value);
}
}
So for example,
I have this datagridview which is bounded to a datatable:
A
B
C
1
4
7
2
5
8
3
6
9
And B is the column I want to check the value for, for example I have a saved int value of 5. The code should check if in B column there is a value of 5, if true the all the rows with value 5 visibility is set to true, and all other rows will have their visibility set to false.
Fow now, I have tried this little code (though it checks the all cells but not by specific column):
foreach (DataGridViewRow row in dataGridView1.Rows)
{
for (int i = 0; i < row.Cells.Count; i++)
{
int rowIndex = row.Index;
if (row.Cells[i].Value.ToString().Equals("16"))
{
CurrencyManager currencyManager1 = (CurrencyManager)BindingContext[dataGridView1.DataSource];
currencyManager1.SuspendBinding();
dataGridView1.Rows[rowIndex].Visible = true;
currencyManager1.ResumeBinding();
}
else
{
CurrencyManager currencyManager1 = (CurrencyManager)BindingContext[dataGridView1.DataSource];
currencyManager1.SuspendBinding();
dataGridView1.Rows[rowIndex].Visible = false;
currencyManager1.ResumeBinding();
}
}
}
Now about this CurrencyManager, when I tried to hide the rows without it, it simply error'd me, but now with it all rows visibility set to false. And the problem is I don't understand where to look at and how to fix it, even if the code checks the cells the value "16" in code should show some rows in datagridview but it doesn't.
It's a lot simpler. For a datagridview bound like this:
myDgv.DataSource = dt;
We can dynamically filter the dgv like this:
dt.DefaultView.RowFilter = "[B] = 5"; //only show rows where B is 5
When a DGV's DataSource is set to a datatable it binds to the DataView exported by the DefaultView property so that you can change properties of the view like filter and sort, and it will influence the DGV. You also have the option of creating your own DataView based on the table and binding the DGV to that. For more info on the syntax you can use in a RowFilter, look at DataColumn.Expression
When working with bound data, do try to get into the habit of accessing the data by looking in the container (the datatable) rather than enumerating the DGV and pulling values out of it
You might find it more useful to bind your datatable to a BindingSource and then bind the BindingSource to the DGV- a bindingsource also has a Filter property with the same syntax but also maintains the notion of current row/ as the user changes the current row in the DGV the value of the Current property on the bindingsource changes, making it easier to manipulate the current row in code
You indicate that this is all set up in the forms Designer, which means on your form you have:
a datagridview called _xDataGridView
a binding source called _xBindingSource
a dataset called _xDataSet
the bindingsource's DataSource is set to the dataset and the datamember is set to the table name in the dataset, and the datagridview's DataSource is set to the bindingsource
(mostly I'm just describing the setup to make sure it's right)
All you need to do is:
_xBindingSource.Filter = "[B] = 5";
So basically what I want to do is to select data from the combo boxes, and write something in the textboxes, and when I press the button all these data will fill the proper places of the datagridview.
I only found codes that fill up rows that are next to each other but I would have to write some logic for them to fill specific rows or don't fill them on certain values
private void fill_Click(object sender, EventArgs e)
{
table.Rows.Add(combo1.Text, combo2.Text, combo3.Text, text1.Text);
dataGridView1.DataSource = table;
}
This code only does the trick if these rows and values are supposed to be next to each other. But in my case, they don't.
How can I refer to a specific row that I want to fill?
It appears you are adding a "new" ROW to the table, so I am not sure what you mean by “specific” row? I assume you mean a “specific column” in a new row? If that is the case then this should work…
DataRow dr = table.NewRow();
// by column Name...
dr["Name"] = combo1.Text;
// or by index
int colIndex = 3;
dr[colIndex] = text1.Text;
// then add the row;
table.Rows.Add(dr);
i am adding an unbound checkbox to my gridcontrol to make multi checking to the rows like the Foto.
now i need to retrive these selected rows data in the code.
any help for code helping me get the selected rows data.
It appears you're using the GridControl's built-in checkbox selection system. In that case, you can use the GridView's GetSelectedRows method to retrieve an array of selected row handles.
int[] selectedRowHandles = gridView1.GetSelectedRows();
for (int i = 0; i < selectedRowHandles.Length; i++)
{
object row = gridView1.GetRow(i); //get a row, do something with it
}
I am showing a DataGridView control on my form, populating it with a DataTable object through the DataSource property of the control. I set the control to select the entire row when it is selected, and enabled multiselect. All of this works swimmingly.
A user can select multiple rows and click a button. I want to make a new DataTable object with copies of the selected rows and pass that to a Crystal Report.
Is there an efficient means of doing this? It seems the only way is to parse the new rows for the new DataTable from the selected grid cells, which strikes me as relatively ridiculous.
This is my solution to the question "How to get selected Rows as DataTable":
copy structure of original DataTable
copy each row (as a row may exist only in one datatable object)
DataTable selectedRows = (sourceDataGridView.DataSource as DataTable).Clone();
foreach(DataGridViewRow row in sourceDataGridView.SelectedRows) {
selectedRows.Rows.Add((row.DataBoundItem as DataRowView).Row.ItemArray);
}
I've found that the best way to avoid iterating over the DataTable to find the rows you want is to bind to a DataView instead. This way, you can sort and all that other fun stuff you like to do with a DataGridView, and can still get the data you need without manually parsing the underlying DataTable.
Below is a succinct example that should show you everything you need to get a DataRow without searching:
DataGridView dataGridView1 = new DataGridView();
DataTable tb = new DataTable();
DataView dv = tb.AsDataView();
dataGridView1.DataSource = dv;
// Get the row indexes in whatever your favorite manner is.
List<int> selectedRowIndexes = YourGetSelectedRowIndexesMethod();
foreach(int selectedRowIndex in selectedRowIndexes)
DataRow dr = dv[selectedRowIndex].Row;
One quick method to get the selected indexes (just in case you need it):
List<int> indexes = new List<int>();
foreach (DataGridViewRow row in dataGridView1.SelectedRows)
{
indexes.Add(row.Index);
}