C# winforms gridview sorting after binding data programatically - c#

http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.columnheadermouseclick(v=vs.110).aspx
private void dataGridView1_ColumnHeaderMouseClick(
object sender, DataGridViewCellMouseEventArgs e)
{
...
// Sort the selected column.
dataGridView1.Sort(newColumn, direction);
newColumn.HeaderCell.SortGlyphDirection =
direction == ListSortDirection.Ascending ?
SortOrder.Ascending : SortOrder.Descending;
}
public CustomersListWrapper(DataGridView gridView)
{
_gridView = gridView;
_gridView.CellClick += dgwCustomersList_CellContentClick;
_gridView.ColumnHeaderMouseClick += dgwCustomersList_ColumnHeaderMouseClick;
ClearGrid();
SetCustomersListHeader();
....
}
private void ClearGrid()
{
_gridView.Rows.Clear();
_gridView.Columns.Clear();
}
private void SetCustomersListHeader()
{
_gridView.Columns.Add(DataGridViewColumnNames.Customers.ID, "Id");
_gridView.Columns.Add(DataGridViewColumnNames.Customers.NAME, "Ime");
_gridView.Columns[1].Width = 360;
_gridView.Columns.Add(DataGridViewColumnNames.Customers.SURNAME, "Priimek");
_gridView.Columns[2].Width = 360;
_gridView.Columns.Add(DataGridViewColumnNames.Customers.ACCOUNT_NUMBER, "Št. računa");
_gridView.Columns[3].Width = 120;
_gridView.Columns.Add(DataGridViewColumnNames.Customers.CUSTOMER_NUMBER, "Št. stranke");
_gridView.Columns[4].Width = 120;
int nLastColumn = _gridView.Columns.Count - 1;
for (int i = 0; i < _gridView.Columns.Count; i++)
{
if (nLastColumn == i)
{
_gridView.Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
}
...
}
but the problem is that sorting is not working it is always Ascending. I am binding every time new data to the grid dynamically and looks like this reset the grid sorting,
newColumn.HeaderCell.SortGlyphDirection
has correct value but after binding data data is lost and SortGlyphDirection is None again.
can I somehow remember value of the Column of the gridview after binding?

I suppose you are binding data source from DataTable right? Changing data source resets previous sorting, I think there is more ways how handle this issue:
- store last used sorting order in variable and use DataGridView.DataSourceChanged or DataGridView.DataBindingComplete events to sort your new data source and refresh grid
- store last used sorting order and before attaching new data source sort your source DataTable to sorting order used in DataGridView and change datasource after this

You must inherit IBindingList on your datasource. On Databind.
Loop and bind this way Instead, is easier in my opinion.
dg_Transactions.Columns.Add("1", "Date");
dg_Transactions.Columns.Add("2", "Amount");
dg_Transactions.Columns.Add("3", "Description");
foreach (var row in data.Transactions)
{
var n = dg_Transactions.Rows.Add();
var i = 0;
dg_Transactions.Rows[n].Cells[i++].Value = row.Date;;
dg_Transactions.Rows[n].Cells[i++].Value = row.Amount;
dg_Transactions.Rows[n].Cells[i++].Value = row.Description;
}

Related

DataGridComboBoxColumn Values Different For each Row

I am trying to populate a datagridview combobox column with comboboxcells in winforms where each row has a different collection derived from the nested list within the dictionary, the dictionary works fine when i iterate over it and get its objects and their string values, however every different combination i have exhausted to populate the combobox cells on form load has failed. Is it possible? I have found other posts where they use cellclick events etc. I prefer to populate on form initialization.
//this works
public void create datatable()
{
DataGridViewComboBoxColumn Data_CmBx_Col_ObjectType = new DataGridViewComboBoxColumn();
Data_CmBx_Col_FamilyType.Name = _ADD_OBJECT_TYPE;
Data_CmBx_Col_FamilyType.HeaderText = _ADD_OBJECT_TYPE;
dataGridView.Columns.Insert(6, Data_CmBx_Col_ObjectType);
//pop combobox, the dictionary works
int i = 0;
foreach (KeyValuePair<object, List<objectType>> objectAndType in combined_Dictionary)
{
i++;
if (rowIndex <= combined_Dictionary.Count)
{
CreateCustomComboBoxDataSouce(i, objectAndType.Value);
}
}
//Bind dataGridView to a datatable
dataGridView_Family.DataSource = data;
}//end method
//method is called and fails with index out of range error collection
private void CreateCustomComboBoxDataSouce(int row, List<objectAndType> type) //row index ,and two parameters
{
DataGridViewComboBoxCell comboCell = dataGridView_objectAndType[6, row] as DataGridViewComboBoxCell;
comboCell.DataSource = new BindingSource(type, null);
}//end method
The index is zero based, so it must be strictly less than its count
if (rowIndex < combined_Dictionary.Count) // not <= but without =

Checked List Box Specific item Checked When Double click DataGridView On Windows Form Application

i am using CheckedListBox so that user can multi select items for this i am populating CheckedListBox dynamically from database Here is CheckedListBox Filling Method
public void FillSubjectsCombo()
{
DataTable dt = objSubCls.FillSubjects();
chkLstBxClass_FrmSubjecClassRelation.DataSource = dt;
chkLstBxClass_FrmSubjecClassRelation.DisplayMember = "Subjects";
chkLstBxClass_FrmSubjecClassRelation.ValueMember = "SubId";
chkLstBxClass_FrmSubjecClassRelation.Enabled = true;
for (int i = 0; i < dt.Rows.Count; i++)
{
//Here i am setting Every item Checked
chkLstBxClass_FrmSubjecClassRelation.SetItemCheckState(i, CheckState.Checked);
}
}
On the Same Windows Form I have DataGridView i Want when i double click any row of datagrid then from selected row get value and from that value make respected item checked in CheckedListBox and other item UnChecked
Here is the DataGridView Event
private void dgv_FrmSubjectClassRelation_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
{
string classId = dgv_FrmSubjectClassRelation.CurrentRow.Cells[3].Value.ToString();
string className = dgv_FrmSubjectClassRelation.CurrentRow.Cells[4].Value.ToString();
foreach (int i in chkLstBxClass_FrmSubjecClassRelation.CheckedIndices)
{
//Here I am UnChecking Every Checked Item
chkLstBxClass_FrmSubjecClassRelation.SetItemCheckState(i, CheckState.Unchecked);
}
My Question : How to Checked The Specific Item When Double Clicking DataGridView
Update: I am binding My DataGridView Like This
for (int i = 0; i < dt.Rows.Count; i++)
{
dgv_FrmSmstrClsAssign.Rows.Add();
dgv_FrmSmstrClsAssign.Rows[i].Cells[0].Value = dt.Rows[i].ItemArray[0];//Acadmc Yr
dgv_FrmSmstrClsAssign.Rows[i].Cells[1].Value = dt.Rows[i].ItemArray[1];// Semester Name
dgv_FrmSmstrClsAssign.Rows[i].Cells[2].Value = dt.Rows[i].ItemArray[2]; //College
dgv_FrmSmstrClsAssign.Rows[i].Cells[3].Value = dt.Rows[i].ItemArray[3];//Class
dgv_FrmSmstrClsAssign.Rows[i].Cells[4].Value = dt.Rows[i].ItemArray[4]; //Entry Date
dgv_FrmSmstrClsAssign.Rows[i].Cells[5].Value = dt.Rows[i].ItemArray[5];//IsActive
dgv_FrmSmstrClsAssign.Rows[i].Cells[6].Value = dt.Rows[i].ItemArray[6];//AcadmicYr Id
dgv_FrmSmstrClsAssign.Rows[i].Cells[7].Value = dt.Rows[i].ItemArray[7];//Semster Id
dgv_FrmSmstrClsAssign.Rows[i].Cells[8].Value = dt.Rows[i].ItemArray[8];//Semster Id
}
I was unable to find any method that allows you to easily map the bound value, so you will have to use IndexOf method of Items collection to obtain the index and then manually check-uncheck the items.
To obtain the bound item from DataGridView row you can use DataGridViewRow.DataBoundItem property:
private void CheckSelectedItem()
{
// Get bound item object from datagrid
object item = dgv_FrmSubjectClassRelation.CurrentRow.DataBoundItem;
// Get corresponding index in listView
Int32 itemIndexInCheckedListView = chkLstBxClass_FrmSubjecClassRelation.Items.IndexOf(item);
// Check the item in listView
chkLstBxClass_FrmSubjecClassRelation.SetItemCheckState(itemIndexInCheckedListView,
CheckState.Checked);
}
private void dgv_FrmSubjectClassRelation_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
{
string classId = dgv_FrmSubjectClassRelation.CurrentRow.Cells[3].Value.ToString();
string className = dgv_FrmSubjectClassRelation.CurrentRow.Cells[4].Value.ToString();
foreach (int i in chkLstBxClass_FrmSubjecClassRelation.CheckedIndices)
{
//Here I am UnChecking Every Checked Item
chkLstBxClass_FrmSubjecClassRelation.SetItemCheckState(i, CheckState.Unchecked);
}
// --------------Check the selected item----------------
this.CheckSelectedItem();
}
EDIT:
What you do is not exactly binding (well, it is binding, just not as Windows Forms defines it), so the previous solution won't work for you. If both your DataTable and DataGridView contain primary key or another unique identifier, then it is possible to map CurrentRow to the Item in DataTable:
private void CheckSelectedItem()
{
// Get bound item object from datagrid
object uniqueKey = dgv_FrmSubjectClassRelation.
CurrentRow.
Cells["SOME UNIQUE VALUE COLUMN"].
Value;
// Adapting http://stackoverflow.com/a/9300237/3745022 - there are more simple LINQless
// solutions for this situation, but it is not important for the concept.
Int32 itemIndexInCheckedListView = chkLstBxClass_FrmSubjecClassRelation.
Items.
Select((value, index) => new { value, index }).
Where(pair => pair.value.UniqueValue == uniqueKey ).
Select(pair => pair.index + 1).
FirstOrDefault() - 1;
// Check the item in listView
chkLstBxClass_FrmSubjecClassRelation.SetItemCheckState(itemIndexInCheckedListView,
CheckState.Checked);
}
If you do not have such Unique column you may want to add it(just make it hidden)
OR even better - use full-blown DataBinding - http://msdn.microsoft.com/en-us/library/fbk67b6z(v=vs.90).aspx;

Exclude the Totals Row from sorting a bound DataGridView

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)

DevExpress GridView | DataGridView

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.

Display the particular rows in datagridview selected by the treeview control

I have trying to update datagridview based on the node selection in the treeviewcontrol.For example if i select the node wit the value 13 in the treeviewcontrol then it should display all the datagridview rows with the id=13 and clear all the other rows.I have started the code but i don't know where to go from here.Here is the code.
private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
int outva;
if (int.TryParse((e.Node.Text), out outva))
{
save=Convert.ToInt16(e.Node.Text);
dataGridView1.Columns.Clear();
int row = dataGridView1.Rows.Count;
for (int i = 0; i < row - 1; i++)
{
if (save == Convert.ToInt16(dataGridView1[0, i].Value.ToString()))
{
dataGridView1[0, i].Selected = true;
}
}
}
else { }
}
What is DataSourse in your GridView?
Populating grid with dataGridView1.Rows.Add is not a good choice for your task. The better way is to create a DataTable, fill it with needed data, set it as DataSource of a DataGridView, and use RowFilter for DataTable.DefaultView.
Why are you making int.TryParse and then Convert.ToInt16, if Int16.MaxValue < idand id < int.MaxValue then you will take an exception. Use this if statment if (Int16.TryParse((e.Node.Text), out save))

Categories

Resources