I know there are a bunch of questions on this in here and tons of information elsewhere. I cannot, for some reason, get this to work. Here is one of my starting points... Add entire row to DataTable at once using list
This is for a List of Lists. The very first List should be the column headers.
dat is a List<List<string>> that looks like:
{"index", "filename0", "filename1"},
{"A-100", "yes", "no"},
{"A-200", "no", "yes"}
etc...
Code:
/// Dictionary containing as Key => FileName
/// as Value => All drawing numbers found in FileName
Dictionary<string, List<string>> AllDrawingLists = new Dictionary<string, List<string>>();
private void processGrid()
{
List<string> index = new List<string>();
/// Build a comprehensive INDEX from the dictionary - A list
/// of all the drawing numbers found in all the FIlenames
foreach (KeyValuePair<string, List<string>> item in AllDrawingLists)
{
foreach (string dwg in item.Value)
{
if (index.Contains(dwg) == false)
{
index.Add(dwg); }
}
}
List<List<string>> dat = new List<List<string>>();
List<String> headers = new List<string>();
headers.Add("Index");
foreach (KeyValuePair<string, List<string>> item in AllDrawingLists)
{
headers.Add(item.Key);
}
dat.Add(headers);
foreach(string i in index)
{
List<string> row = new List<string>();
row.Add(i);
foreach(KeyValuePair<string, List<string>> item in AllDrawingLists)
{
string cell = "no";
if (item.Value.Contains(i))
{
cell = "yes";
}
row.Add(cell);
}
dat.Add(row);
}
dataGrid.Columns.Clear();
DataTable dt = new DataTable();
int ii = 0;
foreach (List<string> row in dat)
{
if (ii == 0)
{
foreach(string t in row)
{
dt.Columns.Add(t);
}
ii++;
} else
{
dt.Rows.Add(row.ToArray<string>());
}
}
dataGrid.ItemsSource = dt.AsDataView();
}
My expected result would be :
| | | |
| index | file1 | file2 |
-------------------------
| A-100 | yes | no |
-------------------------
| A-200 | no | yes |
-------------------------
| A-300 | yes | yes |
but instead I get :
| | | |
| index | file1 | file2 |
-------------------------
| A-100 | | |
-------------------------
| A-200 | | |
-------------------------
| A-300 | | |
The List of Lists is what I would expect, clearly its working for the definition of columns. I'm not sure why nothing goes into the DataGrid after the first column
Here is the output of dat. It is what I think Im looking for. All rows and column accounted for.
Index C:\py\narrver2\lists.txt C:\py\narrver2\list2.docx
A-1001 yes yes
A-1002 yes yes
A-1003 yes yes
A-1004 no yes
A-1005 no yes
A-1006 no yes
A-1007 no yes
In case you want to keep the dot in the header name, you can set the header column binding path with the name of the header surrounded by square brackets to make the special character (in this case, the dot notation) escaped.
You can do that inside an event handler that subscribes to event AutoGeneratingColumn of your DataGrid.
private void dataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
if (e.PropertyName.Contains('.') && e.Column is DataGridBoundColumn)
{
DataGridBoundColumn dataGridBoundColumn = e.Column as DataGridBoundColumn;
dataGridBoundColumn.Binding = new Binding("[" + e.PropertyName + "]");
dataGridBoundColumn.SortMemberPath = e.PropertyName;
}
}
Related
I have a couple of lists that contain the name of events and the type of event, whether a team or individual event. The name of each event and type of event are separated by semicolons.
Properties.Settings.Default.eventName = "Chess;Football;Paintball;Swimming";
Properties.Settings.Default.eventName = "indiv;team;team;indiv";
List<string> eventName = Properties.Settings.Default.eventName.Split(';').ToList();
List<string> eventType = Properties.Settings.Default.eventType.Split(';').ToList();
I want these in the form of a list so I can easily add in more events later. How do you add these into a ListView in the following configuration, with event names as items and event types as subitems?
_________________________
| Name | Type | <--- column names
|____________|____________|
| Chess | Individual |
|____________|____________|
| Football | Team |
|____________|____________|
| Paintball | Team |
|____________|____________|
| Swimming | Individual |
|____________|____________|
What I have so far. This just adds the items, not sure what to do about adding the subitems as it says "ListView does not contain a definition for SubItems".
foreach (string eventNameItem in eventName)
{
lvEvents.Items.Add(eventNameItem);
}
use following code. I think it works.
Properties.Settings.Default.eventName = "Chess;Football;Paintball;Swimming";
Properties.Settings.Default.eventType = "indiv;team;team;indiv";
List<string> eventName = Properties.Settings.Default.eventName.Split(';').ToList();
List<string> eventType = Properties.Settings.Default.eventType.Split(';').ToList();
for(int i=0;i< eventName.Count; i++)
{
string[] row = { eventName[i], eventType[i] };
ListViewItem item = new ListViewItem(row);
listView1.Items.Add(item);
}
or use
//Width of -2 indicates auto-size.
listView1.Columns.Add("eventName", -2, HorizontalAlignment.Left);
listView1.Columns.Add("eventType", -2, HorizontalAlignment.Left);
for (int i = 0; i < eventName.Count; i++)
{
string[] row = { eventName[i], eventType[i] };
ListViewItem item = new ListViewItem(row);
listView1.Items.Add(item);
}
I have two datatables, I am trying to copy row from one table to another, I have tried this. the thing is that my tables are not exactly the same, both tables have common headers, but to the second table have more columns, therefore I need "smart" copy, i.e to copy the row according to the column header name.
d1:
+--------+--------+--------+
| ID | aaa | bbb |
+--------+--------+--------+
| 23 | value1 | value2 | <----copy this row
d2:
+--------+--------+--------+--------+
| ID | ccc | bbb | aaa |
+--------+--------+--------+--------+
| 23 | | value2 | value1 | <----I need this result
but this code:
string rowID=23;
DataRow[] result = dt1.Select($"ID = {rowID}");
dt2.Rows.Add(result[0].ItemArray);
gives:
d2:
+--------+--------+--------+--------+
| ID | ccc | bbb | aaa |
+--------+--------+--------+--------+
| 23 | value1 | value2 | | <---- :( NOT what I need
I think this is your homework, but here you have some simple and not very smart solution:
private DataTable DTCopySample()
{
int cnt = 0;
DataTable dt1 = new DataTable();
dt1.Columns.Add("ID");
dt1.Columns.Add("aaa");
dt1.Columns.Add("bbb");
DataTable dt2 = new DataTable();
dt2.Columns.Add("ID");
dt2.Columns.Add("ccc");
dt2.Columns.Add("bbb");
dt2.Columns.Add("aaa");
dt1.Rows.Add();
dt1.Rows[0]["ID"] = "23";
dt1.Rows[0]["aaa"] = "val1";
dt1.Rows[0]["bbb"] = "val2";
dt1.Rows.Add();
dt1.Rows[1]["ID"] = "99";
dt1.Rows[1]["aaa"] = "val99";
dt1.Rows[1]["bbb"] = "val98";
string colName = string.Empty;
foreach (DataRow row in dt1.Rows)
{
dt2.Rows.Add();
foreach (DataColumn col in dt1.Columns)
{
dt2.Rows[cnt][col.ColumnName] = row[col.ColumnName].ToString();
}
cnt++;
}
return dt2;
}
There are more smart and better solutions, but this is fast-written (2 mins) and works.
Remeber, that you have not specified columns datatypes or anything else, so I assumed there are strings everywhere for creating simple sample.
Is it possible to replace row value with empty string if duplicate value found?
For example
--------------------
ProductCode | Color
--------------------
00A0B | Red
00A0B | Blue
00A0C | Red
00A0C | Black
00A0C | White
--------------------
to
--------------------
ProductCode | Color
--------------------
00A0B | Red
| Blue
00A0C | Red
| Black
| White
--------------------
I wrote an extension for this.
public static DataTable Dedup(this DataTable dt, string columnName)
{
for (int rowIndex = dt.Rows.Count - 1; rowIndex >= 1; rowIndex--)
{
var row = dt.Rows[rowIndex][columnName];
var previousRow = dt.Rows[rowIndex - 1][columnName];
if (row.ToString() == previousRow.ToString())
{
dt.Rows[rowIndex][columnName] = "";
}
}
return dt;
}
How to use :
DataTable dt = _product.GetProduct();
dt.Dedup("ProductCode");
| FieldName | header | code |
| a1 | test | |
| a1 | test | 1 |
| a1 | test | 2 |
| a1 | test | 3 |
| a1 | test | 4 |
| a1 | test | 5 |
| b2 | prod | 1 |
| b2 | prod | 2 |
| b2 | prod | 3 |
I have the following code to loop thru the table and add the data in a hashtable.
Currently my code only adds one a1 and b2. but what I am planing to do is add them all in my hashtable with a different key.
For example: ("a11",value), ("a12",value); ("a13",value); and the same for b2..ect.
public Hashtable GetData(Table table, string headerType)
{
var data = table.CreateSet<SpecFlowData>();
var hashtable = new Hashtable();
hashtable.Clear();
foreach (var currentRow in data)
{
var key = currentRow.FieldName;
var value = new Hashtable();
GetValue(value, currentRow);
if (hashtable.ContainsKey(key)) //continue;
{
var r = new Random();
key = key + r.Next();
}
var format = (string)value["header"];
if (headerType == format)
{
hashtable.Add(key, value);
}
break;
}
return hashtable;
}
Update: Here is the getvalue method:
private static void GetValue(Hashtable value, SpecFlowData currentRow)
{
value.Clear();
value.Add("code", currentRow.Code);
value.Add("name", currentRow.FieldName);
value.Add("header", currentRow.HeaderType);
}
Why is my data not added properly. thanks for your help.
You are breaking out of the foreach loop at the end of your first iteration, so there can only be one key-value-pair in your Hashtable.
If you remove the break statement, you will get more values. As #JohnGardner mentioned, you should not use a random, because it may produce identical values. Simply use an increasing integer variable.
So after all, this should do:
public Hashtable GetData(Table table, string headerType)
{
var data = table.CreateSet<SpecFlowData>();
var hashtable = new Hashtable(); // no need to clear a newly created Hashtable
int i = 1;
foreach (var currentRow in data)
{
var key = currentRow.FieldName;
var value = GetValue(currentRow);
if (hashtable.ContainsKey(key))
{
key = key + i++;
}
var format = (string)value["header"];
if (headerType == format)
{
hashtable.Add(key, value);
}
}
return hashtable;
}
private static Hashtable GetValue(SpecFlowData currentRow)
{
var value = new Hashtable();
value.Add("code", currentRow.Code);
value.Add("name", currentRow.FieldName);
value.Add("header", currentRow.HeaderType);
}
I have some data in a grid that currently displays like this:
------------------
|Hd1| Value |
------------------
|A | A1 |
------------------
|A | A2 |
------------------
|A | A3 |
------------------
|A | A4 |
------------------
|B | B1 |
------------------
|B | B2 |
------------------
|B | B3 |
------------------
|B | B4 |
------------------
|B | B5 |
------------------
|C | C1 |
------------------
|C | C2 |
------------------
I want to make it look like this:
|Hd | Value |
------------------
|A | A1 |
----------
| | A2 |
----------
| | A3 |
----------
| | A4 |
------------------
|B | B1 |
----------
| | B2 |
----------
| | B3 |
----------
| | B4 |
----------
| | B5 |
------------------
|C | C1 |
----------
| | C2 |
------------------
Is there any way that I can merge these cells?
I have tried in many ways also google but did not find any suitable way.
If it is possible showing this data another way without using datagridview but the result is the way I have showed, that will also solve my problem.
You must first find a duplicate values
Need to two methods:
bool IsTheSameCellValue(int column, int row)
{
DataGridViewCell cell1 = dataGridView1[column, row];
DataGridViewCell cell2 = dataGridView1[column, row - 1];
if (cell1.Value == null || cell2.Value == null)
{
return false;
}
return cell1.Value.ToString() == cell2.Value.ToString();
}
in the event, cellpainting:
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
e.AdvancedBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.None;
if (e.RowIndex < 1 || e.ColumnIndex < 0)
return;
if (IsTheSameCellValue(e.ColumnIndex, e.RowIndex))
{
e.AdvancedBorderStyle.Top = DataGridViewAdvancedCellBorderStyle.None;
}
else
{
e.AdvancedBorderStyle.Top = dataGridView1.AdvancedCellBorderStyle.Top;
}
}
now in cell formatting:
if (e.RowIndex == 0)
return;
if (IsTheSameCellValue(e.ColumnIndex, e.RowIndex))
{
e.Value = "";
e.FormattingApplied = true;
}
and in form_load:
dataGridView1.AutoGenerateColumns = false;
The DataGridView control has no related properties or methods to merge cells, but you can accomplish the same using custom painting. You can use DataGridView.CellPainting event or override the Paint method.
Plus you will need to override the DataGridView.CellClick, CellEnter, CellFormatting and other methods as well in order to give your DataGridView a full featured functionality. For eg on cell click, the entire merged cell (or group of cells that constitute a merged cell) will have to be custom painted.
You can find some sample code here:
http://social.msdn.microsoft.com/forums/en-US/vbinterop/thread/5b659cbd-7d29-4da4-8b38-5d427c3762e2
http://forums.codeguru.com/showthread.php?415930-DataGridView-Merging-Cells
http://www.codeproject.com/Questions/152113/How-can-i-merge-DataGridView-Rows-Cells-with-Equal
There are some good responses on asp.net but in winforms and for this example(merging same data in columns) it is not defined.
You can use color.transparent to hide the same values in datagridview.
even by this code, same rows do not deleted and can be good for matematical calculations.
even you can define which columns to merge by your desire sort.
In this way if end of "A" was "A4" and also start of "B" was "A4" those would not be merge. which is Often more desired. (If you do not want this, better use other responses)
MergeGridviewCells(DGV,new int[] {0,1});//For example if you want to merge first columns data/then 3th column and then second column you can use new int[] {0,2,1}
private void MergeGridviewCells(DataGridView DGV, int[] idx)
{
DataGridViewRow Prev = null;
foreach (DataGridViewRow item in DGV.Rows)
{
if (Prev != null)
{
string firstCellText = string.Empty;
string secondCellText = string.Empty;
foreach (int i in idx)
{
DataGridViewCell firstCell = Prev.Cells[i];
DataGridViewCell secondCell = item.Cells[i];
firstCellText = (firstCell != null && firstCell.Value != null ? firstCell.Value.ToString() : string.Empty);
secondCellText = (secondCell != null && secondCell.Value != null ? secondCell.Value.ToString() : string.Empty);
if (firstCellText == secondCellText)
{
secondCell.Style.ForeColor = Color.Transparent;
}
else
{
Prev = item;
break;
}
}
}
else
{
Prev = item;
}
}
}
Preview:
I spent a long time looking for this as my boss didn't want to buy any off-the-shelf components. This should be submitted into the .NET code: datagridvewtextboxcell-with-span-behaviour It just works and is soo simple to use. Works with VB/C# .NET 4.5 to 6. Spans rows and columns including headers.
DataGridView.Columns.Add(new DataGridViewTextBoxColumnEx());
DataGridViewTextBoxCellEx dataGridViewCell = (DataGridViewTextBoxCellEx)DataGridView[colIdx, rowIdx];
dataGridViewCell.ColSpan = 2;
dataGridViewCell.RowSpan = 6;