Hello sir i am not native english, new here in stackoverflow and new in programming but i will try my best to share my problem with you:
I added some comments in my code so i hope you can better see what the problems are
i am trying to make something like a temporary datatable that gets informations (only the rows matter) from 1 specific datatable(there will be more see in the code) and the "temporarydatatable" gives these to a list<> i tried it with linq. ofc i have my own mind and tried to change it a way i understand (LINQ query on a DataTable this wasnt really helpful for me :X ) and i tried some other things as well but i dont want to smash 10 links here :P
so here comes the code:
public MainWindow()
{
InitializeComponent();
datatable1();
}
public void datatable1()
{
/*This Table should get the informations from datatable_1 or
another one (there will be some more tables and the viewtable will
get the informations from the table where the
Type ==(i guess it will be a combobox) selected Type */
DataTable viewtable = new DataTable();
viewtable.Columns.Add("Typ", typeof(string));
viewtable.Columns.Add("Name", typeof(string));
viewtable.Columns.Add("Anzahl", typeof(string));
viewtable.Columns.Add("Zeit", typeof(string));
/*here is the main problem i have*/
viewtable.Rows =from _Row1 in datatable_1 where "Typ" =="Una";
/*it "worked" like this so i get the informations in my list*/
viewtable.Rows.Add("Una", "Testschrank2", "9000", "0:20:30");
//this table is a example table holding the informations
DataTable datatable_1 = new DataTable();
datatable_1.Clear();
datatable_1.Columns.Add("Typ");
datatable_1.Columns.Add("Name");
datatable_1.Columns.Add("Anzahl");
datatable_1.Columns.Add("Zeit");
DataRow _Row1 = datatable_1.NewRow();
datatable_1.Rows.Add("Una", "Testschrank2", "9000", "0:20:30");
// _Row1["Zeit"] = (4, 30, 0);
datatable_1.Rows.Add(_Row1);`
}
well i guess i added too much code but like i said i am really new to this so its a bit difficult for me to point on my problem with little code excuse me sir
and
thanks for your help o/
To get the value from the first DataTable you have to pull all DataRows from it as Lei Yang suggested in his comment.
DataRow temp = datatable_1.Rows.OfType<DataRow>()
.SingleOrDefault(x=>x["Typ"].ToString() == "Una");
1: You cannot assign to the property Rows since it is readonly.
2: You cannot just use simply viewtable.Rows.Add(temp) because this row already belongs to another table. This will result in a System.ArgumentException
So you need to import the row:
if (temp != null)
{
viewtable.ImportRow(temp);
}
EDIT:
If you intend to capture more than one row using the where clause you can use a List<DataRow> to save them temporarily and import each row afterwards in a loop:
List<DataRow> temp = datatable_1.Rows.OfType<DataRow>()
.Where(x => x["Typ"].ToString() == "Una").ToList();
if (temp.Count > 0)
{
foreach (var row in temp)
{
viewtable.ImportRow(row);
}
}
EDIT 2:
Here are some sources for further research:
How to: Locate a Specific Row in a DataTable
In this example you can use also the Select method to get the desired rows. This would look like this:
DataRow [] temp2 = datatable_1.Select("Typ ='Una'", "Name DESC", DataViewRowState.Added);
// or the short version:
DataRow [] temp2 = datatable_1.Select("Typ ='Una'");
the outcome will be the same. This version is from a an answer to a similar question.
I am showing a Totals Row as last row. I want to exclude that row from sorting when the user clicks on the column header. By using sql union I am adding total column to my result. I am using SQL, C# and DataGridView control. I am not able to expose ColumnHeader_Click event. I am only using TableStyle[0].AllowSorting = false. How can I apply that custom sorting on the control?
Thanks
Thanks TaW, your answer helped me. My needs were a little different, I needed the Total to appear at the top and also retain the sort column throughout as my grid is highly interactive with loads of filtering and changes in the data being presented.
My sorting is done via
protected void ReportGridView_Sorting(object sender, GridViewSortEventArgs e)
Here's what I ended up using in my method to populate the GridView:
if (!myDataTable.Columns.Contains("SortLevel"))
{
myDataTable.Columns.Add("SortLevel", typeof(Int16));
foreach (DataRow dr in myDataTable.Rows)
{
dr["SortLevel"] = 0;
}
dt.Rows[0]["SortLevel"] = 1;
}
if ((Session["SortDirection"] != null) && (Session["SortExpression"] != null))
{
myDataTable.DefaultView.Sort = "SortLevel DESC, " + Session["SortExpression"] + " " + Session["SortDirection"];
}
MyGridView.DataSource = myDataTable;
MyGridView.AllowSorting = true;
MyGridView.DataBind();
Side note: I had to use Sessions to hold the custom sorting instead of ViewState as this wasn't working properly with the dynamically created buttons in my gridview
This solution is based on #T.S.'s suggestion but works directly in the DataSet, not in SQL.
I have tested it in VS2013; I don't know if it will work in .Net 1.1 and would have to revive a very old machine to test that..
I don't know what you mean by
I am not able to expose columnheader_click event.
I have split the solution into a function and the ColumnHeaderMouseClick event; if you really can't use that event you will have to find another way; but one way or the other you need to trigger the sort and decide by which column(s) to sort.
The heart of the solution are the setting of the new column values and the expression by which you identify your 'TotalsRow'. I have used my test table's PK column 'ID' and push the 'ID=1 record' to the bottom. After setting the sort column to 0 on all rows the first row to fit the expression is set to maxInt.
You will have to adapt that to an expression that works for your result set.
I am adding and setting the custom sort column dynamically and remove it after the sort; the DGV is suspending its layout until the whole affair is completed.
DataTable yourTable = ..
private void dataGridView1_ColumnHeaderMouseClick(object sender,
DataGridViewCellMouseEventArgs e)
{
string col = dataGridView1.Columns[e.ColumnIndex].Name;
if (col != "") sortDGV (col );
}
private void sortDGV(string col)
{
dataGridView1.SuspendLayout();
yourTable.Columns.Add("sortMe", typeof(Int32));
yourTable.DefaultView.Sort = col;
DataRow[] dr = yourTable.Select("ID='1'");
for (int r = 0; r < yourTable.Rows.Count; r++) yourTable.Rows[r]["sortMe"] = 0;
dr[0]["sortMe"] = int.MaxValue;
yourTable.DefaultView.Sort = "sortMe," + col;
yourTable.Columns.Remove("sortMe");
dataGridView1.ResumeLayout();
}
Although 8 years old, I came across this desire myself but had a different solution: I wanted the last Row to be an "Editing Row" which would always remain at the bottom regardless of sorting.
So, create a DataGridViewRow separate from the regular data grid, add it after filling the DGV. Before sorting, remove the Row from the DGV, then add it again after sorting. I used Clicking on the RowHeaderText to trigger the sort (manual sort using a private class comparer for the sorting, and the DGV_Sort event to indicate sorting has completed. (You have to keep track of when you enter this event as I found it enters before and after sorting - so I used a form global boolean to keep tack of that.
I keep track of any editing on my 'my Removeable Row' separately, but you can always just Clone the row before removing if you don't want to do that.
Note that I have AllowUserToAddRows = true when I start, but after I programmatically fill the grid, I switch it to false, to prevent the addition of more 'editable' rows beneath my 1 row edit at a time desire. I have a button to add the row to the DGV when finished editing, at which time I just create a new myRemovableRow and add that row to the DGV.
Public partial class Form1 : Form
{
public DataGridViewRow myRemoveableRow;
public bool bDoingSort = false;
.
.
private DataGridViewRow CreateNewBlankRow()
{ // create your a new row with whatever default values for DGV
DataGridViewRow newRow = (DataGridViewRow) DGV.Rows[0].Clone();
// or however you want to create your row and fill in default values
return newRow;
}
private void FillDGV()
{
// Do whatever to fill your DataGridView (called DGV here)
myRemoveableRow = CreateNewBlankRow();
DGV.Rows.Add(myRemoveableRow);
}
private void DGV_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
bDoingSort = true;
DGV.Rows.Remove(myRemoveableRow);
SortOrder dir = SortOrder.Ascending; // or whatever logic you use
.
DGV.Sort(new RowComparer(dir, e.ColumnIndex));
}
private void DGV_Sorted(object sender, EventArgs e)
{ // 'Sorted' Event from the DataGridView events
if (bDoingSort)
{
DGV.Rows.Add(myRemoveableRow);
bDoingSort = false; //reset sorting boolean
}
else // we haven't set the sorting up yet
{
return;
}
}
// setup manual sorter (I use separate Classes for int, string, double etc)
private class RowComparer : System.Collections.IComparer
{
private static int sortOrderModifier = 1;
private readonly int Column;
public RowComparer(SortOrder sortorder, int iColumn)
{
if (sortOrder == SortOrder.Descending)
sortOrderModifier = -1;
else
sortOrderModifier = 1;
this.Column = iColumn;
}
public int Compare (Object objA, Object objB)
{
DataGridViewRow row1 = (DataGridViewRow)objA;
DataGridViewRow row2 = (DataGridViewRow)objB;
// do your sort compare (for eg.)
return sortOrderModifier * (row1.Cells[Column].Value).CompareTo(row2.Cells[Column].Value);
}
}
}
I find this works well - I not only sort but filter (out) entries on the fly. (VS 2022)
I want to do the following but using GridView of DevExpress , how Can I do that please ?
List<RCDATA_INDEX> somethings = new List<RCDATA_INDEX>();
foreach (DataGridViewRow row in (IEnumerable)this.dataGridView1.Rows)
{
AZ.RCDATA_INDEX items = new AZ.RCDATA_INDEX
{
datasize = Convert.ToUInt32(row.Cells[5].Value.ToString())
};
item.filenum = Convert.ToUInt32(row.Cells[2].Value.ToString()[7].ToString());
item.hash = row.Cells[1].Value.ToString();
item.realname = row.Cells[3].Value.ToString();
item.offset = Convert.ToUInt32(row.Cells[4].Value.ToString());
item.new_value = row.Cells[6].Value.ToString();
somethings.Add(items);
}
You can traverse through all the data rows within a GridView one-by-one, using the following approach:
// Obtain the number of data rows.
int dataRowCount = gridView.DataRowCount;
// Traverse data rows
for (int i = 0; i < dataRowCount; i++) {
object cellValue = gridView.GetRowCellValue(i, "... specify field name here ...");
// do something with cell Value
}
Please refer the Traversing Rows and Obtaining and Setting Cell Values help-articles to learn more;
I would prefer using BindingSource and bind it into Gridview. After that if you want to making manipulation of your data. You just need to call like this :
List<RCDATA_INDEX> somethings = new List<RCDATA_INDEX>();
var Result = RCDataBS.Cast<RCDATA_INDEX>();
somethings.AddRange(Result);
It would be much easier using this code and you don't need to spend your resource to convert all the data into your model.
I created a class library and I am trying to add data from a List<string[]> to a DataGridView.
The string is in the format as follows:
"Test, 1^Glucose^10/24/2013 10:00;Test, 2^BUN^10/25/2013 11:00;Test, 3^BUN^10/25/2013 11:00"
I am passing the string from another program. I then an making it into a list and then trying to add it to the DataTable but no such luck. I created the datagridview with four columns.
Selected - Checkbox
Patient Name - String
Order Name - String
Order Date - String
Reason - Combo Box
I am getting an error:
the list is greater than the number of columns.
Note: I can make the string anyway I want before passing to this program so if I need to do something with the string before passing it to the program, please let me know. Is there an easier way?
I just want the data to display and I will work on the rest of it.
Any help would be appreciated.
This is my code:
public partial class RenewOrders : Form
{
public static string strMLMPatientData = string.Empty;
public RenewOrders(string all_patient_data)
{
InitializeComponent();
strMLMPatientData = "Test, 1^Glucose^10/24/2013 10:00;Test, 2^BUN^10/25/2013 11:00;Test, 3^BUN^10/25/2013 11:00"
}
private void RenewOrders_Load(object sender, EventArgs e)
{
this.ConvertStringToList(strMLMPatientData);
}
private void ConvertStringToList(string strMLMPatientData)
{
var patient_list = strMLMPatientData.Split(';').Select(x => x.Split('^')).ToList();
DataTable dtTable = ConvertListToDataTable(patient_list);
dataGridView1.DataSource = dtTable;
}
// Convert to DataTable.
static DataTable ConvertListToDataTable(List<string[]> patient_list)
{
// New table.
DataTable dtTable = new DataTable();
dtTable.Columns.Add("Name", typeof(string));
dtTable.Columns.Add("Order Name", typeof(string));
dtTable.Columns.Add("Order Date/Time", typeof(string));
foreach (var row in patient_list)
{
table.Rows.Add(row);
}
return dtTable;
}
}
First you need to spit the string appropriately to get a list of string arrays. Something like this:
var patient_list = new List<string[]>(strMLMPatientData.Split(';').Select(x => x.Split(',')));
or even better:
var patient_list = strMLMPatientData.Split(';').Select(x => x.Split(',')).ToList();
You need Linq for that, but you get the idea.
Then you need to add columns to your data table. You cant add rows to it when there are no columns..
Try something like this in your function
//add columns appropriately
DataTable table = new DataTable();
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Order", typeof(string));
table.Columns.Add("Date", typeof(string));
foreach (var row in patient_list)
table.Rows.Add(row);
return table;
See an example here. As it stands, your comma separated input string doesnt seem to match your data table column structure. You need to work it out. But I hope you got the idea which way to go.
I am using a BindingSource control (reference here) to populate my DataGridView control. There are around 1000+ records populating on it. I am using threading to do so. The DataGridView performs very slow in this case.
I tried to set DoubleBuffered property to true, RowHeadersWidthSizeMode to disabled, AutoSizeColumnsMode to none. But still the same behavior.
How can I improve the performance of the Grid?
If you have a huge amount of rows, like 10,000 and more,
to avoid performance leaks - do the following before data binding:
dataGridView1.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.EnableResizing;
// or even better, use .DisableResizing. Most time consuming enum is DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders
// set it to false if not needed
dataGridView1.RowHeadersVisible = false;
After the data is bound, you may re-enable it.
Generally turning auto-sizing off and double buffering help to speed up DataGridView population. Check whether the DGV double buffering is turned on properly:
if (!System.Windows.Forms.SystemInformation.TerminalServerSession)
{
Type dgvType = dataGridView1.GetType();
PropertyInfo pi = dgvType.GetProperty("DoubleBuffered",
BindingFlags.Instance | BindingFlags.NonPublic);
pi.SetValue(dataGridView1, value, null);
}
Disabling the redrawing with the WinAPI WM_SETREDRAW message also helps:
// *** API Declarations ***
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);
private const int WM_SETREDRAW = 11;
// *** DataGridView population ***
SendMessage(dataGridView1.Handle, WM_SETREDRAW, false, 0);
// Add rows to DGV here
SendMessage(dataGridView1.Handle, WM_SETREDRAW, true, 0);
dataGridView1.Refresh();
If you do not need 2-way data-binding or some features provided by BindingSource (filtering, etc.), you may consider adding rows at one go with the DataGridView.Rows.AddRange() method.
The link to the source article with the sample: http://10tec.com/articles/why-datagridview-slow.aspx
Make sure u don't auto-size columns, it improves performance.
i.e. don't do this:
Datagridview.Columns[I].AutoSizeMode = DataGridViewAutoSizeColumnMode.xxxxx;
I know I'm late to the party, but I recently got fed up with how slow the auto resizing was for the the DataGridView control, and felt someone somewhere might benefit from my solution.
I created this extension method for manually measuring and resizing columns in a DataGridView. Set the AutoSizeColumnsMode to DataGridViewAutoSizeColumnsMode.None and call this method after setting the DataSource.
/// <summary>
/// Provides very fast and basic column sizing for large data sets.
/// </summary>
public static void FastAutoSizeColumns(this DataGridView targetGrid)
{
// Cast out a DataTable from the target grid datasource.
// We need to iterate through all the data in the grid and a DataTable supports enumeration.
var gridTable = (DataTable)targetGrid.DataSource;
// Create a graphics object from the target grid. Used for measuring text size.
using (var gfx = targetGrid.CreateGraphics())
{
// Iterate through the columns.
for (int i = 0; i < gridTable.Columns.Count; i++)
{
// Leverage Linq enumerator to rapidly collect all the rows into a string array, making sure to exclude null values.
string[] colStringCollection = gridTable.AsEnumerable().Where(r => r.Field<object>(i) != null).Select(r => r.Field<object>(i).ToString()).ToArray();
// Sort the string array by string lengths.
colStringCollection = colStringCollection.OrderBy((x) => x.Length).ToArray();
// Get the last and longest string in the array.
string longestColString = colStringCollection.Last();
// Use the graphics object to measure the string size.
var colWidth = gfx.MeasureString(longestColString, targetGrid.Font);
// If the calculated width is larger than the column header width, set the new column width.
if (colWidth.Width > targetGrid.Columns[i].HeaderCell.Size.Width)
{
targetGrid.Columns[i].Width = (int)colWidth.Width;
}
else // Otherwise, set the column width to the header width.
{
targetGrid.Columns[i].Width = targetGrid.Columns[i].HeaderCell.Size.Width;
}
}
}
}
While I certainly would never recommend populating a DGV with 1000+ rows, this method results in a huge performance benefit while producing very similar results to the AutoResizeColumns method.
For 10k Rows: (10K rows * 12 columns.)
AutoResizeColumns = ~3000 ms
FastAutoSizeColumns = ~140 ms
I had to disable auto-sizing in a few places to see the greatest improvement in performance. In my case, I had auto-size modes enabled for AutoSizeRowsMode, AutoSizeColumnsMode, and ColumnHeadersHeightSizeMode. So I had to disable each of these before binding the data to the DataGridView:
dataGridView.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None;
dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
dataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
// ... Bind the data here ...
// Set the DataGridView auto-size modes back to their original settings.
dataGridView.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
dataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
If you don't want to override the virtual mode required methods of the DataGridView there is another alternative if you could consider using Listview:
http://www.codeproject.com/Articles/16009/A-Much-Easier-to-Use-ListView
It has a version (FastObjectListView) that can build a list of 100,000
objects in less than 0.1 seconds.
It has a version (DataListView)
that supports data binding, and another (FastDataListView) that
supports data binding on large (100,000+) data sets.
I had problem with performance when user load 10000 items or sort them.
When I commented line:
this.dataEvents.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells;
Everything became well.
Setting
AutoSizeColumnsMode to None and
AutoSizeRowsMode to DisplayedCells
fixed it for me.
#Bobby L answer is great, but blocks the UI thread. Here's my adaptation which calculates the widths of the column in a BackgroundWorker before applying the calculated values to the UI thread
public partial class Form1 : Form
{
private BackgroundWorker _worker;
public Form1()
{
InitializeComponent();
_worker = new BackgroundWorker();
_worker.DoWork += _worker_DoWork;
_worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
}
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = GetAutoSizeColumnsWidth(dataGridView1);
}
private void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
SetAutoSizeColumnsWidth(dataGridView1, (int[])e.Result);
}
private int[] GetAutoSizeColumnsWidth(DataGridView grid)
{
var src = ((IEnumerable)grid.DataSource)
.Cast<object>()
.Select(x => x.GetType()
.GetProperties()
.Select(p => p.GetValue(x, null)?.ToString() ?? string.Empty)
.ToArray()
);
int[] widths = new int[grid.Columns.Count];
// Iterate through the columns.
for (int i = 0; i < grid.Columns.Count; i++)
{
// Leverage Linq enumerator to rapidly collect all the rows into a string array, making sure to exclude null values.
string[] colStringCollection = src.Where(r => r[i] != null).Select(r => r[i].ToString()).ToArray();
// Sort the string array by string lengths.
colStringCollection = colStringCollection.OrderBy((x) => x.Length).ToArray();
// Get the last and longest string in the array.
string longestColString = colStringCollection.Last();
// Use the graphics object to measure the string size.
var colWidth = TextRenderer.MeasureText(longestColString, grid.Font);
// If the calculated width is larger than the column header width, set the new column width.
if (colWidth.Width > grid.Columns[i].HeaderCell.Size.Width)
{
widths[i] = (int)colWidth.Width;
}
else // Otherwise, set the column width to the header width.
{
widths[i] = grid.Columns[i].HeaderCell.Size.Width;
}
}
return widths;
}
public void SetAutoSizeColumnsWidth(DataGridView grid, int[] widths)
{
for (int i = 0; i < grid.Columns.Count; i++)
{
grid.Columns[i].Width = widths[i];
}
}
}
This solved my problem:
for (int z = 0; z < dataGridView1.Columns.Count; z++)
{
dataGridView1.Columns[z].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
}
... Code where I change the content of dataGridView1 in a loop ...
for (int z = 0; z < dataGridView1.Columns.Count; z++)
{
dataGridView1.Columns[z].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
}
I think you need to consider using your data grid in virtual mode. Basically, you set the extents of the grid up front, then override "OnCellValueNeeded" as required.
You should find (especially for only 1000 or so rows) that your grid population becomes effectively instant.
Good luck,
I had the same problem and i resolved it by setting
AutoSizeRowsMode to DisplayedCellsExceptHeaders
And set the same for columns too
After troubleshooting this a while, I found that the main issue with speed still comes from changing the dataGridView from a non-UI thread. The code below has completely solved my datagridview populating slowly from a dataTable datasource
dataGridView1.Invoke(new MethodInvoker(() =>
dataGridView1.DataSource = table)
);
Dim asrm = DataGridView1.AutoSizeRowsMode
Dim ascm = DataGridView1.AutoSizeColumnsMode
Dim chhs = DataGridView1.ColumnHeadersHeightSizeMode
DataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None
DataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None
DataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing
DataGridView1.SuspendLayout()
bs.SuspendBinding()
DataGridView1.DataSource = Nothing
For Each t As obj In lt_list
bs.Add(t)
Next
DataGridView1.DataSource = bs
DataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders
DataGridView1.AutoSizeColumnsMode = ascm
DataGridView1.ColumnHeadersHeightSizeMode = chhs
bs.ResumeBinding()
DataGridView1.ResumeLayout()
16000 items,
16 seconds with .DataSource <> Nothing,
300 miliseconds with .DataSource = Nothing