This repository has been archived on 2024-01-02. You can view files and clone it, but cannot push or open issues or pull requests.
Sic06/FrameworkLocal/UIClient/DataGridTransform/DataGrid/Microsoft/Windows/Controls/Primitives/DataGridColumnHeadersPresen...

1053 lines
41 KiB
C#

//---------------------------------------------------------------------------
//
// 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
{
/// <summary>
/// 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.
/// </summary>
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
/// <summary>
/// Tells the row owner about this element.
/// </summary>
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
/// <summary>
/// Measure
/// </summary>
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;
}
/// <summary>
/// Arrange
/// </summary>
/// <param name="finalSize">Arrange size</param>
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;
}
/// <summary>
/// Override of UIElement.GetLayoutClip(). This is a tricky way to ensure we always clip regardless of the value of ClipToBounds.
/// </summary>
protected override Geometry GetLayoutClip(Size layoutSlotSize)
{
RectangleGeometry clip = new RectangleGeometry(new Rect(RenderSize));
clip.Freeze();
return clip;
}
#endregion
#region Column Header Generation
/// <summary>
/// Instantiates an instance of a container.
/// </summary>
/// <returns>A new DataGridColumnHeader.</returns>
protected override DependencyObject GetContainerForItemOverride()
{
return new EGC.DataGridColumnHeader();
}
/// <summary>
/// Determines if an item is its own container.
/// </summary>
/// <param name="item">The item to test.</param>
/// <returns>true if the item is a DataGridColumnHeader, false otherwise.</returns>
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is EGC.DataGridColumnHeader;
}
/// <summary>
/// Method which returns the result of IsItemItsOwnContainerOverride to be used internally
/// </summary>
internal bool IsItemItsOwnContainerInternal(object item)
{
return IsItemItsOwnContainerOverride(item);
}
/// <summary>
/// Prepares a new container for a given item.
/// </summary>
/// <remarks>We do not want to call base.PrepareContainerForItemOverride in this override because it will set local values on the header</remarks>
/// <param name="element">The new container.</param>
/// <param name="item">The item that the container represents.</param>
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);
}
}
/// <summary>
/// Clears a container of references.
/// </summary>
/// <param name="element">The container being cleared.</param>
/// <param name="item">The data item that the container represented.</param>
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
/// <summary>
/// General notification for DependencyProperty changes from the grid.
/// </summary>
internal void NotifyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e, NotificationTarget target)
{
NotifyPropertyChanged(d, string.Empty, e, target);
}
/// <summary>
/// Notification for column header-related DependencyProperty changes from the grid or from columns.
/// </summary>
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<EGC.DataGridColumnHeader> tracker = _headerTrackingRoot;
while (tracker != null)
{
tracker.Container.NotifyPropertyChanged(d, e);
tracker = tracker.Next;
}
}
}
}
#endregion
#region Column Virtualization
/// <summary>
/// Property changed callback for VirtualizingStackPanel.IsVirtualizing property
/// </summary>
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();
}
}
/// <summary>
/// Coercion callback for VirtualizingStackPanel.IsVirtualizing property
/// </summary>
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);
}
/// <summary>
/// Helper method which invalidate the underlying itemshost's measure and arrange
/// </summary>
private void InvalidateDataGridCellsPanelMeasureAndArrange()
{
if (_internalItemsHost != null)
{
_internalItemsHost.InvalidateMeasure();
_internalItemsHost.InvalidateArrange();
}
}
/// <summary>
/// Helper method which invalidate the underlying itemshost's measure and arrange
/// </summary>
/// <param name="withColumnVirtualization">
/// True to invalidate only when virtualization is on.
/// False to invalidate only when virtualization is off.
/// </param>
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();
}
}
/// <summary>
/// Workaround for not being able to access the panel instance of
/// itemscontrol directly
/// </summary>
internal Panel InternalItemsHost
{
get { return _internalItemsHost; }
set { _internalItemsHost = value; }
}
#endregion
#region Column Reordering
/// <summary>
/// Override of VisualChildrenCount which accomodates the indicators as visual children
/// </summary>
protected override int VisualChildrenCount
{
get
{
int visualChildrenCount = base.VisualChildrenCount;
if (_columnHeaderDragIndicator != null)
{
visualChildrenCount++;
}
if (_columnHeaderDropLocationIndicator != null)
{
visualChildrenCount++;
}
return visualChildrenCount;
}
}
/// <summary>
/// Override of GetVisualChild which accomodates the indicators as visual children
/// </summary>
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);
}
/// <summary>
/// Gets called on mouse left button down of child header, and ensures preparation for column header drag
/// </summary>
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();
}
}
/// <summary>
/// Gets called on mouse move of child header, and ensures column header drag
/// </summary>
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);
}
}
}
}
/// <summary>
/// Gets called on mouse left button up of child header, and ensures reordering of columns on successful completion of drag
/// </summary>
internal void OnHeaderMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (_isColumnHeaderDragging)
{
_columnHeaderDragCurrentPosition = e.GetPosition(this);
FinishColumnHeaderDrag(false);
}
else
{
ClearColumnHeaderDragInfo();
}
}
/// <summary>
/// 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
/// </summary>
internal void OnHeaderLostMouseCapture(MouseEventArgs e)
{
if (_isColumnHeaderDragging &&
Mouse.LeftButton == MouseButtonState.Pressed)
{
FinishColumnHeaderDrag(true);
}
}
/// <summary>
/// Helper method which clears the header drag state
/// </summary>
private void ClearColumnHeaderDragInfo()
{
_isColumnHeaderDragging = false;
_prepareColumnHeaderDragging = false;
_draggingSrcColumnHeader = null;
if (_columnHeaderDragIndicator != null)
{
RemoveVisualChild(_columnHeaderDragIndicator);
_columnHeaderDragIndicator = null;
}
if (_columnHeaderDropLocationIndicator != null)
{
RemoveVisualChild(_columnHeaderDropLocationIndicator);
_columnHeaderDropLocationIndicator = null;
}
}
/// <summary>
/// Method which prepares the state for the start of column header drag
/// </summary>
private void PrepareColumnHeaderDrag(EGC.DataGridColumnHeader header, Point pos, Point relativePos)
{
_prepareColumnHeaderDragging = true;
_isColumnHeaderDragging = false;
_draggingSrcColumnHeader = header;
_columnHeaderDragStartPosition = pos;
_columnHeaderDragStartRelativePosition = relativePos;
}
/// <summary>
/// Method which checks if mouse move is sufficient to start the drag
/// </summary>
private static bool CheckStartColumnHeaderDrag(Point currentPos, Point originalPos)
{
return DoubleUtil.GreaterThan(Math.Abs(currentPos.X - originalPos.X), SystemParameters.MinimumHorizontalDragDistance);
}
/// <summary>
/// Method which checks during and after the drag if the position is valid for the drop
/// </summary>
private bool IsMousePositionValidForColumnDrag(double dragFactor)
{
int nearestDisplayIndex = -1;
return IsMousePositionValidForColumnDrag(dragFactor, out nearestDisplayIndex);
}
/// <summary>
/// Method which checks during and after the drag if the position is valid for the drop and returns the drop display index
/// </summary>
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));
}
/// <summary>
/// Method which start the column header drag. Includes raising events and creating default ghosts
/// </summary>
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);
}
}
/// <summary>
/// Method which returns a default control for column header drag indicator
/// </summary>
private Control CreateColumnHeaderDragIndicator()
{
Debug.Assert(_draggingSrcColumnHeader != null, "Dragging header is null");
EGC.DataGridColumnFloatingHeader floatingHeader = new EGC.DataGridColumnFloatingHeader();
floatingHeader.ReferenceHeader = _draggingSrcColumnHeader;
return floatingHeader;
}
/// <summary>
/// Method which set the default values on drag indicator
/// </summary>
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);
}
/// <summary>
/// Method which returns the default control for the column header drop indicator
/// </summary>
private Control CreateColumnHeaderDropIndicator()
{
Debug.Assert(_draggingSrcColumnHeader != null, "Dragging header is null");
EGC.DataGridColumnDropSeparator indicator = new EGC.DataGridColumnDropSeparator();
indicator.ReferenceHeader = _draggingSrcColumnHeader;
return indicator;
}
/// <summary>
/// Method which sets the default values on drop indicator
/// </summary>
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);
}
/// <summary>
/// Method which completes the column header drag. Includes raising of events and changing column display index if needed.
/// </summary>
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;
}
/// <summary>
/// Helper method to determine the display index based on the given position
/// </summary>
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;
}
/// <summary>
/// Helper method to determine the column header based on the given position
/// </summary>
private EGC.DataGridColumnHeader FindColumnHeaderByPosition(Point startPos)
{
Point headerPos;
int displayIndex;
EGC.DataGridColumnHeader header;
FindDisplayIndexAndHeaderPosition(startPos, false, out displayIndex, out headerPos, out header);
return header;
}
/// <summary>
/// Helper method to determine the position of drop indicator based on the given mouse position
/// </summary>
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;
}
/// <summary>
/// Helper method which estimates the column width
/// </summary>
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;
}
/// <summary>
/// Helper method to find display index, header and header start position based on given mouse position
/// </summary>
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<EGC.DataGrid>(this);
}
return _parentDataGrid;
}
}
internal EGC.ContainerTracking<EGC.DataGridColumnHeader> HeaderTrackingRoot
{
get
{
return _headerTrackingRoot;
}
}
#endregion
#region Data
private EGC.ContainerTracking<EGC.DataGridColumnHeader> _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
}
}