3-Way sorting in ultragrid - c#

how can i have a ultragrid with 3-way sorting on every columns? I mean :
a. Ascendiing -Indicated by default ascending SortIndicator.
b. Descending- Indicated by default descending SortIndicator.
c. No Sort- UnSort the column.
Note: I have tried BeforeSortChanged Event but i had 2 problems:
I could not get the previous column sort indicator to find out when
should i disable sorting.
I have got an Exception where it is saying that we can't change
SortIndicator in BeforeSortChange Event

It's not that trivial to implement the "unsorted" state. Once the data has been sorted, it is simply reordered within the band. You can see for yourself by applying the below code:
Infragistics.Win.UltraWinGrid.UltraGridColumn[] oldSort;
private void Sort() {
ultraGrid1.BeforeSortChange += new Infragistics.Win.UltraWinGrid.BeforeSortChangeEventHandler(ultraGrid1_BeforeSortChange);
ultraGrid1.AfterSortChange += new Infragistics.Win.UltraWinGrid.BandEventHandler(ultraGrid1_AfterSortChange);
}
void ultraGrid1_BeforeSortChange(object sender, Infragistics.Win.UltraWinGrid.BeforeSortChangeEventArgs e) {
oldSort = new Infragistics.Win.UltraWinGrid.UltraGridColumn[e.Band.SortedColumns.Count];
e.Band.SortedColumns.CopyTo(oldSort, 0);
}
void ultraGrid1_AfterSortChange(object sender, Infragistics.Win.UltraWinGrid.BandEventArgs e) {
for (int i = 0; i < oldSort.Length; i++) {
for (int j = 0; j < e.Band.SortedColumns.Count; j++) {
Infragistics.Win.UltraWinGrid.UltraGridColumn column = e.Band.SortedColumns[j];
if (column.Key == oldSort[i].Key) {
if (column.SortIndicator == Infragistics.Win.UltraWinGrid.SortIndicator.Ascending) {
//column.SortIndicator = Infragistics.Win.UltraWinGrid.SortIndicator.None;
e.Band.SortedColumns.Remove(column.Key);
j--;
break;
}
}
}
}
}
My guess is that you would need to keep a separate array with the row indices in it, and reorder rows within a band once the sorting is removed, according to the array. But that could be memory consuming in my opinion.
Another approach would be to have an additional hidden column, which could be filled with integers incremented in a simple for loop. Once the column's sort is "removed", you simply apply the Ascending sort to that hidden column. There are other aspects that needs to be maintained with such method though.

I solved my problem by using UIElement.GetContext() to find clicked column in Mouse Up event handler of my grid and then i checked and changed the sortIndicator property of that column to what i want.
Edit :
private void Grid_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
UIElement element = Grid.DisplayLayout.UIElement.ElementFromPoint(new Point(e.X, e.Y));
if (element == null)
return;
ColumnHeader clickedHeader = (ColumnHeader)element.GetContext(typeof(ColumnHeader), true);
UltraGridColumn clickedColumn;
if (clickedHeader != null)
clickedColumn = clickedHeader.Column;
if (clickedColumn == null)
return;
Switch ( clickedColumn.SortIndicator)
{
case SortIndicator.Ascending :
clickedColumn.SortIndicator= SortIndicator.Descending;
break;
case SortIndicator.Descending :
clickedColumn.SortIndicator= SortIndicator.Disabled;
break;
case SortIndicator.Disabled :
default :
clickedColumn.SortIndicator= SortIndicator.Ascending;
break;
}
}
}

Related

Make only (2) checkboxes in a datagridview column checkable at a time

