File: src\Framework\System\Windows\Controls\Primitives\DataGridColumnHeader.cs
Project: wpf\PresentationFramework.csproj (PresentationFramework)
//---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//---------------------------------------------------------------------------
 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
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);
                    if (nextColumnHeader != null)
                    {
                        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;
                    if (dataGrid != null)
                    {
                        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;
                    if (dataGrid != null)
                    {
                        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);
                    if (nextHeader != null)
                    {
                        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 - see Dev11 146729.
            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);
                    if (peer != null)
                    {
                        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
            {
                // 
                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
    }
}