Sic.Framework/MECF.Framework.UI.Client/RecipeEditorLib/DGExtension/XDataGrid.cs

466 lines
18 KiB
C#
Raw Normal View History

2023-04-13 11:51:03 +08:00
using System;
using System.Collections;
using System.Linq;
2023-04-13 11:51:03 +08:00
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
2023-04-13 11:51:03 +08:00
using System.Windows.Input;
using System.Windows.Media;
using MECF.Framework.Common.Account;
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;
2023-04-13 11:51:03 +08:00
namespace MECF.Framework.UI.Client.RecipeEditorLib.DGExtension
2023-04-13 11:51:03 +08:00
{
public sealed class XDataGrid : ExtendedGrid.Microsoft.Windows.Controls.DataGrid
2023-04-13 11:51:03 +08:00
{
#region Variables
public ScrollViewer ref_scrollviewer;
#endregion
#region Constructors
public XDataGrid()
{
SelectionChanged += OnSelectionChanged;
}
#endregion
#region Properties
2023-04-13 11:51:03 +08:00
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 AllowDragToFillProperty = DependencyProperty.Register(
nameof(AllowDragToFill), typeof(bool), typeof(XDataGrid), new PropertyMetadata(true));
public bool AllowDragToFill
{
get => (bool)GetValue(AllowDragToFillProperty);
set => SetValue(AllowDragToFillProperty, value);
}
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();
if (!AllowDragToFill)
return;
// 如果选中了单元格并且没有在编辑模式显示Adorner
if (CurrentCellContainer.IsEditing || !cellInfo.HasValue)
return;
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
2023-04-13 11:51:03 +08:00
#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);
}
2023-04-13 11:51:03 +08:00
private void _UseHorizontalScrollingChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
PreviewMouseWheel += delegate (object sender, MouseWheelEventArgs args)
2023-04-13 11:51:03 +08:00
{
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))
2023-04-13 11:51:03 +08:00
throw new ArgumentException("Element is not an ItemsControl");
dg._UseHorizontalScrollingChangedCallback(dependencyObject, dependencyPropertyChangedEventArgs);
2023-04-13 11:51:03 +08:00
}
public static void SetUseHorizontalScrolling(ItemsControl element, bool value)
{
element.SetValue(UseHorizontalScrollingProperty, value);
}
public static bool GetUseHorizontalScrolling(ItemsControl element)
{
return (bool)element.GetValue(UseHorizontalScrollingProperty);
}
#endregion
}
}