iT邦幫忙

1

C# Listbox 的排序問題

  • 分享至 

  • xImage

listbox 本身有 sort 按鍵 可以自動排序
但它有個問題:在遇到 1、2、10、20,它的排序結果 會是 1、10、2、20
它會依照前面的數字 先排1 再排2,如下圖:
https://ithelp.ithome.com.tw/upload/images/20220814/2014995491BCgIyOL8.png

想問問有什麼很快速的方式,來更改它
我自己是用了一個蠻笨的方式:

1.先把前面位數的數字抓出來做排序
2.我先預設排序的數字最多只到 999 等於只有三位數
3.我將前三位數字 特別拉出來
4.創建一個字典的集合 讓數字可以對應到文字,之後可以方便使用排序過後的文字
5.先抓出第一位的數字
6.後面第二位之後依序跟前面的位數比,EX:第五位 就跟第1~4位比 第十位 就跟 第1~9位比
只要比前面的某一位小,就把那一位比較大的位數全部往後移,當前的位數移到它的位置
7.創建一個暫時陣列,將移動的數字存到該暫存陣列
8.再將原先陣列的值改成該暫存陣列相對應的位置值
9.再利用第4點的字典 來將 排好順序的數字 重新放到 listbox 裡頭,放之前會把原先的內容刪掉

大方向大概是如上述,但總覺得很笨,想問問大家有沒有更快速的方式??

順便附上我笨拙排序的程式碼:

public form2()
{
InitializeComponent();

        //初始的排順序
        int list_box_count = listBox1.Items.Count;

        //將listbox1裡的內容都丟到這個集合裡
        List<string> choose = new List<string>();

        //創建開頭數字的文字陣列,作為數字比較大小用途,目前當作只會有3位數 
        string[] num_compare = new string[3];

        //創建前三位數字總和的 int 陣列,總共的量,就是listbox1的總數量
        int[] num_sum = new int[list_box_count];


        //創建字典,給予數字 對應其後方文字 做使用
        Dictionary<int, string> di = new Dictionary<int, string>();

        //要將 num_compare 初始化 一個空值,在下面的 num_sum總和,若少於三位數時才不會報錯
        for (int i = 0; i < 3; i++)
        {
            num_compare[i] = "";

        }

        //將listbox1 裡面原本的名單,都匯進 choose 集合裡,這樣才方便使用它的功能
        for (int i = 0; i < list_box_count; i++)
        {
            choose.Add(listBox1.Items[i].ToString());
        }

        //確認前面1~3格數字 是否為數字,如果是就把它抓出來,最後再結合
        for (int d = 0; d < 3; d++)
        {

            if (char.IsDigit(choose[0][d]))
            {
                num_compare[d] = (choose[0][d] - '0').ToString();
            }


        }


        //將前三位結合成數字型態
        num_sum[0] = Int32.Parse(num_compare[0] + num_compare[1] + num_compare[2]);

        //存到第一個字典當中
        di.Add(num_sum[0], choose[0]);


        //開始做後面的比大小,從第2個開始一直到 choose.Count  選擇的總數量
        //每次比大小,都從第 1 個開始依序往後比,只要找到比自己大的,
        //就將這個比自己大的後續皆往後一格,自己填到它的位置


        for (int order_num = 1; order_num < choose.Count; order_num++)
        {

            //因為不一定每個數字 都會是三位數或 二位數,所以必須把 num_compare的值 更新掉
            for (int i = 0; i < 3; i++)
            {
                num_compare[i] = "";

            }

            //確認前面1~3格數字 是否為數字,如果是就把它抓出來
            for (int d = 0; d < 3; d++)
            {

                if (char.IsDigit(choose[order_num][d]))
                {
                    num_compare[d] = (choose[order_num][d] - '0').ToString();
                }


            }


            //將前三位結合成數字型態
            num_sum[order_num] = Int32.Parse(num_compare[0] + num_compare[1] + num_compare[2]);

            //依序存到字典當中
            di.Add(num_sum[order_num], choose[order_num]);

            //創立一個暫時的數字總和 陣列,之後用來做為陣列移動暫時存取的空間
            int[] temp_num_sum = new int[listBox1.Items.Count];

            //每創建出一組數字 就開始與第 1 位 到 自己的前一位 做比較
            for (int i = 0; i < order_num; i++)
            {


                //假如自己比 第 i 位 還要小,就將比自己大的那個位置以後的數字陣列,往後移動一位
                if (num_sum[order_num] < num_sum[i])
                {


                    for (int x = i; x < order_num; x++)
                    {

                        //比自己大的那個位置,要改成自己 num_sum[order_num],存到暫存陣列裡
                        if (x == i)
                        {
                            temp_num_sum[x] = num_sum[order_num];
                        }

                        //再依序每個 num_sum 陣列內容都往後移一位,也存在暫存陣列裡
                        temp_num_sum[x + 1] = num_sum[x];

                    }

                    //再將暫存陣列的值,存回到原本的陣列
                    for (int z = i; z <= order_num; z++)
                    {

                        num_sum[z] = temp_num_sum[z];
                    }



                }
            }

        }

        //將原先 listbox1 未排序的內容 刪掉
        int r = listBox1.Items.Count;
        for (int b = 0; b < r; b++)
        {

            if (listBox1.Items.Count > 0)
            {

                listBox1.Items.Remove(listBox1.Items[0]);
            }
        }



        //將排過順序的文字 放到 listBox1
        for (int z = 0; z < choose.Count(); z++)
        {
            //利用字典的紀錄方式,找到數字,即可找到對應的內容
            listBox1.Items.Add(di[num_sum[z]]);
        }
    }
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
2
小魚
iT邦大師 1 級 ‧ 2022-08-14 19:51:30

