I have a DataTable, which I construct in the following way:
DataTable data = new DataTable();
data.Columns.Add("year");
data.Columns.Add("month");
data.Columns.Add("id");
data.Columns.Add("displayText");
data.Columns["displayText"].Expression = "Convert(year, 'System.String') + ' / ' + Convert(month , 'System.String')";
DataColumn[] keyColumns = new DataColumn[2];
keyColumns[0] = data.Columns["year"];
keyColumns[1] = data.Columns["month"];
data.PrimaryKey = keyColumns;
data is used as a data source for the monthList drop down list
this.monthList.DataTextField = "displayText";
this.monthList.DataValueField = "id";
this.monthList.DataSource = data;
this.monthList.DataBind();
Then I have an event handler, in which I need to extract both components of the key column.
How can I do that?
I tried to use monthList.SelectedItem.Value, but it contains a string "System.Object[]".
Update 1: Here's the debugger's screenshot.
Update 2: Here's how I add items to data:
var row = data.NewRow();
row["year"] = 2014;
row["month"] = 5;
row["id"] = new object[] { 2014, 5 };
data.Rows.Add(row);
Not sure why you are trying to store an array in your id field considering that the same values are stored in the same row but in different columns, but you could extract the values in that field if you declare your column of type object
data.Columns.Add("id", typeof(object));
and then retrieve them with
object[] ids = (object[])data.Rows[0]["id"];
for(int x = 0; x < ids.Length; x++)
Console.WriteLine(Convert.ToInt32(ids[x]));
You have set:
this.monthList.DataValueField = "id";
Therefore the Value property will be set to the value of the "id" column in your DataTable.
The "id" column in your DataTable is defined (by default) as type "String", but you've set it to an object array:
row["id"] = new object[] { 2014, 5 };
The column will actually contain
(new object[] { 2014, 5 }).ToString();
which is the string you're seeing.
You could try declaring the id column as type object[]:
dt.Columns.Add("id", typeof(object[]));
After which you should be able to cast monthList.SelectedItem.Value to object[] and extract your two values.
Having said that: personally I would probably put the two values composing the id into separate int columns.
It seems that binding a column that contains collection of objects to DataValueField property of DropDownList is not supported by design(it makes value attribute of option tag to be set to collection type name).
You could try to dump collection objects to a single string value and use that string value as a key...
For example:
row["id"] = "2014_05";
EDIT:
Of course you may calculate this key value automatically using the same feature as you used for displayText column:
data.Columns["id"].Expression = "Convert(year, 'System.String') + '_' + Convert(month , 'System.String')";
Related
I have a DataTable, dtHOURS, that consists of two columns of data passed from an SQL database and I am trying to include a third column that consists of calculated values within the array, hours. I've looked at many similar asked questions, but none of the solutions seem to work in my case. I've confirmed that the correct values are being stored in the array, but I can't seem to populate those values within column "Total Hours". All the row data comes out blank for this column.
Any help would be appreciated.
Thank you in advance.
//Create new column and row for the "Total Hours" in data table
DataColumn TotalHoursCol;
DataRow TotalHoursRow;
// Create new DataColumn, set DataType,
// ColumnName and add to DataTable.
TotalHoursCol = new DataColumn();
TotalHoursCol.DataType = System.Type.GetType("System.Double");
TotalHoursCol.ColumnName = "Total Hours";
// Add the Column to the Table.
dtHOURS.Columns.Add(TotalHoursCol);
//This loop calculates the total hours for all jobs and adds them to the data table
for (int i = 1; i < numberOfRecordsHours; i++)
{
//Console.WriteLine(dtHOURS.Rows[i]["ID"]);
//Console.WriteLine(dtHOURS.Rows[i]["ACT_RUN_HRS"]);
if ((Convert.ToString(dtHOURS.Rows[i]["ID"])) == (Convert.ToString(dtHOURS.Rows[i-1]["ID"])))
{
hours[i] = (Convert.ToDouble(dtHOURS.Rows[i]["ACT_RUN_HRS"])) + (hours[i-1]);
//Console.WriteLine(hours[i]);
}
else
{
hours[i] = 0;
//Console.WriteLine("NEW JOB");
}
TotalHoursRow = dtHOURS.NewRow();
TotalHoursRow["Total Hours"] = hours[i];
dtHOURS.Rows.Add(TotalHoursRow);
//Console.WriteLine(dtHOURS.Rows[i]["Total Hours"]);
}
If I am understanding the problem correctly, it looks like you are adding a new row instead of assigning to your new column.
Instead of
TotalHoursRow = dtHOURS.NewRow();
TotalHoursRow["Total Hours"] = hours[i];
dtHOURS.Rows.Add(TotalHoursRow);
Just put
dtHOURS.Rows[i]["Total Hours"] = hours[i];
I am trying to populate a DataGridView with a dynamically generated DataTable and I need one of the column to be a editable TextBox
This is a snippet from my application that populates the data grid view:
public static void PopulatePOLines(MSSQLConnection mssqlConnection, string supplierAccountNumber, DataGridView poLineList)
{
// Init
DataTable dataTable = new DataTable();
DataColumn dataColumn;
DataRow dataRow;
List<POLine> poLines = new List<POLine>();
// Define sage sku column
dataColumn = new DataColumn();
dataColumn.DataType = Type.GetType("System.String");
dataColumn.ColumnName = "sageSku";
dataTable.Columns.Add(dataColumn);
// Define required quantity column
dataColumn = new DataColumn();
dataColumn.DataType = Type.GetType("System.Int32");
dataColumn.ColumnName = "requiredQuantity";
dataTable.Columns.Add(dataColumn);
// Define on pop quantity column
dataColumn = new DataColumn();
dataColumn.DataType = Type.GetType("System.Int32");
dataColumn.ColumnName = "onPOPQuantity";
dataTable.Columns.Add(dataColumn);
// Define order quantity column
dataColumn = new DataColumn();
dataColumn.DataType = Type.GetType("System.Windows.Forms.TextBox");
dataColumn.ColumnName = "orderQuantity";
dataTable.Columns.Add(dataColumn);
// Consolidate & populate po lines from failed allocations by supplier from db
try
{
// Query database
List<Dictionary<string, object>> results = mssqlConnection.ExecuteReader(
"... snipped...",
new Dictionary<string, object>()
{
{ "SupplierAccountNumber", supplierAccountNumber }
});
// Parse result
foreach (Dictionary<string, object> dbRow in results)
{
// Parse row field value
var sageSku = dbRow["SageSku"].ToString();
var quantity = Convert.ToInt32(dbRow["Quantity"]);
// Check if this failed allocation line is known
var poLine = poLines.Find(s => s.SageSku == sageSku);
// If this line isn't known
if (null == poLine)
{
// Workout quantity on pop for this sage sku
var onPOPQty = mssqlConnection.ExecuteScalar<int>(
"... snipped...",
new Dictionary<string, object>()
{
{ "SageSku", sageSku }
});
// Insert a new record
poLines.Add(new POLine()
{
SageSku = sageSku,
RequiredQty = quantity,
OnPOPQty = onPOPQty
});
}
else
{
// Update existing record
poLine.RequiredQty += quantity;
}
// Clean-up
sageSku = null;
}
// Clean-up
results = null;
// Iterate through po lines
foreach (POLine poLine in poLines)
{
dataRow = dataTable.NewRow();
dataRow["sageSku"] = poLine.SageSku;
dataRow["requiredQuantity"] = poLine.RequiredQty;
dataRow["onPOPQuantity"] = poLine.OnPOPQty;
dataRow["orderQuantity"] = (poLine.OnPOPQty > poLine.RequiredQty ? 0 : (poLine.RequiredQty - poLine.OnPOPQty));
dataTable.Rows.Add(dataRow);
}
}
catch (Exception ex)
{
ShowError("Failed to consolidate & populate purchase order lines by selected supplier.", ex);
}
// Set data source
poLineList.DataSource = new DataView(dataTable);
// Clean-up
dataTable = null;
dataColumn = null;
dataRow = null;
poLines = null;
// Update column header text
poLineList.Columns[0].HeaderText = "Sage Sku";
poLineList.Columns[1].HeaderText = "Required Qty";
poLineList.Columns[2].HeaderText = "On POP Qty";
poLineList.Columns[3].HeaderText = "Suggested Order Qty";
}
When this code executes, I get the following error:
Column requires a valid DataType.
What am I doing wrong here?
If I set the orderQuantity column's DataType to System.Int32, the datagrid view renders correctly with my data, but it's not editable:
Any ideas how I can solve this?
I have solved this.
I kept the data type as System.Int32 and then in the visual designer, I've clicked the DataGridView and ticked Enable Editing and then in the code that populates the datasource, I've added the following at the end:
// Disallow editting on certain columns
poLineList.Columns[0].ReadOnly = true;
poLineList.Columns[1].ReadOnly = true;
poLineList.Columns[2].ReadOnly = true;
I have 4 columns on my grid, so the above will make the first three as read only and the other columns editable, which is just the 4th column (which I wanted editable).
Thanks Steve.
As indicated in the MSDN, a DataColumn.DataType only supports a specific set of data types (mostly primitive types, but there's a few exceptions). Unfortunately, you're not going to be able to add a TextBox directly like that.
However, have you looked into the DataGridViewTextBoxColumn Class? You should be able to create your column as an instance of this class instead of a normal column that has been populated with a TextBox.
It may also be a possibility to create the DataGridView Template and have the desired column be one of this class (You can define the columns from the design view), while leaving the other columns empty shells that you populate in your code.
If I set the orderQuantity column's DataType to System.Int32, the datagrid view renders correctly with my data, but it's not editable:
in this case if your column is primary key it will not allow you to edit as it is a PK. make sure the editable column is not a PK.
I am using a datatable created by program. In this datatable i want to insert values in some specified columns.
Initially I am inserting primary key values leaving remaining columns null, when I am querying datatable with recently inserted value in Primary column to update same row, I am facing error Missing operand after ID operator
Can any one tell me the exact issue.
I am trying following code:
dt.Rows.Add(1);
int insertedValue = 1;
DataRow[] dr = dt.Select("ID = '" + insertedValue.toString() + "'");
And the table structure after entring primary value is as follows.
ID Volumn1 Volumn2 volumn3
--------------------------------------
1
You can do this more cleanly with LINQ and make this a strongly typed operation.
Something like:
dt.Rows.Add(1);
int insertedValue = 1;
var result =
dt.AsEnumerable().Where( dr => dr.Field<int>( "ID" ) == insertedValue );
Working example:
DataTable dt = new DataTable();
dt.Columns.Add( "ID", typeof( int ) );
dt.Rows.Add( 1 );
var result = dt.AsEnumerable().Where( dr => dr.Field<int>( "ID" ) == 1 );
You can simply format the selection string as shown below:
DataRow[] dr = dt.Select(string.Format("ID ='{0}' ", insertedValue));
Feel free to let me know if this works for you.. Thanks
You do not need ' ' in your filter.
I think this should work:
DataRow[] dr = dt.Select("ID = " + insertedValue.toString());
By the way, reference System.Data.DataSetExtensions
If you are looking for a specific row and your datatable has a primary key you could use the Find method and target the primary key which would return just the row you want rather than an array:
DataRow foundRow = dt.Rows.Find([INSERT SEARCH PARAMETER HERE]);
if(foundRow != null)
{
TO SET A STRING EQUAL TO A FOUND VALUE:
string str = foundRow["COLUMN NAME / INDEX];
OR IF YOU ARE INSERTING A VALUE YOU CAN USE IT LIKE THIS:
foundRow["COLUMN NAME / INDEX"] = NEW VALUE;
}
select column of row
dt.Rows[0].Field<string>("MyColumnName")
I have a problem with assigning string array into Datarow. Firstly, i have created the object for string array and put 2 values in the array out of 100(whole size). How many values should be placed in the array dependds on a different, which i am not showing here, though.
Then i tried converting into DataRow. But it says. "object reference not set to an instance of an object"
DataRow dr = null;
string[] strconcat = new string[100];
dr["concat"] = strconcat[i];
Thanks in advance
Edit-- Actually i was trying put these string array values into dropdown (ddchooseadeal). Is there any other good way other than this.
locname = ddchoosealoc.SelectedValue.ToString();
string[] strdeals = new string[100];
string[] strconcat = new string[100];
int i;
for(i =0; i< dsdeal.Tables[0].Rows.Count; i++)
{
strdeals[i] = Convert.ToString( dsdeal.Tables[0].Rows[i]["Title"]);
strconcat[i] = strdeals[i]+" -- "+ locname;
}
DataRow dr = null;
ddchooseadeal.Items.Clear();
ListItem li = new ListItem("Choose a Deal");
ddchooseadeal.Items.Add(li);
dr["drconcat"] = strconcat[0];
ListItem item = new ListItem();
item.Text = NullHandler.NullHandlerForString(strconcat[i], string.Empty);
ddchoosealoc.Items.Add(item);
Your DataRow is not a part of any DataTable which is actually right, that's why you can't instantiate a direct DataRow object.
So that's the theory part, to solve your problem
// Declare a DataTable object.
DataTable dt = new DataTable();
// Add some columns to the DataTable
dt.Columns.Add("StringHolder");
// Now suppose , you are having 10 items in your string array
foreach(string str in strArray)
{
DataRow drow = dt.NewRow() ; // Here you will get an actual instance of a DataRow
drow ["StringHolder"] = str; // Assign values
dt.Rows.Add(drow); // Don't forget to add the row to the DataTable.
}
So by following the above steps, you will have a DataTable populated with rows.
Your string array here has 100 elements, all null. So if you assign one of the elements into your data row, you are assigning null. Not a string.
If you are creating an array, the elements will remain uninitialised until you populate it with something. Value types will have their default value (0 for int, false for bool, etc.) while reference types (like string) default to null.
Also, dr is set to null in your example.
You are attempting to add values to a variable that is null DataRow dr = null; which is why you are getting the "object reference not set to an instance of an object" error.
You need to create a new datarow using your DataTable object, then adding the values to that DataRow. Without seeing more of the code it would be hard to offer much more help, but the following article from MSDN will get you started:
How to: Add Rows to a DataTable
check with your debugger if your desired column exists:
var x = dr["concat"];
and check if your desired value exists in the string array with:
var y = strconcat[i];
your datarow above is initialized with null so the error message is absolutely plausible.
you have to design a datatable with the columns you want. after that get a new row from that table and save the values from your string array to the datarow.
see msdn: how to add rows to a datatable => http://msdn.microsoft.com/en-us/library/5ycd1034%28VS.80%29.aspx
It throws NullPointerException because dr is null, you have to create dataRow with DataTable.NewRow Method
Building off Saurabh's answer, you can build a DataTable and it's columns and add each DataRow.
But there is a constructor for the DataRow that takes a params object[] values. So you can automatically add a whole string array to a DataRow, provided that the order of the elements matches the order of the columns specified:
// Create the data table
var dataTable = new DataTable("TableName");
// Add the columns you will need
dataTable.Columns.Add("FirstName");
dataTable.Columns.Add("LastName");
dataTable.Columns.Add("Whatever");
// Get your data in string array format
// Will need to be FirstName, LastName, Whatever
string[] data = LoadStringArrayFromCsvOrSomething();
// Add the DataRow using the string array
dataTable.Rows.Add(data);
This works great in conjunction with the Microsoft.VisualBasic.FileIO.TextFieldParser and the System.Data.SqlClient.SqlBulkCopy. You can throw data into SQL Server like its nobody's business.
I have a DataTable with data loaded from an external source, one of the column is an int.
I'd like to add a column to the DataTable, containing a description of that int.
I have a Dictionary, or in some cases something like:
Pair<int,string> mapping_values[] = {
new Pair<String, int>("start", 3),
new Pair<String, int>("end", 6),
... etc.
};
The DataTable contains a "status_value", an integer which maps to the int in mapping_values
DataTable tbl = ...;
tbl.Columns.add("Status Text",typeof(string));
Now, I'd like to fill the values of this new Status Text column with
the string from mapping_values where tbl["status_value"] matches the integer (3 or 6 in this case) and fill in "start" or "end". Can linq help me here, or something else ?
You'll need to manually populate the column in a loop.
To do that, you should put your mapping in a dictionary.
For example:
var mapping = mapping_values.ToDictionary(p => p.Value1, p => p.Value2);
foreach(DataRow row in table.Rows)
row["Status Text"] = mapping[row.Field<int>("Status Value")];