Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
TextBoxBase.cs
Go to the documentation of this file.
1 using System.Windows;
2 using System.Windows.Data;
3 using System.Windows.Input;
4 
5 using SiliconStudio.Presentation.Core;
6 
7 namespace SiliconStudio.Presentation.Controls
8 {
9  /// <summary>
10  /// An implementation of the <see cref="System.Windows.Controls.TextBox"/> control
11  /// that provides additional features such as a proper validation/cancellation workflow.
12  /// </summary>
13  public class TextBoxBase : System.Windows.Controls.TextBox
14  {
15  private bool validating;
16 
17  /// <summary>
18  /// Identifies the <see cref="HasText"/> dependency property.
19  /// </summary>
20  private static readonly DependencyPropertyKey HasTextPropertyKey = DependencyProperty.RegisterReadOnly("HasText", typeof(bool), typeof(TextBoxBase), new PropertyMetadata(false));
21 
22  /// <summary>
23  /// Identifies the <see cref="GetFocusOnLoad"/> dependency property.
24  /// </summary>
25  public static readonly DependencyProperty GetFocusOnLoadProperty = DependencyProperty.Register("GetFocusOnLoad", typeof(bool), typeof(TextBoxBase), new PropertyMetadata(false));
26 
27  /// <summary>
28  /// Identifies the <see cref="SelectAllOnFocus"/> dependency property.
29  /// </summary>
30  public static readonly DependencyProperty SelectAllOnFocusProperty = DependencyProperty.Register("SelectAllOnFocus", typeof(bool), typeof(TextBoxBase), new PropertyMetadata(false));
31 
32  /// <summary>
33  /// Identifies the <see cref="ValidateWithEnter"/> dependency property.
34  /// </summary>
35  public static readonly DependencyProperty ValidateWithEnterProperty = DependencyProperty.Register("ValidateWithEnter", typeof(bool), typeof(TextBoxBase), new PropertyMetadata(true));
36 
37  /// <summary>
38  /// Identifies the <see cref="ValidateOnTextChange"/> dependency property.
39  /// </summary>
40  public static readonly DependencyProperty ValidateOnTextChangeProperty = DependencyProperty.Register("ValidateOnTextChange", typeof(bool), typeof(TextBoxBase), new PropertyMetadata(false));
41 
42  /// <summary>
43  /// Identifies the <see cref="ValidateOnLostFocus"/> dependency property.
44  /// </summary>
45  public static readonly DependencyProperty ValidateOnLostFocusProperty = DependencyProperty.Register("ValidateOnLostFocus", typeof(bool), typeof(TextBoxBase), new PropertyMetadata(true, OnLostFocusActionChanged));
46 
47  /// <summary>
48  /// Identifies the <see cref="CancelWithEscape"/> dependency property.
49  /// </summary>
50  public static readonly DependencyProperty CancelWithEscapeProperty = DependencyProperty.Register("CancelWithEscape", typeof(bool), typeof(TextBoxBase), new PropertyMetadata(true));
51 
52  /// <summary>
53  /// Identifies the <see cref="CancelOnLostFocus"/> dependency property.
54  /// </summary>
55  public static readonly DependencyProperty CancelOnLostFocusProperty = DependencyProperty.Register("CancelOnLostFocus", typeof(bool), typeof(TextBoxBase), new PropertyMetadata(false, OnLostFocusActionChanged));
56 
57  /// <summary>
58  /// Identifies the <see cref="ValidateCommand"/> dependency property.
59  /// </summary>
60  public static readonly DependencyProperty ValidateCommandProperty = DependencyProperty.Register("ValidateCommand", typeof(ICommand), typeof(TextBoxBase));
61 
62  /// <summary>
63  /// Identifies the <see cref="ValidateCommandParameter"/> dependency property.
64  /// </summary>
65  public static readonly DependencyProperty ValidateCommandParameterProprty = DependencyProperty.Register("ValidateCommandParameter", typeof(object), typeof(TextBoxBase));
66 
67  /// <summary>
68  /// Identifies the <see cref="CancelCommand"/> dependency property.
69  /// </summary>
70  public static readonly DependencyProperty CancelCommandProperty = DependencyProperty.Register("CancelCommand", typeof(ICommand), typeof(TextBoxBase));
71 
72  /// <summary>
73  /// Identifies the <see cref="CancelCommandParameter"/> dependency property.
74  /// </summary>
75  public static readonly DependencyProperty CancelCommandParameterProprty = DependencyProperty.Register("CancelCommandParameter", typeof(object), typeof(TextBoxBase));
76 
77  /// <summary>
78  /// Raised just before the TextBox changes are validated. This event is cancellable
79  /// </summary>
80  public static readonly RoutedEvent ValidatingEvent = EventManager.RegisterRoutedEvent("Validating", RoutingStrategy.Bubble, typeof(CancelRoutedEventHandler), typeof(TextBox));
81 
82  /// <summary>
83  /// Raised when TextBox changes have been validated.
84  /// </summary>
85  public static readonly RoutedEvent ValidatedEvent = EventManager.RegisterRoutedEvent("Validated", RoutingStrategy.Bubble, typeof(ValidationRoutedEventHandler<string>), typeof(TextBox));
86 
87  /// <summary>
88  /// Raised when the TextBox changes are cancelled.
89  /// </summary>
90  public static readonly RoutedEvent CancelledEvent = EventManager.RegisterRoutedEvent("Cancelled", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TextBox));
91 
92  static TextBoxBase()
93  {
94  TextProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, OnTextChanged, null, true, UpdateSourceTrigger.Explicit));
95  }
96 
97  public TextBoxBase()
98  {
99  Loaded += OnLoaded;
100  }
101 
102  /// <summary>
103  /// Gets whether this TextBox contains a non-empty text.
104  /// </summary>
105  public bool HasText { get { return (bool)GetValue(HasTextPropertyKey.DependencyProperty); } private set { SetValue(HasTextPropertyKey, value); } }
106 
107  /// <summary>
108  /// Gets or sets whether the associated text box should get keyboard focus when this behavior is attached.
109  /// </summary>
110  public bool GetFocusOnLoad { get { return (bool)GetValue(GetFocusOnLoadProperty); } set { SetValue(GetFocusOnLoadProperty, value); } }
111 
112  /// <summary>
113  /// Gets or sets whether the text of the TextBox must be selected when the control gets focus.
114  /// </summary>
115  public bool SelectAllOnFocus { get { return (bool)GetValue(SelectAllOnFocusProperty); } set { SetValue(SelectAllOnFocusProperty, value); } }
116 
117  /// <summary>
118  /// Gets or sets whether the validation should happen when the user press <b>Enter</b>.
119  /// </summary>
120  public bool ValidateWithEnter { get { return (bool)GetValue(ValidateWithEnterProperty); } set { SetValue(ValidateWithEnterProperty, value); } }
121 
122  /// <summary>
123  /// Gets or sets whether the validation should happen as soon as the <see cref="TextBox.Text"/> is changed.
124  /// </summary>
125  public bool ValidateOnTextChange { get { return (bool)GetValue(ValidateOnTextChangeProperty); } set { SetValue(ValidateOnTextChangeProperty, value); } }
126 
127  /// <summary>
128  /// Gets or sets whether the validation should happen when the control losts focus.
129  /// </summary>
130  public bool ValidateOnLostFocus { get { return (bool)GetValue(ValidateOnLostFocusProperty); } set { SetValue(ValidateOnLostFocusProperty, value); } }
131 
132  /// <summary>
133  /// Gets or sets whether the cancellation should happen when the user press <b>Escape</b>.
134  /// </summary>
135  public bool CancelWithEscape { get { return (bool)GetValue(CancelWithEscapeProperty); } set { SetValue(CancelWithEscapeProperty, value); } }
136 
137  /// <summary>
138  /// Gets or sets whether the cancellation should happen when the control losts focus.
139  /// </summary>
140  public bool CancelOnLostFocus { get { return (bool)GetValue(CancelOnLostFocusProperty); } set { SetValue(CancelOnLostFocusProperty, value); } }
141 
142  /// <summary>
143  /// Gets or sets the command to execute when the validation occurs.
144  /// </summary>
145  public ICommand ValidateCommand { get { return (ICommand)GetValue(ValidateCommandProperty); } set { SetValue(ValidateCommandProperty, value); } }
146 
147  /// <summary>
148  /// Gets or sets the parameter of the command to execute when the validation occurs.
149  /// </summary>
150  public object ValidateCommandParameter { get { return GetValue(ValidateCommandParameterProprty); } set { SetValue(ValidateCommandParameterProprty, value); } }
151 
152  /// <summary>
153  /// Gets or sets the command to execute when the cancellation occurs.
154  /// </summary>
155  public ICommand CancelCommand { get { return (ICommand)GetValue(CancelCommandProperty); } set { SetValue(CancelCommandProperty, value); } }
156 
157  /// <summary>
158  /// Gets or sets the parameter of the command to execute when the cancellation occurs.
159  /// </summary>
160  public object CancelCommandParameter { get { return GetValue(CancelCommandParameterProprty); } set { SetValue(CancelCommandParameterProprty, value); } }
161 
162  /// <summary>
163  /// Raised just before the TextBox changes are validated. This event is cancellable
164  /// </summary>
165  public event CancelRoutedEventHandler Validating { add { AddHandler(ValidatingEvent, value); } remove { RemoveHandler(ValidatingEvent, value); } }
166 
167  /// <summary>
168  /// Raised when TextBox changes have been validated.
169  /// </summary>
170  public event ValidationRoutedEventHandler<string> Validated { add { AddHandler(ValidatedEvent, value); } remove { RemoveHandler(ValidatedEvent, value); } }
171 
172  /// <summary>
173  /// Raised when the TextBox changes are cancelled.
174  /// </summary>
175  public event RoutedEventHandler Cancelled { add { AddHandler(CancelledEvent, value); } remove { RemoveHandler(CancelledEvent, value); } }
176 
177  /// <summary>
178  /// Validates the current changes in the TextBox.
179  /// </summary>
180  public void Validate()
181  {
182  var cancelRoutedEventArgs = new CancelRoutedEventArgs(ValidatingEvent);
183  OnValidating(cancelRoutedEventArgs);
184  if (cancelRoutedEventArgs.Cancel)
185  return;
186 
187  RaiseEvent(cancelRoutedEventArgs);
188  if (cancelRoutedEventArgs.Cancel)
189  return;
190 
191  validating = true;
192  var coercedText = CoerceTextForValidation(Text);
193  SetCurrentValue(TextProperty, coercedText);
194 
195  BindingExpression expression = GetBindingExpression(TextProperty);
196  if (expression != null)
197  expression.UpdateSource();
198 
199  ClearUndoStack();
200 
201  var validatedArgs = new ValidationRoutedEventArgs<string>(ValidatedEvent, coercedText);
202  OnValidated();
203 
204  RaiseEvent(validatedArgs);
205  if (ValidateCommand != null && ValidateCommand.CanExecute(ValidateCommandParameter))
206  ValidateCommand.Execute(ValidateCommandParameter);
207  validating = false;
208  }
209 
210  /// <summary>
211  /// Cancels the current changes in the TextBox.
212  /// </summary>
213  public void Cancel()
214  {
215  BindingExpression expression = GetBindingExpression(TextProperty);
216  if (expression != null)
217  expression.UpdateTarget();
218 
219  ClearUndoStack();
220 
221  var cancelledArgs = new RoutedEventArgs(CancelledEvent);
222  OnCancelled();
223  RaiseEvent(cancelledArgs);
224 
225  if (CancelCommand != null && CancelCommand.CanExecute(CancelCommandParameter))
226  CancelCommand.Execute(CancelCommandParameter);
227  }
228 
229  /// <summary>
230  /// Raised when the text of the TextBox changes.
231  /// </summary>
232  /// <param name="oldValue">The old value of the <see cref="TextBox.Text"/> property.</param>
233  /// <param name="newValue">The new value of the <see cref="TextBox.Text"/> property.</param>
234  protected virtual void OnTextChanged(string oldValue, string newValue)
235  {
236  }
237 
238  /// <summary>
239  /// Raised when the text of the TextBox is being validated.
240  /// </summary>
241  /// <param name="e">The event argument.</param>
242  protected virtual void OnValidating(CancelRoutedEventArgs e)
243  {
244  }
245 
246  /// <summary>
247  /// Raised when the current changes have has been validated.
248  /// </summary>
249  protected virtual void OnValidated()
250  {
251  }
252 
253  /// <summary>
254  /// Raised when the current changes have been cancelled.
255  /// </summary>
256  protected virtual void OnCancelled()
257  {
258  }
259 
260  /// <summary>
261  /// Coerces the text during the validation process. This method is invoked by <see cref="Validate"/>.
262  /// </summary>
263  /// <param name="baseValue">The value to coerce.</param>
264  /// <returns>The coerced value.</returns>
265  protected virtual string CoerceTextForValidation(string baseValue)
266  {
267  return MaxLength > 0 && baseValue.Length > MaxLength ? baseValue.Substring(0, MaxLength) : baseValue;
268  }
269 
270  /// <inheritdoc/>
271  protected override void OnPreviewKeyDown(KeyEventArgs e)
272  {
273  base.OnPreviewKeyDown(e);
274 
275  if (IsReadOnly)
276  {
277  e.Handled = true;
278  return;
279  }
280 
281  if (e.Key == Key.Enter && ValidateWithEnter)
282  {
283  Validate();
284  }
285  if (e.Key == Key.Escape && CancelWithEscape)
286  {
287  Cancel();
288  }
289  }
290 
291  protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
292  {
293  base.OnGotKeyboardFocus(e);
294 
295  if (SelectAllOnFocus)
296  {
297  SelectAll();
298  }
299  }
300 
301  protected override void OnMouseDown(MouseButtonEventArgs e)
302  {
303  if (!IsKeyboardFocusWithin)
304  {
305  if (SelectAllOnFocus)
306  {
307  // We handle the event only when the SelectAllOnFocus property is active. If we don't handle it, base.OnMouseDown will clear the selection
308  // we're just about to do. But if we handle it, the caret won't be moved to the cursor position, which is the behavior we expect when SelectAllOnFocus is inactive.
309  e.Handled = true;
310  }
311  Focus();
312  }
313  base.OnMouseDown(e);
314  }
315 
316  protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
317  {
318  if (ValidateOnLostFocus)
319  {
320  Validate();
321  }
322  if (CancelOnLostFocus)
323  {
324  Cancel();
325  }
326 
327  base.OnLostKeyboardFocus(e);
328  }
329 
330  private void ClearUndoStack()
331  {
332  var limit = UndoLimit;
333  UndoLimit = 0;
334  UndoLimit = limit;
335  }
336 
337  private void OnLoaded(object sender, RoutedEventArgs e)
338  {
339  if (GetFocusOnLoad)
340  {
341  Keyboard.Focus(this);
342  }
343  }
344 
345  private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
346  {
347  var input = (TextBoxBase)d;
348  input.HasText = e.NewValue != null && ((string)e.NewValue).Length > 0;
349  input.OnTextChanged((string)e.OldValue, (string)e.NewValue);
350  if (input.ValidateOnTextChange && !input.validating)
351  input.Validate();
352  }
353 
354  private static void OnLostFocusActionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
355  {
356  var input = (TextBox)d;
357  if (e.Property == ValidateOnLostFocusProperty && (bool)e.NewValue)
358  {
359  input.SetCurrentValue(CancelOnLostFocusProperty, false);
360  }
361  if (e.Property == CancelOnLostFocusProperty && (bool)e.NewValue)
362  {
363  input.SetCurrentValue(ValidateOnLostFocusProperty, false);
364  }
365  }
366  }
367 }
virtual void OnValidated()
Raised when the current changes have has been validated.
Definition: TextBoxBase.cs:249
void Validate()
Validates the current changes in the TextBox.
Definition: TextBoxBase.cs:180
An implementation of the TextBoxBase control that provides additional features such as a proper valid...
Definition: TextBox.cs:29
override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
Definition: TextBoxBase.cs:291
virtual string CoerceTextForValidation(string baseValue)
Coerces the text during the validation process. This method is invoked by Validate.
Definition: TextBoxBase.cs:265
override void OnMouseDown(MouseButtonEventArgs e)
Definition: TextBoxBase.cs:301
virtual void OnTextChanged(string oldValue, string newValue)
Raised when the text of the TextBox changes.
Definition: TextBoxBase.cs:234
An implementation of the System.Windows.Controls.TextBox control that provides additional features su...
Definition: TextBoxBase.cs:13
override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
Definition: TextBoxBase.cs:316
virtual void OnValidating(CancelRoutedEventArgs e)
Raised when the text of the TextBox is being validated.
Definition: TextBoxBase.cs:242
virtual void OnCancelled()
Raised when the current changes have been cancelled.
Definition: TextBoxBase.cs:256
void Cancel()
Cancels the current changes in the TextBox.
Definition: TextBoxBase.cs:213
delegate void CancelRoutedEventHandler(object sender, CancelRoutedEventArgs e)
override void OnPreviewKeyDown(KeyEventArgs e)
Definition: TextBoxBase.cs:271