/Application/GUI/Controls/EditableTextBlock.xaml.cs

http://yet-another-music-application.googlecode.com/ · C# · 465 lines · 237 code · 67 blank · 161 comment · 20 complexity · 5e818c240ad0d4ee8f522b976979650c MD5 · raw file

  1. /**
  2. * EditableTextBlock.xaml.cs
  3. *
  4. * A custom textblock that can be turned into a textbox for
  5. * editing. This code was originally written by Jesper Borgstrup
  6. * and can be found at
  7. * http://www.codeproject.com/KB/WPF/editabletextblock.aspx
  8. *
  9. * * * * * * * * *
  10. *
  11. * This code is part of the Stoffi Music Player Project.
  12. * Visit our website at: stoffiplayer.com
  13. *
  14. * This program is free software; you can redistribute it and/or
  15. * modify it under the terms of the GNU General Public License
  16. * as published by the Free Software Foundation; either version
  17. * 3 of the License, or (at your option) any later version.
  18. *
  19. * See stoffiplayer.com/license for more information.
  20. **/
  21. using System;
  22. using System.Collections.Generic;
  23. using System.ComponentModel;
  24. using System.Linq;
  25. using System.Text;
  26. using System.Windows;
  27. using System.Windows.Controls;
  28. using System.Windows.Data;
  29. using System.Windows.Documents;
  30. using System.Windows.Input;
  31. using System.Windows.Media;
  32. using System.Windows.Media.Imaging;
  33. using System.Windows.Navigation;
  34. using System.Windows.Shapes;
  35. using System.Windows.Threading;
  36. namespace Stoffi
  37. {
  38. /// <summary>
  39. ///
  40. /// </summary>
  41. public partial class EditableTextBlock : UserControl, INotifyPropertyChanged
  42. {
  43. #region Members
  44. // We keep the old text when we go into editmode
  45. // in case the user aborts with the escape key
  46. private string oldText;
  47. private bool clickToEdit = false;
  48. #endregion
  49. #region Properties
  50. /// <summary>
  51. /// Gets current text of the text box
  52. /// </summary>
  53. public string CurrentText { get; private set; }
  54. /// <summary>
  55. /// Gets or sets the text value
  56. /// </summary>
  57. public string Text
  58. {
  59. get { return (string)GetValue(TextProperty); }
  60. set { SetValue(TextProperty, value); OnPropertyChanged("Text"); OnPropertyChanged("FormattedText"); }
  61. }
  62. /// <summary>
  63. /// The text property of the control
  64. /// </summary>
  65. public static readonly DependencyProperty TextProperty =
  66. DependencyProperty.Register(
  67. "Text",
  68. typeof(string),
  69. typeof(EditableTextBlock),
  70. new PropertyMetadata(""));
  71. /// <summary>
  72. /// Gets or sets whether the control is editable
  73. /// </summary>
  74. public bool IsEditable
  75. {
  76. get { return (bool)GetValue(IsEditableProperty); }
  77. set { SetValue(IsEditableProperty, value); }
  78. }
  79. /// <summary>
  80. /// The property describing whether the control is editable
  81. /// </summary>
  82. public static readonly DependencyProperty IsEditableProperty =
  83. DependencyProperty.Register(
  84. "IsEditable",
  85. typeof(bool),
  86. typeof(EditableTextBlock),
  87. new PropertyMetadata(true));
  88. /// <summary>
  89. /// Gets or sets whether the control is in edit mode
  90. /// Note: Must be editable for this to take effect
  91. /// </summary>
  92. public bool IsInEditMode
  93. {
  94. get
  95. {
  96. if (IsEditable)
  97. return (bool)GetValue(IsInEditModeProperty);
  98. else
  99. return false;
  100. }
  101. set
  102. {
  103. if (IsEditable)
  104. {
  105. if (value)
  106. {
  107. oldText = Text;
  108. DispatchEnteredEditMode();
  109. }
  110. U.ListenForShortcut = !value;
  111. SetValue(IsInEditModeProperty, value);
  112. }
  113. }
  114. }
  115. /// <summary>
  116. /// The property describing whether the control is in edit mode
  117. /// </summary>
  118. public static readonly DependencyProperty IsInEditModeProperty =
  119. DependencyProperty.Register(
  120. "IsInEditMode",
  121. typeof(bool),
  122. typeof(EditableTextBlock),
  123. new PropertyMetadata(false));
  124. /// <summary>
  125. /// Gets or sets the format of the text
  126. /// </summary>
  127. public string TextFormat
  128. {
  129. get { return (string)GetValue(TextFormatProperty); }
  130. set
  131. {
  132. if (value == "") value = "{0}";
  133. SetValue(TextFormatProperty, value);
  134. }
  135. }
  136. /// <summary>
  137. /// The property describing the format of the text value
  138. /// </summary>
  139. public static readonly DependencyProperty TextFormatProperty =
  140. DependencyProperty.Register(
  141. "TextFormat",
  142. typeof(string),
  143. typeof(EditableTextBlock),
  144. new PropertyMetadata("{0}"));
  145. /// <summary>
  146. /// Gets the formatted text value
  147. /// </summary>
  148. public string FormattedText
  149. {
  150. get { return String.Format(TextFormat, Text); }
  151. }
  152. /// <summary>
  153. /// Gets or sets whether the user can initiate
  154. /// edit mode by clicking on the text.
  155. /// </summary>
  156. public bool ClickToEdit
  157. {
  158. get { return clickToEdit; }
  159. set { clickToEdit = value; }
  160. }
  161. /// <summary>
  162. /// Gets or sets whether the hover effect is a simple black
  163. /// line (true) or if it looks similar to a textbox (false).
  164. /// </summary>
  165. public bool SimpleHover { get; set; }
  166. #endregion PropertiesWindow
  167. #region Constructor
  168. /// <summary>
  169. /// Creates an editable text block
  170. /// </summary>
  171. public EditableTextBlock()
  172. {
  173. ClickToEdit = false;
  174. SimpleHover = true;
  175. InitializeComponent();
  176. base.Focusable = true;
  177. base.FocusVisualStyle = null;
  178. }
  179. #endregion Constructor
  180. #region Methods
  181. #region Public
  182. /// <summary>
  183. /// Cancels the edit
  184. /// </summary>
  185. public void Cancel()
  186. {
  187. Text = oldText;
  188. this.IsInEditMode = false;
  189. DispatchCanceledEvent();
  190. }
  191. /// <summary>
  192. /// Ends the edit successfully
  193. /// </summary>
  194. public void Done()
  195. {
  196. this.IsInEditMode = false;
  197. DispatchEditedEvent(CurrentText, oldText);
  198. }
  199. #endregion
  200. #region Event handlers
  201. /// <summary>
  202. /// Invoked when we enter edit mode.
  203. /// </summary>
  204. /// <param name="sender">The sender of the event</param>
  205. /// <param name="e">The event data</param>
  206. private void TextBox_Loaded(object sender, RoutedEventArgs e)
  207. {
  208. TextBox txt = sender as TextBox;
  209. txt.Text = Text;
  210. CurrentText = Text;
  211. // Give the TextBox input focus
  212. txt.Focus();
  213. txt.SelectAll();
  214. }
  215. /// <summary>
  216. /// Invoked when we exit edit mode.
  217. /// </summary>
  218. /// <param name="sender">The sender of the event</param>
  219. /// <param name="e">The event data</param>
  220. private void TextBox_LostFocus(object sender, RoutedEventArgs e)
  221. {
  222. if (this.IsInEditMode)
  223. Done();
  224. }
  225. /// <summary>
  226. /// Invoked when the user edits the annotation.
  227. /// </summary>
  228. /// <param name="sender">The sender of the event</param>
  229. /// <param name="e">The event data</param>
  230. private void TextBox_KeyUp(object sender, KeyEventArgs e)
  231. {
  232. if (e.Key == Key.Enter)
  233. {
  234. Done();
  235. e.Handled = true;
  236. }
  237. else if (e.Key == Key.Escape)
  238. {
  239. Cancel();
  240. e.Handled = true;
  241. }
  242. else
  243. {
  244. TextBox txt = sender as TextBox;
  245. CurrentText = txt.Text;
  246. }
  247. }
  248. /// <summary>
  249. /// Invoked when the user clicks on the control.
  250. /// Will enter edit mode if ClickToEdit is true.
  251. /// </summary>
  252. /// <param name="sender">The sender of the event</param>
  253. /// <param name="e">The event data</param>
  254. private void TextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  255. {
  256. IsInEditMode = (!IsInEditMode && ClickToEdit);
  257. }
  258. #endregion Event Handlers
  259. #endregion
  260. #region Events
  261. /// <summary>
  262. /// Occurs when the control is edited succesfully
  263. /// </summary>
  264. public event EditableTextBlockDelegate Edited;
  265. /// <summary>
  266. /// Occurs when the edit is canceled
  267. /// </summary>
  268. public event EditableTextBlockCanceledDelegate Canceled;
  269. /// <summary>
  270. /// Occurs when the control enters edit mode
  271. /// </summary>
  272. public event EventHandler EnteredEditMode;
  273. /// <summary>
  274. /// The dispatcher for the EnteredEditMode event
  275. /// </summary>
  276. private void DispatchEnteredEditMode()
  277. {
  278. if (EnteredEditMode != null)
  279. {
  280. EnteredEditMode(this, null);
  281. }
  282. }
  283. /// <summary>
  284. /// The subscriber for the edited event
  285. /// </summary>
  286. /// <param name="eventHandler">The event handler</param>
  287. private void SubscribeEdited(EditableTextBlockDelegate eventHandler)
  288. {
  289. Edited += eventHandler;
  290. }
  291. /// <summary>
  292. /// The unsubscriber for the edited event
  293. /// </summary>
  294. /// <param name="eventHandler">The event handler</param>
  295. private void UnsubsribeEdited(EditableTextBlockDelegate eventHandler)
  296. {
  297. Edited -= eventHandler;
  298. }
  299. /// <summary>
  300. /// The dispatcher for the edit event
  301. /// </summary>
  302. /// <param name="ntxt">The new text value</param>
  303. /// <param name="otxt">The old text value</param>
  304. private void DispatchEditedEvent(string ntxt, string otxt)
  305. {
  306. if (Edited != null)
  307. {
  308. Edited(this, new EditableTextBlockEventArgs(ntxt,otxt));
  309. }
  310. }
  311. /// <summary>
  312. /// The subscriber for the canceled event
  313. /// </summary>
  314. /// <param name="eventHandler">The event handler</param>
  315. private void SubscribeCanceled(EditableTextBlockDelegate eventHandler)
  316. {
  317. Edited += eventHandler;
  318. }
  319. /// <summary>
  320. /// The unsubscriber for the canceled event
  321. /// </summary>
  322. /// <param name="eventHandler">The event handler</param>
  323. private void UnsubsribeCanceled(EditableTextBlockDelegate eventHandler)
  324. {
  325. Edited -= eventHandler;
  326. }
  327. /// <summary>
  328. /// The dispatcher for the canceled event
  329. /// </summary>
  330. private void DispatchCanceledEvent()
  331. {
  332. if (Canceled != null)
  333. {
  334. Canceled(new EventArgs());
  335. }
  336. }
  337. #endregion
  338. #region INotifyPropertyChanged Members
  339. /// <summary>
  340. ///
  341. /// </summary>
  342. public event PropertyChangedEventHandler PropertyChanged;
  343. /// <summary>
  344. ///
  345. /// </summary>
  346. /// <param name="name"></param>
  347. public void OnPropertyChanged(string name)
  348. {
  349. if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name));
  350. }
  351. #endregion
  352. }
  353. /// <summary>
  354. /// Provides data for the <see cref="EditableTextBlock.Edited"/> event
  355. /// </summary>
  356. public class EditableTextBlockEventArgs : EventArgs
  357. {
  358. #region Members
  359. private string newText;
  360. private string oldText;
  361. #endregion
  362. #region Properties
  363. /// <summary>
  364. /// The new text value
  365. /// </summary>
  366. public string NewText { get { return newText; } }
  367. /// <summary>
  368. /// The old text value
  369. /// </summary>
  370. public string OldText { get { return oldText; } }
  371. #endregion
  372. #region Constructor
  373. /// <summary>
  374. /// Initializes a new instance of the <see cref="EditableTextBlockEventArgs"/> class
  375. /// </summary>
  376. /// <param name="ntxt">The new text value</param>
  377. /// <param name="otxt">The old text value</param>
  378. public EditableTextBlockEventArgs(string ntxt, string otxt)
  379. {
  380. newText = ntxt;
  381. oldText = otxt;
  382. }
  383. #endregion
  384. }
  385. #region Events
  386. /// <summary>
  387. /// Represents the method that will handle the <see cref="EditableTextBlock.Edited"/> event.
  388. /// </summary>
  389. /// <param name="e">The event arguments</param>
  390. /// <param name="sender">The sender of the event</param>
  391. public delegate void EditableTextBlockDelegate(object sender, EditableTextBlockEventArgs e);
  392. /// <summary>
  393. /// Represents the method that will handle the <see cref="EditableTextBlock.Canceled"/> event.
  394. /// </summary>
  395. /// <param name="e">The event arguments</param>
  396. public delegate void EditableTextBlockCanceledDelegate(EventArgs e);
  397. #endregion
  398. }