| File: System\ComponentModel\DataAnnotations\UIHintAttribute.cs | Web Access |
| Project: src\runtime\src\libraries\System.ComponentModel.Annotations\src\System.ComponentModel.Annotations.csproj (System.ComponentModel.Annotations) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; namespace System.ComponentModel.DataAnnotations { /// <summary> /// Attribute to provide a hint to the presentation layer about what control it should use /// </summary> [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] public class UIHintAttribute : Attribute { private readonly UIHintImplementation _implementation; /// <summary> /// Constructor that accepts the name of the control, without specifying which presentation layer to use /// </summary> /// <param name="uiHint">The name of the UI control.</param> public UIHintAttribute(string uiHint) : this(uiHint, null, Array.Empty<object>()) { } /// <summary> /// Constructor that accepts both the name of the control as well as the presentation layer /// </summary> /// <param name="uiHint">The name of the control to use</param> /// <param name="presentationLayer">The name of the presentation layer that supports this control</param> public UIHintAttribute(string uiHint, string? presentationLayer) : this(uiHint, presentationLayer, Array.Empty<object>()) { } /// <summary> /// Full constructor that accepts the name of the control, presentation layer, and optional parameters /// to use when constructing the control /// </summary> /// <param name="uiHint">The name of the control</param> /// <param name="presentationLayer">The presentation layer</param> /// <param name="controlParameters">The list of parameters for the control</param> public UIHintAttribute(string uiHint, string? presentationLayer, params object?[]? controlParameters) { _implementation = new UIHintImplementation(uiHint, presentationLayer, controlParameters); } /// <summary> /// Gets the name of the control that is most appropriate for this associated property or field /// </summary> public string UIHint => _implementation.UIHint; /// <summary> /// Gets the name of the presentation layer that supports the control type in <see cref="UIHint" /> /// </summary> public string? PresentationLayer => _implementation.PresentationLayer; /// <summary> /// Gets the name-value pairs used as parameters to the control's constructor /// </summary> /// <exception cref="InvalidOperationException"> is thrown if the current attribute is ill-formed.</exception> public IDictionary<string, object?> ControlParameters => _implementation.ControlParameters; public override int GetHashCode() => _implementation.GetHashCode(); public override bool Equals([NotNullWhen(true)] object? obj) => obj is UIHintAttribute otherAttribute && _implementation.Equals(otherAttribute._implementation); internal sealed class UIHintImplementation { private readonly object?[]? _inputControlParameters; private IDictionary<string, object?>? _controlParameters; public UIHintImplementation(string uiHint, string? presentationLayer, params object?[]? controlParameters) { UIHint = uiHint; PresentationLayer = presentationLayer; if (controlParameters != null) { _inputControlParameters = new object[controlParameters.Length]; Array.Copy(controlParameters, _inputControlParameters, controlParameters.Length); } } /// <summary> /// Gets the name of the control that is most appropriate for this associated property or field /// </summary> public string UIHint { get; } /// <summary> /// Gets the name of the presentation layer that supports the control type in <see cref="UIHint" /> /// </summary> public string? PresentationLayer { get; } // Lazy load the dictionary. It's fine if this method executes multiple times in stress scenarios. // If the method throws (indicating that the input params are invalid) this property will throw // every time it's accessed. public IDictionary<string, object?> ControlParameters => _controlParameters ??= BuildControlParametersDictionary(); /// <summary> /// Returns the hash code for this UIHintAttribute. /// </summary> /// <returns>A 32-bit signed integer hash code.</returns> public override int GetHashCode() { var a = UIHint ?? string.Empty; var b = PresentationLayer ?? string.Empty; return a.GetHashCode() ^ b.GetHashCode(); } /// <summary> /// Determines whether this instance of UIHintAttribute and a specified object, /// which must also be a UIHintAttribute object, have the same value. /// </summary> /// <param name="obj">An System.Object.</param> /// <returns>true if obj is a UIHintAttribute and its value is the same as this instance; otherwise, false.</returns> public override bool Equals([NotNullWhen(true)] object? obj) { var otherImplementation = obj as UIHintImplementation; if (otherImplementation is null || UIHint != otherImplementation.UIHint || PresentationLayer != otherImplementation.PresentationLayer) { return false; } IDictionary<string, object?> leftParams; IDictionary<string, object?> rightParams; try { leftParams = ControlParameters; rightParams = otherImplementation.ControlParameters; } catch (InvalidOperationException) { return false; } Debug.Assert(leftParams != null, "leftParams shouldn't be null"); Debug.Assert(rightParams != null, "rightParams shouldn't be null"); if (leftParams.Count != rightParams.Count) { return false; } return leftParams.OrderBy(p => p.Key).SequenceEqual(rightParams.OrderBy(p => p.Key)); } /// <summary> /// Validates the input control parameters and throws InvalidOperationException if they are not correct. /// </summary> /// <returns> /// Dictionary of control parameters. /// </returns> private Dictionary<string, object?> BuildControlParametersDictionary() { var controlParameters = new Dictionary<string, object?>(); object?[]? inputControlParameters = _inputControlParameters; if (inputControlParameters == null || inputControlParameters.Length == 0) { return controlParameters; } if (inputControlParameters.Length % 2 != 0) { throw new InvalidOperationException(SR.UIHintImplementation_NeedEvenNumberOfControlParameters); } for (int i = 0; i < inputControlParameters.Length; i += 2) { object? key = inputControlParameters[i]; object? value = inputControlParameters[i + 1]; if (key == null) { throw new InvalidOperationException(SR.Format(SR.UIHintImplementation_ControlParameterKeyIsNull, i)); } if (!(key is string keyString)) { throw new InvalidOperationException(SR.Format(SR.UIHintImplementation_ControlParameterKeyIsNotAString, i, inputControlParameters[i]!.ToString())); } if (controlParameters.ContainsKey(keyString)) { throw new InvalidOperationException(SR.Format(SR.UIHintImplementation_ControlParameterKeyOccursMoreThanOnce, i, keyString)); } controlParameters[keyString] = value; } return controlParameters; } } } }