// 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.Windows.Automation.Peers; using System.Windows.Input; using System.Windows.Media; using System.Windows.Automation; using MS.Internal; namespace System.Windows.Controls.Primitives { /// <summary> /// Container for the Column header object. This is instantiated by the DataGridColumnHeadersPresenter. /// </summary> [TemplatePart(Name = "PART_LeftHeaderGripper", Type = typeof(Thumb))] [TemplatePart(Name = "PART_RightHeaderGripper", Type = typeof(Thumb))] public class DataGridColumnHeader : ButtonBase, IProvideDataGridColumn { #region Constructors static DataGridColumnHeader() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(typeof(DataGridColumnHeader))); ContentProperty.OverrideMetadata(typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(OnNotifyPropertyChanged, OnCoerceContent)); ContentTemplateProperty.OverrideMetadata(typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(null, OnNotifyPropertyChanged, OnCoerceContentTemplate)); ContentTemplateSelectorProperty.OverrideMetadata(typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(null, OnNotifyPropertyChanged, OnCoerceContentTemplateSelector)); ContentStringFormatProperty.OverrideMetadata(typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(null, OnNotifyPropertyChanged, OnCoerceStringFormat)); StyleProperty.OverrideMetadata(typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(null, OnNotifyPropertyChanged, OnCoerceStyle)); HeightProperty.OverrideMetadata(typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(OnNotifyPropertyChanged, OnCoerceHeight)); FocusableProperty.OverrideMetadata(typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(false)); ClipProperty.OverrideMetadata(typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(null, OnCoerceClip)); AutomationProperties.IsOffscreenBehaviorProperty.OverrideMetadata(typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(IsOffscreenBehavior.FromClip)); } /// <summary> /// Instantiates a new instance of this class. /// </summary> public DataGridColumnHeader() { _tracker = new ContainerTracking<DataGridColumnHeader>(this); } #endregion #region Public API /// <summary> /// The Column associated with this DataGridColumnHeader. /// </summary> public DataGridColumn Column { get { return _column; } } /// <summary> /// Property that indicates the brush to use when drawing seperators between headers. /// </summary> public Brush SeparatorBrush { get { return (Brush)GetValue(SeparatorBrushProperty); } set { SetValue(SeparatorBrushProperty, value); } } /// <summary> /// DependencyProperty for SeperatorBrush. /// </summary> public static readonly DependencyProperty SeparatorBrushProperty = DependencyProperty.Register("SeparatorBrush", typeof(Brush), typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(null)); /// <summary> /// Property that indicates the Visibility for the header seperators. /// </summary> public Visibility SeparatorVisibility { get { return (Visibility)GetValue(SeparatorVisibilityProperty); } set { SetValue(SeparatorVisibilityProperty, value); } } /// <summary> /// DependencyProperty for SeperatorBrush. /// </summary> public static readonly DependencyProperty SeparatorVisibilityProperty = DependencyProperty.Register("SeparatorVisibility", typeof(Visibility), typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(Visibility.Visible)); #endregion #region Column Header Generation /// <summary> /// Prepares a column header to be used. Sets up the association between the column header and its column. /// </summary> internal void PrepareColumnHeader(object item, DataGridColumn column) { Debug.Assert(column != null, "This header must have been generated with for a particular column"); Debug.Assert(column.Header == item, "The data item for a ColumnHeader is the Header property of a column"); _column = column; TabIndex = column.DisplayIndex; DataGridHelper.TransferProperty(this, ContentProperty); DataGridHelper.TransferProperty(this, ContentTemplateProperty); DataGridHelper.TransferProperty(this, ContentTemplateSelectorProperty); DataGridHelper.TransferProperty(this, ContentStringFormatProperty); DataGridHelper.TransferProperty(this, StyleProperty); DataGridHelper.TransferProperty(this, HeightProperty); CoerceValue(CanUserSortProperty); CoerceValue(SortDirectionProperty); CoerceValue(IsFrozenProperty); CoerceValue(ClipProperty); CoerceValue(DisplayIndexProperty); } internal void ClearHeader() { _column = null; } /// <summary> /// Used by the DataGridRowGenerator owner to send notifications to the cell container. /// </summary> internal ContainerTracking<DataGridColumnHeader> Tracker { get { return _tracker; } } #endregion #region Resize Gripper /// <summary> /// DependencyPropertyKey for DisplayIndex property /// </summary> private static readonly DependencyPropertyKey DisplayIndexPropertyKey = DependencyProperty.RegisterReadOnly( "DisplayIndex", typeof(int), typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(-1, new PropertyChangedCallback(OnDisplayIndexChanged), new CoerceValueCallback(OnCoerceDisplayIndex))); /// <summary> /// The DependencyProperty for the DisplayIndex property. /// </summary> public static readonly DependencyProperty DisplayIndexProperty = DisplayIndexPropertyKey.DependencyProperty; /// <summary> /// The property which represents the displayindex of the column corresponding to this header /// </summary> public int DisplayIndex { get { return (int)GetValue(DisplayIndexProperty); } } /// <summary> /// Coercion callback for DisplayIndex property /// </summary> private static object OnCoerceDisplayIndex(DependencyObject d, object baseValue) { DataGridColumnHeader header = (DataGridColumnHeader)d; DataGridColumn column = header.Column; if (column != null) { return column.DisplayIndex; } return -1; } /// <summary> /// Property changed call back for DisplayIndex property. /// Sets the visibility of resize grippers accordingly /// </summary> /// <param name="d"></param> /// <param name="e"></param> private static void OnDisplayIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataGridColumnHeader header = (DataGridColumnHeader)d; DataGridColumn column = header.Column; if (column != null) { DataGrid dataGrid = column.DataGridOwner; if (dataGrid != null) { header.SetLeftGripperVisibility(); DataGridColumnHeader nextColumnHeader = dataGrid.ColumnHeaderFromDisplayIndex(header.DisplayIndex + 1); nextColumnHeader?.SetLeftGripperVisibility(column.CanUserResize); } } } /// <summary> /// Override for <see cref="System.Windows.FrameworkElement.OnApplyTemplate">FrameworkElement.OnApplyTemplate</see> /// </summary> public override void OnApplyTemplate() { base.OnApplyTemplate(); HookupGripperEvents(); } /// <summary> /// Find grippers and register drag events /// /// The default style for DataGridHeader is /// +-------------------------------+ /// +---------+ +---------+ /// + Gripper + Header + Gripper + /// + + + + /// +---------+ +---------+ /// +-------------------------------+ /// /// The reason we have two grippers is we can't extend the right gripper to straddle the line between two /// headers; the header to the right would render on top of it. /// We resize a column by grabbing the gripper to the right; the leftmost gripper thus adjusts the width of /// the column to its left. /// </summary> private void HookupGripperEvents() { UnhookGripperEvents(); _leftGripper = GetTemplateChild(LeftHeaderGripperTemplateName) as Thumb; _rightGripper = GetTemplateChild(RightHeaderGripperTemplateName) as Thumb; if (_leftGripper != null) { _leftGripper.DragStarted += new DragStartedEventHandler(OnColumnHeaderGripperDragStarted); _leftGripper.DragDelta += new DragDeltaEventHandler(OnColumnHeaderResize); _leftGripper.DragCompleted += new DragCompletedEventHandler(OnColumnHeaderGripperDragCompleted); _leftGripper.MouseDoubleClick += new MouseButtonEventHandler(OnGripperDoubleClicked); SetLeftGripperVisibility(); } if (_rightGripper != null) { _rightGripper.DragStarted += new DragStartedEventHandler(OnColumnHeaderGripperDragStarted); _rightGripper.DragDelta += new DragDeltaEventHandler(OnColumnHeaderResize); _rightGripper.DragCompleted += new DragCompletedEventHandler(OnColumnHeaderGripperDragCompleted); _rightGripper.MouseDoubleClick += new MouseButtonEventHandler(OnGripperDoubleClicked); SetRightGripperVisibility(); } } /// <summary> /// Clear gripper event /// </summary> private void UnhookGripperEvents() { if (_leftGripper != null) { _leftGripper.DragStarted -= new DragStartedEventHandler(OnColumnHeaderGripperDragStarted); _leftGripper.DragDelta -= new DragDeltaEventHandler(OnColumnHeaderResize); _leftGripper.DragCompleted -= new DragCompletedEventHandler(OnColumnHeaderGripperDragCompleted); _leftGripper.MouseDoubleClick -= new MouseButtonEventHandler(OnGripperDoubleClicked); _leftGripper = null; } if (_rightGripper != null) { _rightGripper.DragStarted -= new DragStartedEventHandler(OnColumnHeaderGripperDragStarted); _rightGripper.DragDelta -= new DragDeltaEventHandler(OnColumnHeaderResize); _rightGripper.DragCompleted -= new DragCompletedEventHandler(OnColumnHeaderGripperDragCompleted); _rightGripper.MouseDoubleClick -= new MouseButtonEventHandler(OnGripperDoubleClicked); _rightGripper = null; } } /// <summary> /// Returns either this header or the one before it depending on which Gripper fired the event. /// </summary> /// <param name="sender"></param> private DataGridColumnHeader HeaderToResize(object gripper) { return (gripper == _rightGripper) ? this : PreviousVisibleHeader; } // Save the original widths before header resize private void OnColumnHeaderGripperDragStarted(object sender, DragStartedEventArgs e) { DataGridColumnHeader header = HeaderToResize(sender); if (header != null) { if (header.Column != null) { DataGrid dataGrid = header.Column.DataGridOwner; dataGrid?.InternalColumns.OnColumnResizeStarted(); } e.Handled = true; } } private void OnColumnHeaderResize(object sender, DragDeltaEventArgs e) { DataGridColumnHeader header = HeaderToResize(sender); if (header != null) { RecomputeColumnWidthsOnColumnResize(header, e.HorizontalChange); e.Handled = true; } } /// <summary> /// Method which recomputes the widths of columns on resize of a header /// </summary> private static void RecomputeColumnWidthsOnColumnResize(DataGridColumnHeader header, double horizontalChange) { Debug.Assert(header != null, "Header should not be null"); DataGridColumn resizingColumn = header.Column; if (resizingColumn == null) { return; } DataGrid dataGrid = resizingColumn.DataGridOwner; if (dataGrid == null) { return; } dataGrid.InternalColumns.RecomputeColumnWidthsOnColumnResize(resizingColumn, horizontalChange, false); } private void OnColumnHeaderGripperDragCompleted(object sender, DragCompletedEventArgs e) { DataGridColumnHeader header = HeaderToResize(sender); if (header != null) { if (header.Column != null) { DataGrid dataGrid = header.Column.DataGridOwner; dataGrid?.InternalColumns.OnColumnResizeCompleted(e.Canceled); } e.Handled = true; } } private void OnGripperDoubleClicked(object sender, MouseButtonEventArgs e) { DataGridColumnHeader header = HeaderToResize(sender); if (header != null && header.Column != null) { // DataGridLength is a struct, so setting to Auto resets desired and display widths to 0.0. header.Column.Width = DataGridLength.Auto; e.Handled = true; } } private DataGridLength ColumnWidth { get { return Column != null ? Column.Width : DataGridLength.Auto; } } private double ColumnActualWidth { get { return Column != null ? Column.ActualWidth : ActualWidth; } } #endregion #region Property Change Notification Propagation /// <summary> /// Notifies the Header of a property change. /// </summary> private static void OnNotifyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((DataGridColumnHeader)d).NotifyPropertyChanged(d, e); } /// <summary> /// Notification for column header-related DependencyProperty changes from the grid or from columns. /// </summary> internal void NotifyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DataGridColumn column = d as DataGridColumn; if ((column != null) && (column != Column)) { // This notification does not apply to this column header return; } if (e.Property == DataGridColumn.WidthProperty) { DataGridHelper.OnColumnWidthChanged(this, e); } else if (e.Property == DataGridColumn.HeaderProperty || e.Property == ContentProperty) { DataGridHelper.TransferProperty(this, ContentProperty); } else if (e.Property == DataGridColumn.HeaderTemplateProperty || e.Property == ContentTemplateProperty) { DataGridHelper.TransferProperty(this, ContentTemplateProperty); } else if (e.Property == DataGridColumn.HeaderTemplateSelectorProperty || e.Property == ContentTemplateSelectorProperty) { DataGridHelper.TransferProperty(this, ContentTemplateSelectorProperty); } else if (e.Property == DataGridColumn.HeaderStringFormatProperty || e.Property == ContentStringFormatProperty) { DataGridHelper.TransferProperty(this, ContentStringFormatProperty); } else if (e.Property == DataGrid.ColumnHeaderStyleProperty || e.Property == DataGridColumn.HeaderStyleProperty || e.Property == StyleProperty) { DataGridHelper.TransferProperty(this, StyleProperty); } else if (e.Property == DataGrid.ColumnHeaderHeightProperty || e.Property == HeightProperty) { DataGridHelper.TransferProperty(this, HeightProperty); } else if (e.Property == DataGridColumn.DisplayIndexProperty) { CoerceValue(DisplayIndexProperty); TabIndex = column.DisplayIndex; } else if (e.Property == DataGrid.CanUserResizeColumnsProperty) { OnCanUserResizeColumnsChanged(); } else if (e.Property == DataGridColumn.CanUserSortProperty) { CoerceValue(CanUserSortProperty); } else if (e.Property == DataGridColumn.SortDirectionProperty) { CoerceValue(SortDirectionProperty); } else if (e.Property == DataGridColumn.IsFrozenProperty) { CoerceValue(IsFrozenProperty); } else if (e.Property == DataGridColumn.CanUserResizeProperty) { OnCanUserResizeChanged(); } else if (e.Property == DataGridColumn.VisibilityProperty) { OnColumnVisibilityChanged(e); } } private void OnCanUserResizeColumnsChanged() { Debug.Assert(Column != null, "column can't be null if we got a notification for this property change"); if (Column.DataGridOwner != null) { SetLeftGripperVisibility(); SetRightGripperVisibility(); } } private void OnCanUserResizeChanged() { Debug.Assert(Column != null, "column can't be null if we got a notification for this property change"); DataGrid dataGrid = Column.DataGridOwner; if (dataGrid != null) { SetNextHeaderLeftGripperVisibility(Column.CanUserResize); SetRightGripperVisibility(); } } private void SetLeftGripperVisibility() { if (_leftGripper == null || Column == null) { return; } DataGrid dataGridOwner = Column.DataGridOwner; bool canPrevColumnResize = false; for (int index = DisplayIndex - 1; index >= 0; index--) { DataGridColumn column = dataGridOwner.ColumnFromDisplayIndex(index); if (column.IsVisible) { canPrevColumnResize = column.CanUserResize; break; } } SetLeftGripperVisibility(canPrevColumnResize); } private void SetLeftGripperVisibility(bool canPreviousColumnResize) { if (_leftGripper == null || Column == null) { return; } DataGrid dataGrid = Column.DataGridOwner; if (dataGrid != null && dataGrid.CanUserResizeColumns && canPreviousColumnResize) { _leftGripper.Visibility = Visibility.Visible; } else { _leftGripper.Visibility = Visibility.Collapsed; } } private void SetRightGripperVisibility() { if (_rightGripper == null || Column == null) { return; } DataGrid dataGrid = Column.DataGridOwner; if (dataGrid != null && dataGrid.CanUserResizeColumns && Column.CanUserResize) { _rightGripper.Visibility = Visibility.Visible; } else { _rightGripper.Visibility = Visibility.Collapsed; } } private void SetNextHeaderLeftGripperVisibility(bool canUserResize) { DataGrid dataGrid = Column.DataGridOwner; int columnCount = dataGrid.Columns.Count; for (int index = DisplayIndex + 1; index < columnCount; index++) { if (dataGrid.ColumnFromDisplayIndex(index).IsVisible) { DataGridColumnHeader nextHeader = dataGrid.ColumnHeaderFromDisplayIndex(index); nextHeader?.SetLeftGripperVisibility(canUserResize); break; } } } private void OnColumnVisibilityChanged(DependencyPropertyChangedEventArgs e) { Debug.Assert(Column != null, "column can't be null if we got a notification for this property change"); DataGrid dataGrid = Column.DataGridOwner; if (dataGrid != null) { bool oldIsVisible = (((Visibility)e.OldValue) == Visibility.Visible); bool newIsVisible = (((Visibility)e.NewValue) == Visibility.Visible); if (oldIsVisible != newIsVisible) { if (newIsVisible) { SetLeftGripperVisibility(); SetRightGripperVisibility(); SetNextHeaderLeftGripperVisibility(Column.CanUserResize); } else { bool canPrevColumnResize = false; for (int index = DisplayIndex - 1; index >= 0; index--) { DataGridColumn column = dataGrid.ColumnFromDisplayIndex(index); if (column.IsVisible) { canPrevColumnResize = column.CanUserResize; break; } } SetNextHeaderLeftGripperVisibility(canPrevColumnResize); } } } } #endregion #region Style and Template Coercion callbacks /// <summary> /// Coerces the Content property. We're choosing a value between Column.Header and the Content property on ColumnHeader. /// </summary> private static object OnCoerceContent(DependencyObject d, object baseValue) { var header = d as DataGridColumnHeader; object content = DataGridHelper.GetCoercedTransferPropertyValue( header, baseValue, ContentProperty, header.Column, DataGridColumn.HeaderProperty); // if content is a WPF element with a logical parent, disconnect it now // so that it can be connected to the DGColumnHeader. This happens during // a theme change FrameworkObject fo = new FrameworkObject(content as DependencyObject); if (fo.Parent != null && fo.Parent != header) { fo.ChangeLogicalParent(null); } return content; } /// <summary> /// Coerces the ContentTemplate property based on the templates defined on the Column. /// </summary> private static object OnCoerceContentTemplate(DependencyObject d, object baseValue) { var columnHeader = d as DataGridColumnHeader; return DataGridHelper.GetCoercedTransferPropertyValue( columnHeader, baseValue, ContentTemplateProperty, columnHeader.Column, DataGridColumn.HeaderTemplateProperty); } /// <summary> /// Coerces the ContentTemplateSelector property based on the selector defined on the Column. /// </summary> private static object OnCoerceContentTemplateSelector(DependencyObject d, object baseValue) { var columnHeader = d as DataGridColumnHeader; return DataGridHelper.GetCoercedTransferPropertyValue( columnHeader, baseValue, ContentTemplateSelectorProperty, columnHeader.Column, DataGridColumn.HeaderTemplateSelectorProperty); } /// <summary> /// Coerces the ContentStringFormat property based on the templates defined on the Column. /// </summary> private static object OnCoerceStringFormat(DependencyObject d, object baseValue) { var columnHeader = d as DataGridColumnHeader; return DataGridHelper.GetCoercedTransferPropertyValue( columnHeader, baseValue, ContentStringFormatProperty, columnHeader.Column, DataGridColumn.HeaderStringFormatProperty); } /// <summary> /// Coerces the Style property based on the templates defined on the Column or DataGrid. /// </summary> private static object OnCoerceStyle(DependencyObject d, object baseValue) { var columnHeader = (DataGridColumnHeader)d; DataGridColumn column = columnHeader.Column; DataGrid dataGrid = null; // Propagate style changes to any filler column headers. if (column == null) { DataGridColumnHeadersPresenter presenter = columnHeader.TemplatedParent as DataGridColumnHeadersPresenter; if (presenter != null) { dataGrid = presenter.ParentDataGrid; } } else { dataGrid = column.DataGridOwner; } return DataGridHelper.GetCoercedTransferPropertyValue( columnHeader, baseValue, StyleProperty, column, DataGridColumn.HeaderStyleProperty, dataGrid, DataGrid.ColumnHeaderStyleProperty); } #endregion #region Auto Sort /// <summary> /// DependencyPropertyKey for CanUserSort property /// </summary> private static readonly DependencyPropertyKey CanUserSortPropertyKey = DependencyProperty.RegisterReadOnly( "CanUserSort", typeof(bool), typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(true, null, new CoerceValueCallback(OnCoerceCanUserSort))); /// <summary> /// The DependencyProperty for the CanUserSort property. /// </summary> public static readonly DependencyProperty CanUserSortProperty = CanUserSortPropertyKey.DependencyProperty; /// <summary> /// CanUserSort is the flag which determines if the datagrid can be sorted based on the column of this header /// </summary> public bool CanUserSort { get { return (bool)GetValue(CanUserSortProperty); } } /// <summary> /// DependencyPropertyKey for SortDirection property /// </summary> private static readonly DependencyPropertyKey SortDirectionPropertyKey = DependencyProperty.RegisterReadOnly( "SortDirection", typeof(Nullable<ListSortDirection>), typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(null, OnVisualStatePropertyChanged, new CoerceValueCallback(OnCoerceSortDirection))); /// <summary> /// The DependencyProperty for the SortDirection property. /// </summary> public static readonly DependencyProperty SortDirectionProperty = SortDirectionPropertyKey.DependencyProperty; /// <summary> /// The property for current sort direction of the column of this header /// </summary> public Nullable<ListSortDirection> SortDirection { get { return (Nullable<ListSortDirection>)GetValue(SortDirectionProperty); } } /// <summary> /// The override of ButtonBase.OnClick. /// Informs the owning datagrid to sort itself after the execution of usual button stuff /// </summary> protected override void OnClick() { if (!SuppressClickEvent) { if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked)) { AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(this); peer?.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked); } base.OnClick(); if (Column != null && Column.DataGridOwner != null) { Column.DataGridOwner.PerformSort(Column); } } } /// <summary> /// Coercion callback for Height property. /// </summary> private static object OnCoerceHeight(DependencyObject d, object baseValue) { var columnHeader = (DataGridColumnHeader)d; DataGridColumn column = columnHeader.Column; DataGrid dataGrid = null; // Propagate style changes to any filler column headers. if (column == null) { DataGridColumnHeadersPresenter presenter = columnHeader.TemplatedParent as DataGridColumnHeadersPresenter; if (presenter != null) { dataGrid = presenter.ParentDataGrid; } } else { dataGrid = column.DataGridOwner; } return DataGridHelper.GetCoercedTransferPropertyValue( columnHeader, baseValue, HeightProperty, dataGrid, DataGrid.ColumnHeaderHeightProperty); } /// <summary> /// Coercion callback for CanUserSort property. Checks for the value of CanUserSort on owning column /// and returns accordingly /// </summary> private static object OnCoerceCanUserSort(DependencyObject d, object baseValue) { DataGridColumnHeader header = (DataGridColumnHeader)d; DataGridColumn column = header.Column; if (column != null) { return column.CanUserSort; } return baseValue; } /// <summary> /// Coercion callback for SortDirection property /// </summary> private static object OnCoerceSortDirection(DependencyObject d, object baseValue) { DataGridColumnHeader header = (DataGridColumnHeader)d; DataGridColumn column = header.Column; if (column != null) { return column.SortDirection; } return baseValue; } #endregion #region Automation protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer() { return new System.Windows.Automation.Peers.DataGridColumnHeaderAutomationPeer(this); } // Called from DataGridColumnHeaderAutomationPeer internal void Invoke() { this.OnClick(); } #endregion #region Frozen Columns /// <summary> /// DependencyPropertyKey for IsFrozen property /// </summary> private static readonly DependencyPropertyKey IsFrozenPropertyKey = DependencyProperty.RegisterReadOnly( "IsFrozen", typeof(bool), typeof(DataGridColumnHeader), new FrameworkPropertyMetadata(false, null, new CoerceValueCallback(OnCoerceIsFrozen))); /// <summary> /// The DependencyProperty for the IsFrozen property. /// </summary> public static readonly DependencyProperty IsFrozenProperty = IsFrozenPropertyKey.DependencyProperty; /// <summary> /// The property to determine if the column corresponding to this header is frozen or not /// </summary> public bool IsFrozen { get { return (bool)GetValue(IsFrozenProperty); } } /// <summary> /// Coercion callback for IsFrozen property /// </summary> private static object OnCoerceIsFrozen(DependencyObject d, object baseValue) { DataGridColumnHeader header = (DataGridColumnHeader)d; DataGridColumn column = header.Column; if (column != null) { return column.IsFrozen; } return baseValue; } /// <summary> /// Coercion call back for clip property which ensures that the header overlapping with frozen /// column gets clipped appropriately. /// </summary> private static object OnCoerceClip(DependencyObject d, object baseValue) { DataGridColumnHeader header = (DataGridColumnHeader)d; Geometry geometry = baseValue as Geometry; Geometry frozenGeometry = DataGridHelper.GetFrozenClipForCell(header); if (frozenGeometry != null) { if (geometry == null) { return frozenGeometry; } geometry = new CombinedGeometry(GeometryCombineMode.Intersect, geometry, frozenGeometry); } return geometry; } #endregion #region Column Reordering internal DataGridColumnHeadersPresenter ParentPresenter { get { if (_parentPresenter == null) { _parentPresenter = ItemsControl.ItemsControlFromItemContainer(this) as DataGridColumnHeadersPresenter; } return _parentPresenter; } } /// <summary> /// Property which determines if click event has to raised or not. Used during column drag drop which could /// be mis-interpreted as a click /// </summary> internal bool SuppressClickEvent { get { return _suppressClickEvent; } set { _suppressClickEvent = value; } } protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); DataGridColumnHeadersPresenter parentPresenter = ParentPresenter; if (parentPresenter != null) { // If clickmode is hover then during the mouse move the hover events will be sent // all the headers in the path. To avoid that we are using a capture if (ClickMode == ClickMode.Hover && e.ButtonState == MouseButtonState.Pressed) { CaptureMouse(); } parentPresenter.OnHeaderMouseLeftButtonDown(e); e.Handled = true; } } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); DataGridColumnHeadersPresenter parentPresenter = ParentPresenter; if (parentPresenter != null) { parentPresenter.OnHeaderMouseMove(e); e.Handled = true; } } protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { base.OnMouseLeftButtonUp(e); DataGridColumnHeadersPresenter parentPresenter = ParentPresenter; if (parentPresenter != null) { if (ClickMode == ClickMode.Hover && IsMouseCaptured) { ReleaseMouseCapture(); } parentPresenter.OnHeaderMouseLeftButtonUp(e); e.Handled = true; } } protected override void OnLostMouseCapture(MouseEventArgs e) { base.OnLostMouseCapture(e); DataGridColumnHeadersPresenter parentPresenter = ParentPresenter; if (parentPresenter != null) { parentPresenter.OnHeaderLostMouseCapture(e); e.Handled = true; } } /// <summary> /// Style key for DataGridColumnDropSeparator /// </summary> public static ComponentResourceKey ColumnHeaderDropSeparatorStyleKey { get { return SystemResourceKey.DataGridColumnHeaderColumnHeaderDropSeparatorStyleKey; } } /// <summary> /// Style key for DataGridColumnFloatingHeader /// </summary> public static ComponentResourceKey ColumnFloatingHeaderStyleKey { get { return SystemResourceKey.DataGridColumnHeaderColumnFloatingHeaderStyleKey; } } #endregion #region VSM internal override void ChangeVisualState(bool useTransitions) { // Common States if (IsPressed) { VisualStates.GoToState(this, useTransitions, VisualStates.StatePressed, VisualStates.StateMouseOver, VisualStates.StateNormal); } else if (IsMouseOver) { VisualStates.GoToState(this, useTransitions, VisualStates.StateMouseOver, VisualStates.StateNormal); } else { VisualStateManager.GoToState(this, VisualStates.StateNormal, useTransitions); } // Sort States var sortDirection = SortDirection; if (sortDirection != null) { if (sortDirection == ListSortDirection.Ascending) { VisualStates.GoToState(this, useTransitions, VisualStates.StateSortAscending, VisualStates.StateUnsorted); } if (sortDirection == ListSortDirection.Descending) { VisualStates.GoToState(this, useTransitions, VisualStates.StateSortDescending, VisualStates.StateUnsorted); } } else { VisualStateManager.GoToState(this, VisualStates.StateUnsorted, useTransitions); } // Don't call base.ChangeVisualState because we dont want to pick up the button's state changes. // This is because SL didn't declare several of the states that Button defines, and we need to be // compatible with them. ChangeValidationVisualState(useTransitions); } #endregion #region Helpers DataGridColumn IProvideDataGridColumn.Column { get { return _column; } } private Panel ParentPanel { get { return VisualParent as Panel; } } /// <summary> /// Used by the resize code -- this is the header that the left gripper should be resizing. /// </summary> private DataGridColumnHeader PreviousVisibleHeader { get { // we need to be able to find the corner header too. DataGridColumn column = Column; if (column != null) { DataGrid dataGridOwner = column.DataGridOwner; if (dataGridOwner != null) { for (int index = DisplayIndex - 1; index >= 0; index--) { if (dataGridOwner.ColumnFromDisplayIndex(index).IsVisible) { return dataGridOwner.ColumnHeaderFromDisplayIndex(index); } } } } return null; } } #endregion #region Data private DataGridColumn _column; private ContainerTracking<DataGridColumnHeader> _tracker; private DataGridColumnHeadersPresenter _parentPresenter; private Thumb _leftGripper, _rightGripper; private bool _suppressClickEvent; private const string LeftHeaderGripperTemplateName = "PART_LeftHeaderGripper"; private const string RightHeaderGripperTemplateName = "PART_RightHeaderGripper"; #endregion } }