Sic.Framework-Nanjing-Baishi/MECF.Framework.UI.Client/RecipeEditorLib/DGExtension/XDataGrid.cs

454 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using MECF.Framework.UI.Client.ClientBase;
using MECF.Framework.UI.Client.DataGridTransform.DataGrid.Microsoft.Windows.Controls.Primitives;
using MECF.Framework.UI.Client.RecipeEditorLib.DGExtension.CustomColumn;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel.Params;
using EGC = ExtendedGrid.Microsoft.Windows.Controls;
using Point = System.Windows.Point;
using Size = System.Windows.Size;
namespace MECF.Framework.UI.Client.RecipeEditorLib.DGExtension
{
public sealed class XDataGrid : ExtendedGrid.Microsoft.Windows.Controls.DataGrid
{
#region Variables
public ScrollViewer ref_scrollviewer;
#endregion
#region Constructors
public XDataGrid()
{
SelectionChanged += OnSelectionChanged;
}
#endregion
#region Properties
public static readonly DependencyProperty SelectedItemsListProperty = DependencyProperty.Register(
nameof(SelectedItemsList), typeof(IList), typeof(XDataGrid), new PropertyMetadata(default(IList)));
public IList SelectedItemsList
{
get => (IList)GetValue(SelectedItemsListProperty);
set => SetValue(SelectedItemsListProperty, value);
}
public static readonly DependencyProperty UseHorizontalScrollingProperty = DependencyProperty.RegisterAttached("UseHorizontalScrolling", typeof(bool), typeof(XDataGrid),
new PropertyMetadata(default(bool), UseHorizontalScrollingChangedCallback));
public bool UseHorizontalScrolling
{
get => (bool)GetValue(UseHorizontalScrollingProperty);
set => SetValue(UseHorizontalScrollingProperty, value);
}
public static readonly DependencyProperty IsAccessibleWhitelistEditModeProperty = DependencyProperty.Register(
nameof(IsAccessibleWhitelistEditMode), typeof(bool), typeof(XDataGrid), new PropertyMetadata(default(bool), IsCellAccessPermissionEditModePropertyChangedCallback));
private static void IsCellAccessPermissionEditModePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is XDataGrid grid && e.NewValue is bool isAweMode)
{
if (isAweMode)
{
// 如果进入单元格访问权限编辑模式,清除之前的拖拽框。
grid.ClearCellSelectionAdorner();
}
}
}
/// <summary>
/// 设置或返回当前DataGrid是否处于单元格访问权限编辑模式。
/// </summary>
public bool IsAccessibleWhitelistEditMode
{
get => (bool)GetValue(IsAccessibleWhitelistEditModeProperty);
set => SetValue(IsAccessibleWhitelistEditModeProperty, value);
}
#endregion
#region DPs
public static readonly DependencyProperty DragToFillAdornerThumbStyleProperty = DependencyProperty.Register(
nameof(DragToFillAdornerThumbStyle), typeof(Style), typeof(XDataGrid),
new PropertyMetadata(default(Style)));
public Style DragToFillAdornerThumbStyle
{
get => (Style)GetValue(DragToFillAdornerThumbStyleProperty);
set => SetValue(DragToFillAdornerThumbStyleProperty, value);
}
public static readonly DependencyProperty DragToFillAdornerColorBrushProperty = DependencyProperty.Register(
nameof(DragToFillAdornerColorBrush), typeof(Brush), typeof(XDataGrid), new PropertyMetadata(default(Brush)));
public Brush DragToFillAdornerColorBrush
{
get => (Brush)GetValue(DragToFillAdornerColorBrushProperty);
set => SetValue(DragToFillAdornerColorBrushProperty, value);
}
#endregion
#region Excel-Like Cell Drag-To-Fill Adorner
private DataGridCellDragToFillAdorner _lastAdorner;
private void ClearCellSelectionAdorner()
{
// 清空之前的Adorner
if (_lastAdorner != null && _lastAdorner.AdornedElement is EGC.DataGridCell cell)
{
var al = AdornerLayer.GetAdornerLayer(cell);
var ads = al?.GetAdorners(cell)?.ToList();
if (ads != null && ads.Any())
foreach (var ad in ads)
al.Remove(ad);
}
if (_lastAdorner != null)
{
(_lastAdorner.AdornedElement as EGC.DataGridCell).KeyDown -= Cell_KeyDown;
_lastAdorner.OnDragStart -= LastAdornerOnOnDragStart;
_lastAdorner.OnDragDelta -= LastAdornerOnOnDragDelta;
_lastAdorner.OnDragCompleted -= LastAdornerOnOnDragCompleted;
}
_lastAdorner = null;
}
private void CreateCellSelectionAdorner(EGC.DataGridCellInfo? cellInfo)
{
ClearCellSelectionAdorner();
// 如果选中了单元格并且没有在编辑模式显示Adorner
if (!CurrentCellContainer.IsEditing && cellInfo.HasValue)
{
if (!CheckIsCellSupportDragToFill(TryFindCell(cellInfo.Value)))
return;
var cell = TryFindCell(cellInfo.Value);
cell.KeyDown += Cell_KeyDown;
if (cell != null && cell.DataContext is Param param)
{
// 根据Cell中Param的权限决定是否禁用拖拽填充功能
var isEnabled = param.Permission != MenuPermissionEnum.MP_NONE;
_lastAdorner =
new DataGridCellDragToFillAdorner(cell, new Size(cell.ActualWidth, cell.ActualHeight))
{
IsEnabled = isEnabled,
ThumbStyle = DragToFillAdornerThumbStyle,
Stroke = DragToFillAdornerColorBrush
};
_lastAdorner.OnDragStart += LastAdornerOnOnDragStart;
_lastAdorner.OnDragDelta += LastAdornerOnOnDragDelta;
_lastAdorner.OnDragCompleted += LastAdornerOnOnDragCompleted;
AdornerLayer.GetAdornerLayer(cell).Add(_lastAdorner);
}
}
}
private Param GetParamFromCell(EGC.DataGridCellInfo? cellInfo)
{
if (cellInfo.HasValue
&& cellInfo.Value.Item is RecipeStep rs
&& cellInfo.Value.Column is EditorDataGridTemplateColumnBase col)
{
return rs.FirstOrDefault(x => x.Name == col.ControlName);
}
return null;
}
private Param GetParamFromCell(EGC.DataGridCell cell)
{
return GetParamFromCell(new EGC.DataGridCellInfo(cell));
}
/// <summary>
/// 检查指定的单元格是否支持拖动填充功能。
/// </summary>
/// <param name="cell"></param>
/// <returns></returns>
private bool CheckIsCellSupportDragToFill(EGC.DataGridCell cell)
{
// 仅当单元格绑定的参数为数值型参数、并且参数访问权限不为MP_NONE、并且所属列不为ReadOnly可以开启拖动填充功能
if (cell.DataContext is Param param and IValueParam
&& cell.Column is EditorDataGridTemplateColumnBase { IsExpander: false, IsReadOnly: false })
return true;
return false;
}
/// <summary>
/// If ESC key down when dragging, cancel the dragging.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Cell_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
_lastAdorner?.Cancel();
}
}
private void LastAdornerOnOnDragStart(object sender, DragStartedEventArgs e)
{
//throw new NotImplementedException();
}
private void LastAdornerOnOnDragCompleted(object sender, DragCompletedEventArgs e)
{
if (sender is DataGridCellDragToFillAdorner adorner
&& adorner.AdornedElement is EGC.DataGridCell cell
&& _selectionAnchor != null)
{
// 源单元格不支持拖放填充
if (!CheckIsCellSupportDragToFill(cell))
return;
if (adorner.StartCellIndex != adorner.EndCellIndex)
{
// 复制值
var srcParam = GetParamFromCell(_selectionAnchor);
if (srcParam is IValueParam param)
{
// 获取源数据
var value = param.GetValue();
var itemsHost = InternalItemsHost;
if (itemsHost != null)
{
var start = Math.Min(adorner.StartCellIndex, adorner.EndCellIndex);
var end = Math.Max(adorner.StartCellIndex, adorner.EndCellIndex);
for (var i = start; i <= end; i++)
{
if (itemsHost.Children[i] is EGC.DataGridRow row)
{
var targetCell = row.TryGetCell(_selectionAnchor.Value.Column.DisplayIndex);
// 如果当前行被设置为Visibility.Collapsed则无法通过TryGetCell方法获取Cell对象
if(targetCell == null)
continue;
// 获取目标单元格绑定的Recipe参数对象
var targetParam = GetParamFromCell(targetCell);
// 如果目标参数没有写权限,则不要赋值
if(targetParam.Permission != MenuPermissionEnum.MP_READ_WRITE)
continue;
// 如果目标参数是值型参数,则写入源参数数值
if (targetParam is IValueParam p)
p.SetValue(value);
}
}
}
}
}
// 更新adorner尺寸
//adorner.Arrange(adorner.GetSelectionBound());
}
}
private void LastAdornerOnOnDragDelta(object sender, DragDeltaEventArgs e)
{
if (sender is DataGridCellDragToFillAdorner adorner && adorner.AdornedElement is EGC.DataGridCell cell)
{
// 检查当前Cell是否支持拖动填充
if (!CheckIsCellSupportDragToFill(cell))
return;
var itemsHost = InternalItemsHost;
if (itemsHost == null)
return;
// 获取当前鼠标附近的Cell
var closestCell = MouseOverCell ?? GetCellNearMouse();
if (closestCell != null)
{
var originCellInfo = new EGC.DataGridCellInfo(cell);
var closestCellInfo = new EGC.DataGridCellInfo(closestCell);
var startIndex = Items.IndexOf(originCellInfo.Item);
var endIndex = Items.IndexOf(closestCellInfo.Item);
// 判断当前鼠标的Y坐标是否超过了当前Row高度的一半。
// 如果超过一半,则选中当前行对应的单元格;否则不选择。
var closestRow = closestCell.RowOwner;
var pt = Mouse.GetPosition(closestRow);
var rowBounds = new Rect(new Point(), closestRow.RenderSize);
// 默认选择当前单元格
var recRender = new Rect(0, 0, cell.ActualWidth, cell.ActualHeight);
// 判断鼠标是否划过当前Cell一半的位置超过一半选中没超过则不选中。
if (endIndex > startIndex) // Select the cells below the origin cell.
{
if (pt.Y < (rowBounds.Height / 2.0))
endIndex -= 1; // don't select the cell nearby mouse
recRender.Height = 0;
for (var i = startIndex; i <= endIndex; i++)
{
if (itemsHost.Children[i] is EGC.DataGridRow row)
recRender.Height += row.RenderSize.Height;
}
}
else if (endIndex < startIndex) // Select the cells above the origin cell.
{
if (pt.Y > (rowBounds.Height / 2.0))
endIndex += 1; // don't select the cell nearby mouse
recRender.Height = 0;
for (var i = endIndex; i <= startIndex; i++)
{
if (itemsHost.Children[i] is EGC.DataGridRow row)
recRender.Height += row.RenderSize.Height;
}
/*Debug.WriteLine($"Start: {startIndex}, End: {endIndex}", "Adorner");
Debug.WriteLine($"Rendering Adorner: {recRender}", "Adorner");
Debug.WriteLine($"Cell Height: {cell.ActualHeight}, Render Height: {recRender.Height}", "Adorner");*/
recRender.Y = cell.ActualHeight - recRender.Height;
//Debug.WriteLine($"Render Y: {recRender.Y}", "Adorner");
}
// 重新计算Adorner尺寸
adorner.ArrangeSelectionBound(recRender, startIndex, endIndex);
}
}
}
protected override void EnsureInternalScrollControls()
{
base.EnsureInternalScrollControls();
if (_internalScrollHost != null)
_internalScrollHost.ScrollChanged += (sender, args) =>
{
// Clear the cell adorner while the view is scrolling.
ClearCellSelectionAdorner();
};
}
protected override void MakeFullRowSelection(object dataItem, bool allowsExtendSelect, bool allowsMinimalSelect)
{
base.MakeFullRowSelection(dataItem, allowsExtendSelect, allowsMinimalSelect);
/*if (CurrentCell != null)
CreateCellSelectionAdorner(CurrentCell);*/
}
protected override void MakeCellSelection(EGC.DataGridCellInfo cellInfo, bool allowsExtendSelect, bool allowsMinimalSelect)
{
base.MakeCellSelection(cellInfo, allowsExtendSelect, allowsMinimalSelect);
// 如果是单元格访问权限编辑模式则不要启用Drag-To-Fill功能
if (IsAccessibleWhitelistEditMode)
return;
if (_selectionAnchor != null)
CreateCellSelectionAdorner(_selectionAnchor);
}
public override bool BeginEdit(RoutedEventArgs editingEventArgs)
{
if (IsAccessibleWhitelistEditMode)
return false;
return base.BeginEdit(editingEventArgs);
}
#endregion
#region Methods
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectedItemsList = SelectedItems;
}
protected override void OnArrowKeyDown(KeyEventArgs e)
{
//TODO 重映射方向键后,拖拽填充控件的位置不对,此功能还需要调试。
var remapKey = e.Key;
remapKey = remapKey switch
{
Key.Left => Key.Down,
Key.Down => Key.Right,
Key.Right => Key.Up,
Key.Up => Key.Left,
_ => remapKey
};
var args = new KeyEventArgs(e.KeyboardDevice, e.InputSource, e.Timestamp, remapKey);
args.RoutedEvent = e.RoutedEvent;
base.OnArrowKeyDown(e);
}
private void _UseHorizontalScrollingChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
PreviewMouseWheel += delegate (object sender, MouseWheelEventArgs args)
{
ScrollViewer scrollViewer = GetTemplateChild("DG_ScrollViewer") as ScrollViewer;
if (scrollViewer != null)
{
if (args.Delta > 0)
scrollViewer.LineLeft();
else
scrollViewer.LineRight();
}
ref_scrollviewer = scrollViewer;
args.Handled = true;
};
}
private static void UseHorizontalScrollingChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (!(dependencyObject is XDataGrid dg))
throw new ArgumentException("Element is not an ItemsControl");
dg._UseHorizontalScrollingChangedCallback(dependencyObject, dependencyPropertyChangedEventArgs);
}
public static void SetUseHorizontalScrolling(ItemsControl element, bool value)
{
element.SetValue(UseHorizontalScrollingProperty, value);
}
public static bool GetUseHorizontalScrolling(ItemsControl element)
{
return (bool)element.GetValue(UseHorizontalScrollingProperty);
}
#endregion
}
}