listbox 本身有 sort 按鍵 可以自動排序
但它有個問題:在遇到 1、2、10、20,它的排序結果 會是 1、10、2、20
它會依照前面的數字 先排1 再排2,如下圖:
想問問有什麼很快速的方式,來更改它
我自己是用了一個蠻笨的方式:
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]]);
}
}
會這樣排序是因為當成字串,
如果原始資料就是字串的話,
可以用分隔(split)的方式取出.前面的數字,
然後再針對數字排序,
如果原始資料可以抓到數字的話,
還是先排序好就好.
我的想法是不要用屬性預設的排序方式
而是要在資料集合本身先處理完排序,再給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;
感覺好攏長, 怎不創一個類別給予string、int成員再將類別加入list, 最終使用CompareTo來對int做排序.
簡單做可以試試
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}";
}