Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / UIAutomation / Win32Providers / MS / Internal / AutomationProxies / WindowsEditBox.cs / 1 / WindowsEditBox.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: HWND-based Edit Box proxy // // History: // 01/10/03 : a-jeanp - created // 08/12/03: alexsn - bug fixes // 03/02/04: a-davidj - added text pattern // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Diagnostics; using System.Globalization; using System.Text; using System.Runtime.InteropServices; using System.ComponentModel; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Text; using MS.Win32; namespace MS.Internal.AutomationProxies { // TERMINOLOGY: Win32 Edit controls use the term "index" to mean a character position. // For example the EM_LINEINDEX message converts a line number to it's starting character position. // Perhaps not the best choice but we use it to be consistent. class WindowsEditBox : ProxyHwnd, IValueProvider, ITextProvider { // ----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors // This is the default contructor that calls the base class constructor. // This function determines the type of the edit control and then calls the default constructor. internal WindowsEditBox (IntPtr hwnd, ProxyFragment parent, int item) : base( hwnd, parent, item) { _type = GetEditboxtype (hwnd); if (IsMultiline) { _cControlType = ControlType.Document; } else { _cControlType = ControlType.Edit; } // When embedded inside of the combobox, hide the edit portion in the content element tree _fIsContent = !IsInsideOfCombo(); _fIsKeyboardFocusable = true; if (IsInsideOfListView(hwnd)) { _sAutomationId = "edit"; } // support for events _createOnEvent = new WinEventTracker.ProxyRaiseEvents (RaiseEvents); } #endregion #region Proxy Create // Static Create method called by UIAutomation to create this proxy. //= 6) { cEvent = 1; return new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty (NativeMethods.EventObjectTextSelectionChanged, idEvent) }; } */ return base.EventToWinEvent(idEvent, out cEvent); } #endregion #region Value Pattern // Sets the text of the edit. void IValueProvider.SetValue (string str) { // Ensure that the edit box and all its parents are enabled. Misc.CheckEnabled(_hwnd); int styles = WindowStyle; if (Misc.IsBitSet(styles, NativeMethods.ES_READONLY)) { throw new InvalidOperationException(SR.Get(SRID.ValueReadonly)); } // check if control only accepts numbers if (Misc.IsBitSet(styles, NativeMethods.ES_NUMBER)) { // check if string contains any non-numeric characters. foreach (char ch in str) { if (char.IsLetter (ch)) { throw new ArgumentException(SR.Get(SRID.NotAValidValue, str), "val"); } } } // bug# 11147, Text/edit box should not enter more characters than what is allowed through keyboard. // Determine the max number of chars this editbox accepts int result = Misc.ProxySendMessageInt(_hwnd, NativeMethods.EM_GETLIMITTEXT, IntPtr.Zero, IntPtr.Zero); // A result of -1 means that no limit is set. if (result != -1 && result < str.Length) { throw new InvalidOperationException (SR.Get(SRID.OperationCannotBePerformed)); } // Send the message... result = Misc.ProxySendMessageInt(_hwnd, NativeMethods.WM_SETTEXT, IntPtr.Zero, new StringBuilder(str)); if (result != 1) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } } // Request to get the value that this UI element is representing as a string string IValueProvider.Value { get { return GetValue(); } } bool IValueProvider.IsReadOnly { get { return IsReadOnly(); } } #endregion #region Text Pattern Methods ITextRangeProvider [] ITextProvider.GetSelection() { int start, end; GetSel(out start, out end); return new ITextRangeProvider[] { new WindowsEditBoxRange(this, start, end) }; } ITextRangeProvider [] ITextProvider.GetVisibleRanges() { int start, end; GetVisibleRangePoints(out start, out end); return new ITextRangeProvider[] { new WindowsEditBoxRange(this, start, end) }; } ITextRangeProvider ITextProvider.RangeFromChild(IRawElementProviderSimple childElement) { // we don't have any children so this call must be in error. throw new InvalidOperationException(SR.Get(SRID.EditControlsHaveNoChildren,GetType().FullName)); } ITextRangeProvider ITextProvider.RangeFromPoint(Point screenLocation) { // convert screen to client coordinates. // (Essentially ScreenToClient but MapWindowPoints accounts for window mirroring using WS_EX_LAYOUTRTL.) NativeMethods.Win32Point clientLocation = (NativeMethods.Win32Point)screenLocation; if (!Misc.MapWindowPoints(IntPtr.Zero, WindowHandle, ref clientLocation, 1)) { return null; } // we have to deal with the possibility that the coordinate is inside the window rect // but outside the client rect. in that case we just scoot it over so it is at the nearest // point in the client rect. NativeMethods.Win32Rect clientRect = new NativeMethods.Win32Rect(); if (!Misc.GetClientRect(WindowHandle, ref clientRect)) { return null; } clientLocation.x = Math.Max(clientLocation.x, clientRect.left); clientLocation.x = Math.Min(clientLocation.x, clientRect.right); clientLocation.y = Math.Max(clientLocation.y, clientRect.top); clientLocation.y = Math.Min(clientLocation.y, clientRect.bottom); // get the character at those client coordinates int start = CharFromPosEx(clientLocation); return new WindowsEditBoxRange(this, start, start); } #endregion TextPattern Methods #region TextPattern Properties ITextRangeProvider ITextProvider.DocumentRange { get { return new WindowsEditBoxRange(this, 0, GetTextLength()); } } // THESE PROPERTIES ARE REMOVED IN 8.2 BREAKING CHANGE. // SupportedTextSelection ITextProvider.SupportedTextSelection { get { return SupportedTextSelection.Single; } } #endregion Text Properties //------------------------------------------------------ // // Internal Properties // //------------------------------------------------------ #region Internal Properties internal bool IsMultiline { get { return _type == EditboxType.Multiline; } } internal bool IsScrollable { get { return _type == EditboxType.Scrollable; } } #endregion Internal Properties //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ #region Internal Methods // send an EM_CHARPOS message with an (x,y) coordinate in lParam and // report the returned character index and line number. // IMPORTANT: the character index and line number are each 16-bit (half of the lResult). // the high-order word is zero. internal void CharFromPos(NativeMethods.Win32Point point, out ushort indexLowWord, out ushort lineLowWord) { Debug.Assert(point.x >= 0 && point.x < 65536, "WindowsEditBox.CharFromPos() x coordinate out of range."); Debug.Assert(point.y >= 0 && point.y < 65536, "WindowsEditBox.CharFromPos() y coordinate out of range."); // ask edit control for line number and character offset at client coordinates. // low order word of return value is the character position. // high order word is the zero-based line index. IntPtr lParam = NativeMethods.Util.MAKELPARAM(point.x, point.y); int result = Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_CHARFROMPOS, IntPtr.Zero, lParam); indexLowWord = unchecked((ushort)(NativeMethods.Util.LOWORD(result))); lineLowWord = unchecked((ushort)(NativeMethods.Util.HIWORD(result))); } // an improvement on EM_CHARFROMPOS that handles edit controls with content greater than 65535 characters. internal int CharFromPosEx(NativeMethods.Win32Point point) { // get the low words of the character position and line number at the coordinate. ushort indexLowWord, lineLowWord; CharFromPos(point, out indexLowWord, out lineLowWord); // we handle multi-line edit controls differently than single line edit controls. int index; if (IsMultiline) { // note: an optimization would be to call GetTextLength and do the following only if the length is // greater than 65535. // to make the line number accurate in the case there are more than 65535 lines we get a 32-bit line number // for the first visible line then use it to figure out the high-order word of our line number. // this assumes that there aren't more than 65535 visible lines in the window, which is a // pretty safe assumption. int line = IntFromLowWord(lineLowWord, GetFirstVisibleLine()); // to make the character position accurate in case there are more than 65535 characters we use our line number to // get the character position of the beginning of that line and use it to figure out the high-order word // of our character position. // this assumes that there aren't more than 65535 characters on the line before our target character, // which is a pretty safe assumption. index = IntFromLowWord(indexLowWord, LineIndex(line)); } else { // single-line edit control // to make the character position accurate in case there are more than 65535 characters we get a 32-bit // character position from the first visible character, then use it to figure out the high-order word // of our character position. // this assumes that there aren't more than 65535 visible characters in the window, // which is a pretty safe assumption. index = IntFromLowWord(indexLowWord, GetFirstVisibleChar()); } return index; } // Retrive edit box type internal static EditboxType GetEditboxtype (IntPtr hwnd) { int style = Misc.GetWindowStyle(hwnd); EditboxType type = EditboxType.Editbox; if (Misc.IsBitSet(style, NativeMethods.ES_PASSWORD)) { type = EditboxType.Password; } else if (Misc.IsBitSet(style, NativeMethods.ES_MULTILINE)) { type = EditboxType.Multiline; } else if (Misc.IsBitSet(style, NativeMethods.ES_AUTOHSCROLL)) { type = EditboxType.Scrollable; } return type; } // send an EM_GETFIRSTVISIBLELINE message to a single-line edit box to get the first visible character. internal int GetFirstVisibleChar() { Debug.Assert(!IsMultiline); return Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_GETFIRSTVISIBLELINE, IntPtr.Zero, IntPtr.Zero); } // send an EM_GETFIRSTVISIBLELINE message to a multi-line edit box to get the first visible line. internal int GetFirstVisibleLine() { Debug.Assert(IsMultiline); return Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_GETFIRSTVISIBLELINE, IntPtr.Zero, IntPtr.Zero); } // send a WM_GETFONT message to find out what font the edit control is using. internal IntPtr GetFont() { IntPtr result = Misc.ProxySendMessage(WindowHandle, NativeMethods.WM_GETFONT, IntPtr.Zero, IntPtr.Zero); // // A null result is within normal bounds, as per the WM_GETFONT documentation: // The return value is a handle to the font used by the control, or NULL if the control is using the system font. // if (result == IntPtr.Zero) { result = UnsafeNativeMethods.GetStockObject(NativeMethods.SYSTEM_FONT); } return result; } // send an EM_GETLINECOUNT message to find out how many lines are in the edit box. internal int GetLineCount() { return Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_GETLINECOUNT, IntPtr.Zero, IntPtr.Zero); } internal NativeMethods.LOGFONT GetLogfont() { IntPtr hfont = GetFont(); Debug.Assert(hfont != IntPtr.Zero, "WindowsEditBox.GetLogfont got null HFONT"); NativeMethods.LOGFONT logfont = new NativeMethods.LOGFONT(); int cb = Marshal.SizeOf(typeof(NativeMethods.LOGFONT)); if (Misc.GetObjectW(hfont, cb, ref logfont) != cb) { Debug.Assert(false, "WindowsEditBox.GetObject unexpected return value"); } return logfont; } // send an EM_GETRECT message to find out the bounding rectangle internal Rect GetRect() { NativeMethods.Win32Rect rect = new NativeMethods.Win32Rect(); Misc.ProxySendMessage(WindowHandle, NativeMethods.EM_GETRECT, IntPtr.Zero, ref rect); return new Rect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); } // send an EM_GETSEL message to get the starting and ending // character positions of the selection. internal void GetSel(out int start, out int end) { Misc.ProxySendMessage(WindowHandle, NativeMethods.EM_GETSEL, out start, out end); } // send a WM_GETTEXT message to get the text of the edit box. internal string GetText() { return Misc.ProxyGetText(_hwnd, GetTextLength()); } // send a WM_GETTEXTLENGTH to find out how long the text is in the edit box. internal int GetTextLength() { return Misc.ProxySendMessageInt(WindowHandle, NativeMethods.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero); } internal void GetVisibleRangePoints(out int start, out int end) { start = 0; end = 0; NativeMethods.Win32Rect rect = new NativeMethods.Win32Rect(); if (Misc.GetClientRect(_hwnd, ref rect) && !rect.IsEmpty) { NativeMethods.SIZE size; string s = new string('E', 1); GetTextExtentPoint32(s, out size); NativeMethods.Win32Point ptStart = new NativeMethods.Win32Point((int)(rect.left + size.cx / 4), (int)(rect.top + size.cy / 4)); NativeMethods.Win32Point ptEnd = new NativeMethods.Win32Point((int)(rect.right - size.cx / 8), (int)(rect.bottom - size.cy / 4)); start = CharFromPosEx(ptStart); end = CharFromPosEx(ptEnd); if (start > 0) { Point pt = PosFromChar(start); if (pt.X < rect.left) { start++; } } } else { // multi-line edit controls are handled differently than single-line edit controls. if (IsMultiline) { // get the line number of the first visible line and start the range at // the beginning of that line. int firstLine = GetFirstVisibleLine(); start = LineIndex(firstLine); // calculate the line number of the first line scrolled off the bottom and // end the range at the beginning of that line. end = LineIndex(firstLine + LinesPerPage()); } else { // single-line edit control // the problem is that using a variable-width font the number of characters visible // depends on the text that is in the edit control. so we can't just divide the // width of the edit control by the width of a character. // so instead we do a binary search of the characters from the first visible character // to the end of the text to find the visibility boundary. Rect r = GetRect(); int limit = GetTextLength(); start = GetFirstVisibleChar(); int lo = start; // known visible int hi = limit; // known non-visible while (lo + 1 < hi) { int mid = (lo + hi) / 2; Point pt = PosFromChar(mid); if (pt.X >= r.Left && pt.X < r.Right) { lo = mid; } else { hi = mid; } } // trim off one character unless the range is empty or reaches the end. end = hi > start && hi < limit ? hi - 1 : hi; } } } // return true iff the user can modify the text in the edit control. internal bool IsReadOnly() { return (!SafeNativeMethods.IsWindowEnabled(WindowHandle) || Misc.IsBitSet(WindowStyle, NativeMethods.ES_READONLY)); } // send an EM_LINEFROMCHAR message to find out what line contains a character position. internal int LineFromChar(int index) { Debug.Assert(index >= 0, "WindowsEditBox.LineFromChar negative index."); Debug.Assert(index <= GetTextLength(), "WindowsEditBox.LineFromChar index out of range."); // The return of EM_LINEFROMCHAR is the zero-based line number of the line containing the character index. return Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_LINEFROMCHAR, (IntPtr)index, IntPtr.Zero); } // send an EM_LINEINDEX message to get the character position at the start of a line. internal int LineIndex(int line) { int index = Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_LINEINDEX, (IntPtr)(line), IntPtr.Zero); return index >=0 ? index : GetTextLength(); } // send an EM_LINESCROLL message to scroll it horizontally and/or vertically internal bool LineScroll(int charactersHorizontal, int linesVertical) { return 0 != Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_LINESCROLL, (IntPtr)charactersHorizontal, (IntPtr)linesVertical); } // determine the number of lines that are visible in the Edit control. // if there is no scrollbar then this is the actual number of lines displayed. internal int LinesPerPage() { int linePerPage = 0; if (Misc.IsBitSet(WindowStyle, NativeMethods.WS_VSCROLL)) { // we call GetScrollInfo and return the size of the "page" NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo(); si.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(NativeMethods.ScrollInfo)); si.fMask = NativeMethods.SIF_ALL; bool ok = Misc.GetScrollInfo(WindowHandle, NativeMethods.SB_VERT, ref si); linePerPage = ok ? si.nPage : 0; if (IsMultiline && linePerPage <= 0) { linePerPage = 1; } } else { NativeMethods.Win32Rect rect = new NativeMethods.Win32Rect(); if (Misc.GetClientRect(_hwnd, ref rect) && !rect.IsEmpty) { NativeMethods.SIZE size; string s = new string('E', 1); GetTextExtentPoint32(s, out size); if (size.cy != 0) { linePerPage = (rect.bottom - rect.top) / size.cy; } } } return linePerPage; } // send an EM_POSFROMCHAR message to find out the (x,y) position of a character. // note: this assumes that the x and y coordinates will not be greater than 65535. internal Point PosFromChar(int index) { Debug.Assert(index >= 0, "WindowsEditBox.PosFromChar negative index."); Debug.Assert(index < GetTextLength(), "WindowsEditBox.PosFromChar index out of range."); int result = Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_POSFROMCHAR, (IntPtr)index, IntPtr.Zero); Debug.Assert(result!=-1, "WindowsEditBox.PosFromChar index out of bounds."); // A returned coordinate can be a negative value if the specified character is not displayed in the // edit control's client area. So do not loose the sign. int x = (int)((short)NativeMethods.Util.LOWORD(result)); int y = (int)((short)NativeMethods.Util.HIWORD(result)); return new Point(x, y); } // a variation on EM_POSFROMCHAR that returns the upper-right corner instead of upper-left. internal Point PosFromCharUR(int index, string text) { // get the upper-left of the character Point pt; char ch = text[index]; switch(ch) { case '\n': case '\r': // get the UL corner of the character and return it since these characters have no width. pt = PosFromChar(index); break; case '\t': { // for tabs the calculated width of the character is no help so we use the // UL corner of the following character if it is on the same line. bool useNext = index = 0, "WindowsEditBox.SetSel negative start."); Debug.Assert(start <= GetTextLength(), "WindowsEditBox.SetSel start out of range."); Debug.Assert(end >= 0, "WindowsEditBox.SetSel negative end."); Debug.Assert(end <= GetTextLength(), "WindowsEditBox.SetSel end out of range."); Misc.ProxySendMessage(WindowHandle, NativeMethods.EM_SETSEL, (IntPtr)start, (IntPtr)end); } // Retrieves text associated with EditBox control. // this differs from GetText(), above, in that it limits the text to 2000 characters. // it is used by the Value pattern methods, not the Text pattern methods. internal static string Text(IntPtr hwnd) { return Misc.ProxyGetText(hwnd); } #endregion //----------------------------------------------------- // // Internal Fields // //----------------------------------------------------- #region Internal Fields // Different styles of edit boxes. internal enum EditboxType { Editbox, Multiline, Password, Readonly, Scrollable }; #endregion // ----------------------------------------------------- // // Private Methods // // ------------------------------------------------------ #region Private methods private int GetTextExtentPoint32(string text, out NativeMethods.SIZE size) { size.cx = 0; size.cy = 0; // add the width of the character at that position. // note: if any of these can throw an exception then we should use a finally clause // to ensure the DC is released. IntPtr hdc = Misc.GetDC(_hwnd); if (hdc == IntPtr.Zero) { return 0; } IntPtr oldFont = IntPtr.Zero; try { IntPtr hfont = GetFont(); oldFont = Misc.SelectObject(hdc, hfont); return Misc.GetTextExtentPoint32(hdc, text, text.Length, out size); } finally { if (oldFont != IntPtr.Zero) { Misc.SelectObject(hdc, oldFont); } Misc.ReleaseDC(_hwnd, hdc); } } private string GetValue() { if (_type == EditboxType.Password) { // Trying to retrieve the text (through WM_GETTEXT) from an edit box that has // the ES_PASSWORD style throws an InvalidOperationException. throw new InvalidOperationException(); } return Text(_hwnd); } private bool IsInsideOfCombo() { IntPtr hwndParent = UnsafeNativeMethods.GetAncestor(_hwnd, NativeMethods.GA_PARENT); if (hwndParent == IntPtr.Zero) { return false; } // Test for combo NativeMethods.COMBOBOXINFO cbInfo = new NativeMethods.COMBOBOXINFO(NativeMethods.comboboxInfoSize); if (WindowsComboBox.GetComboInfo(hwndParent, ref cbInfo) && cbInfo.hwndItem == _hwnd) { return true; } return false; } private bool IsInsideOfSpinner() { IntPtr hwndParent = UnsafeNativeMethods.GetAncestor(_hwnd, NativeMethods.GA_PARENT); if (hwndParent != IntPtr.Zero) { // Test for spinner - Create checks if the element is a spinner if (WinformsSpinner.Create(hwndParent, 0) != null) { return true; } } return WindowsSpinner.IsSpinnerEdit(_hwnd); } private static bool IsInsideOfIPAddress(IntPtr hwnd) { IntPtr hwndParent = UnsafeNativeMethods.GetAncestor(hwnd, NativeMethods.GA_PARENT); if (hwndParent != IntPtr.Zero) { string classname = Misc.GetClassName(hwndParent); if (classname.Equals("SysIPAddress32")) { return true; } } return false; } private static bool IsInsideOfListView(IntPtr hwnd) { IntPtr hwndParent = UnsafeNativeMethods.GetAncestor(hwnd, NativeMethods.GA_PARENT); if (hwndParent != IntPtr.Zero) { string classname = Misc.GetClassName(hwndParent); if (classname.Equals("SysListView32")) { return true; } } return false; } // given the low-order word of a non-negative integer that is at most 0xffff greater than some floor integer // we calculate what the original integer was. private static int IntFromLowWord(ushort lowWord, int floor) { // get the high-word from our floor integer Debug.Assert(floor >= 0); short hiWord = (short)(floor >> 16); // if our unknown integer has cross a 64k boundary from our floor integer // then we need to increment the high-word ushort floorLowWord = (ushort)(floor & 0xffff); if (lowWord < floorLowWord) hiWord++; // combine the adjusted floor high-word with the known low-word to get our integer int i = NativeMethods.Util.MAKELONG((int)lowWord, (int)hiWord); Debug.Assert(i >= floor); return i; } #endregion Private methods // ----------------------------------------------------- // // Private Fields // // ------------------------------------------------------ #region Private Fields private EditboxType _type; #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // // // Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: HWND-based Edit Box proxy // // History: // 01/10/03 : a-jeanp - created // 08/12/03: alexsn - bug fixes // 03/02/04: a-davidj - added text pattern // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Diagnostics; using System.Globalization; using System.Text; using System.Runtime.InteropServices; using System.ComponentModel; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Text; using MS.Win32; namespace MS.Internal.AutomationProxies { // TERMINOLOGY: Win32 Edit controls use the term "index" to mean a character position. // For example the EM_LINEINDEX message converts a line number to it's starting character position. // Perhaps not the best choice but we use it to be consistent. class WindowsEditBox : ProxyHwnd, IValueProvider, ITextProvider { // ----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors // This is the default contructor that calls the base class constructor. // This function determines the type of the edit control and then calls the default constructor. internal WindowsEditBox (IntPtr hwnd, ProxyFragment parent, int item) : base( hwnd, parent, item) { _type = GetEditboxtype (hwnd); if (IsMultiline) { _cControlType = ControlType.Document; } else { _cControlType = ControlType.Edit; } // When embedded inside of the combobox, hide the edit portion in the content element tree _fIsContent = !IsInsideOfCombo(); _fIsKeyboardFocusable = true; if (IsInsideOfListView(hwnd)) { _sAutomationId = "edit"; } // support for events _createOnEvent = new WinEventTracker.ProxyRaiseEvents (RaiseEvents); } #endregion #region Proxy Create // Static Create method called by UIAutomation to create this proxy. //= 6) { cEvent = 1; return new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty (NativeMethods.EventObjectTextSelectionChanged, idEvent) }; } */ return base.EventToWinEvent(idEvent, out cEvent); } #endregion #region Value Pattern // Sets the text of the edit. void IValueProvider.SetValue (string str) { // Ensure that the edit box and all its parents are enabled. Misc.CheckEnabled(_hwnd); int styles = WindowStyle; if (Misc.IsBitSet(styles, NativeMethods.ES_READONLY)) { throw new InvalidOperationException(SR.Get(SRID.ValueReadonly)); } // check if control only accepts numbers if (Misc.IsBitSet(styles, NativeMethods.ES_NUMBER)) { // check if string contains any non-numeric characters. foreach (char ch in str) { if (char.IsLetter (ch)) { throw new ArgumentException(SR.Get(SRID.NotAValidValue, str), "val"); } } } // bug# 11147, Text/edit box should not enter more characters than what is allowed through keyboard. // Determine the max number of chars this editbox accepts int result = Misc.ProxySendMessageInt(_hwnd, NativeMethods.EM_GETLIMITTEXT, IntPtr.Zero, IntPtr.Zero); // A result of -1 means that no limit is set. if (result != -1 && result < str.Length) { throw new InvalidOperationException (SR.Get(SRID.OperationCannotBePerformed)); } // Send the message... result = Misc.ProxySendMessageInt(_hwnd, NativeMethods.WM_SETTEXT, IntPtr.Zero, new StringBuilder(str)); if (result != 1) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } } // Request to get the value that this UI element is representing as a string string IValueProvider.Value { get { return GetValue(); } } bool IValueProvider.IsReadOnly { get { return IsReadOnly(); } } #endregion #region Text Pattern Methods ITextRangeProvider [] ITextProvider.GetSelection() { int start, end; GetSel(out start, out end); return new ITextRangeProvider[] { new WindowsEditBoxRange(this, start, end) }; } ITextRangeProvider [] ITextProvider.GetVisibleRanges() { int start, end; GetVisibleRangePoints(out start, out end); return new ITextRangeProvider[] { new WindowsEditBoxRange(this, start, end) }; } ITextRangeProvider ITextProvider.RangeFromChild(IRawElementProviderSimple childElement) { // we don't have any children so this call must be in error. throw new InvalidOperationException(SR.Get(SRID.EditControlsHaveNoChildren,GetType().FullName)); } ITextRangeProvider ITextProvider.RangeFromPoint(Point screenLocation) { // convert screen to client coordinates. // (Essentially ScreenToClient but MapWindowPoints accounts for window mirroring using WS_EX_LAYOUTRTL.) NativeMethods.Win32Point clientLocation = (NativeMethods.Win32Point)screenLocation; if (!Misc.MapWindowPoints(IntPtr.Zero, WindowHandle, ref clientLocation, 1)) { return null; } // we have to deal with the possibility that the coordinate is inside the window rect // but outside the client rect. in that case we just scoot it over so it is at the nearest // point in the client rect. NativeMethods.Win32Rect clientRect = new NativeMethods.Win32Rect(); if (!Misc.GetClientRect(WindowHandle, ref clientRect)) { return null; } clientLocation.x = Math.Max(clientLocation.x, clientRect.left); clientLocation.x = Math.Min(clientLocation.x, clientRect.right); clientLocation.y = Math.Max(clientLocation.y, clientRect.top); clientLocation.y = Math.Min(clientLocation.y, clientRect.bottom); // get the character at those client coordinates int start = CharFromPosEx(clientLocation); return new WindowsEditBoxRange(this, start, start); } #endregion TextPattern Methods #region TextPattern Properties ITextRangeProvider ITextProvider.DocumentRange { get { return new WindowsEditBoxRange(this, 0, GetTextLength()); } } // THESE PROPERTIES ARE REMOVED IN 8.2 BREAKING CHANGE. // SupportedTextSelection ITextProvider.SupportedTextSelection { get { return SupportedTextSelection.Single; } } #endregion Text Properties //------------------------------------------------------ // // Internal Properties // //------------------------------------------------------ #region Internal Properties internal bool IsMultiline { get { return _type == EditboxType.Multiline; } } internal bool IsScrollable { get { return _type == EditboxType.Scrollable; } } #endregion Internal Properties //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ #region Internal Methods // send an EM_CHARPOS message with an (x,y) coordinate in lParam and // report the returned character index and line number. // IMPORTANT: the character index and line number are each 16-bit (half of the lResult). // the high-order word is zero. internal void CharFromPos(NativeMethods.Win32Point point, out ushort indexLowWord, out ushort lineLowWord) { Debug.Assert(point.x >= 0 && point.x < 65536, "WindowsEditBox.CharFromPos() x coordinate out of range."); Debug.Assert(point.y >= 0 && point.y < 65536, "WindowsEditBox.CharFromPos() y coordinate out of range."); // ask edit control for line number and character offset at client coordinates. // low order word of return value is the character position. // high order word is the zero-based line index. IntPtr lParam = NativeMethods.Util.MAKELPARAM(point.x, point.y); int result = Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_CHARFROMPOS, IntPtr.Zero, lParam); indexLowWord = unchecked((ushort)(NativeMethods.Util.LOWORD(result))); lineLowWord = unchecked((ushort)(NativeMethods.Util.HIWORD(result))); } // an improvement on EM_CHARFROMPOS that handles edit controls with content greater than 65535 characters. internal int CharFromPosEx(NativeMethods.Win32Point point) { // get the low words of the character position and line number at the coordinate. ushort indexLowWord, lineLowWord; CharFromPos(point, out indexLowWord, out lineLowWord); // we handle multi-line edit controls differently than single line edit controls. int index; if (IsMultiline) { // note: an optimization would be to call GetTextLength and do the following only if the length is // greater than 65535. // to make the line number accurate in the case there are more than 65535 lines we get a 32-bit line number // for the first visible line then use it to figure out the high-order word of our line number. // this assumes that there aren't more than 65535 visible lines in the window, which is a // pretty safe assumption. int line = IntFromLowWord(lineLowWord, GetFirstVisibleLine()); // to make the character position accurate in case there are more than 65535 characters we use our line number to // get the character position of the beginning of that line and use it to figure out the high-order word // of our character position. // this assumes that there aren't more than 65535 characters on the line before our target character, // which is a pretty safe assumption. index = IntFromLowWord(indexLowWord, LineIndex(line)); } else { // single-line edit control // to make the character position accurate in case there are more than 65535 characters we get a 32-bit // character position from the first visible character, then use it to figure out the high-order word // of our character position. // this assumes that there aren't more than 65535 visible characters in the window, // which is a pretty safe assumption. index = IntFromLowWord(indexLowWord, GetFirstVisibleChar()); } return index; } // Retrive edit box type internal static EditboxType GetEditboxtype (IntPtr hwnd) { int style = Misc.GetWindowStyle(hwnd); EditboxType type = EditboxType.Editbox; if (Misc.IsBitSet(style, NativeMethods.ES_PASSWORD)) { type = EditboxType.Password; } else if (Misc.IsBitSet(style, NativeMethods.ES_MULTILINE)) { type = EditboxType.Multiline; } else if (Misc.IsBitSet(style, NativeMethods.ES_AUTOHSCROLL)) { type = EditboxType.Scrollable; } return type; } // send an EM_GETFIRSTVISIBLELINE message to a single-line edit box to get the first visible character. internal int GetFirstVisibleChar() { Debug.Assert(!IsMultiline); return Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_GETFIRSTVISIBLELINE, IntPtr.Zero, IntPtr.Zero); } // send an EM_GETFIRSTVISIBLELINE message to a multi-line edit box to get the first visible line. internal int GetFirstVisibleLine() { Debug.Assert(IsMultiline); return Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_GETFIRSTVISIBLELINE, IntPtr.Zero, IntPtr.Zero); } // send a WM_GETFONT message to find out what font the edit control is using. internal IntPtr GetFont() { IntPtr result = Misc.ProxySendMessage(WindowHandle, NativeMethods.WM_GETFONT, IntPtr.Zero, IntPtr.Zero); // // A null result is within normal bounds, as per the WM_GETFONT documentation: // The return value is a handle to the font used by the control, or NULL if the control is using the system font. // if (result == IntPtr.Zero) { result = UnsafeNativeMethods.GetStockObject(NativeMethods.SYSTEM_FONT); } return result; } // send an EM_GETLINECOUNT message to find out how many lines are in the edit box. internal int GetLineCount() { return Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_GETLINECOUNT, IntPtr.Zero, IntPtr.Zero); } internal NativeMethods.LOGFONT GetLogfont() { IntPtr hfont = GetFont(); Debug.Assert(hfont != IntPtr.Zero, "WindowsEditBox.GetLogfont got null HFONT"); NativeMethods.LOGFONT logfont = new NativeMethods.LOGFONT(); int cb = Marshal.SizeOf(typeof(NativeMethods.LOGFONT)); if (Misc.GetObjectW(hfont, cb, ref logfont) != cb) { Debug.Assert(false, "WindowsEditBox.GetObject unexpected return value"); } return logfont; } // send an EM_GETRECT message to find out the bounding rectangle internal Rect GetRect() { NativeMethods.Win32Rect rect = new NativeMethods.Win32Rect(); Misc.ProxySendMessage(WindowHandle, NativeMethods.EM_GETRECT, IntPtr.Zero, ref rect); return new Rect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); } // send an EM_GETSEL message to get the starting and ending // character positions of the selection. internal void GetSel(out int start, out int end) { Misc.ProxySendMessage(WindowHandle, NativeMethods.EM_GETSEL, out start, out end); } // send a WM_GETTEXT message to get the text of the edit box. internal string GetText() { return Misc.ProxyGetText(_hwnd, GetTextLength()); } // send a WM_GETTEXTLENGTH to find out how long the text is in the edit box. internal int GetTextLength() { return Misc.ProxySendMessageInt(WindowHandle, NativeMethods.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero); } internal void GetVisibleRangePoints(out int start, out int end) { start = 0; end = 0; NativeMethods.Win32Rect rect = new NativeMethods.Win32Rect(); if (Misc.GetClientRect(_hwnd, ref rect) && !rect.IsEmpty) { NativeMethods.SIZE size; string s = new string('E', 1); GetTextExtentPoint32(s, out size); NativeMethods.Win32Point ptStart = new NativeMethods.Win32Point((int)(rect.left + size.cx / 4), (int)(rect.top + size.cy / 4)); NativeMethods.Win32Point ptEnd = new NativeMethods.Win32Point((int)(rect.right - size.cx / 8), (int)(rect.bottom - size.cy / 4)); start = CharFromPosEx(ptStart); end = CharFromPosEx(ptEnd); if (start > 0) { Point pt = PosFromChar(start); if (pt.X < rect.left) { start++; } } } else { // multi-line edit controls are handled differently than single-line edit controls. if (IsMultiline) { // get the line number of the first visible line and start the range at // the beginning of that line. int firstLine = GetFirstVisibleLine(); start = LineIndex(firstLine); // calculate the line number of the first line scrolled off the bottom and // end the range at the beginning of that line. end = LineIndex(firstLine + LinesPerPage()); } else { // single-line edit control // the problem is that using a variable-width font the number of characters visible // depends on the text that is in the edit control. so we can't just divide the // width of the edit control by the width of a character. // so instead we do a binary search of the characters from the first visible character // to the end of the text to find the visibility boundary. Rect r = GetRect(); int limit = GetTextLength(); start = GetFirstVisibleChar(); int lo = start; // known visible int hi = limit; // known non-visible while (lo + 1 < hi) { int mid = (lo + hi) / 2; Point pt = PosFromChar(mid); if (pt.X >= r.Left && pt.X < r.Right) { lo = mid; } else { hi = mid; } } // trim off one character unless the range is empty or reaches the end. end = hi > start && hi < limit ? hi - 1 : hi; } } } // return true iff the user can modify the text in the edit control. internal bool IsReadOnly() { return (!SafeNativeMethods.IsWindowEnabled(WindowHandle) || Misc.IsBitSet(WindowStyle, NativeMethods.ES_READONLY)); } // send an EM_LINEFROMCHAR message to find out what line contains a character position. internal int LineFromChar(int index) { Debug.Assert(index >= 0, "WindowsEditBox.LineFromChar negative index."); Debug.Assert(index <= GetTextLength(), "WindowsEditBox.LineFromChar index out of range."); // The return of EM_LINEFROMCHAR is the zero-based line number of the line containing the character index. return Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_LINEFROMCHAR, (IntPtr)index, IntPtr.Zero); } // send an EM_LINEINDEX message to get the character position at the start of a line. internal int LineIndex(int line) { int index = Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_LINEINDEX, (IntPtr)(line), IntPtr.Zero); return index >=0 ? index : GetTextLength(); } // send an EM_LINESCROLL message to scroll it horizontally and/or vertically internal bool LineScroll(int charactersHorizontal, int linesVertical) { return 0 != Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_LINESCROLL, (IntPtr)charactersHorizontal, (IntPtr)linesVertical); } // determine the number of lines that are visible in the Edit control. // if there is no scrollbar then this is the actual number of lines displayed. internal int LinesPerPage() { int linePerPage = 0; if (Misc.IsBitSet(WindowStyle, NativeMethods.WS_VSCROLL)) { // we call GetScrollInfo and return the size of the "page" NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo(); si.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(NativeMethods.ScrollInfo)); si.fMask = NativeMethods.SIF_ALL; bool ok = Misc.GetScrollInfo(WindowHandle, NativeMethods.SB_VERT, ref si); linePerPage = ok ? si.nPage : 0; if (IsMultiline && linePerPage <= 0) { linePerPage = 1; } } else { NativeMethods.Win32Rect rect = new NativeMethods.Win32Rect(); if (Misc.GetClientRect(_hwnd, ref rect) && !rect.IsEmpty) { NativeMethods.SIZE size; string s = new string('E', 1); GetTextExtentPoint32(s, out size); if (size.cy != 0) { linePerPage = (rect.bottom - rect.top) / size.cy; } } } return linePerPage; } // send an EM_POSFROMCHAR message to find out the (x,y) position of a character. // note: this assumes that the x and y coordinates will not be greater than 65535. internal Point PosFromChar(int index) { Debug.Assert(index >= 0, "WindowsEditBox.PosFromChar negative index."); Debug.Assert(index < GetTextLength(), "WindowsEditBox.PosFromChar index out of range."); int result = Misc.ProxySendMessageInt(WindowHandle, NativeMethods.EM_POSFROMCHAR, (IntPtr)index, IntPtr.Zero); Debug.Assert(result!=-1, "WindowsEditBox.PosFromChar index out of bounds."); // A returned coordinate can be a negative value if the specified character is not displayed in the // edit control's client area. So do not loose the sign. int x = (int)((short)NativeMethods.Util.LOWORD(result)); int y = (int)((short)NativeMethods.Util.HIWORD(result)); return new Point(x, y); } // a variation on EM_POSFROMCHAR that returns the upper-right corner instead of upper-left. internal Point PosFromCharUR(int index, string text) { // get the upper-left of the character Point pt; char ch = text[index]; switch(ch) { case '\n': case '\r': // get the UL corner of the character and return it since these characters have no width. pt = PosFromChar(index); break; case '\t': { // for tabs the calculated width of the character is no help so we use the // UL corner of the following character if it is on the same line. bool useNext = index = 0, "WindowsEditBox.SetSel negative start."); Debug.Assert(start <= GetTextLength(), "WindowsEditBox.SetSel start out of range."); Debug.Assert(end >= 0, "WindowsEditBox.SetSel negative end."); Debug.Assert(end <= GetTextLength(), "WindowsEditBox.SetSel end out of range."); Misc.ProxySendMessage(WindowHandle, NativeMethods.EM_SETSEL, (IntPtr)start, (IntPtr)end); } // Retrieves text associated with EditBox control. // this differs from GetText(), above, in that it limits the text to 2000 characters. // it is used by the Value pattern methods, not the Text pattern methods. internal static string Text(IntPtr hwnd) { return Misc.ProxyGetText(hwnd); } #endregion //----------------------------------------------------- // // Internal Fields // //----------------------------------------------------- #region Internal Fields // Different styles of edit boxes. internal enum EditboxType { Editbox, Multiline, Password, Readonly, Scrollable }; #endregion // ----------------------------------------------------- // // Private Methods // // ------------------------------------------------------ #region Private methods private int GetTextExtentPoint32(string text, out NativeMethods.SIZE size) { size.cx = 0; size.cy = 0; // add the width of the character at that position. // note: if any of these can throw an exception then we should use a finally clause // to ensure the DC is released. IntPtr hdc = Misc.GetDC(_hwnd); if (hdc == IntPtr.Zero) { return 0; } IntPtr oldFont = IntPtr.Zero; try { IntPtr hfont = GetFont(); oldFont = Misc.SelectObject(hdc, hfont); return Misc.GetTextExtentPoint32(hdc, text, text.Length, out size); } finally { if (oldFont != IntPtr.Zero) { Misc.SelectObject(hdc, oldFont); } Misc.ReleaseDC(_hwnd, hdc); } } private string GetValue() { if (_type == EditboxType.Password) { // Trying to retrieve the text (through WM_GETTEXT) from an edit box that has // the ES_PASSWORD style throws an InvalidOperationException. throw new InvalidOperationException(); } return Text(_hwnd); } private bool IsInsideOfCombo() { IntPtr hwndParent = UnsafeNativeMethods.GetAncestor(_hwnd, NativeMethods.GA_PARENT); if (hwndParent == IntPtr.Zero) { return false; } // Test for combo NativeMethods.COMBOBOXINFO cbInfo = new NativeMethods.COMBOBOXINFO(NativeMethods.comboboxInfoSize); if (WindowsComboBox.GetComboInfo(hwndParent, ref cbInfo) && cbInfo.hwndItem == _hwnd) { return true; } return false; } private bool IsInsideOfSpinner() { IntPtr hwndParent = UnsafeNativeMethods.GetAncestor(_hwnd, NativeMethods.GA_PARENT); if (hwndParent != IntPtr.Zero) { // Test for spinner - Create checks if the element is a spinner if (WinformsSpinner.Create(hwndParent, 0) != null) { return true; } } return WindowsSpinner.IsSpinnerEdit(_hwnd); } private static bool IsInsideOfIPAddress(IntPtr hwnd) { IntPtr hwndParent = UnsafeNativeMethods.GetAncestor(hwnd, NativeMethods.GA_PARENT); if (hwndParent != IntPtr.Zero) { string classname = Misc.GetClassName(hwndParent); if (classname.Equals("SysIPAddress32")) { return true; } } return false; } private static bool IsInsideOfListView(IntPtr hwnd) { IntPtr hwndParent = UnsafeNativeMethods.GetAncestor(hwnd, NativeMethods.GA_PARENT); if (hwndParent != IntPtr.Zero) { string classname = Misc.GetClassName(hwndParent); if (classname.Equals("SysListView32")) { return true; } } return false; } // given the low-order word of a non-negative integer that is at most 0xffff greater than some floor integer // we calculate what the original integer was. private static int IntFromLowWord(ushort lowWord, int floor) { // get the high-word from our floor integer Debug.Assert(floor >= 0); short hiWord = (short)(floor >> 16); // if our unknown integer has cross a 64k boundary from our floor integer // then we need to increment the high-word ushort floorLowWord = (ushort)(floor & 0xffff); if (lowWord < floorLowWord) hiWord++; // combine the adjusted floor high-word with the known low-word to get our integer int i = NativeMethods.Util.MAKELONG((int)lowWord, (int)hiWord); Debug.Assert(i >= floor); return i; } #endregion Private methods // ----------------------------------------------------- // // Private Fields // // ------------------------------------------------------ #region Private Fields private EditboxType _type; #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SafeThreadHandle.cs
- FixedSOMSemanticBox.cs
- UriScheme.cs
- TableLayoutColumnStyleCollection.cs
- XPathSingletonIterator.cs
- PlatformCulture.cs
- ListBox.cs
- LinqTreeNodeEvaluator.cs
- ListViewItem.cs
- NonBatchDirectoryCompiler.cs
- ImageList.cs
- HelloOperation11AsyncResult.cs
- BitmapEffectInput.cs
- HeaderUtility.cs
- CustomWebEventKey.cs
- XPathNodeInfoAtom.cs
- AdCreatedEventArgs.cs
- Wildcard.cs
- HttpServerUtilityWrapper.cs
- Profiler.cs
- MediaPlayer.cs
- URLMembershipCondition.cs
- CharacterBufferReference.cs
- GradientStop.cs
- ExpressionList.cs
- BeginStoryboard.cs
- PublisherMembershipCondition.cs
- BitmapInitialize.cs
- PersistenceTask.cs
- SecurityKeyUsage.cs
- FixedBufferAttribute.cs
- PictureBox.cs
- ClaimSet.cs
- PaperSize.cs
- RenderContext.cs
- SimpleLine.cs
- X500Name.cs
- ListManagerBindingsCollection.cs
- keycontainerpermission.cs
- D3DImage.cs
- JapaneseLunisolarCalendar.cs
- DataGridViewSelectedCellCollection.cs
- SafeRightsManagementSessionHandle.cs
- RecordConverter.cs
- SemaphoreSecurity.cs
- AsymmetricAlgorithm.cs
- XmlSchemaIdentityConstraint.cs
- ProbeDuplex11AsyncResult.cs
- ExplicitDiscriminatorMap.cs
- SqlCacheDependencyDatabase.cs
- ComponentCollection.cs
- AnyReturnReader.cs
- ColorBlend.cs
- UserControlAutomationPeer.cs
- UnaryOperationBinder.cs
- DrawListViewColumnHeaderEventArgs.cs
- MatrixValueSerializer.cs
- SqlDataSourceView.cs
- StringComparer.cs
- DiffuseMaterial.cs
- TrackBar.cs
- TaskFileService.cs
- SchemaReference.cs
- DispatcherFrame.cs
- _FtpControlStream.cs
- SemaphoreSlim.cs
- BitmapCacheBrush.cs
- FormView.cs
- GridViewAutoFormat.cs
- SkinIDTypeConverter.cs
- CompilerGeneratedAttribute.cs
- mactripleDES.cs
- PeerToPeerException.cs
- PrintPreviewControl.cs
- TimeSpanStorage.cs
- EditorZone.cs
- AsymmetricSignatureDeformatter.cs
- TargetControlTypeAttribute.cs
- HttpException.cs
- UIElement3D.cs
- SuppressMergeCheckAttribute.cs
- LinkTarget.cs
- SignatureToken.cs
- ContentElementAutomationPeer.cs
- InternalRelationshipCollection.cs
- XmlTextReaderImpl.cs
- HostVisual.cs
- CheckBoxAutomationPeer.cs
- XPathItem.cs
- ActivationArguments.cs
- ModifierKeysValueSerializer.cs
- TransformCollection.cs
- Tile.cs
- Privilege.cs
- SiblingIterators.cs
- DynamicRenderer.cs
- LedgerEntry.cs
- TransformGroup.cs
- TableLayoutPanelCellPosition.cs
- SelectionHighlightInfo.cs