會這樣排序是因為當成字串,
如果原始資料就是字串的話,
可以用分隔(split)的方式取出.前面的數字,
然後再針對數字排序,
如果原始資料可以抓到數字的話,
還是先排序好就好.

1
Coding小僧
iT邦新手 3 級 ‧ 2022-08-14 20:08:58

我的想法是不要用屬性預設的排序方式
而是要在資料集合本身先處理完排序,再給listbox用
大概作法如下:

var list = new string[] { "1.A", "10.X", "11.Y", "12.Z" ,"2.B", "3.B", "4.C", "5.D", "6.E" }.ToList();
var list2 = list.OrderBy(x =>int.Parse(x.Split('.')[0])).ToList();
listBox1.DataSource = list2;
kw6732 iT邦研究生 5 級 ‧ 2022-08-14 22:07:40 檢舉

我想請教一下你的資料來源是從資料庫裡面取得的嗎?
如果是從資料庫取得的,轉入資料的時候就要做好排序。
如果不是從資料庫取得的,也可以像您所說的在外部先處理好排序。
比較建議你尋找可以排序的物件使用,輪胎並不需要重複的被發明。

我不是原PO @@

kw6732 iT邦研究生 5 級 ‧ 2022-08-15 08:23:45 檢舉

抱歉 貼錯格子了~

1
franx0722
iT邦新手 2 級 ‧ 2022-08-15 08:35:01

感覺好攏長, 怎不創一個類別給予string、int成員再將類別加入list, 最終使用CompareTo來對int做排序.

我也傾向這個做法,既然要照數字排序何苦使用字串在哪搞XD...明明是簡易的操作rrrr~

1
水無痕
iT邦新手 3 級 ‧ 2022-08-16 08:28:56

簡單做可以試試

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        // 這邊排序好再放到 listbox 中
        var listBoxItems = GetOptions().OrderBy(o => o.Value).ToArray();

        lstBox.Items.AddRange(listBoxItems);
    }

    private IEnumerable<Option> GetOptions()
    {
        return Enumerable.Range(1, 26)
                         .Select(i => new Option { Value = i, Text = ((char)(64 + i)).ToString() });
    }

    private void btnCheck_Click(object sender, EventArgs e)
    {
        var selectedItem = lstBox.SelectedItem as Option;
        // TODO
    }
}

public class Option
{
    public int Value { get; set; }

    public string Text { get; set; }

    public override string ToString() => $"{Value}. {Text}";
}

我要發表回答

立即登入回答