| File: MS\Internal\Documents\Application\PageTextBox.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationUI\PresentationUI.csproj (PresentationUI) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // Description: PageTextBox is a derived TextBox with some extra // functionality for the Page related behaviours of // DocumentApplicationUI. using System; using System.Globalization; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Windows.Automation.Provider; using System.Windows.Controls; using System.Windows.Input; // For event args using System.Windows.TrustUI; // For string resources namespace MS.Internal.Documents.Application { /// <summary> /// A derived TextBox with some extra functionality for the Page related behaviours of DocumentApplicationUI. /// </summary> internal sealed class PageTextBox : TextBox { //------------------------------------------------------ // // Constructors // //------------------------------------------------------ #region Constructors /// <summary> /// Default PageTextBox constructor. /// </summary> internal PageTextBox() { // Set any TextBox properties. SetDefaults(); // Setup any TextBox event handlers. SetHandlers(); } #endregion Constructors //------------------------------------------------------ // // Public Properties // //------------------------------------------------------ #region Public Properties public static readonly DependencyProperty PageNumberProperty = DependencyProperty.Register( "PageNumber", typeof(int), typeof(PageTextBox), new FrameworkPropertyMetadata( _pageNumberDefault, //default value FrameworkPropertyMetadataOptions.None, //MetaData flags new PropertyChangedCallback(OnPageNumberChanged))); //changed callback /// <summary> /// Sets the page number to be displayed in the PageTextBox. /// </summary> public void SetPageNumber(int pageNumber) { if (CultureInfo.CurrentCulture != null) { this.Text = pageNumber.ToString(CultureInfo.CurrentCulture); // If this is currently focused, refocus to reset text selection. if (IsFocused) { SelectAll(); } } } #endregion Public Properties //------------------------------------------------------ // // Internal Events // //------------------------------------------------------ #region Internal Events /// <summary> /// This event will be fired anytime a user was editing the TextBox but applied /// the new value (ie pressed enter, or tab). /// </summary> internal event EventHandler PageNumberEdited; /// <summary> /// This will fire a PageNumberEdited event if required. /// </summary> internal void OnPageNumberEdited() { // Check if the TextBox is being edited if (_isEditingText) { _isEditingText = false; // Since the TextBox was being edited, fire a cancelled event so that the value // may be applied (if desired by the UI). PageNumberEdited(this, EventArgs.Empty); } } /// <summary> /// This event will be fired anytime a user was editing the TextBox but chose not to apply /// the new value (ie change of focus, or escape pressed). /// </summary> internal event EventHandler PageNumberEditCancelled; /// <summary> /// This will fire a PageNumberEditCancelled event if required. /// </summary> internal void OnPageNumberEditCancelled() { // Check if the TextBox is being edited if (_isEditingText) { _isEditingText = false; // Since the TextBox was being edited, fire a cancelled event so that the value // may be reset (if desired by the UI). PageNumberEditCancelled(this, EventArgs.Empty); } } #endregion Internal Events //------------------------------------------------------ // // Protected Methods // //------------------------------------------------------ /// <summary> /// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>) /// </summary> protected override AutomationPeer OnCreateAutomationPeer() { return new PageTextBoxAutomationPeer(this); } /// <summary> /// Highlight the entire text when the user gives focus to the PageTextBox by /// clicking on it. In this case we must not pass the event to the base handler /// since the base handler will place the cursor at the current position after /// setting focus. If we are already focused, then placing the cursor is the /// correct behavior so the base handler will be used. /// </summary> /// <param name="sender">A reference to the sender of the event (not used)</param> /// <param name="e">Arguments to the event (not used)</param> protected override void OnMouseDown(MouseButtonEventArgs e) { if (!IsFocused) { Focus(); e.Handled = true; } else { base.OnMouseDown(e); } } //------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods /// <summary> /// Select the text when we receive focus. /// </summary> /// <param name="sender">A reference to the sender of the event (not used)</param> /// <param name="e">Arguments to the event (not used)</param> private void OnGotFocus(object sender, EventArgs e) { SelectAll(); } /// <summary> /// This will check incoming key presses for 'enter', 'escape', and 'tab' and take the /// appropriate action. /// </summary> /// <param name="sender">A reference to the sender of the event.</param> /// <param name="e">Arguments to the event, used for the key reference.</param> private void OnPreviewKeyDown(object sender, KeyEventArgs e) { // This will check for the use of 'enter', 'escape' and 'tab' and take the appropriate action // Ensure the arguments are not null. if (e != null) { // Check which Key was pressed. switch (e.Key) { // Erasure keys -- these don't trigger OnPreviewTextInput but should // set _isEditingText nonetheless case Key.Delete: case Key.Back: _isEditingText = true; break; // Submission Keys case Key.Return: // This also covers: case Key.Enter case Key.Tab: case Key.Execute: // Enter pressed, issue submit, mark input as handled. OnPageNumberEdited(); break; // Rejection Keys case Key.Cancel: case Key.Escape: // Escape pressed, issue cancel, mark input as handled. OnPageNumberEditCancelled(); break; } } } /// <summary> /// This will check the characters that have been entered into the TextBox, and restrict it /// to only the valid set. /// </summary> /// <param name="sender">A reference to the sender of the event.</param> /// <param name="e">Arguments to the event, used for the input text.</param> private void OnPreviewTextInput(object sender, TextCompositionEventArgs e) { if ((e != null) && (!String.IsNullOrEmpty(e.Text)) && (IsValidInputChar(e.Text[0]))) { // Set editing mode and allow the ComboBox to handle it. _isEditingText = true; } else { // Do not allow any remaining characters for text input. e.Handled = true; } } /// <summary> /// This will validate incoming strings pasted into the textbox, only pasting /// valid (all digit) strings. /// </summary> /// <param name="sender">A reference to the sender of the event.</param> /// <param name="e">Arguments to the event, used to determine the input.</param> private void OnPaste(object sender, DataObjectPastingEventArgs e) { // Validate the parameters, return if data is null. if ((e == null) || (e.DataObject == null) || (String.IsNullOrEmpty(e.FormatToApply))) { return; } // Acquire a reference to the new string. string incomingString = e.DataObject.GetData(e.FormatToApply) as string; if (IsValidInputString(incomingString)) { // Since the new content is valid set the PageTextBox as in edit mode // and allow the text to be processed normally (ie don't CancelCommand). _isEditingText = true; } else { // Cancel the paste if the string is null, empty, or otherwise invalid. e.Handled = true; e.CancelCommand(); } } /// <summary> /// Checks if the given string contains valid characters for this control. Used for pasting /// and UI Automation. /// </summary> /// <param name="value">The string to test.</param> private bool IsValidInputString(string incomingString) { if (String.IsNullOrEmpty(incomingString)) { return false; } // Check that each character is valid (a digit). foreach (char c in incomingString.ToCharArray()) { if (!(IsValidInputChar(c))) { return false; } } return true; } /// <summary> /// Returns true if the character is valid for input to this control. /// </summary> /// <param name="c">The character to test.</param> private bool IsValidInputChar(char c) { // This will limit which characters are allowed to be entered into the TextBox // Currently this is limited to 0-9 // This method (by using IsDigit) is localization independent, since (as localization // informed me) this avoids worrying about the mapping of the keys, and instead the // actual value that will be input. return (Char.IsDigit(c)); } /// <summary> /// Callback for the PageNumber DependencyProperty. When this event is fired /// PageTextBox updates the page number it is displaying. /// </summary> /// <param name="d">The PageTextBox to update.</param> /// <param name="e">The associated arguments.</param> private static void OnPageNumberChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { PageTextBox PageTextBox = (PageTextBox)d; PageTextBox.SetPageNumber((int)e.NewValue); } /// <summary> /// Set the default ComboBox properties /// </summary> private void SetDefaults() { ToolTip = SR.PageTextBoxToolTip; IsReadOnly = false; VerticalAlignment = VerticalAlignment.Center; HorizontalAlignment = HorizontalAlignment.Center; VerticalContentAlignment = VerticalAlignment.Center; HorizontalContentAlignment = HorizontalAlignment.Center; TextAlignment = TextAlignment.Right; Padding = new Thickness(1); Margin = new Thickness(0); // Since PageTextBox only supports input as numbers, we should disable // IME options so that we don't need multiple Enter presses to parse // the input. InputMethod.SetIsInputMethodEnabled(this, false); } /// <summary> /// Attach any needed TextBox event handlers. /// </summary> private void SetHandlers() { GotFocus += OnGotFocus; PreviewTextInput += OnPreviewTextInput; PreviewKeyDown += OnPreviewKeyDown; DataObject.AddPastingHandler(this, new DataObjectPastingEventHandler(OnPaste)); } #endregion Private Methods #region Nested Classes /// <summary> /// AutomationPeer associated with PageTextBox /// </summary> private class PageTextBoxAutomationPeer : TextBoxAutomationPeer, IValueProvider { /// <summary> /// Constructor /// </summary> /// <param name="owner">Owner of the AutomationPeer.</param> public PageTextBoxAutomationPeer(PageTextBox owner) : base(owner) { } /// <summary> /// <see cref="AutomationPeer.GetClassNameCore"/> /// </summary> protected override string GetClassNameCore() { return "PageTextBox"; } /// <summary> /// <see cref="AutomationPeer.GetPattern"/> /// </summary> public override object GetPattern(PatternInterface patternInterface) { if (patternInterface == PatternInterface.Value) { return this; } else { return base.GetPattern(patternInterface); } } void IValueProvider.SetValue(string value) { ArgumentNullException.ThrowIfNull(value); if (!IsEnabled()) { throw new ElementNotEnabledException(); } PageTextBox owner = (PageTextBox)Owner; if (owner.IsReadOnly) { throw new ElementNotEnabledException(); } if (owner.IsValidInputString(value)) { owner.Text = value; owner._isEditingText = true; owner.OnPageNumberEdited(); } } } #endregion //------------------------------------------------------ // // Private Fields // //------------------------------------------------------ private bool _isEditingText; private const int _pageNumberDefault = 0; } }