Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
TextBoxValidationBehavior.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System.Windows;
4 using System.Windows.Controls;
5 using System.Windows.Data;
6 using System.Windows.Input;
7 using System.Windows.Interactivity;
8 
9 using SiliconStudio.Presentation.Core;
10 using SiliconStudio.Presentation.Extensions;
11 
12 namespace SiliconStudio.Presentation.Behaviors
13 {
14  /// <summary>
15  /// This behavior allows to update the <see cref="Binding"/> of the <see cref="TextBox.Text"/> property both when the control losts focus and when the user press <b>Enter</b>.
16  /// When the user press <b>Escape</b>, the <see cref="TextBox.Text"/> property can be roll-backed to the source value, cancelling the modification.
17  /// Additionally, a command can be bound to be executed on validation.
18  /// </summary>
19  /// <remarks>
20  /// This behavior will clone the <see cref="TextBox.Text"/> property <see cref="Binding"/> and sets the <see cref="Binding.UpdateSourceTrigger"/> property to
21  /// <see cref="UpdateSourceTrigger.Explicit"/> if the current value is not already <see cref="UpdateSourceTrigger.Explicit"/>.
22  /// </remarks>
23  public class TextBoxValidationBehavior : Behavior<TextBox>
24  {
25  /// <summary>
26  /// Identifies the <see cref="GetFocusOnLoad"/> dependency property.
27  /// </summary>
28  public static readonly DependencyProperty GetFocusOnLoadProperty = DependencyProperty.Register("GetFocusOnLoad", typeof(bool), typeof(TextBoxValidationBehavior), new PropertyMetadata(false));
29 
30  /// <summary>
31  /// Identifies the <see cref="ValidateWithEnter"/> dependency property.
32  /// </summary>
33  public static readonly DependencyProperty ValidateWithEnterProperty = DependencyProperty.Register("ValidateWithEnter", typeof(bool), typeof(TextBoxValidationBehavior), new PropertyMetadata(true));
34 
35  /// <summary>
36  /// Identifies the <see cref="ValidateOnLostFocus"/> dependency property.
37  /// </summary>
38  public static readonly DependencyProperty ValidateOnLostFocusProperty = DependencyProperty.Register("ValidateOnLostFocus", typeof(bool), typeof(TextBoxValidationBehavior), new PropertyMetadata(true, LostFocusActionChanged));
39 
40  /// <summary>
41  /// Identifies the <see cref="CancelWithEscape"/> dependency property.
42  /// </summary>
43  public static readonly DependencyProperty CancelWithEscapeProperty = DependencyProperty.Register("CancelWithEscape", typeof(bool), typeof(TextBoxValidationBehavior), new PropertyMetadata(true));
44 
45  /// <summary>
46  /// Identifies the <see cref="CancelOnLostFocus"/> dependency property.
47  /// </summary>
48  public static readonly DependencyProperty CancelOnLostFocusProperty = DependencyProperty.Register("CancelOnLostFocus", typeof(bool), typeof(TextBoxValidationBehavior), new PropertyMetadata(false, LostFocusActionChanged));
49 
50  /// <summary>
51  /// Identifies the <see cref="ValidateCommand"/> dependency property.
52  /// </summary>
53  public static readonly DependencyProperty ValidateCommandProperty = DependencyProperty.Register("ValidateCommand", typeof(ICommand), typeof(TextBoxValidationBehavior));
54 
55  /// <summary>
56  /// Identifies the <see cref="ValidateCommandParameter"/> dependency property.
57  /// </summary>
58  public static readonly DependencyProperty ValidateCommandParameterProprty = DependencyProperty.Register("ValidateCommandParameter", typeof(object), typeof(TextBoxValidationBehavior));
59 
60  /// <summary>
61  /// Identifies the <see cref="CancelCommand"/> dependency property.
62  /// </summary>
63  public static readonly DependencyProperty CancelCommandProperty = DependencyProperty.Register("CancelCommand", typeof(ICommand), typeof(TextBoxValidationBehavior));
64 
65  /// <summary>
66  /// Identifies the <see cref="CancelCommandParameter"/> dependency property.
67  /// </summary>
68  public static readonly DependencyProperty CancelCommandParameterProprty = DependencyProperty.Register("CancelCommandParameter", typeof(object), typeof(TextBoxValidationBehavior));
69 
70  /// <summary>
71  /// Raised when the TextBox changes are being validating.
72  /// </summary>
73  public static readonly RoutedEvent ValidatingEvent = EventManager.RegisterRoutedEvent("Validating", RoutingStrategy.Bubble, typeof(CancelRoutedEventHandler), typeof(TextBoxValidationBehavior));
74 
75  /// <summary>
76  /// Raised when the TextBox changes are validated.
77  /// </summary>
78  public static readonly RoutedEvent ValidatedEvent = EventManager.RegisterRoutedEvent("Validated", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TextBoxValidationBehavior));
79 
80  /// <summary>
81  /// Raised when the TextBox changes are cancelled.
82  /// </summary>
83  public static readonly RoutedEvent CancelledEvent = EventManager.RegisterRoutedEvent("Cancelled", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TextBoxValidationBehavior));
84 
85  /// <summary>
86  /// Gets or sets whether the associated text box should get keyboard focus when this behavior is attached.
87  /// </summary>
88  public bool GetFocusOnLoad { get { return (bool)GetValue(GetFocusOnLoadProperty); } set { SetValue(GetFocusOnLoadProperty, value); } }
89 
90  /// <summary>
91  /// Gets or sets whether the validation should happen when the user press <b>Enter</b>.
92  /// </summary>
93  public bool ValidateWithEnter { get { return (bool)GetValue(ValidateWithEnterProperty); } set { SetValue(ValidateWithEnterProperty, value); } }
94 
95  /// <summary>
96  /// Gets or sets whether the validation should happen when the control losts focus.
97  /// </summary>
98  public bool ValidateOnLostFocus { get { return (bool)GetValue(ValidateOnLostFocusProperty); } set { SetValue(ValidateOnLostFocusProperty, value); } }
99 
100  /// <summary>
101  /// Gets or sets whether the cancellation should happen when the user press <b>Escape</b>.
102  /// </summary>
103  public bool CancelWithEscape { get { return (bool)GetValue(CancelWithEscapeProperty); } set { SetValue(CancelWithEscapeProperty, value); } }
104 
105  /// <summary>
106  /// Gets or sets whether the cancellation should happen when the control losts focus.
107  /// </summary>
108  public bool CancelOnLostFocus { get { return (bool)GetValue(CancelOnLostFocusProperty); } set { SetValue(CancelOnLostFocusProperty, value); } }
109 
110  /// <summary>
111  /// Gets or sets the command to execute when the validation occurs.
112  /// </summary>
113  public ICommand ValidateCommand { get { return (ICommand)GetValue(ValidateCommandProperty); } set { SetValue(ValidateCommandProperty, value); } }
114 
115  /// <summary>
116  /// Gets or sets the parameter of the command to execute when the validation occurs.
117  /// </summary>
118  public object ValidateCommandParameter { get { return GetValue(ValidateCommandParameterProprty); } set { SetValue(ValidateCommandParameterProprty, value); } }
119 
120  /// <summary>
121  /// Gets or sets the command to execute when the cancellation occurs.
122  /// </summary>
123  public ICommand CancelCommand { get { return (ICommand)GetValue(CancelCommandProperty); } set { SetValue(CancelCommandProperty, value); } }
124 
125  /// <summary>
126  /// Gets or sets the parameter of the command to execute when the cancellation occurs.
127  /// </summary>
128  public object CancelCommandParameter { get { return GetValue(CancelCommandParameterProprty); } set { SetValue(CancelCommandParameterProprty, value); } }
129 
130  /// <summary>
131  /// Raised when the TextBox changes are being validating.
132  /// </summary>
133  public event RoutedEventHandler Validating { add { AssociatedObject.AddHandler(ValidatingEvent, value); } remove { AssociatedObject.RemoveHandler(ValidatingEvent, value); } }
134 
135  /// <summary>
136  /// Raised when the TextBox changes are validated.
137  /// </summary>
138  public event RoutedEventHandler Validated { add { AssociatedObject.AddHandler(ValidatedEvent, value); } remove { AssociatedObject.RemoveHandler(ValidatedEvent, value); } }
139 
140  /// <summary>
141  /// Raised when the TextBox changes are cancelled.
142  /// </summary>
143  public event RoutedEventHandler Cancelled { add { AssociatedObject.AddHandler(CancelledEvent, value); } remove { AssociatedObject.RemoveHandler(CancelledEvent, value); } }
144 
145  /// <inheritdoc/>
146  protected override void OnAttached()
147  {
148  base.OnAttached();
149  AssociatedObject.Loaded += TextBoxLoaded;
150  AssociatedObject.KeyDown += TextBoxKeyDown;
151  AssociatedObject.LostFocus += TextBoxLostFocus;
152 
153  var textBinding = BindingOperations.GetBinding(AssociatedObject, TextBox.TextProperty);
154  if (textBinding != null)
155  {
156  if (textBinding.UpdateSourceTrigger != UpdateSourceTrigger.Explicit)
157  {
158  var newBinding = textBinding.CloneBinding(textBinding.Mode);
159  AssociatedObject.SetBinding(TextBox.TextProperty, newBinding);
160  }
161  }
162  }
163 
164  /// <inheritdoc/>
165  protected override void OnDetaching()
166  {
167  AssociatedObject.Loaded -= TextBoxLoaded;
168  AssociatedObject.LostFocus -= TextBoxLostFocus;
169  AssociatedObject.KeyDown -= TextBoxKeyDown;
170  base.OnDetaching();
171  }
172 
173  private void TextBoxLoaded(object sender, RoutedEventArgs e)
174  {
175  if (GetFocusOnLoad)
176  {
177  Keyboard.Focus(AssociatedObject);
178  }
179  }
180 
181  private void TextBoxKeyDown(object sender, KeyEventArgs e)
182  {
183  if (e.Key == Key.Enter && ValidateWithEnter)
184  {
185  Validate();
186  }
187  if (e.Key == Key.Escape && CancelWithEscape)
188  {
189  Cancel();
190  }
191  }
192 
193  private void TextBoxLostFocus(object sender, RoutedEventArgs e)
194  {
195  if (ValidateOnLostFocus)
196  {
197  Validate();
198  }
199  if (CancelOnLostFocus)
200  {
201  Cancel();
202  }
203  }
204 
205  private void ClearUndoStack()
206  {
207  var limit = AssociatedObject.UndoLimit;
208  AssociatedObject.UndoLimit = 0;
209  AssociatedObject.UndoLimit = limit;
210  }
211 
212  private void Validate()
213  {
214  var cancelRoutedEventArgs = new CancelRoutedEventArgs(ValidatingEvent);
215  AssociatedObject.RaiseEvent(cancelRoutedEventArgs);
216  if (cancelRoutedEventArgs.Cancel)
217  {
218  return;
219  }
220 
221  BindingExpression expression = AssociatedObject.GetBindingExpression(TextBox.TextProperty);
222  if (expression != null)
223  expression.UpdateSource();
224 
225  ClearUndoStack();
226 
227  AssociatedObject.RaiseEvent(new RoutedEventArgs(ValidatedEvent));
228  if (ValidateCommand != null && ValidateCommand.CanExecute(ValidateCommandParameter))
229  ValidateCommand.Execute(ValidateCommandParameter);
230  }
231 
232  private void Cancel()
233  {
234  BindingExpression expression = AssociatedObject.GetBindingExpression(TextBox.TextProperty);
235  if (expression != null)
236  expression.UpdateTarget();
237 
238  ClearUndoStack();
239 
240  AssociatedObject.RaiseEvent(new RoutedEventArgs(CancelledEvent));
241 
242  if (CancelCommand != null && CancelCommand.CanExecute(CancelCommandParameter))
243  CancelCommand.Execute(CancelCommandParameter);
244  }
245 
246  private static void LostFocusActionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
247  {
248  var behavior = (TextBoxValidationBehavior)d;
249  if (e.Property == ValidateOnLostFocusProperty)
250  {
251  behavior.SetCurrentValue(CancelOnLostFocusProperty, !(bool)e.NewValue);
252  }
253  if (e.Property == CancelOnLostFocusProperty)
254  {
255  behavior.SetCurrentValue(ValidateOnLostFocusProperty, !(bool)e.NewValue);
256  }
257  }
258  }
259 }
The dialog has been closed by a cancellation from the user.
This behavior allows to update the Binding of the TextBox.Text property both when the control losts f...
delegate void CancelRoutedEventHandler(object sender, CancelRoutedEventArgs e)