I have a DataGridView that is being filled with DataTable → Fill() method.
I want to add another Table in the same DataGridView, in the next column, just when the other one ends. Is it possible?
Now I have something like this
da.Fill(dt);
dataGridView1.DataSource = null;
dataGridView1.Rows.Clear();
dataGridView1.DataSource = dt;
I want to add another DataTable to the same DataGridView in new columns. Thanks
Use the Merge function of DataTable and then shift elements up. See codes below:
private void AddTwoDataTable()
{
DataTable dt1 = new DataTable();
dt1.Columns.Add("ID1", typeof(int));
dt1.Columns.Add("Value1", typeof(string));
dt1.Columns.Add("Value2", typeof(string));
DataTable dt2 = new DataTable();
dt2.Columns.Add("ID2", typeof(int));
dt2.Columns.Add("Value3", typeof(string));
dt1.Rows.Add(new object[] { 1, "a", "ab"});
dt1.Rows.Add(new object[] { 2, "b", "bc" });
dt1.Rows.Add(new object[] { 3, "c", "cd" });
dt1.Rows.Add(new object[] { 4, "d", "de" });
dt2.Rows.Add(new object[] { 101, "x" });
dt2.Rows.Add(new object[] { 102, "y" });
dt2.Rows.Add(new object[] { 103, "y" });
var newtable = MergetwoTables( dt1, dt2 );
dataGridView1.DataSource = newtable;
}
public static DataTable MergetwoTables(DataTable dt1, DataTable dt2)
{
DataTable table = new DataTable("NewTable");
table.Merge(dt1);
table.Merge(dt2);
int maxRows1 = dt1.Rows.Count;
int maxColu1 = dt1.Columns.Count;
int maxRows2 = dt2.Rows.Count;
int maxColu2 = dt2.Columns.Count;
// copy elements from new rows
for (int r = maxRows1; r < maxRows1 + maxRows2; r++)
for (int c = maxColu1; c < maxColu1 + maxColu2; c++)
table.Rows[r - maxRows1][c] = table.Rows[r][c];
//delete new rows
var maxrows = maxRows1 > maxRows2 ? maxRows1 : maxRows2;
for (int r = maxRows1 + maxRows2 - 1; r >= maxrows; r--)
table.Rows[r].Delete();
return table;
}
Output:
Related
My DataTable looks something like this:
id1
id2
id3
1
2
1
1
2
3
1
2
1
1
1
3
3
3
2
3
2
I want my program to remove all "duplicate combinations", so the table would look like this:
id1
id2
id3
1
2
1
1
1
3
3
3
2
3
2
Basically I want it to check if other rows are already contained within other rows, while treating the empty cells as a cell that could have any value at all.
I have tried working with the DataTable.DefaultView.ToTable(true) distinct method and adding column names as parameters but couldn't get it to do exactly what i want.
Also the size of the table is dynamic - There can be an infinite amount of columns.
I finally found a good solution. The distinct doesn't work (using IComarable because it leaves the row with the null instead of the row with values. I had to add a sort so the row with null is before the one with values. Then I delete the first row leaving the second row with values. Also found I had to add dt.AcceptChanges(); because the row did get deleted.
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("id1", typeof(int));
dt.Columns.Add("id2", typeof(int));
dt.Columns.Add("id3", typeof(int));
dt.Columns["id1"].AllowDBNull = true;
dt.Columns["id2"].AllowDBNull = true;
dt.Columns["id3"].AllowDBNull = true;
dt.Rows.Add(new object[] { 1});
dt.Rows.Add(new object[] { 2 });
dt.Rows.Add(new object[] { 1, 1 });
dt.Rows.Add(new object[] { 2, 3 });
dt.Rows.Add(new object[] { 1, 2, 1 });
dt.Rows.Add(new object[] { 1, 1, 3 });
dt.Rows.Add(new object[] { 3, 3 });
dt.Rows.Add(new object[] { 2, 3, 2 });
dt = RemoveDuplicates(dt);
}
static DataTable RemoveDuplicates(DataTable dt)
{
for (int col = dt.Columns.Count - 1; col >= 0; col--)
{
dt = dt.AsEnumerable().OrderBy(x => x.Field<int?>(col)).CopyToDataTable(); ;
}
for (int i = dt.Rows.Count - 2; i >= 0; i--)
{
bool delete = true;
for (int col = 0; col <= dt.Columns.Count - 1; col++ )
{
if (dt.Rows[i][col] == DBNull.Value)
continue;
if((int)dt.Rows[i][col] != (int)dt.Rows[i + 1][col])
{
delete = false;
break;
}
}
if (delete)
{
dt.Rows[i].Delete();
dt.AcceptChanges();
}
}
return dt;
}
I want to select and re-assign the datatable after spesific hour. I use below code;
dtMasterPivot = dtMasterPivot.AsEnumerable().Where(x => x.Field<DateTime>("SAMPLE_TIME").Hour >= 4).CopyToDataTable();
Like above what i want is only select the data after 04:00. However, it doesn't work. It still brings the before 04:00.
This works as advertised:
class Program
{
static void Main(string[] args)
{
var dT = new DataTable();
dT.Columns.Add("Id", typeof(int));
dT.Columns.Add("Sample_Time", typeof(DateTime));
dT.Columns.Add("Misc", typeof(string));
var row = dT.NewRow();
row[0] = 1;
row[1] = new DateTime(2017, 8, 3, 15, 15, 0);
row[2] = "3:15 PM";
dT.Rows.Add(row);
row = dT.NewRow();
row[0] = 2;
row[1] = new DateTime(2017, 8, 3, 3, 59, 0);
row[2] = "3:59 AM";
dT.Rows.Add(row);
row = dT.NewRow();
row[0] = 3;
row[1] = new DateTime(2017, 8, 3, 12, 0, 0);
row[2] = "Noon";
dT.Rows.Add(row);
dT = dT.AsEnumerable().Where(x => x.Field<DateTime>("Sample_Time").Hour >= 4).CopyToDataTable();
for (int i = 0; i < dT.Rows.Count; i++)
{
Console.WriteLine((string)dT.Rows[i][2]);
}
Console.ReadKey();
}
}
Silly question really, but are you sure about your field names? And also whether you are using 12/24 hour clock?
I have a datatable that looks like this:
Data Value 1 Value 2 Value 3
series1 32 -2 46
series2 -62 99
series3 19 23 98
On the chart I will need it to look like this:
32 -2 46 -62 99 19 23 98
series1 series2 series3
and the legend : value 1, value 2, value 3
the codes I have tried:
private void LoadChartCurrencyTotal(DataTable initialDataSource)
{
DataTable pivotedDt = Pivot(initialDataSource);
chart1.DataSource = pivotedDt;
foreach (DataRow dr in pivotedDt.Rows)
{
Series series = new Series(dr["Data"].ToString());
List<string> colNames = (from DataColumn col in pivotedDt.Columns where col.ColumnName != "Data" select col.ColumnName).ToList();
series.XValueMember = "Data";
series.YValueMembers = string.Join(",", colNames);
chart1.Series.Add(series);
}
chart1.DataBind();
FormatChart(chart1);
}
this returns Data points insertion error. Only 1 Y values can be set for this data series. because of the joined column names.
Also tried:
private void LoadChartCurrencyTotal(DataTable initialDataSource)
{
DataTable pivotedDt = Pivot(initialDataSource);
foreach (DataRow pivotDr in pivotedDt.Rows)
{
Series serie = new Series(pivotDr["Data"].ToString());
List<decimal?> colValues = new List<decimal?>();
foreach (DataColumn col in pivotedDt.Columns)
{
if (col.ColumnName != "Data")
{
//colValues.Add(pivotDr[col.ColumnName] != DBNull.Value
// ? decimal.Parse(pivotDr[col.ColumnName].ToString())
// : new decimal?());
decimal? colValue = pivotDr[col.ColumnName] != DBNull.Value
? decimal.Parse(pivotDr[col.ColumnName].ToString())
: new decimal?();
serie.Points.AddXY(pivotDr["Data"], colValue);
}
}
//serie.Points.AddXY(pivotDr["Data"], string.Join(",", colValues));
chart1.Series.Add(serie);
}
FormatChart(chart1);
}
This compiles but the result is completly messed up: The legend sais series1, series2, series3 and the result is:
32 -62 19 -2 23 46 99 98
series1 series2 series3
I get the results per column not per row.
And the last I have tried is:
DataView pivotedDv = pivotedDt.AsDataView();
chart1.DataBindTable(pivotedDv, pivotedDt.Columns[0].ColumnName);
but this only returns:
0.00 0.00 0.00
series1 series2 series3
and the legend: 0.00
Hope someone has a clue how this should be acomplished. but please, no drag and drop and click solutions, but code.
Thanks
I think I have got this working how you like using the following code. Hope this helps:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
DataTable dt = GetTestData();
LoadChartCurrencyTotal(dt);
}
}
private void LoadChartCurrencyTotal(DataTable initialDataSource)
{
for (int i = 1; i < initialDataSource.Columns.Count; i++)
{
Series series = new Series();
foreach (DataRow dr in initialDataSource.Rows)
{
int y = (int)dr[i];
series.Points.AddXY(dr["Data"].ToString(), y);
}
Chart1.Series.Add(series);
}
}
private DataTable GetTestData()
{
DataTable dt = new DataTable();
dt.Columns.Add("Data", Type.GetType("System.String"));
dt.Columns.Add("Value1", Type.GetType("System.Int32"));
dt.Columns.Add("Value2", Type.GetType("System.Int32"));
dt.Columns.Add("Value3", Type.GetType("System.Int32"));
DataRow dr1 = dt.NewRow();
dr1["Data"] = "series1";
dr1["Value1"] = 32;
dr1["Value2"] = -2;
dr1["Value3"] = 46;
dt.Rows.Add(dr1);
DataRow dr2 = dt.NewRow();
dr2["Data"] = "series2";
dr2["Value1"] = -62;
dr2["Value2"] = 0;
dr2["Value3"] = 99;
dt.Rows.Add(dr2);
DataRow dr3 = dt.NewRow();
dr3["Data"] = "series3";
dr3["Value1"] = 19;
dr3["Value2"] = 23;
dr3["Value3"] = 98;
dt.Rows.Add(dr3);
return dt;
}
I have a datagridview with a bound combobox column which contains decimal value. There is a range of valid decimal values so user can choose one of them. However I face a strange problem - when you click on a combobox in the selected value somehow resets to the first one in a list thus losing the one already selected. Look at the pictures
This is the initial view of a window:
This is when I select similar column with a double value in it(note the selection in combobox's list)
This is when I select column with a decimal value. The selection(number 293) is missing
Here is the code I use:
public Form1()
{
InitializeComponent();
dgwResult.AutoGenerateColumns = false;
var list = new List<string>(){"A", "B", "C", "D"};
var list2 = new List<double>();
var list3 = new List<decimal>();
for (int i = 0; i < 300; i++)
{
list2.Add((double)i);
list3.Add((decimal)i);
}
dgw_2.DataSource = list;
dgw_2.DataPropertyName = "two";
dgw_3.DataSource = list2;
dgw_3.DataPropertyName = "three";
dgw_4.DataSource = list3;
dgw_4.DataPropertyName = "four";
DataTable dt = new DataTable();
dt.Columns.Add("one", typeof(string));
dt.Columns.Add("two", typeof(string));
dt.Columns.Add("three", typeof(double));
dt.Columns.Add("four", typeof(decimal));
dt.Rows.Add(new object[] { "akjsgdf", "A", 10.0, 10.0m });
dt.Rows.Add(new object[] { "akjsgdf", "B", 15.0, 15.0m });
dt.Rows.Add(new object[] { "akjsgdf", "C", 20.0, 20.0m });
dt.Rows.Add(new object[] { "akjsgdf", "D", 15.0, 15.0m });
dt.Rows.Add(new object[] { "akjsgdf", "C", 293.0, 293.0m });
dgwResult.DataSource = dt;
}
private void InitializeComponent()
{
this.dgwResult = new System.Windows.Forms.DataGridView();
this.dgw_1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dgw_2 = new System.Windows.Forms.DataGridViewComboBoxColumn();
this.dgw_3 = new System.Windows.Forms.DataGridViewComboBoxColumn();
this.dgw_4 = new System.Windows.Forms.DataGridViewComboBoxColumn();
((System.ComponentModel.ISupportInitialize)(this.dgwResult)).BeginInit();
this.SuspendLayout();
//
// dgwResult
//
this.dgwResult.AllowUserToAddRows = false;
this.dgwResult.AllowUserToDeleteRows = false;
this.dgwResult.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.dgwResult.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dgwResult.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.dgw_1,
this.dgw_2,
this.dgw_3,
this.dgw_4});
this.dgwResult.Location = new System.Drawing.Point(12, 12);
this.dgwResult.Name = "dgwResult";
this.dgwResult.RowHeadersVisible = false;
this.dgwResult.Size = new System.Drawing.Size(268, 150);
this.dgwResult.TabIndex = 0;
this.dgwResult.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgwResult_CellClick);
//
// dgw_1
//
this.dgw_1.DataPropertyName = "one";
this.dgw_1.HeaderText = "One";
this.dgw_1.Name = "dgw_1";
//
// dgw_2
//
this.dgw_2.DataPropertyName = "two";
this.dgw_2.HeaderText = "Two";
this.dgw_2.Name = "dgw_2";
//
// dgw_3
//
this.dgw_3.DataPropertyName = "three";
this.dgw_3.HeaderText = "Double";
this.dgw_3.Name = "dgw_3";
//
// dgw_4
//
this.dgw_4.DataPropertyName = "four";
this.dgw_4.HeaderText = "Decimal";
this.dgw_4.Name = "dgw_4";
this.dgw_4.Resizable = System.Windows.Forms.DataGridViewTriState.True;
this.dgw_4.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.dgwResult);
this.Name = "Form1";
this.Text = "Form1";
((System.ComponentModel.ISupportInitialize)(this.dgwResult)).EndInit();
this.ResumeLayout(false);
}
private System.Windows.Forms.DataGridView dgwResult;
private System.Windows.Forms.DataGridViewTextBoxColumn dgw_1;
private System.Windows.Forms.DataGridViewComboBoxColumn dgw_2;
private System.Windows.Forms.DataGridViewComboBoxColumn dgw_3;
private System.Windows.Forms.DataGridViewComboBoxColumn dgw_4;
Can someone point out why the behavior is so strange for decimal? Maybe I'm missing something simple here?
The reason you're getting that 'strange' operation is because the decimal numbers are not in the list used to populate the column's DataSource.
The problem is fixed by changing the code that populates the DataTable to this:
dt.Rows.Add(new object[] { "akjsgdf", "A", 10.0, 10m });
dt.Rows.Add(new object[] { "akjsgdf", "B", 15.0, 15m });
dt.Rows.Add(new object[] { "akjsgdf", "C", 20.0, 20m });
dt.Rows.Add(new object[] { "akjsgdf", "D", 15.0, 15m });
dt.Rows.Add(new object[] { "akjsgdf", "C", 293.0, 293m });
I reckon the DataGridView calls the .ToString() function on each element in the column's DataSource to find the match. Since 1.0 does not equal 1 (in string terms), no item is selected when you open the drop down menu of the combo box.
public class Employee
{
public int ColumnName {set; get;}
public int RowOrder {set; get;}
public string TabName {set; get;}
public string Names {set; get;}
}
BindingList<Employee> workers = new BindingList<Employee>();
workers.Add(new Employee(1, 0, "Foo", "Bob Jones"));
workers.Add(new Employee(2, 0, "Foo", "Jane Jones"));
workers.Add(new Employee(3, 0, "Foo", "Jim Jones"));
workers.Add(new Employee(1, 1, "Foo", "Joe Jones"));
workers.Add(new Employee(3, 1, "Foo", "John Jones"));
workers.Add(new Employee(1, 0, "Bar", "Worker Bee1"));
workers.Add(new Employee(2, 0, "Bar", "Worker Bee2"));
I have a winform with a tab strip. Tabs can be added dynamically and are named the same as the TabName property. Each tab contains a DataGridView that is also named the same as the TabName property.
So, each tab/gridview should display only the data from the correct objects (the foo tab only shows foo people in the grid, etc). I would like the column name to be the values for the ColumnName in each object (e.g. 1,2,3) and i want the RowOrder to be the row number that the name appears in. So I'm looking for output that would look like the following:
1 2 3
==================================
Bob Jones Jane Jones Jim Jones
Joe Jones John Jones
If the grid coordinates don't include a value, it should be left blank.
I'm relatively new to winforms programming -- searching left me with more questions about the best way to do this. I set up generic data binding to the object list, but that left me with columns named after each property (ColumnName, RowOrder, etc), and I won't mention that this doesn't solve my each tab/datagrid only shows that data.
Is a linq expression a good way to go about putting together each DataGridView? If so, how would I craft an expression that changes the column names to the values in ColumnName? Can I order by RowOrder to get each row correct leaving the blank spaces?
Would I be better off if I just create additional object lists to populate each tab/grid and do the sorting/stacking manually?
In the first place, you will need to reorganize your data structures. A list of names with their row and column number don't help. Think in terms of tab contains table contains rows contains names.
private void AddRow(DataTable dt, params string[] names)
{
// Expand columns as required
while (dt.Columns.Count < names.Length)
{
DataColumn col = dt.Columns.Add();
col.ColumnName = dt.Columns.Count.ToString();
}
// Add new row
DataRow row = dt.NewRow();
for (int i = 0; i < names.Length; i++)
row[i] = names[i];
dt.Rows.Add(row);
}
private void AddToColumn(DataTable dt, int rowIdx, int colIdx, string name)
{
while (dt.Columns.Count < colIdx)
{
DataColumn col = dt.Columns.Add();
col.ColumnName = dt.Columns.Count.ToString();
}
if (dt.Rows.Count > 0)
{
while (dt.Rows[0].ItemArray.Length < colIdx)
dt.Rows[0][dt.Rows[0].ItemArray.Length] = "";
}
DataRow row;
if (rowIdx < dt.Rows.Count)
{
row = dt.Rows[rowIdx];
row[colIdx - 1] = name;
}
else
{
row = dt.NewRow();
row[colIdx - 1] = name;
dt.Rows.Add(row);
}
}
private void buttonStart_Click(object sender, EventArgs e)
{
Dictionary<string, DataTable> workers = new Dictionary<string, DataTable>();
DataTable dt = new DataTable();
AddRow(dt, "Bob Jones", "Jane Jones", "Jim Jones");
AddRow(dt, "Joe Jones", "", "John Jones");
workers.Add("Foo", dt);
AddToColumn(dt, 0, 4, "Testing"); // Use this if you have to add by column
dt = new DataTable();
AddRow(dt, "Worker Bee1", "Worker Bee2");
workers.Add("Bar", dt);
string tabName = "Foo";
dataGridView1.DataSource = workers.FirstOrDefault(x => x.Key == tabName).Value;
}