如何按2列对数据网格视图进行排序
本文关键字:视图 排序 网格 数据网 何按 2列 数据 | 更新日期: 2023-09-27 18:20:35
如何按两列(升序)对DataGridView进行排序?我有两列:day
和status
。
如果我需要按一列排序,我会:
this.dataGridView1.Sort (this.dataGridView1.Columns["day"], ListSortDirection.Ascending);
但是两个人?
如果您的DataGridView
是数据绑定的,您可以对Datatable
视图进行排序并重新绑定到数据表,如下所示:
private DataGridView dataGridView1 = new DataGridView();
private BindingSource bindingSource1 = new BindingSource();
private void Form1_Load(object sender, System.EventArgs e)
{
// Bind the DataGridView to the BindingSource
dataGridView1.DataSource = bindingSource1;
SortDataByMultiColumns(); //Sort the Data
}
private void SortDataByMultiColumns()
{
DataView view = dataTable1.DefaultView;
view.Sort = "day ASC, status DESC";
bindingSource1.DataSource = view; //rebind the data source
}
OR,不使用bindingsource并直接绑定到DataView
:
private void SortDataByMultiColumns()
{
DataView view = ds.Tables[0].DefaultView;
view.Sort = "day ASC, status DESC";
dataGridView1.DataSource = view; //rebind the data source
}
添加一个隐藏列,将两者结合起来并按其排序。
您可以使用DataGridView的Sort方法,但指定一个参数,该参数是实现IComparer的类的实例。
下面是这样一个类的例子:
public class MyTwoColumnComparer : System.Collections.IComparer
{
private string _SortColumnName1;
private int _SortOrderMultiplier1;
private string _SortColumnName2;
private int _SortOrderMultiplier2;
public MyTwoColumnComparer(string pSortColumnName1, SortOrder pSortOrder1, string pSortColumnName2, SortOrder pSortOrder2)
{
_SortColumnName1 = pSortColumnName1;
_SortOrderMultiplier1 = (pSortOrder1 == SortOrder.Ascending) ? 1 : -1;
_SortColumnName2 = pSortColumnName2;
_SortOrderMultiplier2 = (pSortOrder2 == SortOrder.Ascending) ? 1 : -1;
}
public int Compare(object x, object y)
{
DataGridViewRow r1 = (DataGridViewRow)x;
DataGridViewRow r2 = (DataGridViewRow)y;
int iCompareResult = _SortOrderMultiplier1 * String.Compare(r1.Cells[_SortColumnName1].Value.ToString(), r2.Cells[_SortColumnName1].Value.ToString());
if (iCompareResult == 0) iCompareResult = _SortOrderMultiplier2 * String.Compare(r1.Cells[_SortColumnName2].Value.ToString(), r2.Cells[_SortColumnName2].Value.ToString());
return iCompareResult;
}
}
现在,我们可以从一个鼠标点击SortMode为"Programmatic"的列中调用它:
private void dgvAllMyEmployees_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
DataGridViewColumn dgvcClicked = dgvAllEmployees.Columns[e.ColumnIndex];
if (dgvcClicked.SortMode == DataGridViewColumnSortMode.Programmatic)
{
_SortOrder = (_SortOrder == SortOrder.Ascending) ? SortOrder.Descending : SortOrder.Ascending;
MyTwoColumnComparer Sort2C = new MyTwoColumnComparer(dgvcClicked.Name, _SortOrder, "LastName", SortOrder.Ascending);
dgvAllEmployees.Sort(Sort2C);
}
}
类级变量_SortOrder有助于跟踪要进入的顺序。可以通过记住最后两列并按所需顺序对其进行排序来进一步增强这一点。
TLDR;我有一个双线解决方案
我不得不做同样的事情,但在研究了所有这些复杂的方法之后,我知道必须有一个更简单的方法。事实证明我是对的,因为我发现了如何只用两行代码就可以实现这一点。这对我有效。
幸运的是,事实证明.NET Framework Sort()方法确实有助于我们做到这一点。其想法是,您希望单独对列进行排序,但排序的顺序将产生所需的输出。
因此,作为一个例子,我有一列表示文件类型,一列表示一个文件名。每当我想按类型对数据进行排序时,我都要确保在显示的每个类型中也对名称进行排序。
目标:按类型排序还会按字母顺序对文件名进行排序。
数据:
zxcv.css
testimg.jpg
asdf.html
testimg2.jpg
testimg1.jpg
按名称排序数据:
mConflictsDataGridView.Sort(mConflictsDataGridView.Columns[mNameLabel.Index], ListSortDirection.Ascending);
asdf.html
testimg1.jpg
testimg2.jpg
testimg.jpg
zxcv.css
正如您所看到的,这将命名确保名称将被相应地排序,这样当我现在按文件类型排序时,这两个要求都将得到满足。
按文件类型排序数据:
mConflictsDataGridView.Sort(mConflictsDataGridView.Columns[mFileExtensionLabel.Index], ListSortDirection.Ascending);
zxcv.css
asdf.html
testimg1.jpg
testimg2.jpg
testimg.jpg
哇!它已排序!
解决方案:在您的情况下,您可能想尝试以下内容,您可能需要对其进行更多调整,使其符合您自己的代码。
DataGridView1.Sort(DataGridView1.Columns["status"], ListSortDirection.Ascending);
DataGridView1.Sort(DataGridView1.Columns["day"], ListSortDirection.Asscending);
这应该能够按天显示您的结果,并对其状态字段进行排序。
John Kurtz提供的答案让我很接近。但我发现,当我点击一列时,它确实按两列排序。。。在他的例子中:dgvcClicked.Name,"LastName"。所以,很好!!
但是,如果我再次点击该列,它将不会按相反的方向排序。所以柱子被卡住了。
为了克服这个问题,我不得不手动跟踪排序顺序。从这个类开始:
public class ColumnSorting
{
public int ColumnIndex { get; set; }
public ListSortDirection Direction { get; set; }
}
然后,我添加了这个全局范围的列表:
List<ColumnSorting> _columnSortingList = new List<ColumnSorting>();
然后,在进行排序的方法中,我会
- 检查_columnSortingList中是否已存在正在排序的列索引。如果没有,请添加它
- 如果它已经存在,则交换排序顺序
鲍勃是你的叔叔。
您可以尝试此操作,也可以使用自定义排序:
private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (dataGridView1.Columns[e.ColumnIndex].HeaderText =="day")
{
myBindingSource.Sort = "day, hour";
}
}
我在处理绑定数据时使用此解决方案。这适用于我们的用户,并显示当前的排序条件。所有排序都只按升序排列。
添加CheckBox、TextBox、ColumnHeaderMouseClick事件和代码,如图所示。复选框将切换TextBox的可见性,单击任何列标题将向TextBox添加排序条件。要清除文本框,只需双击即可。
private void FooDataGridView_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if(chkMultiSort.Checked == true)
{
string columnHeader = FooDataGridView.Columns[e.ColumnIndex].DataPropertyName;
txtMultiSort.Text += (columnHeader + ", ");
try
{
FooBindingSource.Sort = txtMultiSort.Text.Remove(txtMultiSort.Text.Length - 2);
}
catch
{
MessageBox.Show("Invalid Sort Data", "Information", MessageBoxButtons.OK, MessageBoxIcon.None);
txtMultiSort.Text = String.Empty;
}
}
}
private void ChkMultiSort_CheckedChanged(object sender, EventArgs e)
{
if(chkMultiSort.Checked == true)
{
txtMultiSort.Visible = true;
}
else
{
txtMultiSort.Visible = false;
txtMultiSort.Text = String.Empty;
}
}
private void TxtMultiSort_DoubleClick(object sender, EventArgs e)
{
txtMultiSort.Text = String.Empty;
}
这是@John Kurtz对IComparer类的改进,它支持多列和按数字或日期排序。
public static void SortOnMultipleColumns(DataGridView dgv, Dictionary<string /*Column Name*/, ColumnSortInfo> sortingColumns)
{
// Show the glyphs
foreach (DataGridViewColumn col in dgv.Columns)
{
System.Windows.Forms.SortOrder sortOrder = System.Windows.Forms.SortOrder.None;
foreach (var kvp in sortingColumns)
{
if (kvp.Key == col.Name)
{
sortOrder = kvp.Value.SortOrder;
break;
}
}
col.HeaderCell.SortGlyphDirection = sortOrder;
}
// Sort the grid
MultiColumnCompararor multiColumnCompararor = new MultiColumnCompararor(sortingColumns);
dgv.Sort(multiColumnCompararor);
}
public class ColumnSortInfo
{
public enum ValueConversion { ToString, ToNumber, ToDate}
public ColumnSortInfo(System.Windows.Forms.SortOrder sortOrder, ValueConversion valueConversion = ValueConversion.ToString)
{
SortOrder = sortOrder;
MyValueConversion = valueConversion;
SortOrderMultiplier = (SortOrder == SortOrder.Ascending) ? 1 : -1;
}
public System.Windows.Forms.SortOrder SortOrder { get; set; }
public int SortOrderMultiplier { get; }
public ValueConversion MyValueConversion { get; set; }
public static double StringToDouble(string sVal)
{
if (Double.TryParse(sVal, out double dVal))
{
return dVal;
}
return 0;
}
public static DateTime StringToDateTime(string sVal)
{
if (DateTime.TryParse(sVal, out DateTime dt))
{
return dt;
}
return DateTime.MinValue;
}
}
private class MultiColumnCompararor : System.Collections.IComparer
{
IDictionary<string /*Column Name*/, ColumnSortInfo> _sortingColumns;
public MultiColumnCompararor(IDictionary<string /*Column Name*/, ColumnSortInfo> sortingColumns)
{
_sortingColumns = sortingColumns;
}
public int Compare(object x, object y)
{
try
{
DataGridViewRow r1 = (DataGridViewRow)x;
DataGridViewRow r2 = (DataGridViewRow)y;
foreach (var kvp in _sortingColumns)
{
string colName = kvp.Key;
ColumnSortInfo csi = kvp.Value;
string sVal1 = r1.Cells[colName].Value?.ToString().Trim()??"";
string sVal2 = r2.Cells[colName].Value?.ToString().Trim()??"";
int iCompareResult = 0;
switch (csi.MyValueConversion)
{
case ColumnSortInfo.ValueConversion.ToString:
iCompareResult = String.Compare(sVal1, sVal2);
break;
case ColumnSortInfo.ValueConversion.ToNumber:
double d1 = ColumnSortInfo.StringToDouble(sVal1);
double d2 = ColumnSortInfo.StringToDouble(sVal2);
iCompareResult = ((d1 == d2) ? 0 : ((d1 > d2) ? 1 : -1));
break;
case ColumnSortInfo.ValueConversion.ToDate:
DateTime dt1 = ColumnSortInfo.StringToDateTime(sVal1);
DateTime dt2 = ColumnSortInfo.StringToDateTime(sVal2);
iCompareResult = ((dt1 == dt2) ? 0 : ((dt1 > dt2) ? 1 : -1));
break;
default:
break;
}
iCompareResult = csi.SortOrderMultiplier * iCompareResult;
if (iCompareResult != 0) { return iCompareResult; }
}
return 0;
}
catch (Exception ex)
{
return 0;
}
}
}
用法:
Dictionary<String, ColumnSortInfo> sortingColumns = new Dictionary<String, ColumnSortInfo>
{ {"policyNumber", new ColumnSortInfo(System.Windows.Forms.SortOrder.Ascending)},
{"MessageId", new ColumnSortInfo(System.Windows.Forms.SortOrder.Descending, ColumnSortInfo.ValueConversion.ToNumber)},
{"CreationDate", new ColumnSortInfo(System.Windows.Forms.SortOrder.Ascending, ColumnSortInfo.ValueConversion.ToDate)}};
CsUtils.SortOnMultipleColumns(dgv, sortingColumns);