寫在前面
今天忙裏偷閒在瀏覽外文的時候看到一篇講C#中泛型的使用的文章,所以加上本人的理解以及四級沒過的英語水平斗膽給大夥進行了翻譯,固然在翻譯的過程當中發現了一些問題,所以也進行了糾正,固然,原文的地址我放在最下面,若是你的英文水平比較好的話,能夠直接直接閱讀全文。同時最近建了一個.NET Core實戰項目交流羣637326624,有興趣的朋友能夠來相互交流。目前.NET Core實戰項目之CMS的教程也已經更新了6篇了,目前兩到三天更新一篇。javascript
做者:依樂祝
原文地址:http://www.javashuo.com/article/p-ytlwppbl-gq.htmlhtml
介紹
C#和.NET中的泛型程序具備強類型集合的許多優勢,併爲代碼提供更高質量和性能提高。泛型是C#語言和公共語言運行庫(CLR)中的一個新功能,它將類型參數的概念引入.NET Framework。類型參數使得設計某些類和方法成爲可能,例如,經過使用泛型類型參數T,能夠大大簡化類型之間的強制轉換或裝箱操做的過程(裝箱、拆箱問題)。說白了,泛型就是經過參數化類型來實如今同一份代碼上操做多種數據類型,利用「參數化類型」將類型抽象化,從而實現靈活的複用。每一個集合的詳細規範能夠在System.Collection.Generic名稱空間下找到。前端
裝箱和拆箱
.Net定義了兩種主要的數據類型來表示變量,也就是傳說中的值類型和引用類型。這是須要裝箱和拆箱的地方。裝箱是一種經過將變量存儲到System.Object中來顯式地將值類型轉換爲引用類型的機制。當您裝入值時,CLR會將新對象分配到堆中,並將值類型的值複製到該實例中。例如,您建立了一個int類型的變量:java
int a = 20; object b = a; //裝箱
相反的操做是拆箱,它是將引用類型轉換回值類型的過程。此過程驗證接收數據類型是否與裝箱類型一致;node
int c = (int)b; // 拆箱
C#編譯器能夠看到從int到object的賦值,反之亦然。當編譯該程序並經過IL解析器檢查IL生成的代碼時,您會注意到當b被賦值爲a時,程序經過在IL中自動插入一個box指令來響應,當c被賦值爲b時以下;程序員
代碼加載常量20並將其存儲在本地插槽中;它將值20加載到堆棧中並將其裝箱。最後,它將被裝箱的20返回到堆棧上,並將其拆箱爲int類型ajax
這個過程.NET CLR執行了一系列操做,例如,首先在託管堆中分配一個對象,而後在裝箱中將值轉換爲內存位置,並在拆箱期間將值存儲在堆上而且必須轉回到堆棧。所以,從性能的角度來看,裝箱和拆箱過程在泛型中具備很是重要的意義,由於這個過程若是不使用泛型的話會耗費更多地資源。算法
泛型類
能夠經過在類名後面加上符號來定義泛型類。這裏沒有強制必須將「T」字放在泛型的定義中。您能夠在TestClass <>類聲明中使用任何單詞。數據庫
public class TestClass<T> { }
System.Collection.Generic命名空間下還定義了許多實現了這些關鍵字接口的類型。下表列出了此命名空間的核心類類型。編程
泛型類 | 描述 |
---|---|
Collection | 泛型集合的基類,能夠比較兩個泛型對象是否相等 |
Dictionary<TKey, TValue> | 鍵值對的泛型集合 |
List | 可動態調整列表項的大小 |
Queue | 先進先出(FIFO)列表的泛型實現 |
Stack | 後進先出(LIFO)列表的泛型實現 |
簡單的泛型類示例
如下示例顯示了一個簡單的泛型類型的操做。TestClass 定義一個長度爲5的泛型類型數組。Add()方法負責將任何類型的對象添加到集合中,而Indexer屬性是循環語句迭代的實現。最後在主類中,咱們使用整形類型來實例化TestClass 類,並使用Add()方法將一些整數類型數據添加到集合中。
using System; using System.Collections.Generic; namespace GenericApp { public class TestClass<T> { // 定義一個長度爲5的泛型類型的數組 T[] obj = new T[5]; int count = 0; // 向檢討類型添加數據 public void Add(T item) { //checking length if (count + 1 < 6) { obj[count] = item; } count++; } //foreach語句迭代索引 public T this[int index] { get { return obj[index]; } set { obj[index] = value; } } } class Program { static void Main(string[] args) { //用整形來實例化泛型類 TestClass<int> intObj = new TestClass<int>(); //向集合中添加int數據 intObj.Add(1); intObj.Add(2); intObj.Add(3); //沒有裝箱 intObj.Add(4); intObj.Add(5); //遍歷顯示數據 for (int i = 0; i < 5; i++) { Console.WriteLine(intObj[i]); //沒有拆箱 } Console.ReadKey(); } } }
在構建並運行該程序以後,程序的輸出以下所示;
泛型的主要特性
泛型類型的一些重要特徵使它們相比傳統的非泛型類型具備以下的顯著特徵:
- 類型安全
- 性能
- 二進制代碼複用
類型安全
泛型最重要的特徵之一是類型安全性。對於非泛型ArrayList類,若是使用對象類型,則能夠向集合中添加任何類型,這些類型有時會致使嚴重的問題。下面的示例顯示向ArrayList類型的集合添加一個整數、字符串和對象;
ArrayList obj = new ArrayList(); obj.Add(50); obj.Add("Dog"); obj.Add(new TestClass());
如今,若是使用整數對象來使用foreach語句進行遍歷的話,當編譯器接受到代碼,可是由於集合中的全部元素都不是整數,因此會致使運行時異常;
foreach(int i in obj) { Console.WriteLine(i); }
編程的經驗法則是應該儘早檢測到錯誤。對於泛型類Test,泛型類型T定義容許哪些類型。經過使用Test的定義,只能向集合添加整型類型的數據。這時候當Add()方法具備如下無效參數的時候編譯器將不編譯代碼;
Test<int> obj = new Test<int>(); obj.Add(50); obj.Add("Dog"); //編譯錯誤 obj.Add(new TestClass()); //編譯錯誤
性能
在下面的示例中,ArrayList類存儲對象,而且定義了Add()方法來存儲一些整型參數。所以,整數類型被裝箱。當使用foreach語句讀取ArrayList中的值時,將發生拆箱。
ArrayList obj = new ArrayList(); obj.Add(50); //裝箱- 值類型轉換成引用類型 int x= (int)obj[0]; //拆箱 foreach(int i in obj) { Console.WriteLine(i); // 拆箱 }
注意:泛型比其餘集合(如ArrayList)更快。
代替使用對象類型,TestClass類的泛型類型被定義爲int,所以在從編譯器動態生成的類中將使用int類型。因此將不會發生裝箱和拆箱,以下所示;
TestClass<int> obj = new TestClass<int>(); obj.Add(50); //沒有裝箱 int x= obj[0]; // 沒有拆箱 foreach(int i in obj) { Console.WriteLine(i); //沒有拆箱 }
二進制代碼複用
泛型類型提供了一種源代碼保護機制。泛型類能夠定義一次,而且可使用許多不一樣類型來進行實例化。泛型能夠在一種CLR支持的語言中定義,並能夠被另外一種.NET語言使用。如下TestClass 使用int和string類型進行實例化:
TestClass<int> obj = new TestClass<int>(); obj.Add(50); TestClass<string> obj1 = new TestClass<string>(); Obj1.Add("hello");
通用方法
雖然大多數開發人員一般會使用基類庫中的現有泛型類型,但也有可能會構建本身的泛型成員和自定義的泛型類型。
本示例的目的是構建一個交換方法,該方法可使用單個類型參數對任何可能的數據類型(基於值或基於引用)進行操做。因爲交換算法的性質,傳入的參數將做爲使用ref關鍵字修飾的引用類型來進行發送。
using System; using System.Collections.Generic; namespace GenericApp { class Program { //泛型方法 static void Swap<T>(ref T a, ref T b) { T temp; temp = a; a = b; b = temp; } static void Main(string[] args) { //交換兩個整形數據 int a = 40, b = 60; Console.WriteLine("Before swap: {0}, {1}", a, b); Swap<int>(ref a, ref b); Console.WriteLine("After swap: {0}, {1}", a, b); Console.ReadLine(); } } }
編譯此泛型方法實現的程序後,輸出以下所示;
字典
字典也被稱爲映射或散列表。它表示容許您基於關鍵字來訪問元素的數據結構。字典的一個重要特徵是更快的查找; 您能夠添加或刪除選項而不會產生性能開銷。
.Net提供了幾個字典類,例如Dictionary <TKey,TValue>。類型參數TKey和TValue分別表示關鍵字的類型和它能夠存儲的值。
簡單的字典示例
如下示例演示使用泛型的簡單字典集合。在此程序中,將建立一個Dictionary類型對象,該對象接受int做爲鍵,字符串做爲值。而後咱們將一些字符串值添加到字典集合中,最後顯示字典集合元素。
using System; using System.Collections.Generic; namespace GenericApp { public class Program { static void Main(string[] args) { //定義一個字典集合 Dictionary<int,string> dObj = new Dictionary<int,string>(5); //向字典中添加類型 dObj.Add(1,1,"Tom"); dObj.Add(2,"John"); dObj.Add(3, "Maria"); dObj.Add(4, "Max"); dObj.Add(5, "Ram"); //輸出數據 for (int i = 1; i <= dObj.Count;i++) { Console.WriteLine(dObj[i]); } Console.ReadKey(); } } }
如下示例經過定義附加類emp來描述一些更復雜的問題,其中咱們覆蓋ToString()方法以顯示特定員工的名稱和薪水。稍後在Main()方法中,建立一個新的Dictionary <TKey,TValue)的實例,其中鍵的類型爲string,值爲emp類型。構造函數分配2個元素的容量。emp對象和做爲鍵的字符串值被添加到字典集合中。最後,使用foreach語句迭代集合元素並顯示在屏幕上。
using System; using System.Text; using System.Collections.Generic; namespace GenericApp { public class emp { private string name; private int salary; public emp(string name,int salary) { this.name = name; this.salary = salary; } public override string ToString() { StringBuilder sb = new StringBuilder(200); sb.AppendFormat("{0},{1}",name,salary); return sb.ToString(); } } public class Program { static void Main(string[] args) { //定義一個字典集合 Dictionary<string, emp> dObj = new Dictionary<string, emp>(2); //向字典中添加元素 emp tom = new emp("tom", 2000); dObj.Add("tom",tom); // 鍵,值 emp john = new emp("john", 4000); dObj.Add("john",john); //print data foreach(Object str in dObj.Values) { Console.WriteLine(str); } Console.ReadKey(); } } }
隊列
隊列是一種特殊類型的容器,可確保以FIFO(先進先出)方式訪問元素。隊列集合最適合實現消息傳遞的組件。咱們可使用如下語法定義Queue集合對象:
Queue qObj = new Queue();
Queue集合的屬性,方法和其餘規則定義都位於Sysyem.Collection命名空間下。下表定義了關鍵成員;
System.Collection.Queue成員 | 定義 |
---|---|
Enqueue() | 將對象添加到隊列的末尾。 |
Dequeue() | 從隊列的開頭刪除對象。 |
Peek() | 返回隊列開頭的對象而不刪除它。 |
下面演示了一個基本的隊列類型的集合,將一些字符串類型值添加到集合中,最後使用while語句來顯示整個集合中的數據 。
using System; using System.Collections; namespace GenericApp { class Program { static void Main(string[] args) { //定義一個隊列 Queue qObj = new Queue(); //向隊列中添加字符串數據 qObj.Enqueue("Tom"); qObj.Enqueue("Harry"); qObj.Enqueue("Maria"); qObj.Enqueue("john"); //顯示隊列中的數據 while(qObj.Count !=0 ) { Console.WriteLine(qObj.Dequeue()); } Console.ReadKey(); } } }
堆棧
Stack集合是LIFO的抽象(後進先出)。咱們可使用如下語法定義Stack集合對象:
Stack qObj = new Stack();
下表說明了堆棧的關鍵成員;
System.Collection.Stack成員 | 定義 |
---|---|
Contains() | 若是在集合中找到特定元素,則返回true。 |
Clear() | 刪除集合的全部元素。 |
Peek() | 預覽堆棧中的最新元素。 |
Push() | 它將元素推入堆棧。 |
Pop() | 返回並刪除堆棧的頂部元素。 |
如下演示了堆棧集合。首先,將數組類型對象引用到堆棧集合中。而後使用Pop()方法從堆棧中刪除集合中元素的值並顯示在屏幕上。
using System; using System.Collections; namespace GenericApp { class Program { static void Main(string[] args) { int[] iArray = new int[] {1,2,3,4,5,6,7,8,9,10 }; //定義一個堆棧 Stack sObj = new Stack(iArray); Console.WriteLine("Total items="+sObj.Count); //顯示集合數據 for (int i = 0; i < sObj.Count;++i ) { Console.WriteLine(sObj.Pop()); } Console.ReadKey(); } } }
在使用泛型實現的另外一個示例中,使用Push()方法將5個項添加到堆棧中。而後使用循環迭代輸出堆棧中的數據。堆棧的枚舉器不會刪除數據; 它只是以LIFO方式返回每一個項目,以下所示:
using System; using System.Collections; namespace GenericApp { class Program { static void Main(string[] args) { //定義一個堆棧 Stack sObj = new Stack(); //向集合添加數據 for (int i = 0; i < 5; ++i) { sObj.Push(i+1); } Console.WriteLine("Total items=" + sObj.Count); //打印數據 foreach (int i in sObj) { Console.WriteLine(i); } Console.ReadKey(); } } }
總結
今天忙裏偷閒,在瀏覽外文的時候看到一篇講泛型的文章,所以就加上本身的理解進行了相關翻譯,也加深了本身對泛型的理解!若是英文比較好的話能夠直接訪問https://www.c-sharpcorner.com/UploadFile/84c85b/using-generics-with-C-Sharp/ 自行查看!固然,我在翻譯的過程當中也發現了文中的一些錯誤,因此進行了更正!同時最近建了一個.NET Core實戰項目交流羣637326624,有興趣的朋友能夠來相互交流。目前.NET Core實戰項目之CMS的教程也已經更新了6篇了,目前兩到三天更新一篇。最後感謝你們的閱讀。
Seaching TreeVIew WPF
項目中有一個樹形結構的資源,須要支持搜索功能,搜索出來的結果仍是須要按照樹形結構展現,下面是簡單實現的demo。
1.首先建立TreeViewItem的ViewModel,通常狀況下,樹形結構都包含DisplayName,Deepth,Parent,Children,Id, IndexCode,Visibility等屬性,具體代碼以下所示:
1 using System; 2 using System.Collections.Generic; 3 using System.Collections.ObjectModel; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 using System.Windows; 8 9 namespace TreeViewDemo 10 { 11 public class TreeViewItemVM : NotifyPropertyChangedBase 12 { 13 public TreeViewItemVM () 14 { 15 Visible = Visibility.Visible; 16 } 17 18 private TreeViewItemVM parent; 19 public TreeViewItemVM Parent 20 { 21 get 22 { 23 return this.parent; 24 } 25 set 26 { 27 if (this.parent != value) 28 { 29 this.parent = value; 30 this.OnPropertyChanged(() => this.Parent); 31 } 32 } 33 } 34 35 private ObservableCollection<TreeViewItemVM> children; 36 public ObservableCollection<TreeViewItemVM> Children 37 { 38 get 39 { 40 return this.children; 41 } 42 set 43 { 44 if (this.children != value) 45 { 46 this.children = value; 47 this.OnPropertyChanged(() => this.Children); 48 } 49 } 50 } 51 52 private string id; 53 public string ID 54 { 55 get 56 { 57 return this.id; 58 } 59 set 60 { 61 if (this.id != value) 62 { 63 this.id = value; 64 this.OnPropertyChanged(() => this.ID); 65 } 66 } 67 } 68 69 private string indexCode; 70 public string IndexCode 71 { 72 get { return indexCode; } 73 set 74 { 75 if (indexCode != value) 76 { 77 indexCode = value; 78 this.OnPropertyChanged(() => IndexCode); 79 } 80 } 81 } 82 83 private string displayName; 84 public string DisplayName 85 { 86 get 87 { 88 return this.displayName; 89 } 90 set 91 { 92 if (this.displayName != value) 93 { 94 this.displayName = value; 95 this.OnPropertyChanged(() => this.DisplayName); 96 } 97 } 98 } 99 100 private int deepth; 101 public int Deepth 102 { 103 get 104 { 105 return this.deepth; 106 } 107 set 108 { 109 if (this.deepth != value) 110 { 111 this.deepth = value; 112 this.OnPropertyChanged(() => this.Deepth); 113 } 114 } 115 } 116 117 private bool hasChildren; 118 public bool HasChildren 119 { 120 get 121 { 122 return this.hasChildren; 123 } 124 set 125 { 126 if (this.hasChildren != value) 127 { 128 this.hasChildren = value; 129 this.OnPropertyChanged(() => this.HasChildren); 130 } 131 } 132 } 133 134 private NodeType type; 135 public NodeType Type 136 { 137 get { return type; } 138 set 139 { 140 if (type != value) 141 { 142 type = value; 143 OnPropertyChanged(() => this.Type); 144 } 145 } 146 } 147 148 private Visibility visible; 149 public Visibility Visible 150 { 151 get { return visible; } 152 set 153 { 154 if (visible != value) 155 { 156 visible = value; 157 OnPropertyChanged(() => this.Visible); 158 } 159 } 160 } 161 162 public bool NameContains(string filter) 163 { 164 if (string.IsNullOrWhiteSpace(filter)) 165 { 166 return true; 167 } 168 169 return DisplayName.ToLowerInvariant().Contains(filter.ToLowerInvariant()); 170 } 171 } 172 }
2.建立TreeViewViewModel,其中定義了用於過濾的屬性Filter,以及過濾函數,並在構造函數中初始化一些測試數據,具體代碼以下:
1 using System; 2 using System.Collections.Generic; 3 using System.Collections.ObjectModel; 4 using System.ComponentModel; 5 using System.Linq; 6 using System.Text; 7 using System.Threading.Tasks; 8 using System.Windows.Data; 9 10 namespace TreeViewDemo 11 { 12 public class TreeViewViewModel : NotifyPropertyChangedBase 13 { 14 public static TreeViewViewModel Instance = new TreeViewViewModel(); 15 16 private TreeViewViewModel() 17 { 18 Filter = string.Empty; 19 20 Root = new TreeViewItemVM() 21 { 22 Deepth = 0, 23 DisplayName = "五號線", 24 HasChildren = true, 25 Type = NodeType.Unit, 26 ID = "0", 27 Children = new ObservableCollection<TreeViewItemVM>() { 28 new TreeViewItemVM() { DisplayName = "站臺", Deepth = 1, HasChildren = true, ID = "1", Type = NodeType.Region, 29 Children = new ObservableCollection<TreeViewItemVM>(){ 30 new TreeViewItemVM() { DisplayName = "Camera 01", Deepth = 2, HasChildren = false, ID = "3",Type = NodeType.Camera }, 31 new TreeViewItemVM() { DisplayName = "Camera 02", Deepth = 2, HasChildren = false, ID = "4",Type = NodeType.Camera }, 32 new TreeViewItemVM() { DisplayName = "Camera 03", Deepth = 2, HasChildren = false, ID = "5",Type = NodeType.Camera }, 33 new TreeViewItemVM() { DisplayName = "Camera 04", Deepth = 2, HasChildren = false, ID = "6",Type = NodeType.Camera }, 34 new TreeViewItemVM() { DisplayName = "Camera 05", Deepth = 2, HasChildren = false, ID = "7", Type = NodeType.Camera}, 35 }}, 36 new TreeViewItemVM() { DisplayName = "進出口", Deepth = 1, HasChildren = true, ID = "10", Type = NodeType.Region, 37 Children = new ObservableCollection<TreeViewItemVM>(){ 38 new TreeViewItemVM() { DisplayName = "Camera 11", Deepth = 2, HasChildren = false, ID = "13",Type = NodeType.Camera }, 39 new TreeViewItemVM() { DisplayName = "Camera 12", Deepth = 2, HasChildren = false, ID = "14",Type = NodeType.Camera }, 40 new TreeViewItemVM() { DisplayName = "Camera 13", Deepth = 2, HasChildren = false, ID = "15",Type = NodeType.Camera }, 41 new TreeViewItemVM() { DisplayName = "Camera 14", Deepth = 2, HasChildren = false, ID = "16", Type = NodeType.Camera}, 42 new TreeViewItemVM() { DisplayName = "Camera 15", Deepth = 2, HasChildren = false, ID = "17", Type = NodeType.Camera}, 43 }}, 44 } 45 }; 46 47 InitTreeView(); 48 } 49 50 private ObservableCollection<TreeViewItemVM> selectedCameras = new ObservableCollection<TreeViewItemVM>(); 51 52 private TreeViewItemVM root; 53 public TreeViewItemVM Root 54 { 55 get 56 { 57 return this.root; 58 } 59 set 60 { 61 if (this.root != value) 62 { 63 this.root = value; 64 this.OnPropertyChanged(() => this.Root); 65 } 66 } 67 } 68 69 /// <summary> 70 /// 過濾字段 71 /// </summary> 72 private string filter; 73 public string Filter 74 { 75 get 76 { 77 return this.filter; 78 } 79 set 80 { 81 if (this.filter != value) 82 { 83 84 this.filter = value; 85 this.OnPropertyChanged(() => this.Filter); 86 87 this.Refresh(); 88 } 89 } 90 } 91 92 /// <summary> 93 /// View 94 /// </summary> 95 protected ICollectionView view; 96 public ICollectionView View 97 { 98 get 99 { 100 return this.view; 101 } 102 set 103 { 104 if (this.view != value) 105 { 106 this.view = value; 107 this.OnPropertyChanged(() => this.View); 108 } 109 } 110 } 111 112 /// <summary> 113 /// 刷新View 114 /// </summary> 115 public void Refresh() 116 { 117 if (this.View != null) 118 { 119 this.View.Refresh(); 120 } 121 } 122 123 private bool DoFilter(Object obj) 124 { 125 TreeViewItemVM item = obj as TreeViewItemVM; 126 if (item == null) 127 { 128 return true; 129 } 130 131 bool result = false; 132 foreach (var node in item.Children) 133 { 134 result = TreeItemDoFilter(node) || result; 135 } 136 137 return result || item.NameContains(this.Filter); 138 } 139 140 private bool TreeItemDoFilter(TreeViewItemVM vm) 141 { 142 if (vm == null) 143 { 144 return true; 145 } 146 147 bool result = false; 148 if (vm.Type == NodeType.Region || vm.Type == NodeType.Unit) 149 { 150 foreach (var item in vm.Children) 151 { 152 result = TreeItemDoFilter(item) || result; 153 } 154 } 155 156 if (result || vm.NameContains(this.Filter)) 157 { 158 result = true; 159 vm.Visible = System.Windows.Visibility.Visible; 160 } 161 else 162 { 163 vm.Visible = System.Windows.Visibility.Collapsed; 164 } 165 166 return result; 167 } 168 169 public void InitTreeView() 170 { 171 this.View = CollectionViewSource.GetDefaultView(this.Root.Children); 172 this.View.Filter = this.DoFilter; 173 this.Refresh(); 174 } 175 } 176 }
3.在界面添加一個TreeView,並添加一個簡單的Style,將ViewModel中必要數據進行綁定:
1 <Window x:Class="TreeViewDemo.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="MainWindow" Height="450" Width="525"> 5 <Window.Resources> 6 <Style x:Key="style" TargetType="{x:Type TreeViewItem}"> 7 <Setter Property="Template"> 8 <Setter.Value> 9 <ControlTemplate TargetType="{x:Type TreeViewItem}"> 10 <Grid Visibility="{Binding Visible}" Background="{Binding Background}"> 11 <ContentPresenter ContentSource="Header"/> 12 </Grid> 13 14 <ControlTemplate.Triggers> 15 <Trigger Property="IsSelected" Value="true"> 16 <Setter Property="Background" Value="Green"/> 17 </Trigger> 18 </ControlTemplate.Triggers> 19 </ControlTemplate> 20 </Setter.Value> 21 </Setter> 22 </Style> 23 </Window.Resources> 24 <Grid> 25 <Grid.RowDefinitions> 26 <RowDefinition Height="Auto"/> 27 <RowDefinition Height="*"/> 28 </Grid.RowDefinitions> 29 30 <TextBox x:Name="searchTxt" Width="200" HorizontalAlignment="Center" Height="40" 31 Margin="20" Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> 32 33 <TreeView 34 Grid.Row="1" 35 ItemsSource="{Binding View}"> 36 <TreeView.ItemTemplate> 37 <HierarchicalDataTemplate ItemContainerStyle ="{StaticResource style}" ItemsSource="{Binding Children}"> 38 <Grid Height="25" > 39 <TextBlock 40 x:Name="txt" 41 VerticalAlignment="Center" 42 Text="{Binding DisplayName}" 43 TextTrimming="CharacterEllipsis" 44 ToolTip="{Binding DisplayName}" /> 45 </Grid> 46 </HierarchicalDataTemplate> 47 </TreeView.ItemTemplate> 48 </TreeView> 49 </Grid> 50 </Window>
4.在給界面綁定具體的數據
1 using System.Windows; 2 3 namespace TreeViewDemo 4 { 5 /// <summary> 6 /// MainWindow.xaml 的交互邏輯 7 /// </summary> 8 public partial class MainWindow : Window 9 { 10 public MainWindow() 11 { 12 InitializeComponent(); 13 this.Loaded += MainWindow_Loaded; 14 } 15 16 void MainWindow_Loaded(object sender, RoutedEventArgs e) 17 { 18 this.DataContext = TreeViewViewModel.Instance; 19 } 20 } 21 }
5.運行結果:
可編輯樹Ztree的使用(包括對後臺數據庫的增刪改查)
找了不少網上關於Ztree的例子和代碼才搞定。
首先,關於Ztree的代碼不介紹了,網上下載以後,引用下列四個文件就能使用了。
1.關於配置選項。主要經過回調函數來實現向後臺發送數據,實現增刪改查。
1 var setting = { 2 view: { 3 addHoverDom: addHoverDom,//顯示操做圖標 4 removeHoverDom: removeHoverDom,//移除圖標 5 selectedMulti: false 6 }, 7 check: { 8 enable: true 9 }, 10 data: { 11 simpleData: { 12 enable: true 13 } 14 }, 15 callback: { 16 onRename: zTreeOnRename,//修改 17 onRemove: zTreeOnRemove,//刪除 18 onClick: zTreeOnClickRight, 19 20 }, 21 edit: { 22 enable: true, 23 showRemoveBtn: true, 24 showRenameBtn: true, 25 removeTitle: "刪除", 26 renameTitle: "修改" 27 } 28 };
$(document).ready(function () { $.ajax({ url: "GetZtreeJson", data: { ProjectId: "@ViewBag.ProjectId" }, type: "post", dataType: "json", success: function (data) { $.fn.zTree.init($("#test"), setting, data);//實現加載樹的方法 } }) $("#btnReturn").click(function () { window.parent.frameReturnByClose(); }); //$.fn.zTree.init($("#treeDemo"), setting, zNodes); }); var newCount = 1; function addHoverDom(treeId, treeNode) { var sObj = $("#" + treeNode.tId + "_span"); if (treeNode.editNameFlag || $("#addBtn_" + treeNode.tId).length > 0) return; var addStr = "<span class='button add' id='addBtn_" + treeNode.tId + "' title='add node' onfocus='this.blur();'></span>"; sObj.after(addStr); var btn = $("#addBtn_" + treeNode.tId); if (btn) btn.bind("click", function () { var zTree = $.fn.zTree.getZTreeObj("test"); //增長節點的方法 $.ajax({ url: "AddNode", data: { ParentId: treeNode.id }, type: "post", success: function (data) { if (data.message == "success") {
//此方法是js在前段增長節點方法,必定要經過後臺返回的id來增長,否則新增的節點會出現節點id爲null的問題 zTree.addNodes(treeNode, { id: data.id, ParentId: treeNode.id, name: "new node" + (newCount++) }); } else { $.messageBox5s('@Resource.Tip', "新增節點失敗!聯繫管理員!"); } } }) return false; }); };
//刪除節點 function zTreeOnRemove(event, treeId, treeNode) { { $.ajax({ url: "DeleteNode", type: "post", data: { NodeId: treeNode.id }, success: function (data) { if (data == "success") { } else { $.messageBox5s('@Resource.Tip', "刪除節點失敗!聯繫管理員!"); } } }) } } function removeHoverDom(treeId, treeNode) { $("#addBtn_" + treeNode.tId).unbind().remove(); }; //修改節點 function zTreeOnRename(event, treeId, treeNode) { $.ajax({ url: "EditNode", type: "post", data: { NodeId: $.trim(treeNode.id), name: treeNode.name }, success: function (data) { if (data != "success") { $.messageBox5s('@Resource.Tip', "修改節點失敗!聯繫管理員!"); } } }) }; // 樹的單擊事件 function zTreeOnClickRight(event, treeId, treeNode) { var treeid = treeNode.id; $("#hidId").val(treeid); $("#ifm").attr("src", "FileView?NodeId=" + treeid); } function treeShow(data) { zTreeObj = $.fn.zTree.init($("#test"), setting, data); zTreeObj.expandAll(true); }
2.後臺處理的方法。。我項目中是使用C#代碼寫的,mvc框架
1 [Description("項目資料-樹形")] 2 [KeyCode(Name = "Tree")] 3 [IsModule] 4 [SupportFilter(ActionName = "Tree")] 5 public ActionResult TreeIndex(Guid ProjectId) 6 { 7 8 ViewBag.ProjectId = ProjectId; 9 var ProjectCode = IProjectContract.GetProjectInputById(ProjectId).ProjectCode; 10 string count = ProjectResourceContract.CountNumber(ProjectCode); 11 ViewBag.Count = count; 12 return View(); 13 } 14 15 public JsonResult GetZtreeJson(Guid ProjectId) 16 { 17 var list = ProjectResourceContract.GetNodeJsonByProject(ProjectId); 18 19 20 return Json((from p in list 21 select new 22 { 23 id = p.Id, 24 pId = p.pid, 25 open = "true", 26 name = p.name 27 }).ToList()); 28 } 29 public JsonResult AddNode(string ParentId) 30 { 31 int sort = ProjectResourceContract.GetLastNodeSortFormParent(ParentId); 32 //string nodeid = ProjectResourceContract.GetCurrentNewNodeId(ParentId); 33 if (sort == 1) 34 { 35 var node = ProjectResourceContract.GetNodeByNodeId(ParentId); 36 node.type = "1"; 37 ProjectResourceContract.EditNode(ParentId, node.type); 38 } 39 Guid nodeId = Guid.NewGuid(); 40 var ProjectCode = ProjectResourceContract.GetNodeByNodeId(ParentId).ProjectCode; 41 var result = ProjectResourceContract.AddNode(new TB_Project_Nodes() 42 { 43 Id = nodeId, 44 name = "New Node" + sort, 45 type = "2", 46 pid = ParentId, 47 sort = sort, 48 state = "true", 49 ProjectCode = ProjectCode, 50 }); 51 if (result.Successed) 52 { 53 return Json(new { message = "success", id = nodeId }); 54 } 55 else 56 { 57 return Json("error"); 58 } 59 } 60 public JsonResult DeleteNode(string NodeId) 61 { 62 var result = ProjectResourceContract.DeleteNode(NodeId); 63 if (result.Successed) 64 { 65 return Json("success"); 66 } 67 else 68 { 69 return Json("error"); 70 } 71 } 72 public JsonResult EditNode(string NodeId, string name, string path = "", string ProjectCode = "") 73 { 74 OperationResult result; 75 if (!string.IsNullOrEmpty(path)) 76 { 77 path = path.TrimEnd('+'); 78 79 path = "UpDir/" + ProjectCode + "/施工資料/" + path; 80 result = ProjectResourceContract.EditNode(NodeId, name, path); 81 } 82 else 83 { 84 result = ProjectResourceContract.EditNode(NodeId, name, ""); 85 } 86 if (result.Successed) 87 { 88 return Json("success"); 89 } 90 else 91 { 92 return Json("error"); 93 } 94 }
我項目中的狀況是須要用ztree來實現建立目錄,而後上傳施工資料的方法。因此,projectid字段,你們不須要在乎。是項目的id
3.給你們看一下個人數據庫字段設計,附上關於增刪改查操做數據庫的代碼。
這裏頂級節點的pid爲0,其次projectcode是我項目中使用到的,能夠不用考慮。state原本是用於ztree中open配置信息用的。默認展開節點的配置。
type是我用於判斷此節點是否包是文件節點用的。(包含子節點的爲文件夾節點,沒有子節點的就是文件節點)
4.serveices代碼
1 //獲取樹全部節點顯示 2 public List<TB_Project_Nodes> GetNodeJsonByProject(Guid ProjectId) 3 { 4 5 6 var project = ProjectsRepository.GetByKey(ProjectId); 7 string TopNode = project.ProjectCode; 8 List<TB_Project_Nodes> ALLLIST = NodesRepository.Entities.Where(t => t.ProjectCode == TopNode).ToList(); 9 //var top = NodesRepository.Entities.FirstOrDefault(t => t.Id.ToString() == TopNode); 10 TB_Project_Nodes top = ALLLIST.FirstOrDefault(t => t.ProjectCode == TopNode&&t.pid=="0"); 11 if (top == null)//第一次建立 12 { 13 TB_Project_Nodes pn = new TB_Project_Nodes() { ProjectCode = TopNode, Id = Guid.NewGuid(), type = "1", pid = "0", sort = 1, name = project.ProjectName, state = "true" }; 14 NodesRepository.Insert(pn); 15 return new List<TB_Project_Nodes>() { pn }; 16 } 17 else//存在頂級節點 18 { 19 20 //List<TB_Project_Nodes> nodes = NodesRepository.Entities.Where(t => t.pid == TopNode).OrderBy(t => t.sort).ToList();//頂級節點下面的一級節點 21 List<TB_Project_Nodes> nodes = ALLLIST.Where(t => t.pid == top.Id.ToString()).OrderBy(t => t.sort).ToList();//頂級節點下面的一級節點 22 23 if (nodes.Count <= 0)//沒有子節點 24 { 25 return new List<TB_Project_Nodes>() { top }; 26 } 27 else//存在子節點,遍歷全部的子節點 28 { 29 30 List<TB_Project_Nodes> LIST = new List<TB_Project_Nodes>(); 31 LIST.Add(top); 32 LIST.AddRange(nodes); //添加一級節點 33 34 LIST = Test(nodes, LIST, ALLLIST); 35 36 return LIST; 37 } 38 39 } 40 41 } 42 //遞歸函數---把全部二級節點以及如下全部節點獲取 43 public List<TB_Project_Nodes> Test(List<TB_Project_Nodes> list, List<TB_Project_Nodes> LIST, List<TB_Project_Nodes> ALLLIST) 44 { 45 List<TB_Project_Nodes> NEWLIST = new List<TB_Project_Nodes>(); 46 foreach (var item2 in list) 47 { 48 var secondNodes = ALLLIST.Where(t => t.pid == item2.Id.ToString()).OrderBy(t => t.sort).ToList(); 49 if (secondNodes.Count > 0) 50 { 51 52 NEWLIST.AddRange(secondNodes); 53 } 54 } 55 LIST.AddRange(NEWLIST); 56 //已經添加完本級節點 57 //添加下一級節點 58 if (NEWLIST.Count > 0) 59 { 60 Test(NEWLIST, LIST, ALLLIST); 61 } 62 return LIST; 63 } 64 //增長節點信息 65 public OperationResult AddNode(TB_Project_Nodes node) 66 { 67 68 int result = NodesRepository.Insert(node); 69 if (result == 0) 70 { 71 return new OperationResult(OperationResultType.Error, "error"); 72 } 73 else 74 { 75 return new OperationResult(OperationResultType.Success, "success"); 76 } 77 78 } 79 /// <summary> 80 /// 修改節點類型 81 /// </summary> 82 /// <param name="NodeId"></param> 83 /// <param name="type"></param> 84 /// <returns></returns> 85 public OperationResult EditNode(string NodeId, string type) 86 { 87 var node = NodesRepository.Entities.FirstOrDefault(t => t.Id.ToString() == NodeId); 88 node.type = type; 89 int result = NodesRepository.Update(node); 90 if (result == 0) 91 { 92 return new OperationResult(OperationResultType.Error, "error"); 93 } 94 else 95 { 96 return new OperationResult(OperationResultType.Success, "success"); 97 } 98 } 99 100 /// <summary> 101 /// 獲取父級節點下面最大的一個排序+1 102 /// </summary> 103 /// <param name="ParentId"></param> 104 /// <returns></returns> 105 106 public int GetLastNodeSortFormParent(string ParentId) 107 { 108 var list = NodesRepository.Entities.Where(t => t.pid == ParentId).OrderByDescending(t => t.sort).ToList(); 109 if (list.Count <= 0) 110 { 111 return 1; 112 } 113 else 114 { 115 return list[0].sort + 1; 116 } 117 } 118 119 /// <summary> 120 /// 刪除此節點時候,要把下面全部子節點刪除 121 /// </summary> 122 /// <param name="NodeId"></param> 123 /// <returns></returns> 124 public OperationResult DeleteNode(string NodeId) 125 { 126 List<TB_Project_Nodes> ALLLIST = NodesRepository.Entities.ToList(); 127 // var node = NodesRepository.Entities.FirstOrDefault(t => t.Id.ToString() == NodeId); 128 var node = ALLLIST.FirstOrDefault(T => T.Id.ToString() == NodeId); 129 //獲取下面的全部子節點 130 List<TB_Project_Nodes> LIST = new List<TB_Project_Nodes>(); 131 LIST.Add(node); 132 var list = ALLLIST.Where(t => t.pid == NodeId).ToList(); 133 if (list.Count > 0) 134 { 135 LIST.AddRange(list); 136 LIST = Test(list, LIST, ALLLIST); 137 } 138 for (int i = LIST.Count - 1; i >= 0; i--) 139 { 140 try 141 { 142 int result = NodesRepository.Delete(LIST[i].Id); 143 } 144 catch (Exception ex) 145 { 146 return new OperationResult(OperationResultType.Error, "error"); 147 throw ex; 148 } 149 150 } 151 152 return new OperationResult(OperationResultType.Success, "success"); 153 154 }
今天寫一個wpf的demo,用到綁定數據,給控件綁定了數據源,可是數據卻沒有顯示出來,排查代碼發現綁定數據源的的成員用的是字段不是屬性。
前端代碼:
<Grid> <StackPanel Grid.Row="2" Margin="10"> <ListBox x:Name="listBox" Height="100"> </ListBox> </StackPanel> </Grid>
後臺代碼:
public Window3() { InitializeComponent(); List<Employe> list = new List<Employe>() { new Employe() { name="jack",age=18}, new Employe() { name="bob",age=20}, new Employe() { name="alice",age=21} }; listBox.ItemsSource = list; listBox.DisplayMemberPath = "name"; listBox.SelectedValuePath = "age"; }
//實體 public class Employe { public string name { get; set; } public int age { get; set; } }
若是把Employe的name,去掉{get;set;},改成一個字段, public string name;數據就沒法綁定了。緣由是屬性的訪問是由訪問器完成的,於是屬性能夠進行數據綁定。
網上的文章有不少,可是好些沒說到重點,基本都是說屬性能夠保護數據安全云云之類,整理了一下,有一下幾個區別:
相同點:
都是類的成員,屬性是類的屬性,而字段是類的數據成員
不一樣點:
1 屬性可進行數據綁定
2 屬性可經過set和get方法進行數據安全性檢驗,而字段不行
3 屬性可進行線程同步
public string Name
{
set{
lock(this)
{
}
}
}
4 屬性能夠是抽象的,而字段不行
5 屬性能夠接口的形式表現
6 基於屬性的索引
7 不要直接把字段轉化爲屬性
MSDN:
屬性與字段
屬性與字段均可在對象中存儲和檢索信息。它們的類似性使得在給定狀況下很難肯定哪一個是更好的編程選擇。
在如下狀況下使用屬性過程:
1. 須要控制設置或檢索值的時間和方式時。
2. 屬性有定義完善的一組值須要進行驗證時。
3. 設置值致使對象的狀態發生某些明顯的變化(如 IsVisible 屬性)。
4. 設置屬性會致使更改其餘內部變量或其餘屬性的值時。
5.必須先執行一組步驟,而後才能設置或檢索屬性時。
在如下狀況下使用字段:
1. 值爲自驗證類型時。例如,若是將 True 或 False 之外的值賦給 Boolean 變量,就會發生錯誤或自動數據轉換。
2. 在數據類型所支持範圍內的任何值均有效時。Single 或 Double 類型的不少屬性屬於這種狀況。
3. 屬性是 String 數據類型,且對於字符串的大小或值沒有任何約束時
C# 遍歷Dictionary並修改其中的Value
C#的Dictionary類型的值,知道key後,value能夠修改嗎?答案是確定能修改的。我在遍歷的過程當中能夠修改Value嗎?答案是也是確定能修改的,可是不能用For each循環。不然會報如下的Exception.
System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'
之因此會報Exception是For each自己的問題,和Dictionary不要緊。For each循環不能改變集合中各項的值,若是須要迭代並改變集合項中的值,請用For循環。
你們來看下例子:
1 // defined the Dictionary variable 2 Dictionary<int, string> td = new Dictionary<int, string>(); 3 td.Add(1, "str1"); 4 td.Add(2, "str2"); 5 td.Add(3, "str3"); 6 td.Add(4, "str4"); 7 // test for 8 TestForDictionary(td); 9 // test for each 10 TestForEachDictionary(td);
TestForDictionary Code
1 static void TestForDictionary(Dictionary<int, string> paramTd) 2 { 3 4 for (int i = 1;i<= paramTd.Keys.Count;i++) 5 { 6 paramTd[i] = "string" + i; 7 Console.WriteLine(paramTd[i]); 8 } 9 }
TestForDictionary的執行結果
string1 string2 string3 string4
TestForEachDictionary Code
1 static void TestForEachDictionary(Dictionary<int, string> paramTd) 2 { 3 int forEachCnt = 1; 4 foreach (KeyValuePair<int,string> item in paramTd)//System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.' 5 { 6 paramTd[item.Key] = "forEach" + forEachCnt; 7 Console.WriteLine(paramTd[item.Key]); 8 forEachCnt += 1; 9 } 10 }
TestForEachDictionary裏的For each會在循環第二次的時候報錯,也就是說它會在窗口中打印出「forEach1」後斷掉。
學習筆記——異步
1.異步同步的定義
同步方法:多個任務一個一個執行,同一時刻系統中只有一個任務在執行
異步方法:發起一個調用,並不等着計算結束,而是直接去運行下一行;剛纔的計算,會啓動一個新的線程去執行
2.異步同步的比較
2.1. 同步方法卡界面,由於UI線程忙於計算;異步多線程方法不卡界面,主線程閒置,計算任務交給子線程在作;
2.2. 同步方法慢,只有一個線程計算;異步多線程方法快,多個線程併發計算;
多線程的資源消耗更多,線程並非越多越好(資源有限/管理線程也消耗資源)
2.3. 異步多線程是無序的:啓動無序 執行時間不肯定 結束無序,,
因此不要試圖經過啓動順序或者時間等待來控制流程
3.異步如何控制執行順序
3.1.回調
//IasyncResult,可用於監視調用進度
//DoSomethingLong方法名稱(要執行的操做)
Action<string> act = this.DoSomethingLong;
IAsyncResult iAsyncResult = null;
AsyncCallback callback = ar =>
{
// Console.WriteLine(object.ReferenceEquals(ar, iAsyncResult));
Console.WriteLine(ar.AsyncState);
Console.WriteLine($"這裏是BeginInvoke調用完成以後才執行的。。。{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
};
iAsyncResult = act.BeginInvoke("btnAsync_Click", callback, "異步學習");
3.2.等待
//IAsyncResult.IsCompeted肯定異步調用什麼時候完成(輪詢)
Action<string> act = this.DoSomethingLong;
IAsyncResult iAsyncResult = act.BeginInvoke("btnAsync_Click", null, null);
int i = 1;
while (!iAsyncResult.IsCompleted)//1 卡界面,主線程在等待 2 邊等待邊作事兒 3有偏差(時間)
{
if (i < 10)
{
Console.WriteLine($"文件上傳{i++ * 10}%。。。請等待");
}
else
{
Console.WriteLine("已完成99%。。。立刻結束");
}
Thread.Sleep(200);//偏差時間
}
Console.WriteLine("文件上傳成功!!!");
3.3.狀態
//使用 IAsyncResult.AsyncWaitHandle 獲取 WaitHandle,使用它的 WaitOne 方法將執行一直阻塞到發出 //WaitHandle 信號,而後調用
Action<string> act = this.DoSomethingLong;
IAsyncResult iAsyncResult = act.BeginInvoke("btnAsync_Click", null, null);
//異步變同步(狀態)
iAsyncResult.AsyncWaitHandle.WaitOne();//一直等待任務完成,第一時間進入下一行
iAsyncResult.AsyncWaitHandle.WaitOne(-1);//一直等待任務完成,第一時間進入下一行
//最多等待1000ms,超時控制
iAsyncResult.AsyncWaitHandle.WaitOne(1000);//最多等待1000ms,不然就進入下一行,能夠作一些超時控制
//調用 BeginInvoke 後可隨時調用 EndInvoke 方法;若是異步調用未完成,EndInvoke 將一直阻塞到
//異步調用完成。
act.EndInvoke(iAsyncResult);//等待
4.其餘相關知識
任何的異步多線程,都是跟委託相關。委託中的Invoke方法 是同步的。BeginInvoke開始一個異步的請求,調用線程池中一個線程來執行。
4.1.異步獲取返回值
Func<int, string> func = i => i.ToString();
IAsyncResult iAsyncResult = func.BeginInvoke(DateTime.Now.Year, ar =>
{
string resultIn = func.EndInvoke(ar);//對於每一個異步操做,只能調用一次 EndInvoke。
Console.WriteLine($"This is {ar.AsyncState} 的異步調用結果 {resultIn}");
}, "異步參數");
//string result = func.EndInvoke(iAsyncResult);//獲取返回值