TreeCategory.xaml.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.ObjectModel;
  5. using System.Reflection;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using System.Windows.Input;
  9. using FWindSoft.Wpf;
  10. namespace FWindSoft.Wpf.Controls
  11. {
  12. /// <summary>
  13. /// TreeCategory.xaml 的交互逻辑
  14. /// </summary>
  15. public partial class TreeCategory : UserControl
  16. {
  17. public TreeCategory()
  18. {
  19. InitializeComponent();
  20. }
  21. }
  22. public partial class TreeCategory
  23. {
  24. /*
  25. * 绑定数据源必须存在Name的属性
  26. * 直接和界面中属性关联的 使用绑定进行关联
  27. * 如果公开的属性和界面中用到的属性经过中间处理,则通过使用空间名字的方法进行赋值
  28. */
  29. #region 依赖属性定义
  30. public static readonly DependencyProperty ItemsProperty;
  31. public static readonly DependencyProperty DisplayPathProperty;
  32. public static readonly DependencyProperty ChildrenPathProperty;
  33. #endregion
  34. #region 定义命令
  35. private static RoutedCommand m_SelectCommand;
  36. public static RoutedCommand SelectCommand
  37. {
  38. get { return m_SelectCommand; }
  39. }
  40. #endregion
  41. #region 定义事件
  42. public static readonly RoutedEvent SelectChangedEvent = EventManager.RegisterRoutedEvent("SelectChanged",
  43. RoutingStrategy.Direct, typeof(RoutedPropertyChangedEventHandler<object>), typeof(TreeCategory));
  44. #endregion
  45. static TreeCategory()
  46. {
  47. ItemsProperty = DependencyProperty.Register("Items", typeof(IEnumerable), typeof(TreeCategory), new PropertyMetadata(default(IEnumerable), new PropertyChangedCallback(PropertyChangedCallback)));
  48. DisplayPathProperty = DependencyProperty.Register("DisplayPath", typeof(string), typeof(TreeCategory), new PropertyMetadata(default(string), new PropertyChangedCallback(PropertyChangedCallback)));
  49. ChildrenPathProperty = DependencyProperty.Register("ChildrenPath", typeof(string), typeof(TreeCategory), new PropertyMetadata(default(string), new PropertyChangedCallback(PropertyChangedCallback)));
  50. m_SelectCommand = new RoutedCommand("SelectCommand", typeof(TreeCategory));
  51. System.Windows.Input.CommandManager.RegisterClassCommandBinding(typeof(TreeCategory), new CommandBinding(m_SelectCommand, OnSelectCommand, CanExecuteRoutedEventHandler));
  52. HeightProperty.OverrideMetadata(typeof(TreeCategory), new FrameworkPropertyMetadata(120d, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(HeightPropertyChangedCallback)));
  53. }
  54. #region 依赖属性封装
  55. /// <summary>
  56. /// 控件包好项目
  57. /// </summary>
  58. public IEnumerable Items
  59. {
  60. get { return (IEnumerable)GetValue(ItemsProperty); }
  61. set { SetValue(ItemsProperty, value); }
  62. }
  63. /// <summary>
  64. /// 类型显示字段
  65. /// </summary>
  66. public string DisplayPath
  67. {
  68. get { return (string)GetValue(DisplayPathProperty); }
  69. set { SetValue(DisplayPathProperty, value); }
  70. }
  71. /// <summary>
  72. /// 控件选中项目
  73. /// </summary>
  74. public IList SelectItems { get; private set; }
  75. /// <summary>
  76. /// 子集合路径
  77. /// </summary>
  78. public string ChildrenPath
  79. {
  80. get { return (string)GetValue(ChildrenPathProperty); }
  81. set { SetValue(ChildrenPathProperty, value); }
  82. }
  83. #endregion
  84. #region 事件封装
  85. public event RoutedPropertyChangedEventHandler<object> SelectChanged
  86. {
  87. add { this.AddHandler(SelectChangedEvent, value); }
  88. remove { this.RemoveHandler(SelectChangedEvent, value); }
  89. }
  90. #endregion
  91. #region 相关静态方法
  92. #region 属性变化与界面关联
  93. private static void HeightPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
  94. {
  95. TreeCategory section = sender as TreeCategory;
  96. if (section != null)
  97. {
  98. section.tree.Height = (double)args.NewValue;
  99. }
  100. }
  101. private static void PropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
  102. {
  103. TreeCategory tree = sender as TreeCategory;
  104. if (tree != null)
  105. {
  106. string propeertyName = args.Property.Name;
  107. switch (propeertyName)
  108. {
  109. case "Items":
  110. {
  111. object tempObject = args.NewValue;
  112. if (tempObject == null)
  113. {
  114. tree.DisplayItemsSource = null;
  115. tree.SelectItems = null;
  116. tree.tree.ItemsSource = null;
  117. break;
  118. }
  119. var type = tempObject.GetType().GetInterface(typeof(IEnumerable).Name);
  120. if (type == null)
  121. {
  122. throw new Exception("数据源需要实现IEnumerable接口");
  123. }
  124. tree.InitSource((IEnumerable)tempObject,tree.DisplayPath, tree.ChildrenPath);
  125. break;
  126. }
  127. case "ChildrenPath":
  128. {
  129. string tempObject = args.NewValue as string;
  130. if (tree.Items == null)
  131. break;
  132. tree.InitSource(tree.Items, tree.DisplayPath, tempObject);
  133. break;
  134. }
  135. case "DisplayPath":
  136. {
  137. string tempObject = args.NewValue as string;
  138. if (tree.Items == null)
  139. break;
  140. tree.InitSource(tree.Items, tempObject, tree.ChildrenPath);
  141. break;
  142. }
  143. }
  144. }
  145. }
  146. #endregion
  147. #endregion
  148. #region 命令处理
  149. private static void OnSelectCommand(object sender, ExecutedRoutedEventArgs e)
  150. {
  151. TreeCategory list = sender as TreeCategory;
  152. if (list != null)
  153. {
  154. InnerCheckItem item = e.Parameter as InnerCheckItem;
  155. if (item != null)
  156. {
  157. item.CheckedChanged();
  158. }
  159. list.ChangeSelectItems();
  160. }
  161. }
  162. private static void CanExecuteRoutedEventHandler(object sender, CanExecuteRoutedEventArgs e)
  163. {
  164. e.CanExecute = true;
  165. }
  166. #endregion
  167. #region 私有属性
  168. private ObservableCollection<InnerCheckItem> DisplayItemsSource { get; set; }
  169. #endregion
  170. #region 类型私有方法集合
  171. private void InitSource(IEnumerable source,string namePath, string childPath)
  172. {
  173. /*
  174. * 这种分两种情况考虑递归的方式,是因为,最顶层可能不存在根节点。所以顶层数据结构需要特殊处理
  175. */
  176. if (source == null)
  177. {
  178. this.DisplayItemsSource = null;
  179. return;
  180. }
  181. childPath = childPath ?? "";
  182. IEnumerator enumerator = ((IEnumerable)source).GetEnumerator();
  183. try
  184. {
  185. var tempSource = new ObservableCollection<InnerCheckItem>();
  186. while (enumerator.MoveNext())
  187. {
  188. var tempCurrent = enumerator.Current;
  189. if (tempCurrent != null)
  190. {
  191. Type type = tempCurrent.GetType();
  192. PropertyInfo propertyInfo = type.GetProperty(childPath);
  193. if (propertyInfo == null || propertyInfo.PropertyType.GetInterface(typeof(IEnumerable).Name) == null)
  194. {
  195. tempSource.Add(new InnerCheckItem(tempCurrent, namePath));
  196. }
  197. else
  198. {
  199. InnerCheckItemCollection tempCollection;
  200. tempSource.Add(tempCollection = new InnerCheckItemCollection(tempCurrent, namePath));
  201. object children = propertyInfo.GetValue(tempCurrent, null);
  202. IEnumerator enumeratorCollection = ((IEnumerable)children).GetEnumerator();
  203. try
  204. {
  205. while (enumeratorCollection.MoveNext())
  206. {
  207. var tempCurrentCollection = enumeratorCollection.Current;
  208. if (tempCurrentCollection != null)
  209. {
  210. InitSource(tempCollection, tempCurrentCollection, namePath, childPath);
  211. }
  212. }
  213. }
  214. catch (Exception)
  215. {
  216. }
  217. finally
  218. {
  219. enumeratorCollection.Reset();
  220. }
  221. }
  222. }
  223. }
  224. this.DisplayItemsSource = tempSource;
  225. }
  226. finally
  227. {
  228. enumerator.Reset();
  229. }
  230. this.tree.ItemsSource = this.DisplayItemsSource;
  231. this.SelectItems = null;
  232. }
  233. private void InitSource(InnerCheckItemCollection collection, object obj,string namePath, string childPath)
  234. {
  235. Type type = obj.GetType();
  236. PropertyInfo propertyInfo = type.GetProperty(childPath);
  237. if (propertyInfo == null || propertyInfo.PropertyType.GetInterface(typeof(IEnumerable).Name) == null)
  238. {
  239. collection.AddItem(new InnerCheckItem(obj, namePath));
  240. }
  241. else
  242. {
  243. InnerCheckItemCollection tempCollection;
  244. collection.AddItem(tempCollection = new InnerCheckItemCollection(obj, namePath));
  245. IEnumerator enumerator = ((IEnumerable)obj).GetEnumerator();
  246. while (enumerator.MoveNext())
  247. {
  248. var tempCurrent = enumerator.Current;
  249. if (tempCurrent != null)
  250. {
  251. InitSource(tempCollection, tempCurrent,namePath, childPath);
  252. }
  253. }
  254. }
  255. }
  256. #endregion
  257. #region 公开selectChange事件
  258. private void ChangeSelectItems()
  259. {
  260. List<object> tempObjects = new List<object>();
  261. List<InnerCheckItem> tempSource=new List<InnerCheckItem>(this.DisplayItemsSource);
  262. for (int i = 0; i < tempSource.Count; i++)
  263. {
  264. InnerCheckItem tempItem = tempSource[i];
  265. if (tempItem is InnerCheckItemCollection)
  266. {
  267. tempSource.AddRange(((InnerCheckItemCollection)tempItem).Items);
  268. continue;
  269. }
  270. if (tempItem.IsChecked)
  271. {
  272. tempObjects.Add(tempItem.RefObject);
  273. continue;
  274. }
  275. }
  276. object oldValue = this.SelectItems;
  277. this.SelectItems = tempObjects;
  278. OnSelectChanged(oldValue, this.SelectItems);
  279. }
  280. protected virtual void OnSelectChanged(object oldValue, object newValue)
  281. {
  282. RoutedPropertyChangedEventArgs<object> arg = new RoutedPropertyChangedEventArgs<object>(oldValue, newValue, SelectChangedEvent);
  283. this.RaiseEvent(arg);
  284. }
  285. #endregion
  286. #region 选择实例方法
  287. /// <summary>
  288. /// 获取所有叶子节点
  289. /// </summary>
  290. /// <returns></returns>
  291. private List<InnerCheckItem> GetAllLeaf()
  292. {
  293. List<InnerCheckItem> leaves = new List<InnerCheckItem>();
  294. if (this.DisplayItemsSource == null)
  295. {
  296. return leaves;
  297. }
  298. foreach (var innerCheckItem in DisplayItemsSource)
  299. {
  300. GetAllLeaf(leaves, innerCheckItem);
  301. }
  302. return leaves;
  303. }
  304. private void GetAllLeaf(List<InnerCheckItem> list, InnerCheckItem item)
  305. {
  306. if (item == null) return;
  307. InnerCheckItemCollection tempCollection = item as InnerCheckItemCollection;
  308. if (tempCollection != null)
  309. {
  310. foreach (InnerCheckItem innerCheckItem in tempCollection.Items)
  311. {
  312. GetAllLeaf(list, innerCheckItem);
  313. }
  314. return;
  315. }
  316. list.Add(item);
  317. }
  318. /// <summary>
  319. /// 设置选中项
  320. /// </summary>
  321. /// <param name="o"></param>
  322. public void SetSelected(object o)
  323. {
  324. var allLeaf = GetAllLeaf();
  325. foreach (var innerCheckItem in allLeaf)
  326. {
  327. if (innerCheckItem.RefObject.Equals(o))
  328. {
  329. innerCheckItem.IsChecked = true;
  330. }
  331. }
  332. allLeaf.ForEach(leaf => leaf.CheckedChanged());
  333. }
  334. /// <summary>
  335. /// 设定选中元素
  336. /// </summary>
  337. /// <typeparam name="T"></typeparam>
  338. /// <param name="o"></param>
  339. /// <param name="compare"></param>
  340. public void SetSelected<T>(T o, IEqualityComparer<T> compare) where T : class
  341. {
  342. var allLeaf = GetAllLeaf();
  343. foreach (var innerCheckItem in allLeaf)
  344. {
  345. T tempRef = innerCheckItem.RefObject as T;
  346. if (compare != null && tempRef != null && compare.Equals(tempRef, o))
  347. {
  348. innerCheckItem.IsChecked = true;
  349. }
  350. }
  351. allLeaf.ForEach(leaf => leaf.CheckedChanged());
  352. }
  353. /// <summary>
  354. /// 设置选中集合
  355. /// </summary>
  356. /// <param name="selectList"></param>
  357. /// <param name="compare"></param>
  358. public void SetSelected<T>(IList<T> selectList, IEqualityComparer<T> compare) where T : class
  359. {
  360. var allLeaf = GetAllLeaf();
  361. foreach (var innerCheckItem in DisplayItemsSource)
  362. {
  363. T tempRef = innerCheckItem.RefObject as T;
  364. if (tempRef == null)
  365. continue;
  366. foreach (T o in selectList)
  367. {
  368. if (compare != null && compare.Equals(tempRef, o))
  369. {
  370. innerCheckItem.IsChecked = true;
  371. }
  372. }
  373. }
  374. allLeaf.ForEach(leaf => leaf.CheckedChanged());
  375. }
  376. /// <summary>
  377. /// 设置选中集合
  378. /// </summary>
  379. /// <param name="selectList"></param>
  380. public void SetSelected(IList selectList)
  381. {
  382. var allLeaf = GetAllLeaf();
  383. foreach (var innerCheckItem in DisplayItemsSource)
  384. {
  385. if (selectList.Contains(innerCheckItem.RefObject))
  386. {
  387. innerCheckItem.IsChecked = true;
  388. }
  389. }
  390. allLeaf.ForEach(leaf => leaf.CheckedChanged());
  391. }
  392. private void SetAllElementChecked(bool isChecked,bool turnChecked=false)
  393. {
  394. bool flag = isChecked;
  395. if (DisplayItemsSource == null)
  396. return;
  397. var tempSource = new List<InnerCheckItem>(this.DisplayItemsSource);
  398. for (int i = 0; i < tempSource.Count; i++)
  399. {
  400. var tempItem = tempSource[i];
  401. if (isChecked)
  402. {
  403. flag = !tempItem.IsChecked;
  404. }
  405. tempItem.IsChecked = flag;
  406. InnerCheckItemCollection collection = tempItem as InnerCheckItemCollection;
  407. if (collection != null)
  408. {
  409. tempSource.AddRange(collection.Items);
  410. }
  411. }
  412. }
  413. /// <summary>
  414. /// 选中所有元素
  415. /// </summary>
  416. public void CheckAll()
  417. {
  418. SetAllElementChecked(true);
  419. ChangeSelectItems();
  420. }
  421. /// <summary>
  422. /// 去除所有选中元素
  423. /// </summary>
  424. public void CheckNone()
  425. {
  426. SetAllElementChecked(false);
  427. ChangeSelectItems();
  428. }
  429. /// <summary>
  430. /// 反选
  431. /// </summary>
  432. public void CheckTurn()
  433. {
  434. SetAllElementChecked(false,true);
  435. var allLeaf = GetAllLeaf();
  436. allLeaf.ForEach(leaf => leaf.CheckedChanged());
  437. ChangeSelectItems();
  438. }
  439. #endregion
  440. }
  441. }