由于WinForm的UI是繪制的,所以在加載大量數(shù)據(jù)數(shù)據(jù)時會有一定的延時,本篇就討論幾個減少延時的方法。
在加載有規(guī)律數(shù)據(jù)時,可以考慮用遞歸,簡單方便快捷來加載數(shù)據(jù),如下,把一個文件夾下的所有文件或文件夾加載到樹形菜單上,以樹形展示,代碼實現(xiàn)如下。
var rootPath = “d://abc/bcd”;var rootNode = treeView1.Nodes.Add(rootPath, Path.GetFileName(rootPath));LoadFile(rootNode);void LoadFile(TreeNode node){foreach (var file in Directory.GetFiles(node.Name)){node.Nodes.Add(file, Path.GetFileName(file));}foreach (var dir in Directory.GetDirectories(node.Name)){var childNode = node.Nodes.Add(dir, Path.GetFileName(dir));LoadFile(childNode);}}
上面的是練了個手,接下來我們加載一個大點的數(shù)據(jù),一個全國的行政區(qū)劃表,有省,市 ,縣,鄉(xiāng)鎮(zhèn)四級,一共49000多條數(shù)據(jù),數(shù)據(jù)字段有sid,pid,name。本篇我們主要是看從內(nèi)存list到UI上,所以加載數(shù)據(jù)不是重點,可以是數(shù)據(jù)庫,也可以是文件中,最終數(shù)據(jù)會在內(nèi)存的list中。
class Province{public string sid { get; set; }public string pid { get; set; }public string name { get; set; }}var rootNode = treeView1.Nodes.Add(“0”, “中國”);LoadProvince(rootNode);void LoadProvince(TreeNode node){foreach (var item in list.Where(s => s.pid == node.Name)){var childNode = node.Nodes.Add(item.sid, item.name);LoadProvince(childNode);}}
如果直接用遞歸加載,速度太慢,為了加快速度,就得并行加載了,于時就增加Task.Run,因為是大多線程中異步操作UI,所以還得用this.Invoke,代碼如下。運行,會看到,速度顯然快了不了,但還不是理想結(jié)果,理想是無感。
var rootNode = treeView1.Nodes.Add(“0”, “中國”);LoadProvince(rootNode);void LoadProvince(TreeNode node){Task.Run(() =>{foreach (var item in list.Where(s => s.pid == node.Name).OrderBy(s => s.sid)){this.Invoke(() =>{node.Nodes.Add(item.sid, item.name);if (node.Level == 0){node.Expand();}});}foreach (TreeNode childNode in node.Nodes){LoadProvince(childNode);}});}
后來又想到,可不可以把樹形菜單給序列化,窗體啟動時,返序列化回來,用BinaryFormatter來實現(xiàn)(現(xiàn)在官方不鼓勵用),首先TreeView不支持序列化,只能換成TreeView的Nodes屬笥來序列化。窗體啟動時,它的加載速度與上面的異步遞歸差不多,沒有明顯改善。
即然一次加載大量數(shù)據(jù)不行,就再換一下思路,一次加載少一些,因為是UI,用戶肯定有交互,利用用戶的交互來觸發(fā)加載他想要看的數(shù)據(jù),這個少量多少為好,對于樹形控件來說,如果想看不出來,那就是兩級,用戶點開第二級的時候再加載兩級,這樣用戶始終感覺用數(shù)據(jù)。
private void Form1_Load(object sender, EventArgs e){var rootNode = treeView2.Nodes.Add(“0”, “中國”);LoadProvince(rootNode, 1);this.treeView2.BeforeExpand += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeView2_BeforeExpand);}private void treeView2_BeforeExpand(object sender, TreeViewCancelEventArgs e){if (e.Node != null & e.Node.Nodes != null &&e.Node.Nodes.Count > 0){e.Node.Nodes.Clear();LoadProvince(e.Node, 1);}}
性能的問題永遠(yuǎn)沒有最好,也沒有一種方式能就通吃各種場景,得一個個換思路來解決,不過上面的思路肯定不是最好的,如果你有一次性加載全部更快的解決方案,請告我,我實現(xiàn),然后再分享給更多的人。