WatermarkService.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. using System.Collections.Generic;
  2. using System.ComponentModel;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Controls.Primitives;
  6. using System.Windows.Documents;
  7. namespace MECF.Framework.UI.Client.ClientBase.AttachedProperties
  8. {
  9. /// <summary>
  10. /// Class that provides the Watermark attached property
  11. /// </summary>
  12. public static class WatermarkService
  13. {
  14. /// <summary>
  15. /// Watermark Attached Dependency Property
  16. /// </summary>
  17. public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached(
  18. "Watermark",
  19. typeof(object),
  20. typeof(WatermarkService),
  21. new FrameworkPropertyMetadata(null, OnWatermarkChanged));
  22. #region Private Fields
  23. /// <summary>
  24. /// Dictionary of ItemsControls
  25. /// </summary>
  26. private static readonly Dictionary<object, ItemsControl> ItemsControls
  27. = new Dictionary<object, ItemsControl>();
  28. #endregion
  29. /// <summary>
  30. /// Gets the Watermark property. This dependency property indicates the watermark for the control.
  31. /// </summary>
  32. /// <param name="d"><see cref="DependencyObject"/> to get the property from</param>
  33. /// <returns>The value of the Watermark property</returns>
  34. public static object GetWatermark(DependencyObject d)
  35. {
  36. return d.GetValue(WatermarkProperty);
  37. }
  38. /// <summary>
  39. /// Sets the Watermark property. This dependency property indicates the watermark for the control.
  40. /// </summary>
  41. /// <param name="d"><see cref="DependencyObject"/> to set the property on</param>
  42. /// <param name="value">value of the property</param>
  43. public static void SetWatermark(DependencyObject d, object value)
  44. {
  45. d.SetValue(WatermarkProperty, value);
  46. }
  47. /// <summary>
  48. /// Handles changes to the Watermark property.
  49. /// </summary>
  50. /// <param name="d"><see cref="DependencyObject"/> that fired the event</param>
  51. /// <param name="e">A <see cref="DependencyPropertyChangedEventArgs"/> that contains the event data.</param>
  52. private static void OnWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  53. {
  54. var control = (Control)d;
  55. control.Loaded += Control_Loaded;
  56. if (d is ComboBox)
  57. {
  58. control.GotKeyboardFocus += Control_GotKeyboardFocus;
  59. control.LostKeyboardFocus += Control_Loaded;
  60. }
  61. else if (d is TextBox)
  62. {
  63. control.GotKeyboardFocus += Control_GotKeyboardFocus;
  64. control.LostKeyboardFocus += Control_Loaded;
  65. ((TextBox)control).TextChanged += Control_GotKeyboardFocus;
  66. }
  67. if (d is ItemsControl o && !(o is ComboBox))
  68. {
  69. // for Items property
  70. o.ItemContainerGenerator.ItemsChanged += ItemsChanged;
  71. ItemsControls.Add(o.ItemContainerGenerator, o);
  72. // for ItemsSource property
  73. var prop = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, o.GetType());
  74. prop.AddValueChanged(o, ItemsSourceChanged);
  75. }
  76. }
  77. #region Event Handlers
  78. /// <summary>
  79. /// Handle the GotFocus event on the control
  80. /// </summary>
  81. /// <param name="sender">The source of the event.</param>
  82. /// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
  83. private static void Control_GotKeyboardFocus(object sender, RoutedEventArgs e)
  84. {
  85. var c = (Control)sender;
  86. if (ShouldShowWatermark(c))
  87. {
  88. ShowWatermark(c);
  89. }
  90. else
  91. {
  92. RemoveWatermark(c);
  93. }
  94. }
  95. /// <summary>
  96. /// Handle the Loaded and LostFocus event on the control
  97. /// </summary>
  98. /// <param name="sender">The source of the event.</param>
  99. /// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
  100. private static void Control_Loaded(object sender, RoutedEventArgs e)
  101. {
  102. var control = (Control)sender;
  103. if (ShouldShowWatermark(control))
  104. {
  105. ShowWatermark(control);
  106. }
  107. }
  108. /// <summary>
  109. /// Event handler for the items source changed event
  110. /// </summary>
  111. /// <param name="sender">The source of the event.</param>
  112. /// <param name="e">A <see cref="EventArgs"/> that contains the event data.</param>
  113. private static void ItemsSourceChanged(object sender, System.EventArgs e)
  114. {
  115. var c = (ItemsControl)sender;
  116. if (c.ItemsSource != null)
  117. {
  118. if (ShouldShowWatermark(c))
  119. {
  120. ShowWatermark(c);
  121. }
  122. else
  123. {
  124. RemoveWatermark(c);
  125. }
  126. }
  127. else
  128. {
  129. ShowWatermark(c);
  130. }
  131. }
  132. /// <summary>
  133. /// Event handler for the items changed event
  134. /// </summary>
  135. /// <param name="sender">The source of the event.</param>
  136. /// <param name="e">A <see cref="ItemsChangedEventArgs"/> that contains the event data.</param>
  137. private static void ItemsChanged(object sender, ItemsChangedEventArgs e)
  138. {
  139. ItemsControl control;
  140. if (ItemsControls.TryGetValue(sender, out control))
  141. {
  142. if (ShouldShowWatermark(control))
  143. {
  144. ShowWatermark(control);
  145. }
  146. else
  147. {
  148. RemoveWatermark(control);
  149. }
  150. }
  151. }
  152. #endregion
  153. #region Helper Methods
  154. /// <summary>
  155. /// Remove the watermark from the specified element
  156. /// </summary>
  157. /// <param name="control">Element to remove the watermark from</param>
  158. private static void RemoveWatermark(UIElement control)
  159. {
  160. var layer = AdornerLayer.GetAdornerLayer(control);
  161. // layer could be null if control is no longer in the visual tree
  162. if (layer != null)
  163. {
  164. var adorners = layer.GetAdorners(control);
  165. if (adorners == null)
  166. {
  167. return;
  168. }
  169. foreach (var adorner in adorners)
  170. {
  171. if (adorner is WatermarkAdorner)
  172. {
  173. adorner.Visibility = Visibility.Hidden;
  174. layer.Remove(adorner);
  175. }
  176. }
  177. }
  178. }
  179. /// <summary>
  180. /// Show the watermark on the specified control
  181. /// </summary>
  182. /// <param name="control">Control to show the watermark on</param>
  183. private static void ShowWatermark(Control control)
  184. {
  185. var layer = AdornerLayer.GetAdornerLayer(control);
  186. // layer could be null if control is no longer in the visual tree
  187. layer?.Add(new WatermarkAdorner(control, GetWatermark(control)));
  188. }
  189. /// <summary>
  190. /// Indicates whether or not the watermark should be shown on the specified control
  191. /// </summary>
  192. /// <param name="c"><see cref="Control"/> to test</param>
  193. /// <returns>true if the watermark should be shown; false otherwise</returns>
  194. private static bool ShouldShowWatermark(Control c)
  195. {
  196. switch (c)
  197. {
  198. case ComboBox box:
  199. return box.Text == string.Empty;
  200. case TextBoxBase _:
  201. return (c as TextBox)?.Text == string.Empty;
  202. case ItemsControl control:
  203. return control.Items.Count == 0;
  204. default:
  205. return false;
  206. }
  207. }
  208. #endregion
  209. }
  210. }