ItemsControl.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Copyright (c) The Perspex Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections;
  5. using System.Collections.ObjectModel;
  6. using System.Collections.Specialized;
  7. using System.Diagnostics.CodeAnalysis;
  8. using System.Linq;
  9. using Perspex.Collections;
  10. using Perspex.Controls.Generators;
  11. using Perspex.Controls.Presenters;
  12. using Perspex.Controls.Primitives;
  13. using Perspex.Controls.Templates;
  14. using Perspex.Controls.Utils;
  15. using Perspex.Styling;
  16. namespace Perspex.Controls
  17. {
  18. /// <summary>
  19. /// Displays a collection of items.
  20. /// </summary>
  21. public class ItemsControl : TemplatedControl, IReparentingHost
  22. {
  23. /// <summary>
  24. /// The default value for the <see cref="ItemsPanel"/> property.
  25. /// </summary>
  26. [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "Needs to be before or a NullReferenceException is thrown.")]
  27. private static readonly FuncTemplate<IPanel> DefaultPanel =
  28. new FuncTemplate<IPanel>(() => new StackPanel());
  29. /// <summary>
  30. /// Defines the <see cref="Items"/> property.
  31. /// </summary>
  32. public static readonly PerspexProperty<IEnumerable> ItemsProperty =
  33. PerspexProperty.Register<ItemsControl, IEnumerable>(nameof(Items));
  34. /// <summary>
  35. /// Defines the <see cref="ItemsPanel"/> property.
  36. /// </summary>
  37. public static readonly PerspexProperty<ITemplate<IPanel>> ItemsPanelProperty =
  38. PerspexProperty.Register<ItemsControl, ITemplate<IPanel>>(nameof(ItemsPanel), DefaultPanel);
  39. /// <summary>
  40. /// Defines the <see cref="MemberSelector"/> property.
  41. /// </summary>
  42. public static readonly PerspexProperty<IMemberSelector> MemberSelectorProperty =
  43. PerspexProperty.Register<ItemsControl, IMemberSelector>(nameof(MemberSelector));
  44. private IItemContainerGenerator _itemContainerGenerator;
  45. /// <summary>
  46. /// Initializes static members of the <see cref="ItemsControl"/> class.
  47. /// </summary>
  48. static ItemsControl()
  49. {
  50. ItemsProperty.Changed.AddClassHandler<ItemsControl>(x => x.ItemsChanged);
  51. }
  52. /// <summary>
  53. /// Initializes a new instance of the <see cref="ItemsControl"/> class.
  54. /// </summary>
  55. public ItemsControl()
  56. {
  57. Classes.Add(":empty");
  58. Items = new PerspexList<object>();
  59. }
  60. /// <summary>
  61. /// Gets the <see cref="IItemContainerGenerator"/> for the control.
  62. /// </summary>
  63. public IItemContainerGenerator ItemContainerGenerator
  64. {
  65. get
  66. {
  67. if (_itemContainerGenerator == null)
  68. {
  69. _itemContainerGenerator = CreateItemContainerGenerator();
  70. }
  71. return _itemContainerGenerator;
  72. }
  73. }
  74. /// <summary>
  75. /// Gets or sets the items to display.
  76. /// </summary>
  77. public IEnumerable Items
  78. {
  79. get { return GetValue(ItemsProperty); }
  80. set { SetValue(ItemsProperty, value); }
  81. }
  82. /// <summary>
  83. /// Gets or sets the panel used to display the items.
  84. /// </summary>
  85. public ITemplate<IPanel> ItemsPanel
  86. {
  87. get { return GetValue(ItemsPanelProperty); }
  88. set { SetValue(ItemsPanelProperty, value); }
  89. }
  90. /// <summary>
  91. /// Selects a member from <see cref="Items"/> to use as the list item.
  92. /// </summary>
  93. public IMemberSelector MemberSelector
  94. {
  95. get { return GetValue(MemberSelectorProperty); }
  96. set { SetValue(MemberSelectorProperty, value); }
  97. }
  98. /// <summary>
  99. /// Gets the items presenter control.
  100. /// </summary>
  101. public IItemsPresenter Presenter
  102. {
  103. get;
  104. set;
  105. }
  106. /// <inheritdoc/>
  107. IPerspexList<ILogical> IReparentingHost.LogicalChildren => LogicalChildren;
  108. /// <summary>
  109. /// Asks the control whether it wants to reparent the logical children of the specified
  110. /// control.
  111. /// </summary>
  112. /// <param name="control">The control.</param>
  113. /// <returns>
  114. /// True if the control wants to reparent its logical children otherwise false.
  115. /// </returns>
  116. bool IReparentingHost.WillReparentChildrenOf(IControl control)
  117. {
  118. return control is IItemsPresenter && control.TemplatedParent == this;
  119. }
  120. /// <summary>
  121. /// Creates the <see cref="ItemContainerGenerator"/> for the control.
  122. /// </summary>
  123. /// <returns>An <see cref="IItemContainerGenerator"/>.</returns>
  124. protected virtual IItemContainerGenerator CreateItemContainerGenerator()
  125. {
  126. return new ItemContainerGenerator(this);
  127. }
  128. /// <inheritdoc/>
  129. protected override void OnTemplateApplied()
  130. {
  131. Presenter = this.FindTemplateChild<IItemsPresenter>("itemsPresenter");
  132. }
  133. /// <summary>
  134. /// Caled when the <see cref="Items"/> property changes.
  135. /// </summary>
  136. /// <param name="e">The event args.</param>
  137. protected virtual void ItemsChanged(PerspexPropertyChangedEventArgs e)
  138. {
  139. var incc = e.OldValue as INotifyCollectionChanged;
  140. if (incc != null)
  141. {
  142. incc.CollectionChanged += ItemsCollectionChanged;
  143. }
  144. var newValue = e.NewValue as IEnumerable;
  145. if (newValue == null || newValue.Count() == 0)
  146. {
  147. Classes.Add(":empty");
  148. }
  149. else
  150. {
  151. Classes.Remove(":empty");
  152. }
  153. incc = newValue as INotifyCollectionChanged;
  154. if (incc != null)
  155. {
  156. incc.CollectionChanged += ItemsCollectionChanged;
  157. }
  158. }
  159. /// <summary>
  160. /// Called when the <see cref="INotifyCollectionChanged.CollectionChanged"/> event is
  161. /// raised on <see cref="Items"/>.
  162. /// </summary>
  163. /// <param name="sender">The event sender.</param>
  164. /// <param name="e">The event args.</param>
  165. protected virtual void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  166. {
  167. var collection = sender as ICollection;
  168. if (collection.Count == 0)
  169. {
  170. Classes.Add(":empty");
  171. }
  172. else
  173. {
  174. Classes.Remove(":empty");
  175. }
  176. }
  177. }
  178. }