| File: System\Windows\Forms\Controls\ToolStrips\ToolStripTextBox.ToolStripTextBoxControl.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; using System.Drawing; using Microsoft.Win32; namespace System.Windows.Forms; public partial class ToolStripTextBox { private class ToolStripTextBoxControl : TextBox { private bool _mouseIsOver; private bool _isFontSet = true; private bool _alreadyHooked; public ToolStripTextBoxControl() { // required to make the text box height match the combo. Font = ToolStripManager.DefaultFont; _isFontSet = false; } // returns the distance from the client rect to the upper left hand corner of the control private RECT AbsoluteClientRECT { get { RECT rect = default; CreateParams cp = CreateParams; AdjustWindowRectExForControlDpi(ref rect, (WINDOW_STYLE)cp.Style, false, (WINDOW_EX_STYLE)cp.ExStyle); // the coordinates we get back are negative, we need to translate this back to positive. int offsetX = -rect.left; // one to get back to 0,0, another to translate int offsetY = -rect.top; // fetch the client rect, then apply the offset. PInvokeCore.GetClientRect(this, out var clientRect); clientRect.left += offsetX; clientRect.right += offsetX; clientRect.top += offsetY; clientRect.bottom += offsetY; return clientRect; } } private Rectangle AbsoluteClientRectangle => AbsoluteClientRECT; private ProfessionalColorTable ColorTable { get { if (Owner is not null) { if (Owner.Renderer is ToolStripProfessionalRenderer renderer) { return renderer.ColorTable; } } return ProfessionalColors.ColorTable; } } private bool IsPopupTextBox { get { return ((BorderStyle == BorderStyle.Fixed3D) && (Owner is not null && (Owner.Renderer is ToolStripProfessionalRenderer))); } } internal bool MouseIsOver { get { return _mouseIsOver; } set { if (_mouseIsOver != value) { _mouseIsOver = value; if (!Focused) { InvalidateNonClient(); } } } } [AllowNull] public override Font Font { get => base.Font; set { base.Font = value; _isFontSet = ShouldSerializeFont(); } } public ToolStripTextBox? Owner { get; set; } private unsafe void InvalidateNonClient() { if (!IsPopupTextBox) { return; } var absoluteClientRectangle = AbsoluteClientRECT; // Get the total client area, then exclude the client by using XOR using RegionScope hTotalRegion = new(0, 0, Width, Height); using RegionScope hClientRegion = new( absoluteClientRectangle.left, absoluteClientRectangle.top, absoluteClientRectangle.right, absoluteClientRectangle.bottom); using RegionScope hNonClientRegion = new(0, 0, 0, 0); PInvokeCore.CombineRgn(hNonClientRegion, hTotalRegion, hClientRegion, RGN_COMBINE_MODE.RGN_XOR); // Call RedrawWindow with the region. PInvoke.RedrawWindow( this, lprcUpdate: null, hNonClientRegion, REDRAW_WINDOW_FLAGS.RDW_INVALIDATE | REDRAW_WINDOW_FLAGS.RDW_ERASE | REDRAW_WINDOW_FLAGS.RDW_UPDATENOW | REDRAW_WINDOW_FLAGS.RDW_ERASENOW | REDRAW_WINDOW_FLAGS.RDW_FRAME); } protected override void OnGotFocus(EventArgs e) { base.OnGotFocus(e); InvalidateNonClient(); } protected override void OnLostFocus(EventArgs e) { base.OnLostFocus(e); InvalidateNonClient(); } protected override void OnMouseEnter(EventArgs e) { base.OnMouseEnter(e); MouseIsOver = true; } protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); MouseIsOver = false; } private void HookStaticEvents(bool hook) { if (hook) { if (!_alreadyHooked) { try { SystemEvents.UserPreferenceChanged += OnUserPreferenceChanged; } finally { _alreadyHooked = true; } } } else if (_alreadyHooked) { try { SystemEvents.UserPreferenceChanged -= OnUserPreferenceChanged; } finally { _alreadyHooked = false; } } } private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) { if (e.Category == UserPreferenceCategory.Window) { if (!_isFontSet) { Font = ToolStripManager.DefaultFont; } } } protected override void OnVisibleChanged(EventArgs e) { base.OnVisibleChanged(e); if (!Disposing && !IsDisposed) { HookStaticEvents(Visible); } } protected override AccessibleObject CreateAccessibilityInstance() => new ToolStripTextBoxControlAccessibleObject(this); protected override void Dispose(bool disposing) { if (disposing) { HookStaticEvents(false); } base.Dispose(disposing); } private void WmNCPaint(ref Message m) { if (!IsPopupTextBox) { base.WndProc(ref m); return; } // Paint over the edges of the text box. // Note that GetWindowDC just calls GetDCEx with DCX_WINDOW | DCX_USESTYLE. using GetDcScope hdc = new(m.HWND, HRGN.Null, GET_DCX_FLAGS.DCX_WINDOW | (GET_DCX_FLAGS)0x00010000 /* DCX_USESTYLE */); if (hdc.IsNull) { throw new Win32Exception(); } // Don't set the clipping region based on the WParam // - windows seems to take out the two pixels intended for the non-client border. bool focused = MouseIsOver || Focused; Color outerBorderColor = focused ? ColorTable.TextBoxBorder : BackColor; Color innerBorderColor = SystemInformation.HighContrast && !focused ? ColorTable.MenuBorder : BackColor; if (!Enabled) { outerBorderColor = SystemColors.ControlDark; innerBorderColor = SystemColors.Control; } using Graphics g = hdc.CreateGraphics(); Rectangle clientRect = AbsoluteClientRectangle; // Could have set up a clip and fill-rectangled, thought this would be faster. using var brush = innerBorderColor.GetCachedSolidBrushScope(); g.FillRectangle(brush, 0, 0, Width, clientRect.Top); // top border g.FillRectangle(brush, 0, 0, clientRect.Left, Height); // left border g.FillRectangle(brush, 0, clientRect.Bottom, Width, Height - clientRect.Height); // bottom border g.FillRectangle(brush, clientRect.Right, 0, Width - clientRect.Right, Height); // right border // Paint the outside rect. using var pen = outerBorderColor.GetCachedPenScope(); g.DrawRectangle(pen, 0, 0, Width - 1, Height - 1); // We've handled WM_NCPAINT. m.ResultInternal = (LRESULT)0; } protected override void WndProc(ref Message m) { if (m.MsgInternal == PInvokeCore.WM_NCPAINT) { WmNCPaint(ref m); return; } else { base.WndProc(ref m); } } } }