I have set up a form in which I have a column of checkboxes in the right column. The left columns contain data that is read-only if the corresponding checkbox column is unchecked and editable when checked. Since the data columns are dependent on each other for calculations I need (2) checked rows of the data columns editable at a time. I would like the checkable boxes to be limited to any (2) with the remaining boxes to not be checkable until at least one of the checked boxes is unchecked. I figured the best way would be to keep a running total of the number of checked boxes and somehow limit this, but I'm stuck at how to accomplish this. The code below just outputs a persistant GlobalVar.NumChecked = -1 value. Any help or direction would be appreciated.
private void dgvParameters_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
DataGridView dgPG = dgvParameters;
if (e.RowIndex < 0) return;
GlobalVar.NumChecked = 0;
foreach (DataGridViewRow row in dgvParameters.Rows)
{
if (e.RowIndex < 0) return;
DataGridViewCheckBoxCell cell = row.Cells[5] as DataGridViewCheckBoxCell;
if (cell.Value == cell.TrueValue)
{
GlobalVar.NumChecked += 1;
break;
}
if (cell.Value != cell.TrueValue)
{
GlobalVar.NumChecked -= 1;
break;
}
}
label2.Text = Convert.ToString(GlobalVar.NumChecked); //Debug output
}
Don't wait until the cell's Value changes by handling the CellValueChanged event. Handle the CurrentCellDirtyStateChanged event instead to cancel assigning the new value (true) by calling the CancelEdit method if you have two checked cells already.
// +
using System.Linq;
// ...
private void dgvParameters_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (dgvParameters.CurrentCell is DataGridViewCheckBoxCell &&
dgvParameters.IsCurrentCellDirty &&
!(bool)dgvParameters.CurrentCell.FormattedValue)
{
var count = dgvParameters.Rows.Cast<DataGridViewRow>()
.SelectMany(r => r.Cells.OfType<DataGridViewCheckBoxCell>()
.Where(c => (bool)c.FormattedValue))
.Count();
if (count == 2) dgvParameters.CancelEdit();
}
}
If you have more than one DataGridViewCheckBoxColumn in the grid, then you need to specify that in the query as well.
private void dgvParameters_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
int tarCol = 2;
if (dgvParameters.CurrentCell is DataGridViewCheckBoxCell &&
dgvParameters.CurrentCell.ColumnIndex == tarCol &&
dgvParameters.IsCurrentCellDirty &&
!(bool)dgvParameters.CurrentCell.FormattedValue)
{
var count = dgvParameters.Rows.Cast<DataGridViewRow>()
.SelectMany(r => r.Cells.OfType<DataGridViewCheckBoxCell>()
.Where(c => c.ColumnIndex == tarCol)
.Where(c => (bool)c.FormattedValue))
.Count();
if (count == 2) dgvParameters.CancelEdit();
}
}

Check through selected items in CheckedListBox and show/ hide columns based on values selected

I am using C# and I need some help. I have DataGridView that I would like to filter (show/ hide columns) based on user selection from the CheckedListBox.
Inside the CheckedListBox, I have listed few items and those are the Column Names from the DataGridView:
All these columns are hidden by default.
CheckedListBox items
Now if user selects THERMAL, I would like to show THERMAL Column in DataGridView. If user deselects THERMAL, I would like to hide THERMAL Column in DataGridView. If user selects/ deselects multiple items, I would like to show/ hide all those items from the DataGridView. I hope this makes sense.
Here is the code that I have:
private void CLB_SHOW_HIDE_SelectedIndexChanged(object sender, EventArgs e)
{
string col = "";
for (int i = 0; i < CLB_SHOW_HIDE.CheckedItems.Count; i++)
{
if (col == "")
{
col = CLB_SHOW_HIDE.GetItemText(CLB_SHOW_HIDE.CheckedItems[i]);
this.DGV_FEATURE.Columns[col].Visible = true;
}
else
{
col += ", " + CLB_SHOW_HIDE.GetItemText(CLB_SHOW_HIDE.CheckedItems[i]);
this.DGV_FEATURE.Columns[col].Visible = false;
}
}
}
Here is the problem... If I remove else statement, I can show All the columns properly only if I go from the bottom up (see my picture above). If I go from top to the bottom, only first item would show. Then I would have to deselect that item and select another one in order for it to show.
If I add else statement like in above code, I get this
Error
Can anyone shed some light on this please?
Just figured out... For anyone that might look for solution similar to this, here is the code:
private void CLB_SHOW_HIDE_SelectedIndexChanged(object sender, EventArgs e)
{
int f = 0;
string qry = "";
for (int i = 0; i < CLB_SHOW_HIDE.Items.Count; i++)
{
if (CLB_SHOW_HIDE.GetItemChecked(i))
{
if (f == 1)
{
qry = CLB_SHOW_HIDE.Items[i].ToString();
this.DGV_FEATURE.Columns[qry].Visible = true;
}
if (f == 0)
{
qry = CLB_SHOW_HIDE.Items[i].ToString();
f = 1;
this.DGV_FEATURE.Columns[qry].Visible = true;
}
}
else
{
qry = CLB_SHOW_HIDE.Items[i].ToString();
this.DGV_FEATURE.Columns[qry].Visible = false;
}
}
}
You are looking for ItemCheck event.
For example, let's say, you have added some columns to DataGridView. Then you can setup checkedListBox and add column names to it. Also add an event handler to handle ItemCheck event:
foreach (DataGridViewColumn c in dataGridView1.Columns)
checkedListBox1.Items.Add(c.Name);
checkedListBox1.ItemCheck += CheckedListBox1_ItemCheck;
Then handle ItemCheck event to show or hide columns:
private void CheckedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
var item = checkedListBox1.GetItemText(checkedListBox1.Items[e.Index]);
dataGridView1.Columns[item].Visible = e.NewValue == CheckState.Checked ? true : false;
}

