| File: System\Windows\Forms\ErrorProvider\ErrorProvider.ControlItem.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.Drawing; namespace System.Windows.Forms; public partial class ErrorProvider { /// <summary> /// There is one ControlItem for each control that the ErrorProvider is tracking state for. /// It contains the values of all the extender properties. /// </summary> internal partial class ControlItem { private string _error; private readonly Control _control; private ErrorWindow? _window; private readonly ErrorProvider _provider; private int _iconPadding; private ErrorIconAlignment _iconAlignment; private const int StartingBlinkPhase = 10; // We want to blink 5 times private AccessibleObject? _accessibleObject; /// <summary> /// Construct the item with its associated control, provider, and a unique ID. The ID is /// used for the tooltip ID. /// </summary> public ControlItem(ErrorProvider provider, Control control, IntPtr id) { _iconAlignment = DefaultIconAlignment; _error = string.Empty; Id = id; _control = control; _provider = provider; _control.HandleCreated += OnCreateHandle; _control.HandleDestroyed += OnDestroyHandle; _control.LocationChanged += OnBoundsChanged; _control.SizeChanged += OnBoundsChanged; _control.VisibleChanged += OnParentVisibleChanged; _control.ParentChanged += OnParentVisibleChanged; } /// <summary> /// The Accessibility Object for this ErrorProvider /// </summary> internal AccessibleObject AccessibilityObject => _accessibleObject ??= CreateAccessibilityInstance(); /// <summary> /// Constructs the new instance of the accessibility object for this ErrorProvider. Subclasses /// should not call base.CreateAccessibilityObject. /// </summary> private ControlItemAccessibleObject CreateAccessibilityInstance() => new(this, _window, _control, _provider); public void Dispose() { if (_control is not null) { _control.HandleCreated -= OnCreateHandle; _control.HandleDestroyed -= OnDestroyHandle; _control.LocationChanged -= OnBoundsChanged; _control.SizeChanged -= OnBoundsChanged; _control.VisibleChanged -= OnParentVisibleChanged; _control.ParentChanged -= OnParentVisibleChanged; } _error = string.Empty; } /// <summary> /// Returns the unique ID for this control. The ID used as the tooltip ID. /// </summary> public IntPtr Id { get; } /// <summary> /// Returns or set the phase of blinking that this control is currently /// in. If zero, the control is not blinking. If odd, then the control /// is blinking, but invisible. If even, the control is blinking and /// currently visible. Each time the blink timer fires, this value is /// reduced by one (until zero), thus causing the error icon to appear /// or disappear. /// </summary> public int BlinkPhase { get; set; } /// <summary> /// Returns or sets the icon padding for the control. /// </summary> public int IconPadding { get => _iconPadding; set { if (_iconPadding == value) { return; } _iconPadding = value; UpdateWindow(); } } /// <summary> /// Returns or sets the error description string for the control. /// </summary> [AllowNull] public string Error { get => _error; set { value ??= string.Empty; // If the error is the same and the blinkStyle is not AlwaysBlink, then // we should not add the error and not start blinking. if (_error.Equals(value) && _provider.BlinkStyle != ErrorBlinkStyle.AlwaysBlink) { return; } bool adding = _error.Length == 0; _error = value; if (value.Length == 0) { RemoveFromWindow(); } else { if (adding) { AddToWindow(); } else { if (_provider.BlinkStyle != ErrorBlinkStyle.NeverBlink) { StartBlinking(); } else { UpdateWindow(); } } } } } /// <summary> /// Returns or sets the location of the error icon for the control. /// </summary> public ErrorIconAlignment IconAlignment { get => _iconAlignment; set { SourceGenerated.EnumValidator.Validate(value); if (_iconAlignment == value) { return; } _iconAlignment = value; UpdateWindow(); } } /// <summary> /// Returns true if the tooltip for this control item is currently shown. /// </summary> public bool ToolTipShown { get; set; } internal ErrorIconAlignment RTLTranslateIconAlignment(ErrorIconAlignment align) { if (_provider.RightToLeft) { switch (align) { case ErrorIconAlignment.TopLeft: return ErrorIconAlignment.TopRight; case ErrorIconAlignment.MiddleLeft: return ErrorIconAlignment.MiddleRight; case ErrorIconAlignment.BottomLeft: return ErrorIconAlignment.BottomRight; case ErrorIconAlignment.TopRight: return ErrorIconAlignment.TopLeft; case ErrorIconAlignment.MiddleRight: return ErrorIconAlignment.MiddleLeft; case ErrorIconAlignment.BottomRight: return ErrorIconAlignment.BottomLeft; default: Debug.Fail("Unknown ErrorIconAlignment value"); return align; } } else { return align; } } /// <summary> /// Returns the location of the icon in the same coordinate system as the control being /// extended. The size passed in is the size of the icon. /// </summary> internal Rectangle GetIconBounds(Size size) { int x = 0; int y = 0; switch (RTLTranslateIconAlignment(IconAlignment)) { case ErrorIconAlignment.TopLeft: case ErrorIconAlignment.MiddleLeft: case ErrorIconAlignment.BottomLeft: x = _control.Left - size.Width - _iconPadding; break; case ErrorIconAlignment.TopRight: case ErrorIconAlignment.MiddleRight: case ErrorIconAlignment.BottomRight: x = _control.Right + _iconPadding; break; } switch (IconAlignment) { case ErrorIconAlignment.TopLeft: case ErrorIconAlignment.TopRight: y = _control.Top; break; case ErrorIconAlignment.MiddleLeft: case ErrorIconAlignment.MiddleRight: y = _control.Top + (_control.Height - size.Height) / 2; break; case ErrorIconAlignment.BottomLeft: case ErrorIconAlignment.BottomRight: y = _control.Bottom - size.Height; break; } return new Rectangle(x, y, size.Width, size.Height); } /// <summary> /// If this control's error icon has been added to the error window, then update the /// window state because some property has changed. /// </summary> private void UpdateWindow() => _window?.Update(timerCaused: false); /// <summary> /// If this control's error icon has been added to the error window, then start blinking /// the error window. /// </summary> private void StartBlinking() { if (_window is not null) { BlinkPhase = StartingBlinkPhase; _window.StartBlinking(); } } /// <summary> /// Add this control's error icon to the error window. /// </summary> private void AddToWindow() { // if we are recreating the control, then add the control. if (_window is null && (_control.Created || _control.RecreatingHandle) && _control.Visible && _control.ParentInternal is not null && _error.Length > 0) { _window = _provider.EnsureErrorWindow(_control.ParentInternal); _window.Add(this); // Make sure that we blink if the style is set to AlwaysBlink or BlinkIfDifferentError if (_provider.BlinkStyle != ErrorBlinkStyle.NeverBlink) { StartBlinking(); } } } /// <summary> /// Remove this control's error icon from the error window. /// </summary> private void RemoveFromWindow() { _window?.Remove(this); _window = null; } /// <summary> /// This is called when a property on the control is changed. /// </summary> private void OnBoundsChanged(object? sender, EventArgs e) => UpdateWindow(); private void OnParentVisibleChanged(object? sender, EventArgs e) { BlinkPhase = 0; RemoveFromWindow(); AddToWindow(); } /// <summary> /// This is called when the control's handle is created. /// </summary> private void OnCreateHandle(object? sender, EventArgs e) => AddToWindow(); /// <summary> /// This is called when the control's handle is destroyed. /// </summary> private void OnDestroyHandle(object? sender, EventArgs e) => RemoveFromWindow(); } }