I have a DataGridView bound to a DataTable. The DataTable is populated from a database query. The table contains a column named BestBefore. BestBefore is a date formatted as a string (SQLite doesn't have date types).
I would like to programmatically add a new column to the DataGridView called Status. If BestBefore is less than the current date, Status value should be set to OK, otherwise Status value should be set to NOT OK.
I'm very new to Winforms, so some example code would be greatly appreciated.
UPDATE:
I think DataColumn.Expression is okay for doing simple calculations such multiplying a column's integer value by another value, but what about doing what I need to do? That is, calculate the difference between now and the date (string formatted) in the BestBefore column to determine what value to give the new status column. Example code would be appreciated.
Keep it simple
dataGridView1.Columns.Add("newColumnName", "Column Name in Text");
To add rows
dataGridView1.Rows.Add("Value for column#1"); // [,"column 2",...]
Add new column to DataTable and use column Expression property to set your Status expression.
Here you can find good example: DataColumn.Expression Property
DataTable and DataColumn Expressions in ADO.NET - Calculated Columns
UPDATE
Code sample:
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("colBestBefore", typeof(DateTime)));
dt.Columns.Add(new DataColumn("colStatus", typeof(string)));
dt.Columns["colStatus"].Expression = String.Format("IIF(colBestBefore < #{0}#, 'Ok','Not ok')", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
dt.Rows.Add(DateTime.Now.AddDays(-1));
dt.Rows.Add(DateTime.Now.AddDays(1));
dt.Rows.Add(DateTime.Now.AddDays(2));
dt.Rows.Add(DateTime.Now.AddDays(-2));
demoGridView.DataSource = dt;
UPDATE #2
dt.Columns["colStatus"].Expression = String.Format("IIF(CONVERT(colBestBefore, 'System.DateTime') < #{0}#, 'Ok','Not ok')", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
Here's a sample method that adds two extra columns programmatically to the grid view:
private void AddColumnsProgrammatically()
{
// I created these columns at function scope but if you want to access
// easily from other parts of your class, just move them to class scope.
// E.g. Declare them outside of the function...
var col3 = new DataGridViewTextBoxColumn();
var col4 = new DataGridViewCheckBoxColumn();
col3.HeaderText = "Column3";
col3.Name = "Column3";
col4.HeaderText = "Column4";
col4.Name = "Column4";
dataGridView1.Columns.AddRange(new DataGridViewColumn[] {col3,col4});
}
A great way to figure out how to do this kind of process is to create a form, add a grid view control and add some columns. (This process will actually work for ANY kind of form control. All instantiation and initialization happens in the Designer.) Then examine the form's Designer.cs file to see how the construction takes place. (Visual Studio does everything programmatically but hides it in the Form Designer.)
For this example I created two columns for the view named Column1 and Column2 and then searched Form1.Designer.cs for Column1 to see everywhere it was referenced. The following information is what I gleaned and, copied and modified to create two more columns dynamically:
// Note that this info scattered throughout the designer but can easily collected.
System.Windows.Forms.DataGridViewTextBoxColumn Column1;
System.Windows.Forms.DataGridViewCheckBoxColumn Column2;
this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column2 = new System.Windows.Forms.DataGridViewCheckBoxColumn();
this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.Column1,
this.Column2});
this.Column1.HeaderText = "Column1";
this.Column1.Name = "Column1";
this.Column2.HeaderText = "Column2";
this.Column2.Name = "Column2";
Related
so my problem is that I want to display a table inside another table in WPF.
I use a DataTable in order to display some data and there is one column, in which I need to display another DataTable. I set AutoGenerateColumns="True". For a little testing, this is what I wrote (well, it works as expected):
var curDataTable = new DataTable();
curDataTable.Columns.Add("name" , typeof(string));
curDataTable.Columns.Add("number", typeof(int));
DataRow curRowData = curDataTable.NewRow();
curRowData[0] = "jones";
curRowData[1] = 90;
curDataTable.Rows.Add(curRowData);
Now, let's say I already have a filled DataTable _dataTable. I now want to display this _dataTable in my second column. This is what i would expect to work, but what does not work:
var curDataTable = new DataTable();
curDataTable.Columns.Add("name" , typeof(string));
curDataTable.Columns.Add("table", typeof(DataTable));
DataRow curRowData = curDataTable.NewRow();
curRowData[0] = "jones";
curRowData[1] = _dataTable;
curDataTable.Rows.Add(curRowData);
Has anyone an idea how to fix this?
Using nested DataTables won't work.
What you should do is to replace the outer DataTable with a custom type and bind to a custom collection property of this type in a DataGridTemplateColumn.
I don't think that nested DataTables are possible...
What you can do is create new column in '_datatable' with the name of "name"
_datatable.Columns.Add("name" , typeof(string)).SetOrdinal(0);
Using SetOrdinal(0) you can make "name" column first column. Now you maybe able to add new columns to it.
I hope it helps
I'm trying to create a DataGridView bound to a DataTable where one column is a ComboBox. The code runs but I get the following error after binding (not when data is bound): System.ArgumentException: DataGridViewComboBoxCell value is not valid.
In the DataGridView one of the columns is a DataGridViewComboBoxColumn that uses an enum (named structureType) as it's source:
// ColumnStructure
//
this.ColumnStructure.ValueType = typeof(structureType);
this.ColumnStructure.DataSource = Enum.GetValues(typeof(structureType));
this.ColumnStructure.HeaderText = "Structure";
this.ColumnStructure.Name = "ColumnStructure";
this.ColumnStructure.DataPropertyName = "Structure";
//
When I populate the DataGridView without using a DataTable, it works just fine:
structureType? structure = GetStructure(part);
dgvObjectTypes.Rows.Add(name, type, structure, count);
Now I would want to use a DataTable instead, but can't get it to work. The DataTable is created as follows:
DataTable table = new DataTable();
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Type", typeof(string));
table.Columns.Add("Structure", typeof(DataGridViewComboBoxCell));
table.Columns.Add("Count", typeof(int));
Other columns work great but I can't get the "Structure" column to work. Here's how I've tried to create the combobox:
var cb = new DataGridViewComboBoxCell();
cb.ValueType = typeof(structureType);
cb.DataSource = Enum.GetValues(typeof(structureType));
cb.Value = (structureType)structure;
After that I just create the rows for the table and and set the table as datasource for the DataGridView:
table.Rows.Add(name, type, cb, count);
dgv.DataSource = table;
I've read a lot of posts where it has been stated that using enums in comboboxes causes problems (this for example: DataGridView linked to DataTable with Combobox column based on enum), but that doesn't seem to be the case here. I even tried to use explicitly typed arrays of strings but still get the same error. I think I'm doing something wrong with the DataGridViewComboBoxCell.
What could be the problem?
It seems like the step you are missing is providing the Names and Values for the CBO. The DataTable can store the value, and the DGV can display the related name, but you need to help provide the translation.
private enum structureType
{ None, Circle, Square, Pyramid}
...
dtStruct = new DataTable();
dtStruct.Columns.Add("Name", typeof(string));
dtStruct.Columns.Add("Type", typeof(string));
dtStruct.Columns.Add("Structure", typeof(structureType));
dtStruct.Columns.Add("Count", typeof(int));
// autogen columns == true
dgv2.DataSource = dtStruct;
// create DataSource as list of Name-Value pairs from enum
var cboSrc = Enum.GetNames(typeof(structureType)).
Select( x => new {Name = x,
Value = (int)Enum.Parse(typeof(structureType),x)
}
).ToList();
// replace auto Text col with CBO col
DataGridViewComboBoxColumn cb = new DataGridViewComboBoxColumn();
cb.ValueType = typeof(structureType);
cb.DataSource = cboSrc;
cb.DisplayMember = "Name"; // important
cb.ValueMember = "Value"; // important
cb.HeaderText = "Structure";
cb.DataPropertyName = "Structure"; // where to store the value
dgv2.Columns.Remove(dgv2.Columns[2]); // remove txt col
dgv2.Columns.Add(cb);
cb.DisplayIndex = 2;
// add data
dtStruct.Rows.Add("Ziggy", "Foo", structureType.Circle, 6);
The first part creates the DataTable, note that the Structure column type is structureType (or often int). The DataTable will be storing data, not DataGridViewComboBoxCell elements. In cases where the data comes from a database, that column would be int since structureType would not be a known type.
A DataSource is then created from the enum names and values. This provides the means for the control to show the name, yet store the value in the DataTable.
If the DGV is set to auto generate columns, you will need to replace the default TextBoxColumn with a ComboBoxColumn. This is being done after the DataSource has been set, but before any data is added. When the data comes from a DB (and so there is not usually an empty, typed table) you can use the ColumnAdded event to swap out one column for another.
The important thing when adding the CBO column is to set the ValueMember and DsiplayMember properties to provide the Value <-> Name translation and DataPropertyName so it knows where to store the selected value in the DataTable.
DataTable dt = new DataTable();
var dr = dt1.Date;
String rr = Convert.ToString(dr);
DataColumn dc1=new DataColumn();
dc1.ColumnName = rr; dt.Columns.Add(dc1);
And if i add datarow after this like
dt.Rows.Add("hello","hello1","hello2");
dataGrid1.ItemsSource = dt.DefaultView;
the data is not displayed in the grid .
If i comment the line
dc1.ColumnName = rr;
the values are displayed properly
But i want the colmn name to be the date that is "dt1" here
pleae note that the dt1 are the date values which is dynamic and it will be incremented in each loop.
like
dt1 = dt1.AddDays(1);
Please help
Without seeing your Xaml for the data grid it's difficult to be sure but I imagine that you've specified a field name for the date column in your xaml.
To resolve this, you'll need to set AutoGenerateColumns=True and let the grid automatically find the field name.
I'm beginner in .Net ,so maybe my question will seem naive to some of you.
I have DataGridView table in WinForm project:
It contain three columns(image,combobox and textBox columns).
Any idea how to create and attach rows to this table?
Thank you in advance!
You create a data source, then bind the data source to the grid's DataSource property. You then add a record to your data source.
// create data source
BindingList<Shape> dataSource = new BindingList<Shape>();
// add record to data source
dataSource.Add(new Shape("Some Contour", "Circle", "Some Name"));
// bind data source
yourDataGridView.DataSource = typeof(BindingList<Shape>);
yourDataGridView.DataSource = dataSource;
Set the DataPropertyName of each column to matches the names of the fields in your Shape class.
DataGridViewTextBoxColumn colName = new DataGridViewTextBoxColumn();
colName.DataPropertyName = "Name";
yourDataGridView.Columns.Add(colName );
However, I recommend you use Virtual Mode instead to keep your data separate and decoupled.
If you wish to accept inputs from user, you have to create a form on this page using which the user can provide inputs. Take those values and add them to a DataTable. Following is a sample snippet showing it:
DataTable dt = new DataTable();
dt.Columns.Add("Contour",typeof(string)); //I am assuming that you will store path
//of image in the DataTable
dt.Columns.Add("Shape",typeof(string));
dt.Columns.Add("Name",typeof(string));
Keep adding new rows to the DataTable as you receive inputs from the user:
DataRow row = dt.NewRow();
row["Contour"] = txtContourPath.Text;
row["Shape"] = ddlShape.SelectedValue;
row["Name"] = txtName.Text;
dt.Rows.Add(row);
Assign above DataTable to DataSource property of the GridView.
dgv.DataSource = dt;
You can use method:
dataGridView1.Rows.Insert(...)
dataGridView1.Rows.Add(...)
Jay's answer : use dataGridView1.DataSource = dataSource;
Hope I can help you.
i have in my program a datagriview,i want to order columns by data,i use:
dataGridView1.Columns["Date"].ValueType = typeof(DateTime);
but when i click on Date column,it's didn't sort correctly.
my date format is: 23-12-1997
is incorrect write format of date?
Your cell Format should be:
dataGridView1.Columns["Date"].DefaultCellStyle.Format = "dd-MM-yyyy";
Generally, this format will not affect sorting of this column if ValueType is DateTime. I suspect that you put a String value into this column, instead of a DateTime value.
An example:
DataTable t = new DataTable();
DataColumn c1 = new DataColumn("Name", typeof(String));
DataColumn c2 = new DataColumn("Date", typeof(DateTime));
t.Columns.Add(c1);
t.Columns.Add(c2);
for (int i = 0; i < 10; i++)
{
t.Rows.Add(new object[] {i.ToString(), DateTime.Now.AddHours(i * 2) });
}
dataGridView1.DataSource = t;
dataGridView1.Columns[1].DefaultCellStyle.Format = "HH MM/dd/yyyy";
I did a lot of looking and then used something really easy. I added a hidden column, and put in a string date in the following format:
File.GetLastWriteTime(fl).ToString("yyyyMMddhhmm") and sorted the hidden column.
While I am using a file time there is no reason that any date/time should not work.
I stumbled across this question researching a DataGridView sorting issue on date columns. My case was slightly more complicated than the OP, so I am posting here in hopes it helps someone.
In my case, the DataSource of my DataGridView is set to a BindingSource object. The DataSource of my BindingSource is a DataSet object. I was able to fix the sorting issue without adding any code by changing a property in the DataSet designer. I went to the Visual Studio designer and selected my DataSet. In the properties window, I selected "Edit in DataSet Designer". Then in the DataSet Designer, I selected my Column, and changed its DataType property to System.DateTime (it had previously been set to String). Problem solved.