DevExpress Winform - How to compare data between two gridcontrol on multiple key field

I have 2 gridControl and I wanna change background of cells (rows) that different between 2 gridControl with primary key is multiple field set by user.
Any solution for this problem?
See this example image
In this two query, key field is col1, col2, col3 and col4. Different in col6 and I want to highlight cell that have different value.
This is my current code for RowCellStyle event
private void gvQuery1_RowCellStyle(object sender, RowCellStyleEventArgs e)
{
if (gcQuery2.DataSource == null || lsKey == null || lsKey.Count <= 0)
return;
List<object> id = new List<object>();
foreach (KeyObject key in lsKey)
{
id.Add((sender as GridView).GetRowCellValue(e.RowHandle, key.key1[1].ToString()));
}
for (int i = 0; i < gvQuery2.RowCount; i++)
{
int rowHandle = gvQuery2.GetVisibleRowHandle(i);
bool flgEqual = true;
for (int j = 0; j < lsKey.Count; j++)
{
object v = gvQuery2.GetRowCellValue(rowHandle, gvQuery2.VisibleColumns[int.Parse(lsKey[j].key2[0].ToString())]);
if (id[j] == null && v == null)
continue;
if (!id[j].GetType().Equals(v.GetType()) || !id[j].ToString().Equals(v.ToString()))
{
flgEqual = false;
break;
}
}
if (flgEqual)
{
for (int k = 0; k < (sender as GridView).Columns.Count; k++)
{
if (!(sender as GridView).GetRowCellValue(e.RowHandle, (sender as GridView).Columns[k].FieldName).ToString()
.Equals(gvQuery2.GetRowCellValue(rowHandle, (sender as GridView).Columns[k].FieldName).ToString()))
{
if (e.Column.FieldName.Equals((sender as GridView).Columns[k].FieldName))
e.Appearance.BackColor = Color.Orange;
}
}
break;
}
}
}
Refer this: Customizing Appearances of Individual Rows and Cells
You can implement your functionality using the
GridView.RowCellStyle event. This event is fired for each cell in
a Grid View and thus it has Column and RowHandle parameters
that identify the cell being painted.
I assume that you have fixed number of records in both grid and also the order of the records are same. Then you can try to user same row handle to access the value from the both grid's MainView at RowCellStyle event as below:
using DevExpress.XtraGrid.Views.Grid;
// ...
private void gridView1_RowCellStyle(object sender, RowCellStyleEventArgs e) {
GridView View = sender as GridView;
GridView leftGridView = leftGrid.MainView as GridView; //It is up to you that which viewtype you have used.
if(e.Column.FieldName == "Col5") {
string srcVal= View.GetRowCellDisplayText(e.RowHandle, View.Columns["Col5"]); // You can use GetRowCellValue() method also to get the value from the cell.
string leftGridVal= leftGridView .GetRowCellDisplayText(e.RowHandle, leftGridView .Columns["Col5"]);
if(srcVal != leftGridVal) {
e.Appearance.BackColor = Color.DeepSkyBlue;
e.Appearance.BackColor2 = Color.LightCyan;
}
}
}
Above code snippet is just like a sudo code to direct you to implement the functionality.. Handle this event on that Grid on which you want to color.. Remember to take care about the RowHandle, you have take care about the row index.
Hope this help..
Use the GridView.RowCellStyle event, which enables appearance settings of individual cells to be changed
Please refer this site for your reference
https://www.devexpress.com/Support/Center/Question/Details/Q520842
void gridView1_RowCellStyle(object sender, RowCellStyleEventArgs e)
{
GridView currentView = sender as GridView;
if (e.Column.FieldName == "Customer")
{
bool value = Convert.ToBoolean(currentView.GetRowCellValue(e.RowHandle, "Flag_Customer"));
if (value)
e.Appearance.BackColor = Color.Red;
}
if (e.Column.FieldName == "Vendor")
{
bool value = Convert.ToBoolean(currentView.GetRowCellValue(e.RowHandle, "Flat_Vendor"));
if (value)
e.Appearance.BackColor = Color.Red;
}
}

