| File: System\Windows\Forms\Controls\ListView\ColumnHeader.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.Design; namespace System.Windows.Forms; /// <summary> /// Displays a single column header in a <see cref="Forms.ListView"/> control. /// </summary> [DesignTimeVisible(false)] [DefaultProperty(nameof(Text))] [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] [ToolboxItem(false)] [TypeConverter(typeof(ColumnHeaderConverter))] public partial class ColumnHeader : Component, ICloneable { // disable C# compiler warning #0414: field assigned unused value #pragma warning disable 0414 internal int _index = -1; #pragma warning restore 0414 internal string? _text; internal string _name; internal int _width = 60; internal int _correspondingListViewSubItemIndex = -1; // Use TextAlign property instead of this member variable, always private HorizontalAlignment _textAlign = HorizontalAlignment.Left; private bool _textAlignInitialized; private AccessibleObject? _accessibilityObject; private readonly ColumnHeaderImageListIndexer _imageIndexer; // We need to send some messages to ListView when it gets initialized. internal ListView? OwnerListview { get { return ListView; } set { int width = Width; ListView = value; // The below properties are set into the listview. Width = width; } } /// <summary> /// Creates a new ColumnHeader object /// </summary> public ColumnHeader() { _imageIndexer = new ColumnHeaderImageListIndexer(this); _name = string.Empty; } /// <summary> /// Creates a new ColumnHeader object /// </summary> public ColumnHeader(int imageIndex) : this() { ImageIndex = imageIndex; } /// <summary> /// Creates a new ColumnHeader object /// </summary> public ColumnHeader(string imageKey) : this() { ImageKey = imageKey; } internal AccessibleObject AccessibilityObject { get { _accessibilityObject ??= new ListViewColumnHeaderAccessibleObject(this); return _accessibilityObject; } } internal int ActualImageIndex_Internal { get { int imgIndex = _imageIndexer.ActualIndex; if (ImageList is null || ImageList.Images is null || imgIndex >= ImageList.Images.Count) { // the ImageIndex equivalent of a ImageKey that does not exist in the ImageList return -1; } else { return imgIndex; } } } [Localizable(true)] [RefreshProperties(RefreshProperties.Repaint)] [SRCategory(nameof(SR.CatBehavior))] [SRDescription(nameof(SR.ColumnHeaderDisplayIndexDescr))] public int DisplayIndex { get { return DisplayIndexInternal; } set { // When the list is being deserialized we need // to take the display index as is. ListView // does correctly synchronize the indices. if (ListView is null) { DisplayIndexInternal = value; return; } if (value < 0 || value > (ListView.Columns.Count - 1)) { throw new ArgumentOutOfRangeException(nameof(DisplayIndex), SR.ColumnHeaderBadDisplayIndex); } int lowDI = Math.Min(DisplayIndexInternal, value); int hiDI = Math.Max(DisplayIndexInternal, value); int[] colsOrder = new int[ListView.Columns.Count]; // set the display indices. This is not an expensive operation because // we only set an integer in the column header class bool hdrMovedForward = value > DisplayIndexInternal; ColumnHeader? movedHdr = null; for (int i = 0; i < ListView.Columns.Count; i++) { ColumnHeader hdr = ListView.Columns[i]; if (hdr.DisplayIndex == DisplayIndexInternal) { movedHdr = hdr; } else if (hdr.DisplayIndex >= lowDI && hdr.DisplayIndex <= hiDI) { hdr.DisplayIndexInternal -= hdrMovedForward ? 1 : -1; } if (i != Index) { colsOrder[hdr.DisplayIndexInternal] = i; } } if (movedHdr is not null) { movedHdr.DisplayIndexInternal = value; colsOrder[movedHdr.DisplayIndexInternal] = movedHdr.Index; } SetDisplayIndices(colsOrder); } } internal int DisplayIndexInternal { get; set; } = -1; /// <summary> /// The index of this column. This index does not necessarily correspond /// to the current visual position of the column in the ListView, because the /// user may order columns if the allowColumnReorder property is true. /// </summary> [Browsable(false)] public int Index { get { if (ListView is not null) { return ListView.GetColumnIndex(this); } return -1; } } [DefaultValue(ImageList.Indexer.DefaultIndex)] [TypeConverter(typeof(ImageIndexConverter))] [Editor($"System.Windows.Forms.Design.ImageIndexEditor, {Assemblies.SystemDesign}", typeof(UITypeEditor))] [RefreshProperties(RefreshProperties.Repaint)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int ImageIndex { get { if (_imageIndexer.Index != ImageList.Indexer.DefaultIndex && ImageList is not null && _imageIndexer.Index >= ImageList.Images.Count) { return ImageList.Images.Count - 1; } return _imageIndexer.Index; } set { ArgumentOutOfRangeException.ThrowIfLessThan(value, ImageList.Indexer.DefaultIndex); if (_imageIndexer.Index == value && value != ImageList.Indexer.DefaultIndex) { return; } _imageIndexer.Index = value; if (ListView is not null && ListView.IsHandleCreated) { ListView.SetColumnInfo(LVCOLUMNW_MASK.LVCF_IMAGE, this); } } } [Browsable(false)] public ImageList? ImageList { // we added the ImageList property so that the ImageIndexConverter can find our image list get { return _imageIndexer.ImageList; } } [DefaultValue(ImageList.Indexer.DefaultKey)] [TypeConverter(typeof(ImageKeyConverter))] [Editor($"System.Windows.Forms.Design.ImageIndexEditor, {Assemblies.SystemDesign}", typeof(UITypeEditor))] [RefreshProperties(RefreshProperties.Repaint)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string ImageKey { get { return _imageIndexer.Key; } set { if (value == _imageIndexer.Key && !string.Equals(value, ImageList.Indexer.DefaultKey)) { return; } _imageIndexer.Key = value; if (ListView is not null && ListView.IsHandleCreated) { ListView.SetColumnInfo(LVCOLUMNW_MASK.LVCF_IMAGE, this); } } } /// <summary> /// Returns the ListView control that this column is displayed in. May be null /// </summary> [Browsable(false)] public ListView? ListView { get; private set; } /// <summary> /// The Name of the column header /// </summary> [Browsable(false)] [SRDescription(nameof(SR.ColumnHeaderNameDescr))] public string? Name { get { return WindowsFormsUtils.GetComponentName(this, _name); } set { if (value is null) { _name = string.Empty; } else { _name = value; } Site?.Name = value; } } /// <summary> /// The text displayed in the column header /// </summary> [Localizable(true)] [SRDescription(nameof(SR.ColumnCaption))] [AllowNull] public string Text { get { return _text ?? nameof(ColumnHeader); } set { if (value is null) { _text = string.Empty; } else { _text = value; } ListView?.SetColumnInfo(LVCOLUMNW_MASK.LVCF_TEXT, this); } } /// <summary> /// The horizontal alignment of the text contained in this column /// </summary> [SRDescription(nameof(SR.ColumnAlignment))] [Localizable(true)] [DefaultValue(HorizontalAlignment.Left)] public HorizontalAlignment TextAlign { get { if (!_textAlignInitialized && (ListView is not null)) { _textAlignInitialized = true; // See below for an explanation of (Index != 0) // Added !IsMirrored if ((Index != 0) && (ListView.RightToLeft == RightToLeft.Yes) && !ListView.IsMirrored) { _textAlign = HorizontalAlignment.Right; } } return _textAlign; } set { // valid values are 0x0 to 0x2. SourceGenerated.EnumValidator.Validate(value); _textAlign = value; // The first column must be left-aligned if (Index == 0 && _textAlign != HorizontalAlignment.Left) { _textAlign = HorizontalAlignment.Left; } if (ListView is not null) { ListView.SetColumnInfo(LVCOLUMNW_MASK.LVCF_FMT, this); ListView.Invalidate(); } } } [SRCategory(nameof(SR.CatData))] [Localizable(false)] [Bindable(true)] [SRDescription(nameof(SR.ControlTagDescr))] [DefaultValue(null)] [TypeConverter(typeof(StringConverter))] public object? Tag { get; set; } internal int WidthInternal { get { return _width; } } /// <summary> /// The width of the column in pixels. /// </summary> [SRDescription(nameof(SR.ColumnWidth))] [Localizable(true)] [DefaultValue(60)] public int Width { get { // Since we can't keep our private width in sync with the real width because // we don't get notified when the user changes it, we need to get this info // from the underlying control every time we're asked. // The underlying control will only report the correct width if it's in Report view if (ListView is not null && ListView.IsHandleCreated && !ListView.Disposing && ListView.View == View.Details) { // Make sure this column has already been added to the ListView, else just return width HWND hwndHdr = (HWND)PInvokeCore.SendMessage(ListView, PInvoke.LVM_GETHEADER); if (!hwndHdr.IsNull) { int nativeColumnCount = (int)PInvokeCore.SendMessage(hwndHdr, PInvoke.HDM_GETITEMCOUNT); if (Index < nativeColumnCount) { _width = (int)PInvokeCore.SendMessage(ListView, PInvoke.LVM_GETCOLUMNWIDTH, (WPARAM)Index); } } } return _width; } set { _width = value; ListView?.SetColumnWidth(Index, ColumnHeaderAutoResizeStyle.None); } } public void AutoResize(ColumnHeaderAutoResizeStyle headerAutoResize) { if (headerAutoResize is < ColumnHeaderAutoResizeStyle.None or > ColumnHeaderAutoResizeStyle.ColumnContent) { throw new InvalidEnumArgumentException(nameof(headerAutoResize), (int)headerAutoResize, typeof(ColumnHeaderAutoResizeStyle)); } ListView?.AutoResizeColumn(Index, headerAutoResize); } /// <summary> /// Creates an identical ColumnHeader, unattached to any ListView /// </summary> public object Clone() { Type clonedType = GetType(); ColumnHeader columnHeader; if (clonedType == typeof(ColumnHeader)) { columnHeader = new ColumnHeader(); } else { columnHeader = (ColumnHeader)Activator.CreateInstance(clonedType)!; } columnHeader._text = _text; columnHeader.Width = _width; columnHeader._textAlign = TextAlign; return columnHeader; } protected override void Dispose(bool disposing) { if (disposing) { if (ListView is not null) { int index = Index; if (index != -1) { ListView.Columns.RemoveAt(index); } } } base.Dispose(disposing); } internal void ReleaseUiaProvider() { PInvoke.UiaDisconnectProvider(_accessibilityObject); _accessibilityObject = null; } private void ResetText() { Text = null; } // Set the display indices of the listview columns private unsafe void SetDisplayIndices(int[] cols) { if (ListView is not null && ListView.IsHandleCreated && !ListView.Disposing) { fixed (int* pCols = cols) { PInvokeCore.SendMessage(ListView, PInvoke.LVM_SETCOLUMNORDERARRAY, (WPARAM)cols.Length, (LPARAM)pCols); } } } private bool ShouldSerializeName() { return !string.IsNullOrEmpty(Name); } private bool ShouldSerializeDisplayIndex() { return DisplayIndex != Index; } internal bool ShouldSerializeText() { return _text is not null; } /// <summary> /// Returns a string representation of this column header /// </summary> public override string ToString() { return $"{nameof(ColumnHeader)}: Text: {Text}"; } }