//--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using MS.Internal; using EGC = ExtendedGrid.Microsoft.Windows.Controls; namespace ExtendedGrid.Microsoft.Windows.Controls { /// /// A control that will be responsible for generating column headers. /// This control is meant to be specified within the template of the DataGrid. /// /// It typically isn't in the subtree of the main ScrollViewer for the DataGrid. /// It thus handles scrolling the column headers horizontally. For this to work /// it needs to be able to find the ScrollViewer -- this is done by setting the /// SourceScrollViewerName property. /// public class DataGridColumnHeadersPresenter : ItemsControl { static DataGridColumnHeadersPresenter() { Type ownerType = typeof(EGC.DataGridColumnHeadersPresenter); DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(ownerType)); FocusableProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(false)); FrameworkElementFactory factory = new FrameworkElementFactory(typeof(EGC.DataGridCellsPanel)); ItemsPanelProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new ItemsPanelTemplate(factory))); VirtualizingStackPanel.IsVirtualizingProperty.OverrideMetadata( ownerType, new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsVirtualizingPropertyChanged), new CoerceValueCallback(OnCoerceIsVirtualizingProperty))); VirtualizingStackPanel.VirtualizationModeProperty.OverrideMetadata( ownerType, new FrameworkPropertyMetadata(VirtualizationMode.Recycling)); } #region Initialization /// /// Tells the row owner about this element. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); // Find the columns collection and set the ItemsSource. DataGrid grid = ParentDataGrid; if (grid != null) { ItemsSource = new EGC.ColumnHeaderCollection(grid.Columns); grid.ColumnHeadersPresenter = this; EGC.DataGridHelper.TransferProperty(this, VirtualizingStackPanel.IsVirtualizingProperty); } else { ItemsSource = null; } } #endregion #region Automation protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer() { return new EGC.DataGridColumnHeadersPresenterAutomationPeer(this); } #endregion #region Layout /// /// Measure /// protected override Size MeasureOverride(Size availableSize) { Size desiredSize; Size childConstraint = availableSize; childConstraint.Width = Double.PositiveInfinity; desiredSize = base.MeasureOverride(childConstraint); Size indicatorSize; if (_columnHeaderDragIndicator != null && _isColumnHeaderDragging) { _columnHeaderDragIndicator.Measure(childConstraint); indicatorSize = _columnHeaderDragIndicator.DesiredSize; desiredSize.Width = Math.Max(desiredSize.Width, indicatorSize.Width); desiredSize.Height = Math.Max(desiredSize.Height, indicatorSize.Height); } if (_columnHeaderDropLocationIndicator != null && _isColumnHeaderDragging) { _columnHeaderDropLocationIndicator.Measure(availableSize); indicatorSize = _columnHeaderDropLocationIndicator.DesiredSize; desiredSize.Width = Math.Max(desiredSize.Width, indicatorSize.Width); desiredSize.Height = Math.Max(desiredSize.Height, indicatorSize.Height); } desiredSize.Width = Math.Min(availableSize.Width, desiredSize.Width); return desiredSize; } /// /// Arrange /// /// Arrange size protected override Size ArrangeOverride(Size finalSize) { UIElement child = (VisualTreeHelper.GetChildrenCount(this) > 0) ? VisualTreeHelper.GetChild(this, 0) as UIElement : null; if (child != null) { Rect childRect = new Rect(finalSize); EGC.DataGrid dataGrid = ParentDataGrid; if (dataGrid != null) { childRect.X = -dataGrid.HorizontalScrollOffset; childRect.Width = Math.Max(finalSize.Width, dataGrid.CellsPanelActualWidth); } child.Arrange(childRect); } if (_columnHeaderDragIndicator != null && _isColumnHeaderDragging) { _columnHeaderDragIndicator.Arrange(new Rect( new Point(_columnHeaderDragCurrentPosition.X - _columnHeaderDragStartRelativePosition.X, 0), new Size(_columnHeaderDragIndicator.Width, _columnHeaderDragIndicator.Height))); } if (_columnHeaderDropLocationIndicator != null && _isColumnHeaderDragging) { Point point = FindColumnHeaderPositionByCurrentPosition(_columnHeaderDragCurrentPosition, true); double dropIndicatorWidth = _columnHeaderDropLocationIndicator.Width; point.X -= dropIndicatorWidth * 0.5; _columnHeaderDropLocationIndicator.Arrange(new Rect(point, new Size(dropIndicatorWidth, _columnHeaderDropLocationIndicator.Height))); } return finalSize; } /// /// Override of UIElement.GetLayoutClip(). This is a tricky way to ensure we always clip regardless of the value of ClipToBounds. /// protected override Geometry GetLayoutClip(Size layoutSlotSize) { RectangleGeometry clip = new RectangleGeometry(new Rect(RenderSize)); clip.Freeze(); return clip; } #endregion #region Column Header Generation /// /// Instantiates an instance of a container. /// /// A new DataGridColumnHeader. protected override DependencyObject GetContainerForItemOverride() { return new EGC.DataGridColumnHeader(); } /// /// Determines if an item is its own container. /// /// The item to test. /// true if the item is a DataGridColumnHeader, false otherwise. protected override bool IsItemItsOwnContainerOverride(object item) { return item is EGC.DataGridColumnHeader; } /// /// Method which returns the result of IsItemItsOwnContainerOverride to be used internally /// internal bool IsItemItsOwnContainerInternal(object item) { return IsItemItsOwnContainerOverride(item); } /// /// Prepares a new container for a given item. /// /// We do not want to call base.PrepareContainerForItemOverride in this override because it will set local values on the header /// The new container. /// The item that the container represents. protected override void PrepareContainerForItemOverride(DependencyObject element, object item) { EGC.DataGridColumnHeader header = element as EGC.DataGridColumnHeader; if (header != null) { EGC.DataGridColumn column = ColumnFromContainer(header); Debug.Assert(column != null, "We shouldn't have generated this column header if we don't have a column."); if (header.Column == null) { // A null column means this is a fresh container. PrepareContainer will also be called simply if the column's // Header property has changed and this container needs to be given a new item. In that case it'll already be tracked. header.Tracker.Debug_AssertNotInList(_headerTrackingRoot); header.Tracker.StartTracking(ref _headerTrackingRoot); } header.Tracker.Debug_AssertIsInList(_headerTrackingRoot); header.PrepareColumnHeader(item, column); } } /// /// Clears a container of references. /// /// The container being cleared. /// The data item that the container represented. protected override void ClearContainerForItemOverride(DependencyObject element, object item) { EGC.DataGridColumnHeader header = element as EGC.DataGridColumnHeader; base.ClearContainerForItemOverride(element, item); if (header != null) { header.Tracker.StopTracking(ref _headerTrackingRoot); header.ClearHeader(); } } private EGC.DataGridColumn ColumnFromContainer(EGC.DataGridColumnHeader container) { Debug.Assert(HeaderCollection != null, "This is a helper method for preparing and clearing a container; if it's called we must have a valid ItemSource"); int index = ItemContainerGenerator.IndexFromContainer(container); return HeaderCollection.ColumnFromIndex(index); } #endregion #region Notification Propagation /// /// General notification for DependencyProperty changes from the grid. /// internal void NotifyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e, NotificationTarget target) { NotifyPropertyChanged(d, string.Empty, e, target); } /// /// Notification for column header-related DependencyProperty changes from the grid or from columns. /// internal void NotifyPropertyChanged(DependencyObject d, string propertyName, DependencyPropertyChangedEventArgs e, NotificationTarget target) { EGC.DataGridColumn column = d as EGC.DataGridColumn; if (EGC.DataGridHelper.ShouldNotifyColumnHeadersPresenter(target)) { if (e.Property == EGC.DataGridColumn.WidthProperty || e.Property == EGC.DataGridColumn.DisplayIndexProperty) { if (column.IsVisible) { InvalidateDataGridCellsPanelMeasureAndArrange(); } } else if (e.Property == EGC.DataGrid.FrozenColumnCountProperty || e.Property == EGC.DataGridColumn.VisibilityProperty || e.Property == EGC.DataGrid.CellsPanelHorizontalOffsetProperty || string.Compare(propertyName, "ViewportWidth", StringComparison.Ordinal) == 0 || string.Compare(propertyName, "DelayedColumnWidthComputation", StringComparison.Ordinal) == 0) { InvalidateDataGridCellsPanelMeasureAndArrange(); } else if (e.Property == EGC.DataGrid.HorizontalScrollOffsetProperty) { InvalidateArrange(); InvalidateDataGridCellsPanelMeasureAndArrange(); } else if (string.Compare(propertyName, "RealizedColumnsBlockListForNonVirtualizedRows", StringComparison.Ordinal) == 0) { InvalidateDataGridCellsPanelMeasureAndArrange(/* withColumnVirtualization */ false); } else if (string.Compare(propertyName, "RealizedColumnsBlockListForVirtualizedRows", StringComparison.Ordinal) == 0) { InvalidateDataGridCellsPanelMeasureAndArrange(/* withColumnVirtualization */ true); } else if (e.Property == EGC.DataGrid.CellsPanelActualWidthProperty) { InvalidateArrange(); } else if (e.Property == EGC.DataGrid.EnableColumnVirtualizationProperty) { EGC.DataGridHelper.TransferProperty(this, VirtualizingStackPanel.IsVirtualizingProperty); } } if (EGC.DataGridHelper.ShouldNotifyColumnHeaders(target)) { if (e.Property == EGC.DataGridColumn.HeaderProperty) { if (HeaderCollection != null) { HeaderCollection.NotifyHeaderPropertyChanged(column, e); } } else { // Notify the DataGridColumnHeader objects about property changes EGC.ContainerTracking tracker = _headerTrackingRoot; while (tracker != null) { tracker.Container.NotifyPropertyChanged(d, e); tracker = tracker.Next; } } } } #endregion #region Column Virtualization /// /// Property changed callback for VirtualizingStackPanel.IsVirtualizing property /// private static void OnIsVirtualizingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { EGC.DataGridColumnHeadersPresenter headersPresenter = (EGC.DataGridColumnHeadersPresenter)d; EGC.DataGridHelper.TransferProperty(headersPresenter, VirtualizingStackPanel.IsVirtualizingProperty); if (e.OldValue != headersPresenter.GetValue(VirtualizingStackPanel.IsVirtualizingProperty)) { headersPresenter.InvalidateDataGridCellsPanelMeasureAndArrange(); } } /// /// Coercion callback for VirtualizingStackPanel.IsVirtualizing property /// private static object OnCoerceIsVirtualizingProperty(DependencyObject d, object baseValue) { var headersPresenter = d as EGC.DataGridColumnHeadersPresenter; return EGC.DataGridHelper.GetCoercedTransferPropertyValue( headersPresenter, baseValue, VirtualizingStackPanel.IsVirtualizingProperty, headersPresenter.ParentDataGrid, EGC.DataGrid.EnableColumnVirtualizationProperty); } /// /// Helper method which invalidate the underlying itemshost's measure and arrange /// private void InvalidateDataGridCellsPanelMeasureAndArrange() { if (_internalItemsHost != null) { _internalItemsHost.InvalidateMeasure(); _internalItemsHost.InvalidateArrange(); } } /// /// Helper method which invalidate the underlying itemshost's measure and arrange /// /// /// True to invalidate only when virtualization is on. /// False to invalidate only when virtualization is off. /// private void InvalidateDataGridCellsPanelMeasureAndArrange(bool withColumnVirtualization) { // Invalidates measure and arrange if the flag and the virtualization // are either both true or both false. if (withColumnVirtualization == VirtualizingStackPanel.GetIsVirtualizing(this)) { InvalidateDataGridCellsPanelMeasureAndArrange(); } } /// /// Workaround for not being able to access the panel instance of /// itemscontrol directly /// internal Panel InternalItemsHost { get { return _internalItemsHost; } set { _internalItemsHost = value; } } #endregion #region Column Reordering /// /// Override of VisualChildrenCount which accomodates the indicators as visual children /// protected override int VisualChildrenCount { get { int visualChildrenCount = base.VisualChildrenCount; if (_columnHeaderDragIndicator != null) { visualChildrenCount++; } if (_columnHeaderDropLocationIndicator != null) { visualChildrenCount++; } return visualChildrenCount; } } /// /// Override of GetVisualChild which accomodates the indicators as visual children /// protected override Visual GetVisualChild(int index) { int visualChildrenCount = base.VisualChildrenCount; if (index == visualChildrenCount) { if (_columnHeaderDragIndicator != null) { return _columnHeaderDragIndicator; } else if (_columnHeaderDropLocationIndicator != null) { return _columnHeaderDropLocationIndicator; } } if (index == visualChildrenCount + 1) { if (_columnHeaderDragIndicator != null && _columnHeaderDropLocationIndicator != null) { return _columnHeaderDropLocationIndicator; } } return base.GetVisualChild(index); } /// /// Gets called on mouse left button down of child header, and ensures preparation for column header drag /// internal void OnHeaderMouseLeftButtonDown(MouseButtonEventArgs e) { if (ParentDataGrid == null) { return; } if (_columnHeaderDragIndicator != null) { RemoveVisualChild(_columnHeaderDragIndicator); _columnHeaderDragIndicator = null; } if (_columnHeaderDropLocationIndicator != null) { RemoveVisualChild(_columnHeaderDropLocationIndicator); _columnHeaderDropLocationIndicator = null; } Point mousePosition = e.GetPosition(this); EGC.DataGridColumnHeader header = FindColumnHeaderByPosition(mousePosition); if (header != null) { EGC.DataGridColumn column = header.Column; if (ParentDataGrid.CanUserReorderColumns && column.CanUserReorder) { PrepareColumnHeaderDrag(header, e.GetPosition(this), e.GetPosition(header)); } } else { _isColumnHeaderDragging = false; _prepareColumnHeaderDragging = false; _draggingSrcColumnHeader = null; InvalidateArrange(); } } /// /// Gets called on mouse move of child header, and ensures column header drag /// internal void OnHeaderMouseMove(MouseEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed) { if (_prepareColumnHeaderDragging) { _columnHeaderDragCurrentPosition = e.GetPosition(this); if (!_isColumnHeaderDragging) { if (CheckStartColumnHeaderDrag(_columnHeaderDragCurrentPosition, _columnHeaderDragStartPosition)) { StartColumnHeaderDrag(); } } else { bool shouldDisplayDragIndicator = IsMousePositionValidForColumnDrag(2.0); Visibility dragIndicatorVisibility = shouldDisplayDragIndicator ? Visibility.Visible : Visibility.Collapsed; if (_columnHeaderDragIndicator != null) { _columnHeaderDragIndicator.Visibility = dragIndicatorVisibility; } if (_columnHeaderDropLocationIndicator != null) { _columnHeaderDropLocationIndicator.Visibility = dragIndicatorVisibility; } InvalidateArrange(); DragDeltaEventArgs dragDeltaEventArgs = new DragDeltaEventArgs( _columnHeaderDragCurrentPosition.X - _columnHeaderDragStartPosition.X, _columnHeaderDragCurrentPosition.Y - _columnHeaderDragStartPosition.Y); _columnHeaderDragStartPosition = _columnHeaderDragCurrentPosition; ParentDataGrid.OnColumnHeaderDragDelta(dragDeltaEventArgs); } } } } /// /// Gets called on mouse left button up of child header, and ensures reordering of columns on successful completion of drag /// internal void OnHeaderMouseLeftButtonUp(MouseButtonEventArgs e) { if (_isColumnHeaderDragging) { _columnHeaderDragCurrentPosition = e.GetPosition(this); FinishColumnHeaderDrag(false); } else { ClearColumnHeaderDragInfo(); } } /// /// Gets called on mouse lost capture of child header and ensures that when capture gets lost /// the drag ends in appropriate state. In this case it restore the drag state to /// the start of the operation by finishing the drag with cancel flag /// internal void OnHeaderLostMouseCapture(MouseEventArgs e) { if (_isColumnHeaderDragging && Mouse.LeftButton == MouseButtonState.Pressed) { FinishColumnHeaderDrag(true); } } /// /// Helper method which clears the header drag state /// private void ClearColumnHeaderDragInfo() { _isColumnHeaderDragging = false; _prepareColumnHeaderDragging = false; _draggingSrcColumnHeader = null; if (_columnHeaderDragIndicator != null) { RemoveVisualChild(_columnHeaderDragIndicator); _columnHeaderDragIndicator = null; } if (_columnHeaderDropLocationIndicator != null) { RemoveVisualChild(_columnHeaderDropLocationIndicator); _columnHeaderDropLocationIndicator = null; } } /// /// Method which prepares the state for the start of column header drag /// private void PrepareColumnHeaderDrag(EGC.DataGridColumnHeader header, Point pos, Point relativePos) { _prepareColumnHeaderDragging = true; _isColumnHeaderDragging = false; _draggingSrcColumnHeader = header; _columnHeaderDragStartPosition = pos; _columnHeaderDragStartRelativePosition = relativePos; } /// /// Method which checks if mouse move is sufficient to start the drag /// private static bool CheckStartColumnHeaderDrag(Point currentPos, Point originalPos) { return DoubleUtil.GreaterThan(Math.Abs(currentPos.X - originalPos.X), SystemParameters.MinimumHorizontalDragDistance); } /// /// Method which checks during and after the drag if the position is valid for the drop /// private bool IsMousePositionValidForColumnDrag(double dragFactor) { int nearestDisplayIndex = -1; return IsMousePositionValidForColumnDrag(dragFactor, out nearestDisplayIndex); } /// /// Method which checks during and after the drag if the position is valid for the drop and returns the drop display index /// private bool IsMousePositionValidForColumnDrag(double dragFactor, out int nearestDisplayIndex) { nearestDisplayIndex = -1; bool isDraggingColumnFrozen = false; if (_draggingSrcColumnHeader.Column != null) { isDraggingColumnFrozen = _draggingSrcColumnHeader.Column.IsFrozen; } int frozenCount = 0; if (ParentDataGrid != null) { frozenCount = ParentDataGrid.FrozenColumnCount; } nearestDisplayIndex = FindDisplayIndexByPosition(_columnHeaderDragCurrentPosition, true); if (isDraggingColumnFrozen && nearestDisplayIndex >= frozenCount) { return false; } if (!isDraggingColumnFrozen && nearestDisplayIndex < frozenCount) { return false; } double height = 0.0; if (_columnHeaderDragIndicator == null) { height = _draggingSrcColumnHeader.RenderSize.Height; } else { height = Math.Max(_draggingSrcColumnHeader.RenderSize.Height, _columnHeaderDragIndicator.Height); } return DoubleUtil.LessThanOrClose(-height * dragFactor, _columnHeaderDragCurrentPosition.Y) && DoubleUtil.LessThanOrClose(_columnHeaderDragCurrentPosition.Y, height * (dragFactor + 1)); } /// /// Method which start the column header drag. Includes raising events and creating default ghosts /// private void StartColumnHeaderDrag() { Debug.Assert(ParentDataGrid != null, "ParentDataGrid is null"); _columnHeaderDragStartPosition = _columnHeaderDragCurrentPosition; DragStartedEventArgs dragStartedEventArgs = new DragStartedEventArgs(_columnHeaderDragStartPosition.X, _columnHeaderDragStartPosition.Y); ParentDataGrid.OnColumnHeaderDragStarted(dragStartedEventArgs); EGC.DataGridColumnReorderingEventArgs reorderingEventArgs = new EGC.DataGridColumnReorderingEventArgs(_draggingSrcColumnHeader.Column); _columnHeaderDragIndicator = CreateColumnHeaderDragIndicator(); _columnHeaderDropLocationIndicator = CreateColumnHeaderDropIndicator(); reorderingEventArgs.DragIndicator = _columnHeaderDragIndicator; reorderingEventArgs.DropLocationIndicator = _columnHeaderDropLocationIndicator; ParentDataGrid.OnColumnReordering(reorderingEventArgs); if (!reorderingEventArgs.Cancel) { _isColumnHeaderDragging = true; _columnHeaderDragIndicator = reorderingEventArgs.DragIndicator; _columnHeaderDropLocationIndicator = reorderingEventArgs.DropLocationIndicator; if (_columnHeaderDragIndicator != null) { SetDefaultsOnDragIndicator(); AddVisualChild(_columnHeaderDragIndicator); } if (_columnHeaderDropLocationIndicator != null) { SetDefaultsOnDropIndicator(); AddVisualChild(_columnHeaderDropLocationIndicator); } _draggingSrcColumnHeader.SuppressClickEvent = true; InvalidateMeasure(); } else { FinishColumnHeaderDrag(true); } } /// /// Method which returns a default control for column header drag indicator /// private Control CreateColumnHeaderDragIndicator() { Debug.Assert(_draggingSrcColumnHeader != null, "Dragging header is null"); EGC.DataGridColumnFloatingHeader floatingHeader = new EGC.DataGridColumnFloatingHeader(); floatingHeader.ReferenceHeader = _draggingSrcColumnHeader; return floatingHeader; } /// /// Method which set the default values on drag indicator /// private void SetDefaultsOnDragIndicator() { Debug.Assert(_columnHeaderDragIndicator != null, "Drag indicator is null"); Debug.Assert(_draggingSrcColumnHeader != null, "Dragging header is null"); EGC.DataGridColumn column = _draggingSrcColumnHeader.Column; Style style = null; if (column != null) { style = column.DragIndicatorStyle; } _columnHeaderDragIndicator.Style = style; _columnHeaderDragIndicator.CoerceValue(WidthProperty); _columnHeaderDragIndicator.CoerceValue(HeightProperty); } /// /// Method which returns the default control for the column header drop indicator /// private Control CreateColumnHeaderDropIndicator() { Debug.Assert(_draggingSrcColumnHeader != null, "Dragging header is null"); EGC.DataGridColumnDropSeparator indicator = new EGC.DataGridColumnDropSeparator(); indicator.ReferenceHeader = _draggingSrcColumnHeader; return indicator; } /// /// Method which sets the default values on drop indicator /// private void SetDefaultsOnDropIndicator() { Debug.Assert(_columnHeaderDropLocationIndicator != null, "Drag indicator is null"); Debug.Assert(_draggingSrcColumnHeader != null, "Dragging header is null"); Style style = null; if (ParentDataGrid != null) { style = ParentDataGrid.DropLocationIndicatorStyle; } _columnHeaderDropLocationIndicator.Style = style; _columnHeaderDropLocationIndicator.CoerceValue(WidthProperty); _columnHeaderDropLocationIndicator.CoerceValue(HeightProperty); } /// /// Method which completes the column header drag. Includes raising of events and changing column display index if needed. /// private void FinishColumnHeaderDrag(bool isCancel) { Debug.Assert(ParentDataGrid != null, "ParentDataGrid is null"); _prepareColumnHeaderDragging = false; _isColumnHeaderDragging = false; _draggingSrcColumnHeader.SuppressClickEvent = false; if (_columnHeaderDragIndicator != null) { _columnHeaderDragIndicator.Visibility = Visibility.Collapsed; EGC.DataGridColumnFloatingHeader floatingHeader = _columnHeaderDragIndicator as EGC.DataGridColumnFloatingHeader; if (floatingHeader != null) { floatingHeader.ClearHeader(); } RemoveVisualChild(_columnHeaderDragIndicator); } if (_columnHeaderDropLocationIndicator != null) { _columnHeaderDropLocationIndicator.Visibility = Visibility.Collapsed; EGC.DataGridColumnDropSeparator separator = _columnHeaderDropLocationIndicator as EGC.DataGridColumnDropSeparator; if (separator != null) { separator.ReferenceHeader = null; } RemoveVisualChild(_columnHeaderDropLocationIndicator); } DragCompletedEventArgs dragCompletedEventArgs = new DragCompletedEventArgs( _columnHeaderDragCurrentPosition.X - _columnHeaderDragStartPosition.X, _columnHeaderDragCurrentPosition.Y - _columnHeaderDragStartPosition.Y, isCancel); ParentDataGrid.OnColumnHeaderDragCompleted(dragCompletedEventArgs); _draggingSrcColumnHeader.InvalidateArrange(); if (!isCancel) { int newDisplayIndex = -1; bool dragEndPositionValid = IsMousePositionValidForColumnDrag( 2.0, out newDisplayIndex); EGC.DataGridColumn column = _draggingSrcColumnHeader.Column; if (column != null && dragEndPositionValid && newDisplayIndex != column.DisplayIndex) { column.DisplayIndex = newDisplayIndex; EGC.DataGridColumnEventArgs columnEventArgs = new EGC.DataGridColumnEventArgs(_draggingSrcColumnHeader.Column); ParentDataGrid.OnColumnReordered(columnEventArgs); } } _draggingSrcColumnHeader = null; _columnHeaderDragIndicator = null; _columnHeaderDropLocationIndicator = null; } /// /// Helper method to determine the display index based on the given position /// private int FindDisplayIndexByPosition(Point startPos, bool findNearestColumn) { Point headerPos; int displayIndex; EGC.DataGridColumnHeader header; FindDisplayIndexAndHeaderPosition(startPos, findNearestColumn, out displayIndex, out headerPos, out header); return displayIndex; } /// /// Helper method to determine the column header based on the given position /// private EGC.DataGridColumnHeader FindColumnHeaderByPosition(Point startPos) { Point headerPos; int displayIndex; EGC.DataGridColumnHeader header; FindDisplayIndexAndHeaderPosition(startPos, false, out displayIndex, out headerPos, out header); return header; } /// /// Helper method to determine the position of drop indicator based on the given mouse position /// private Point FindColumnHeaderPositionByCurrentPosition(Point startPos, bool findNearestColumn) { Point headerPos; int displayIndex; EGC.DataGridColumnHeader header; FindDisplayIndexAndHeaderPosition(startPos, findNearestColumn, out displayIndex, out headerPos, out header); return headerPos; } /// /// Helper method which estimates the column width /// private static double GetColumnEstimatedWidth(EGC.DataGridColumn column, double averageColumnWidth) { double columnEstimatedWidth = column.Width.DisplayValue; if (DoubleUtil.IsNaN(columnEstimatedWidth)) { columnEstimatedWidth = Math.Max(averageColumnWidth, column.MinWidth); columnEstimatedWidth = Math.Min(columnEstimatedWidth, column.MaxWidth); } return columnEstimatedWidth; } /// /// Helper method to find display index, header and header start position based on given mouse position /// private void FindDisplayIndexAndHeaderPosition(Point startPos, bool findNearestColumn, out int displayIndex, out Point headerPos, out EGC.DataGridColumnHeader header) { Debug.Assert(ParentDataGrid != null, "ParentDataGrid is null"); Point originPoint = new Point(0, 0); headerPos = originPoint; displayIndex = -1; header = null; if (startPos.X < 0.0) { if (findNearestColumn) { displayIndex = 0; } return; } double headerStartX = 0.0; double headerEndX = 0.0; int i = 0; EGC.DataGrid dataGrid = ParentDataGrid; double averageColumnWidth = dataGrid.InternalColumns.AverageColumnWidth; bool firstVisibleNonFrozenColumnHandled = false; for (i = 0; i < dataGrid.Columns.Count; i++) { displayIndex++; EGC.DataGridColumnHeader currentHeader = dataGrid.ColumnHeaderFromDisplayIndex(i); if (currentHeader == null) { EGC.DataGridColumn column = dataGrid.ColumnFromDisplayIndex(i); if (!column.IsVisible) { continue; } else { headerStartX = headerEndX; if (i >= dataGrid.FrozenColumnCount && !firstVisibleNonFrozenColumnHandled) { headerStartX -= dataGrid.HorizontalScrollOffset; firstVisibleNonFrozenColumnHandled = true; } headerEndX = headerStartX + GetColumnEstimatedWidth(column, averageColumnWidth); } } else { GeneralTransform transform = currentHeader.TransformToAncestor(this); headerStartX = transform.Transform(originPoint).X; headerEndX = headerStartX + currentHeader.RenderSize.Width; } if (DoubleUtil.LessThanOrClose(startPos.X, headerStartX)) { break; } if (DoubleUtil.GreaterThanOrClose(startPos.X, headerStartX) && DoubleUtil.LessThanOrClose(startPos.X, headerEndX)) { if (findNearestColumn) { double headerMidX = (headerStartX + headerEndX) * 0.5; if (DoubleUtil.GreaterThanOrClose(startPos.X, headerMidX)) { headerStartX = headerEndX; displayIndex++; } if (_draggingSrcColumnHeader != null && _draggingSrcColumnHeader.Column != null && _draggingSrcColumnHeader.Column.DisplayIndex < displayIndex) { displayIndex--; } } else { header = currentHeader; } break; } } if (i == dataGrid.Columns.Count) { displayIndex = dataGrid.Columns.Count - 1; headerStartX = headerEndX; } headerPos.X = headerStartX; return; } #endregion #region Helpers private EGC.ColumnHeaderCollection HeaderCollection { get { return ItemsSource as EGC.ColumnHeaderCollection; } } internal EGC.DataGrid ParentDataGrid { get { if (_parentDataGrid == null) { _parentDataGrid = EGC.DataGridHelper.FindParent(this); } return _parentDataGrid; } } internal EGC.ContainerTracking HeaderTrackingRoot { get { return _headerTrackingRoot; } } #endregion #region Data private EGC.ContainerTracking _headerTrackingRoot; private EGC.DataGrid _parentDataGrid = null; private bool _prepareColumnHeaderDragging; private bool _isColumnHeaderDragging; private EGC.DataGridColumnHeader _draggingSrcColumnHeader; private Point _columnHeaderDragStartPosition; private Point _columnHeaderDragStartRelativePosition; private Point _columnHeaderDragCurrentPosition; private Control _columnHeaderDropLocationIndicator; private Control _columnHeaderDragIndicator; private Panel _internalItemsHost; #endregion } }