Input a value from a combobox, while committing an Add in DataGridView

Using C# and forms. I have a datagridview which has two of the three fields visible in the table which is editable and addable. The third field is in a combobox.
When updating the database (hitting enter after adding in the empty row) I get an error saying that the third field can't be null. I'd like to have the value in the combobox be used for the third field.
I can't seem to figure it out. Should I be using an event, somehow?
private void cmbMCID_SelectedIndexChanged(object sender, EventArgs e)
{
var cMarks = (from cm in DB.Student_Courses
where cm.CID == Convert.ToInt32(cmbMCID.Text)
select cm).ToList();
Student_Course sc = new Student_Course();
//cMarks.Add(sc);
studentCourseBindingSource.DataSource = cMarks;
sc.CID = Convert.ToInt32(cmbMCID.Text);
//studentCourseBindingSource.ResetBindings(false);
}
I've attempted to added a RowsAdded Event but didn't come out on top.
private void dgvSMarks_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
if (tabControl1.SelectedIndex == 3)
{
Student_Course sc = new Student_Course();
sc.CID = Convert.ToInt32(cmbMCID.Text);
}
}
Great, OK. so I was doing horrible things - with the combobox. A mix of needing to making a selection in the combobox first (otherwise returned null) and trying to commit what was already in the database.
Needed to create a for loop through the dgv collection, too.
private void dgvSMarks_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
// if (dgvSMarks.ContainsFocus == true)
// {
// // Get the RowIndex of the CID Cell, and add the combobox MCID value to it
// dgvSMarks.Rows[e.RowIndex-1].Cells[0].Value = Convert.ToInt32(cmbMCID.Text);
// }
var student_course = new Student_Course();
var course = new Course();
//MessageBox.Show("Row Count is " + dgvSMarks.Rows.Count);
for (int i = 1; i < dgvSMarks.Rows.Count; i++)
{
if (i == (dgvSMarks.Rows.Count - 1))
{
student_course.CID = Convert.ToInt32(cmbMCID.Text);
student_course.SID = Convert.ToInt32(dgvSMarks.Rows[i - 1].Cells[1].Value);
student_course.Mark = Convert.ToInt32(dgvSMarks.Rows[i - 1].Cells[2].Value);
DB.Student_Courses.InsertOnSubmit(student_course);
DB.SubmitChanges();
}
}
}
Thanks to the help of my CPI instructors. :)

Which event should I use to get the checkbox value which is in gridcontrol

I have a Devexpress gridcontrol that has a checkbox column in it. I am trying to get the value of the checkbox value after user checks or unchecks one of the checkbox in any row. My problem is I am getting the always getting false value.
How can I get the correct value? what event should I use?
here is my code,
private void gvBobin_CellValueChanged(object sender, DevExpress.XtraGrid.Views.Base.CellValueChangedEventArgs e)
{
setUsageSlipAndProductionEntryRelation();
}
public void setUsageSlipAndProductionEntryRelation() {
for (int i = 0; i < gvBobin.RowCount -1; i++)
{
bool check_ = (bool)gvBobin.GetRowCellValue(i, "CHECK");
if (check_ == true)
{
...............
}
else{
...............
}
}
}
If you want to immediately react to user actions, then you need to use GridView.CellValueChanging event. GridView.CellValueChanged event is fired only after user leaves the cell. In both cases to get the changed value you must use CellValueChangedEventArgs object e and its Value property and before getting value you must to check the column.
private void gvBobin_CellValueChanged(object sender, DevExpress.XtraGrid.Views.Base.CellValueChangedEventArgs e)
{
if (e.Column.FieldName == "CHECK")
{
bool check_ = (bool)e.Value;
if (check_)//There are no need to write check_ == True
//You can use e.RowHandle with gvBobin.GetRowCellValue method to get other row values.
//Example: object value = gvBobin.GetRowCellValue(e.RowHandle,"YourColumnName")
{
//...............
}
else
{
//...............
}
}
}
If you want to iterate through all rows then don't use GridView.RowCount. Use GridView.DataRowCount property instead.
for (int i = 0; i < gvBobin.DataRowCount -1; i++)
//...............

Categories