I want to change a Datarow value. I want to change DT2[0].ItemArray[3] I tried this code, but it didn't work.
private void Func(DataRow[] DtRowTemp)
{
DataRow[] DT2 ;
DT2 = DtRowTemp; // It work and DTRowTemp value has set to DT2
// above code don't work
DT2[0].ItemArray[3] = 3; // Last Value Was 2 and I want to change this value to 3
}
With the ItemArray Property you get or set the ENTIRE array, not a single value.
Use this to set the fourth item:
DT2[0][3] = 3;
Related
This question is closely related to these two (this and this) but I don't think they give a satisfying answer.
I have a DataGridView (i.e. a table) with several columns (DataGridViewTextBoxColumn) of different data types: string, integers and floats. When I click on their respective header, each should be sorted according to their type: string alphabetically and numerical values numerically. I have, simply put, the following code:
private System.Windows.Forms.DataGridView grid;
private System.Windows.Forms.DataGridViewTextBoxColumn stringColumn;
private System.Windows.Forms.DataGridViewTextBoxColumn doubleColumn;
private System.Windows.Forms.DataGridViewTextBoxColumn intColumn;
stringColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
doubleColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
intColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
grid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
stringColumn,
doubleColumn,
intColumn});
However, since the default representation is string, the numerical values also get sorted alphabetically, for example like this:
1, 11, 2, 3, 42, 5
Apparently, as an easy way of getting around this, according some threads (e.g. here and here), the following should work immediately solve the problem:
doubleColumn.ValueType = typeof(double);
intColumn.ValueType = typeof(int);
However, this solution simply doesn't work in my project: values are still sorted alphabetically. So the question is: why not? In the Debugger, I could see that the value type actually changed to (in the double case) System.Double, so something is happening at least. But how can I make sure that it actually sorts it accordingly, without writing my own sorter?
You can handle the event SortCompare to change how the sorting is done, like this:
private void dataGridView1_SortCompare(object sender, DataGridViewSortCompareEventArgs e) {
//Suppose your interested column has index 1
if (e.Column.Index == 1){
e.SortResult = int.Parse(e.CellValue1.ToString()).CompareTo(int.Parse(e.CellValue2.ToString()));
e.Handled = true;//pass by the default sorting
}
}
NOTE: The above code supposes your cell values are convertible to int.
You said your DataGridView doesn't have DataSource assigned, that means you Add the rows manually, so I think you should use numeric values instead of string for your cells. That would make the sorting work as you want.
If you are using a DataTable then you have to set the DataType on the DataColumn. Setting ValueType on the DataGridViewTextBoxColumn won't help.
You can set it when creating it:
table.Columns.Add("Number", typeof(int));
Changing column from string to int32 might be helpful:
for (int i = 0; i < tableDataGridView.Rows.Count; i++) {
DateTime dt = Convert.ToDateTime(tableDataGridView.Rows[i].Cells[9].Value.ToString());
DateTime dtnow = DateTime.Now;
TimeSpan ts = dtnow.Subtract(dt);
tableDataGridView.Rows[i].Cells[1].Value = Convert.ToInt32( ts.Days.ToString());
}
tableDataGridView.Sort(tableDataGridView.Columns[1], ListSortDirection.Descending);
For me, it works. I hope it will help.
Setting your column ValueType to typeof(int) will work, just remember to make sure you put integers into that column. If the rest of your data contains strings, it can be easy to forget to convert your number from a string to an int.
https://www.youtube.com/watch?v=kKeTRPSLxX8 watch this video, it helps a lot
Copying the code from that video:
Private Sub dgvDailySales_SortCompare(sender As Object, e As DataGridViewSortCompareEventArgs) Handles dgvDailySales.SortCompare
Try
Dim dval1, dval2 As Double
If Not Double.TryParse(e.CellValue1.ToString, dval1) Then Return
If Not Double.TryParse(e.CellValue2.ToString, dval2) Then Return
If dval1 = dval2 Then
e.SortResult = 0
ElseIf dval2 > dval1 Then
e.SortResult = -1
ElseIf dval1 > dval2 Then
e.SortResult = 1
End If
e.Handled = True
Catch ex As Exception
MsgBox(ex.Message, vbCritical)
End Try
End Sub
Basically i'm trying to make it so if the Selected Index from a listbox (for example lets say the selected index is "Server1") contains a number ("1") or a certain word ("Server") it would do a certain thing. For example: If the selected index from the listbox contains a number 1, it would enter 1 into a text box. If the selected index contains a number 2, it would open up a application, just for an example.
what i tried:
if (csrListBox.SelectedIndex = "1");
and:
{
List<string> items = ListBox.Items.Cast<string>().ToList();
foreach (string item in ListBox.Items)
{
Regex regex = new Regex(#"1");
Match match = regex.Match(item);
if (match.Success)
{
*Doing Something*
}
}
ListBox.DataSource = items;
}
Try this:
if (csrListBox.SelectedIndex == 1);
The second "=" sign matters - it states you're doing a boolean check instead of assigning value "1" to the value.
Mistake - You are assigning value.
Use compare operator ==. and SelectedIndex is int not string.
if (csrListBox.SelectedIndex == 1)
{
// Your code goes here.
}
I would suggest you work with ListBox item values instead of selectedIndex as the SelectedIndex can only be an integer value. Something like this linq query will return if the ListBox contains a certain value that is selected. This takes care of multi-selections too.
var myValue = "1";
bool listContainsItem = ListBox.Items.Any(item => item.Value == myValue && item.Selected);
First, if the csv file (eg: Date and Price) contains some empty cells, how can I solve this problem when I try to store the data in a list?
Second, once the two lists are okay I'd like to know how to create a 2-D object such as PriceByDate[100,2](Date[100,0],Price[100,1]) which can store the data from the csv file.
Thanks in advance
Here is my code:(Not work)
var reader = new StreamReader(File.OpenRead(#"filenames"));
List<string> Dates = new List<string>();
List<string> Prices = new List<double>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(';');
listA.Add(values[0]);
listB.Add(values[1]);
}
DateTime[] s = Convert.ToDateTime(ListA.toArray());
double[] o = ListB.toArray();
object[,] PriceByDate = new object[,]{{s},{o}};
This article show how to use ADO.net to read a CSV file. It is pretty simple. Using this method your Date and Price information will be on a row/record object already paired.
Article: Reading CSV files in ADO.NET
If you use the above articles solution, all you need to do is a simple string.IsNullOrEmpty test on the fields of the record. If the function returns true you can skip the row/record.
I'm not sure if this is what you are looking for as in a "2-D" object. If you need to create an object in your code to hold the data after reading it from the record I would use something like this. On a side note you may also want to use a decimal for holding monetary values.
public class PriceAndDate
{
public DateTime Date {get;set;}
public Decimal Price {get;set;}
}
List<PriceAndDate> priceAndDate = new List<PriceAndDate>();
First, a recommendation:
I would recommend using a free 3rd-party CSV library, rather than reinventing the wheel, unless there is some reason you can't.
http://joshclose.github.io/CsvHelper/
Now, the answer:
It sounds like your problem is how to handle empty cells. First, you need to make sure each row is the correct length, in a CSV you'll at least get a delimited between each cell, even if they're empty. (NOTE: I wrote all of this code long-hand without an IDE, it likely won't compile as-is, there may be errors).
var line = reader.ReadLine();
var values = line.Split(';');
if (values.Count != numColumnsExpected)
{
throw new System.Exception("Expected " + numColumnsExpected + " columns, only found " + values.Count + " columns for a row.");
}
Each column should have an expected type, you could have a validation and processing function for each column if you want to be thorough. You could map the column number to a function in a Dictionary.
private void ProcessorDelegate(string value);
Dictionary<int, ProcessorDelegate> m_processorMethods = new Dictionary<int, ProcessorDelegate>
{
{ 0, DateProcessor },
{ 1, PriceProcessor },
}
private void DateProcessor(string value)
{
// Make sure 'value' is a date
DateTime date;
if (!DateTime.TryParse(value, out date))
{
// If this field is required you could throw an exception here, or output a console error.
// This is the point at which you could check if 'value' was null or empty.
return;
}
// 'value' was a date, so add it to the DateTime[] array.
Dates.Add(date);
}
int numColumnsExpected = 6;
var Dates = new List<string>();
var Prices = new List<double>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(';');
if (values.Count != numColumnsExpected)
{
throw new System.Exception("Expected " + numColumnsExpected + " columns, only found " + values.Count + " columns for a row.");
}
// Sanity check, you must have a processor for each column
if (values.Count > m_processorMethods.Count)
{
throw new System.Exception("Expected " + numColumnsExpected + " processor methods, only found " + m_processorMethods.Count);
}
for (int i = 0; i < values.Count; ++i)
{
// Pass the value for a column to the processor that handles
// data for that column.
m_processorMethods[i](values[i]);
}
}
DateTime[] s=Convert.ToDateTime(ListA.toArray());
double[] o=ListB.toArray();
object[,] PriceByDate=new object[,]{{s},{o}} ;
}
Warning:
Storing your data in a series of 2D arrays that are supposed to all map to one another by indices, is very fragile. Even storing it in a 2D object array isn't very useful because you'll need to cast those objects to make any use of them, and you'd need to know what data type each column was in order to cast them anyway.
I would highly recommend creating a class that holds the data for a row. Within that class you can store the date, price, and whatever other data you need. Then you can just have a List or array of those objects, each object representing a row.
public class RowObject
{
public DateTime date;
public string price;
}
List<RowObject> m_rowData;
// A delegate that can take the RowObject
private void ProcessorDelegate(string value, RowObject currentRow);
// Pass in the RowObject to your processors
// The processor updates the RowObject with the processed information.
private void DateProcessor(string value, RowObject currentRow)
{
// Make sure 'value' is a date
DateTime date;
if (!DateTime.TryParse(value, out date))
{
// If this field is required you could throw an exception here, or output a console error.
// This is the point at which you could check if 'value' was null or empty.
return;
}
// 'value' was a date, so set this row's date
currentRow.date = date;
}
Now all of your data for a row is tied together nicely, and if there are empty cells then that row's RowObject is missing that data. You could easily validate a row by adding a validation method to RowObject.
public class RowObject
{
public DateTime date;
public string price;
public bool IsValid()
{
if (date == null)
{
// Maybe output a warning to console here
return false;
}
if (string.IsNullOrEmpty(price))
{
// Maybe output a warning to console here
return false;
}
return true;
}
}
Finally
Let me reiterate that a lot of this in reinventing the wheel, if you use the CSVHelper library I provided a link to then you don't need most of this code.
I'm sure it's trivial but it's been a long day.
I have a DataTable in which one row has an empty cell. I want to find the row that has the empty cell, and simply assign a string to that cells value.
However when I step through the code during debug, the value never gets plugged into the table. What am I doing wrong???
currentTime = DateTime.Now.ToString();
for (int i = 0; i < OwnersTable.Rows.Count; i++)
if (OwnersTable.Rows[i].ItemArray[9].ToString() == "")
OwnersTable.Rows[i].ItemArray[9] = currentTime;
I found to accomplish this I had to create an entirely new row, copy every cell's contents of the existing row over, and then add it back to the table.
What???
Why didn't the simple cell assignment work in the code above????
The getter of the DataRow.ItemArray returns an array containing entire values for the row, modifying it's elements does not make any change to the row values.
Gets or sets all the values for this row through an array.
So you need to assign an array to it instead (use the setter), but I do not recommend this way:
currentTime = DateTime.Now.ToString();
for (int i = 0; i < OwnersTable.Rows.Count; i++) {
object[] items = OwnersTable.Rows[i].ItemArray;
if (items[9].ToString() == string.Empty) {
items[9] = currentTime
OwnersTable.Rows[i].ItemArray = items;
}
}
You can use the SetField method of DataRow instead:
currentTime = DateTime.Now.ToString();
foreach (DataRow row in OwnersTable.Rows) {
string value = row.Field<string>(9);
if (string.IsNullOrEmpty(value)) {
row.SetField<string>(9, currentTime)
}
}
Note that I assumed the field is of string type.
Just came across this post after experiencing the same problem. A more direct way to do this that bypasses the ItemArray problem Mehrzad mentions is to both test and assign values using row[columnIndex] wherever row.ItemArray[columnIndex] is used. The direct indexer of the row does not reference a copy of the row's data. The code becomes:
for (int i = 0; i < OwnersTable.Rows.Count; i++)
if (OwnersTable.Rows[i][9].ToString() == "")
OwnersTable.Rows[i][9] = currentTime;
I was facing through same issue. Apparently you don't need the key word ItemArray while assigning the value.
So instead of
OwnersTable.Rows[i].ItemArray[9] = currentTime;
You could use
OwnersTable.Rows[i][9] = currentTime;
I need to hide an excel column entirely. I used the below code but didn't work:
public void Hide (params string[] columns)
{
foreach(var column in columns)
{
Range range = (Range) oSheet.Columns[column, Type.Missing];
range.EntireColumn.Hidden = true;
}
}
What am I missing?
It turns out that hiding a span of rows and columns you specify couldn't be a heck of a lot easier. Here's how you do it, in two easy steps:
1) Name your worksheet:
private Worksheet _xlSheet;
2) Now, name a range, including the first row and column to hide and then the last row and column to hide, like so:
var hiddenRange = yourWorksheet.Range[_xlSheet.Cells[42, 1], _xlSheet.Cells[999, 13]];
hiddenRange.EntireRow.Hidden = true;
This assumes the first row you want to hide is 42, etc. Obviously you will want to change these hardcoded values.
As an example, here's some actual code, using constants and variables instead of hardcoded vals, which responds to a boolean whose value indicates whether the range should be hidden or not:
private bool _hide;
private int _curTopRow;
private static readonly int ITEMDESC_COL = 1;
private static readonly int TOTALS_COL = 16;
. . .
if (_hide)
{
var hiddenRange = _xlSheet.Range[_xlSheet.Cells[_curTopRow, ITEMDESC_COL], _xlSheet.Cells[_curTopRow+3, TOTALS_COL]];
hiddenRange.EntireRow.Hidden = true;
}
Note that you need to reference the Microsoft.Office.Interop.Excel assembly.
The above snippet is working .. sorry guys! The issue was that, my fellow colleague used to auto-fit all columns, right before saving the file, which will override my settings above. Yes, a function called CloseFile() that does two jobs, formats and then saves .. many responsibilities, eh?