簡體   English   中英

如何按 2 列對 datagridview 進行排序

[英]how to sort a datagridview by 2 columns

如何按兩列(升序)對 DataGridView 進行排序? 我有兩列: daystatus

如果我需要按一列排序,我會這樣做:

this.dataGridView1.Sort (this.dataGridView1.Columns["day"], ListSortDirection.Ascending);

但是兩個人呢?

如果您的DataGridView是數據綁定的,您可以對您的Datatable視圖進行排序並重新綁定到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
}

或者,不使用 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 為“程序化”的列中調用它:

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; 我有一個兩行解決方案。

我不得不做同樣的事情,但在研究了所有這些復雜的方法來做到這一點后,要么包含一個單獨的 .dll,要么編寫我自己的類/方法,我知道必須有一個更簡單的方法。 事實證明我是對的,因為我想出了如何僅使用兩行代碼來完成此任務。 這對我有用。

幸運的是,事實證明 .NET Framework Sort() 方法確實可以幫助我們解決這個問題。 這個想法是您想要單獨對列進行排序,但是您對它們進行排序的順序將產生所需的輸出。

因此,作為示例,我有一個文件類型列和一個文件名列。 每當我想按類型對數據進行排序時,我想確保名稱也在顯示的每種類型中排序。

目標:按類型排序還將按字母順序對文件名進行排序。

數據:

zxcv.css

testimg3.jpg

asdf.html

testimg2.jpg

testimg1.jpg

按名稱排序數據:

mConflictsDataGridView.Sort(mConflictsDataGridView.Columns[mNameLabel.Index], ListSortDirection.Ascending);

asdf.html

testimg1.jpg

testimg2.jpg

testimg3.jpg

zxcv.css

如您所見,這將確保名稱將相應地排序,這樣當我現在按文件類型排序時,這兩個要求都將滿足。

按文件類型對數據進行排序:

mConflictsDataGridView.Sort(mConflictsDataGridView.Columns[mFileExtensionLabel.Index], ListSortDirection.Ascending);

zxcv.css

asdf.html

testimg1.jpg

testimg2.jpg

testimg3.jpg

瞧! 整理好了!

解決方案:在您的情況下,您可能想要嘗試類似以下內容,並且您可能需要對其進行更多調整以使其適合您自己的代碼。

DataGridView1.Sort(DataGridView1.Columns["status"], ListSortDirection.Ascending);
DataGridView1.Sort(DataGridView1.Columns["day"], ListSortDirection.Asscending);

這應該能夠按天顯示您的結果,其狀態字段也已排序。

John Kurtz 提供的答案讓我很接近。 但我發現當我點擊一列時,它確實按兩列排序......在他的例子中:dgvcClicked.Name,“LastName”。 這么好!!

但是,如果我再次單擊該列,則它不會按相反的方向排序。 因此,該列卡在 Ascending 中。

為了克服這個問題,我不得不手動跟蹤排序順序。 從這個類開始:

public class ColumnSorting
{
    public int ColumnIndex { get; set; }
    public ListSortDirection Direction { get; set; }
}

然后,我添加了這個全局范圍的列表:

List<ColumnSorting> _columnSortingList = new List<ColumnSorting>();

然后,在進行排序的方法中,我會

  1. 檢查要排序的列索引是否已存在於 _columnSortingList 中。 如果沒有,請添加它。
  2. 如果已經存在,則交換排序順序

而鮑勃是你的叔叔。

我在處理綁定數據時使用此解決方案。 這適用於我們的用戶,並顯示當前的排序標准。 所有排序僅按升序排列。

添加一個 CheckBox、一個 TextBox、一個 ColumnHeaderMouseClick 事件和代碼,如圖所示。 CheckBox 將切換 TextBox 的可見性,單擊任何列標題會將排序條件添加到 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);

你可以試試這個,或者使用自定義排序:

private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (dataGridView1.Columns[e.ColumnIndex].HeaderText =="day")
            {
               myBindingSource.Sort = "day, hour";
            }
        }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM