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/DataGridRowHeader.cs

572 lines
22 KiB
C#
Raw Normal View History

2023-01-13 10:57:37 +08:00
//---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//---------------------------------------------------------------------------
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using MS.Internal;
using EGC = ExtendedGrid.Microsoft.Windows.Controls;
namespace ExtendedGrid.Microsoft.Windows.Controls
{
/// <summary>
/// Represents the header for each row of the DataGrid
/// </summary>
[TemplatePart(Name = "PART_TopHeaderGripper", Type = typeof(Thumb))]
[TemplatePart(Name = "PART_BottomHeaderGripper", Type = typeof(Thumb))]
public class DataGridRowHeader : ButtonBase
{
static DataGridRowHeader()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(EGC.DataGridRowHeader), new FrameworkPropertyMetadata(typeof(EGC.DataGridRowHeader)));
ContentProperty.OverrideMetadata(typeof(EGC.DataGridRowHeader), new FrameworkPropertyMetadata(OnNotifyPropertyChanged, OnCoerceContent));
ContentTemplateProperty.OverrideMetadata(typeof(EGC.DataGridRowHeader), new FrameworkPropertyMetadata(OnNotifyPropertyChanged, OnCoerceContentTemplate));
ContentTemplateSelectorProperty.OverrideMetadata(typeof(EGC.DataGridRowHeader), new FrameworkPropertyMetadata(OnNotifyPropertyChanged, OnCoerceContentTemplateSelector));
StyleProperty.OverrideMetadata(typeof(EGC.DataGridRowHeader), new FrameworkPropertyMetadata(OnNotifyPropertyChanged, OnCoerceStyle));
WidthProperty.OverrideMetadata(typeof(EGC.DataGridRowHeader), new FrameworkPropertyMetadata(OnNotifyPropertyChanged, OnCoerceWidth));
ClickModeProperty.OverrideMetadata(typeof(EGC.DataGridRowHeader), new FrameworkPropertyMetadata(ClickMode.Press));
FocusableProperty.OverrideMetadata(typeof(EGC.DataGridRowHeader), new FrameworkPropertyMetadata(false));
}
#region Automation
protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
{
return new EGC.DataGridRowHeaderAutomationPeer(this);
}
#endregion
#region Layout
/// <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(EGC.DataGridRowHeader), 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(EGC.DataGridRowHeader), new FrameworkPropertyMetadata(Visibility.Visible));
/// <summary>
/// Measure this element and it's child elements.
/// </summary>
/// <remarks>
/// DataGridRowHeader needs to update the DataGrid's RowHeaderActualWidth & use this as it's width so that they all end up the
/// same size.
/// </remarks>
/// <param name="availableSize"></param>
/// <returns></returns>
protected override Size MeasureOverride(Size availableSize)
{
var baseSize = base.MeasureOverride(availableSize);
if (DoubleUtil.IsNaN(DataGridOwner.RowHeaderWidth) &&
baseSize.Width > DataGridOwner.RowHeaderActualWidth)
{
DataGridOwner.RowHeaderActualWidth = baseSize.Width;
}
// Regardless of how width the Header wants to be, we use
// DataGridOwner.RowHeaderActualWidth to ensure they're all the same size.
return new Size(DataGridOwner.RowHeaderActualWidth, baseSize.Height);
}
#endregion
#region Row Communication
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// Give the Row a pointer to the RowHeader so that it can propagate down change notifications
EGC.DataGridRow parent = ParentRow;
if (parent != null)
{
parent.RowHeader = this;
SyncProperties();
}
// Grippers will now be in the Visual tree.
HookupGripperEvents();
}
/// <summary>
/// Update all properties that get a value from the DataGrid
/// </summary>
/// <remarks>
/// See comment on DataGridRow.OnDataGridChanged
/// </remarks>
internal void SyncProperties()
{
EGC.DataGridHelper.TransferProperty(this, ContentProperty);
EGC.DataGridHelper.TransferProperty(this, StyleProperty);
EGC.DataGridHelper.TransferProperty(this, ContentTemplateProperty);
EGC.DataGridHelper.TransferProperty(this, ContentTemplateSelectorProperty);
EGC.DataGridHelper.TransferProperty(this, WidthProperty);
CoerceValue(IsRowSelectedProperty);
// We could be the first row now, so reset the thumb visibility.
OnCanUserResizeRowsChanged();
}
#endregion
#region Property Change Notification
/// <summary>
/// Notifies parts that respond to changes in the properties.
/// </summary>
private static void OnNotifyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((EGC.DataGridRowHeader)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)
{
if (e.Property == EGC.DataGridRow.HeaderProperty || e.Property == ContentProperty)
{
EGC.DataGridHelper.TransferProperty(this, ContentProperty);
}
else if (e.Property == EGC.DataGrid.RowHeaderStyleProperty || e.Property == EGC.DataGridRow.HeaderStyleProperty || e.Property == StyleProperty)
{
EGC.DataGridHelper.TransferProperty(this, StyleProperty);
}
else if (e.Property == EGC.DataGrid.RowHeaderTemplateProperty || e.Property == EGC.DataGridRow.HeaderTemplateProperty || e.Property == ContentTemplateProperty)
{
EGC.DataGridHelper.TransferProperty(this, ContentTemplateProperty);
}
else if (e.Property == EGC.DataGrid.RowHeaderTemplateSelectorProperty || e.Property == EGC.DataGridRow.HeaderTemplateSelectorProperty || e.Property == ContentTemplateSelectorProperty)
{
EGC.DataGridHelper.TransferProperty(this, ContentTemplateSelectorProperty);
}
else if (e.Property == EGC.DataGrid.RowHeaderWidthProperty || e.Property == WidthProperty)
{
EGC.DataGridHelper.TransferProperty(this, WidthProperty);
}
else if (e.Property == EGC.DataGridRow.IsSelectedProperty)
{
CoerceValue(IsRowSelectedProperty);
}
else if (e.Property == EGC.DataGrid.CanUserResizeRowsProperty)
{
OnCanUserResizeRowsChanged();
}
else if (e.Property == EGC.DataGrid.RowHeaderActualWidthProperty)
{
// When the RowHeaderActualWidth changes we need to re-measure to pick up the new value for DesiredSize
this.InvalidateMeasure();
this.InvalidateArrange();
// If the DataGrid has not run layout the headers parent may not position the cells correctly when the header size changes.
// This will cause the cells to be out of sync with the columns. To avoid this we will force a layout of the headers parent panel.
var parent = this.Parent as UIElement;
if (parent != null)
{
parent.InvalidateMeasure();
parent.InvalidateArrange();
}
}
}
#endregion
#region Property Coercion callbacks
/// <summary>
/// Coerces the Content property. We're choosing a value between Row.Header and the Content property on RowHeader.
/// </summary>
private static object OnCoerceContent(DependencyObject d, object baseValue)
{
var header = d as EGC.DataGridRowHeader;
return EGC.DataGridHelper.GetCoercedTransferPropertyValue(
header,
baseValue,
ContentProperty,
header.ParentRow,
EGC.DataGridRow.HeaderProperty);
}
/// <summary>
/// Coerces the ContentTemplate property.
/// </summary>
private static object OnCoerceContentTemplate(DependencyObject d, object baseValue)
{
var header = d as EGC.DataGridRowHeader;
var row = header.ParentRow;
var dataGrid = row != null ? row.DataGridOwner : null;
return EGC.DataGridHelper.GetCoercedTransferPropertyValue(
header,
baseValue,
ContentTemplateProperty,
row,
EGC.DataGridRow.HeaderTemplateProperty,
dataGrid,
EGC.DataGrid.RowHeaderTemplateProperty);
}
/// <summary>
/// Coerces the ContentTemplateSelector property.
/// </summary>
private static object OnCoerceContentTemplateSelector(DependencyObject d, object baseValue)
{
var header = d as EGC.DataGridRowHeader;
var row = header.ParentRow;
var dataGrid = row != null ? row.DataGridOwner : null;
return EGC.DataGridHelper.GetCoercedTransferPropertyValue(
header,
baseValue,
ContentTemplateSelectorProperty,
row,
EGC.DataGridRow.HeaderTemplateSelectorProperty,
dataGrid,
EGC.DataGrid.RowHeaderTemplateSelectorProperty);
}
/// <summary>
/// Coerces the Style property.
/// </summary>
private static object OnCoerceStyle(DependencyObject d, object baseValue)
{
var header = d as EGC.DataGridRowHeader;
return EGC.DataGridHelper.GetCoercedTransferPropertyValue(
header,
baseValue,
StyleProperty,
header.ParentRow,
EGC.DataGridRow.HeaderStyleProperty,
header.DataGridOwner,
EGC.DataGrid.RowHeaderStyleProperty);
}
/// <summary>
/// Coerces the Width property.
/// </summary>
private static object OnCoerceWidth(DependencyObject d, object baseValue)
{
var header = d as EGC.DataGridRowHeader;
return EGC.DataGridHelper.GetCoercedTransferPropertyValue(
header,
baseValue,
WidthProperty,
header.DataGridOwner,
EGC.DataGrid.RowHeaderWidthProperty);
}
#endregion
#region Selection
/// <summary>
/// Indicates whether the owning DataGridRow is selected.
/// </summary>
[Bindable(true), Category("Appearance")]
public bool IsRowSelected
{
get { return (bool)GetValue(IsRowSelectedProperty); }
}
private static readonly DependencyPropertyKey IsRowSelectedPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsRowSelected",
typeof(bool),
typeof(EGC.DataGridRowHeader),
new FrameworkPropertyMetadata(false, null, new CoerceValueCallback(OnCoerceIsRowSelected)));
/// <summary>
/// The DependencyProperty for the IsRowSelected property.
/// </summary>
public static readonly DependencyProperty IsRowSelectedProperty = IsRowSelectedPropertyKey.DependencyProperty;
private static object OnCoerceIsRowSelected(DependencyObject d, object baseValue)
{
EGC.DataGridRowHeader header = (EGC.DataGridRowHeader)d;
EGC.DataGridRow parent = header.ParentRow;
if (parent != null)
{
return parent.IsSelected;
}
return baseValue;
}
/// <summary>
/// Called when the header is clicked.
/// </summary>
protected override void OnClick()
{
base.OnClick();
// The base implementation took capture. This prevents us from doing
// drag selection, so release it.
if (Mouse.Captured == this)
{
ReleaseMouseCapture();
}
EGC.DataGrid dataGridOwner = DataGridOwner;
EGC.DataGridRow parentRow = ParentRow;
if ((dataGridOwner != null) && (parentRow != null))
{
dataGridOwner.HandleSelectionForRowHeaderAndDetailsInput(parentRow, /* startDragging = */ true);
}
}
#endregion
#region Row Resizing
/// <summary>
/// Find grippers and register drag events
///
/// The default style for DataGridRowHeader is
/// +-------------------------------+
/// +-------------------------------+
/// + Gripper +
/// +-------------------------------+
/// + Header +
/// +-------------------------------+
/// + Gripper +
/// +-------------------------------+
/// +-------------------------------+
///
/// The reason we have two grippers is we can't extend the bottom gripper to straddle the line between two
/// headers; the header below would render on top of it.
/// We resize a Row by grabbing the gripper to the bottom; the top gripper thus adjusts the height of
/// the row above it.
/// </summary>
private void HookupGripperEvents()
{
UnhookGripperEvents();
_topGripper = GetTemplateChild(TopHeaderGripperTemplateName) as Thumb;
_bottomGripper = GetTemplateChild(BottomHeaderGripperTemplateName) as Thumb;
if (_topGripper != null)
{
_topGripper.DragStarted += new DragStartedEventHandler(OnRowHeaderGripperDragStarted);
_topGripper.DragDelta += new DragDeltaEventHandler(OnRowHeaderResize);
_topGripper.DragCompleted += new DragCompletedEventHandler(OnRowHeaderGripperDragCompleted);
_topGripper.MouseDoubleClick += new MouseButtonEventHandler(OnGripperDoubleClicked);
SetTopGripperVisibility();
}
if (_bottomGripper != null)
{
_bottomGripper.DragStarted += new DragStartedEventHandler(OnRowHeaderGripperDragStarted);
_bottomGripper.DragDelta += new DragDeltaEventHandler(OnRowHeaderResize);
_bottomGripper.DragCompleted += new DragCompletedEventHandler(OnRowHeaderGripperDragCompleted);
_bottomGripper.MouseDoubleClick += new MouseButtonEventHandler(OnGripperDoubleClicked);
SetBottomGripperVisibility();
}
}
/// <summary>
/// Clear gripper event
/// </summary>
private void UnhookGripperEvents()
{
if (_topGripper != null)
{
_topGripper.DragStarted -= new DragStartedEventHandler(OnRowHeaderGripperDragStarted);
_topGripper.DragDelta -= new DragDeltaEventHandler(OnRowHeaderResize);
_topGripper.DragCompleted -= new DragCompletedEventHandler(OnRowHeaderGripperDragCompleted);
_topGripper.MouseDoubleClick -= new MouseButtonEventHandler(OnGripperDoubleClicked);
_topGripper = null;
}
if (_bottomGripper != null)
{
_bottomGripper.DragStarted -= new DragStartedEventHandler(OnRowHeaderGripperDragStarted);
_bottomGripper.DragDelta -= new DragDeltaEventHandler(OnRowHeaderResize);
_bottomGripper.DragCompleted -= new DragCompletedEventHandler(OnRowHeaderGripperDragCompleted);
_bottomGripper.MouseDoubleClick -= new MouseButtonEventHandler(OnGripperDoubleClicked);
_bottomGripper = null;
}
}
private void SetTopGripperVisibility()
{
if (_topGripper != null)
{
EGC.DataGrid dataGrid = DataGridOwner;
EGC.DataGridRow parent = ParentRow;
if (dataGrid != null && parent != null &&
dataGrid.CanUserResizeRows && dataGrid.Items.Count > 1 &&
!object.ReferenceEquals(parent.Item, dataGrid.Items[0]))
{
_topGripper.Visibility = Visibility.Visible;
}
else
{
_topGripper.Visibility = Visibility.Collapsed;
}
}
}
private void SetBottomGripperVisibility()
{
if (_bottomGripper != null)
{
EGC.DataGrid dataGrid = DataGridOwner;
if (dataGrid != null && dataGrid.CanUserResizeRows)
{
_bottomGripper.Visibility = Visibility.Visible;
}
else
{
_bottomGripper.Visibility = Visibility.Collapsed;
}
}
}
/// <summary>
/// This is the row that the top gripper should be resizing.
/// </summary>
private EGC.DataGridRow PreviousRow
{
get
{
EGC.DataGridRow row = ParentRow;
if (row != null)
{
EGC.DataGrid dataGrid = row.DataGridOwner;
if (dataGrid != null)
{
int index = dataGrid.ItemContainerGenerator.IndexFromContainer(row);
if (index > 0)
{
return (EGC.DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index - 1);
}
}
}
return null;
}
}
/// <summary>
/// Returns either this header or the one before it depending on which Gripper fired the event.
/// </summary>
/// <param name="sender"></param>
private EGC.DataGridRow RowToResize(object gripper)
{
return (gripper == _bottomGripper) ? this.ParentRow : PreviousRow;
}
// Save the original height before resize
private void OnRowHeaderGripperDragStarted(object sender, DragStartedEventArgs e)
{
EGC.DataGridRow rowToResize = RowToResize(sender);
if (rowToResize != null)
{
rowToResize.OnRowResizeStarted();
e.Handled = true;
}
}
private void OnRowHeaderResize(object sender, DragDeltaEventArgs e)
{
EGC.DataGridRow rowToResize = RowToResize(sender);
if (rowToResize != null)
{
rowToResize.OnRowResize(e.VerticalChange);
e.Handled = true;
}
}
// Restores original height if canceled.
private void OnRowHeaderGripperDragCompleted(object sender, DragCompletedEventArgs e)
{
EGC.DataGridRow rowToResize = RowToResize(sender);
if (rowToResize != null)
{
rowToResize.OnRowResizeCompleted(e.Canceled);
e.Handled = true;
}
}
private void OnGripperDoubleClicked(object sender, MouseButtonEventArgs e)
{
EGC.DataGridRow rowToResize = RowToResize(sender);
if (rowToResize != null)
{
rowToResize.OnRowResizeReset();
e.Handled = true;
}
}
private void OnCanUserResizeRowsChanged()
{
SetTopGripperVisibility();
SetBottomGripperVisibility();
}
#endregion
#region Helpers
internal EGC.DataGridRow ParentRow
{
get
{
return EGC.DataGridHelper.FindParent<EGC.DataGridRow>(this);
}
}
private EGC.DataGrid DataGridOwner
{
get
{
EGC.DataGridRow parent = ParentRow;
if (parent != null)
{
return parent.DataGridOwner;
}
return null;
}
}
#endregion
private Thumb _topGripper, _bottomGripper;
private const string TopHeaderGripperTemplateName = "PART_TopHeaderGripper";
private const string BottomHeaderGripperTemplateName = "PART_BottomHeaderGripper";
}
}