using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Diagnostics; using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel.Params; using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel; using MECF.Framework.UI.Client.RecipeEditorLib.DGExtension.CustomColumn; using MECF.Framework.Common.Account.Permissions; using OpenSEMI.ClientBase; using DataGridRowEventArgs = ExtendedGrid.Microsoft.Windows.Controls.DataGridRowEventArgs; using DataGridCellEditEndingEventArgs = ExtendedGrid.Microsoft.Windows.Controls.DataGridCellEditEndingEventArgs; using DataGridBeginningEditEventArgs = ExtendedGrid.Microsoft.Windows.Controls.DataGridBeginningEditEventArgs; using ExDataGridRow = ExtendedGrid.Microsoft.Windows.Controls.DataGridRow; namespace MECF.Framework.UI.Client.RecipeEditorLib.DGExtension { /// /// Interaction logic for DataGridRecipe.xaml /// public partial class DataGridRecipe : UserControl { enum Scroll { VerticalOffset, HorizontalOffset } #region Constructors ScrollViewer scroll; public DataGridRecipe() { InitializeComponent(); } private void dgCustom_ScrollChanged(object sender, ScrollChangedEventArgs e) { if (scroll==null) scroll = (ScrollViewer)e.OriginalSource; if (ScrollViewerOffset == null) ScrollViewerOffset=new double[2]; var _verticalOffset = scroll.VerticalOffset < scroll.ScrollableHeight ? scroll.VerticalOffset : ScrollViewerOffset[(int)Scroll.VerticalOffset] <= scroll.ScrollableHeight? scroll.VerticalOffset : ScrollViewerOffset[(int)Scroll.VerticalOffset]; var _horizontalOffset= scroll.HorizontalOffset < scroll.ScrollableWidth ? scroll.HorizontalOffset : ScrollViewerOffset[(int)Scroll.HorizontalOffset] <= scroll.ScrollableWidth ? scroll.HorizontalOffset: ScrollViewerOffset[(int)Scroll.HorizontalOffset]; ScrollViewerOffset = new double[] { _verticalOffset, _horizontalOffset }; } #endregion #region Properties #endregion #region Dependency Properties public static readonly DependencyProperty AllowDragToFillProperty = DependencyProperty.Register( nameof(AllowDragToFill), typeof(bool), typeof(DataGridRecipe), new PropertyMetadata(true)); public bool AllowDragToFill { get => (bool)GetValue(AllowDragToFillProperty); set => SetValue(AllowDragToFillProperty, value); } public double[] ScrollViewerOffset { get { return (double[])GetValue(ScrollViewerOffsetProperty); } set { SetValue(ScrollViewerOffsetProperty, value); } } // Using a DependencyProperty as the backing store for ScrollViewerOffset. This enables animation, styling, binding, etc... public static readonly DependencyProperty ScrollViewerOffsetProperty = DependencyProperty.Register("ScrollViewerOffset", typeof(double[]), typeof(DataGridRecipe), new PropertyMetadata(new double[2], ScrollViewerOffsetPropertyChangedCallback)); #region RecipeData public static readonly DependencyProperty RecipeProperty = DependencyProperty.Register( nameof(Recipe), typeof(RecipeData), typeof(DataGridRecipe), new PropertyMetadata(default(RecipeData), RecipePropertyChangedCallback)); public ExtendedGrid.Microsoft.Windows.Controls.DataGridHeadersVisibility ComperHeadersVisibility { get { return (ExtendedGrid.Microsoft.Windows.Controls.DataGridHeadersVisibility)GetValue(ComperHeadersVisibilityProperty); } set { SetValue(ComperHeadersVisibilityProperty, value); } } // Using a DependencyProperty as the backing store for ComperHeadersVisibility. This enables animation, styling, binding, etc... public static readonly DependencyProperty ComperHeadersVisibilityProperty = DependencyProperty.Register("ComperHeadersVisibility", typeof(ExtendedGrid.Microsoft.Windows.Controls.DataGridHeadersVisibility), typeof(DataGridRecipe), new PropertyMetadata(ExtendedGrid.Microsoft.Windows.Controls.DataGridHeadersVisibility.Column)); private static void ScrollViewerOffsetPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is DataGridRecipe self) { if (self.scroll != null) { var _scOffset = (double[])e.NewValue; self.scroll.ScrollToVerticalOffset(_scOffset[(int)Scroll.VerticalOffset] <= self.scroll.ScrollableHeight ? _scOffset[(int)Scroll.VerticalOffset] : self.scroll.ScrollableHeight); self.scroll.ScrollToHorizontalOffset(_scOffset[(int)Scroll.HorizontalOffset] <= self.scroll.ScrollableWidth ? _scOffset[(int)Scroll.HorizontalOffset] : self.scroll.ScrollableWidth); } } } private static void RecipePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is DataGridRecipe dg) { if (e.NewValue is RecipeData recipe) { dg.dgCustom.ItemsSource = recipe.Steps; recipe.PropertyChanged += (sender, e) => { // 同步白名单编辑模式 if (e.PropertyName == nameof(RecipeData.IsAccessibleWhitelistEditMode)) { dg.dgCustom.IsAccessibleWhitelistEditMode = recipe.IsAccessibleWhitelistEditMode; } }; // 创建DataGrid列 dg.ClearColumns(); if (recipe.Columns != null) { foreach (var col in recipe.Columns) dg.AddColumn(col); } } else dg.dgCustom.ItemsSource = null; } } /// /// 设置或返回编辑器正在编辑的Recipe。 /// public RecipeData Recipe { get => (RecipeData)GetValue(RecipeProperty); set => SetValue(RecipeProperty, value); } #endregion #region FrozenColumnCount public static readonly DependencyProperty FrozenColumnCountProperty = DependencyProperty.Register( nameof(FrozenColumnCount), typeof(int), typeof(DataGridRecipe), new PropertyMetadata(default(int), FrozenColumnCountPropertyChangedCallback)); private static void FrozenColumnCountPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is DataGridRecipe dg) { if (e.NewValue is int count) dg.dgCustom.FrozenColumnCount = count; else dg.dgCustom.FrozenColumnCount = 0; } } /// /// 设置或返回编辑器冻结的列数。 /// public int FrozenColumnCount { get => (int)GetValue(FrozenColumnCountProperty); set => SetValue(FrozenColumnCountProperty, value); } #endregion #endregion #region Methods private void RowOnSelected(object sender, RoutedEventArgs e) { if (!(e.Source is ExDataGridRow dgr)) return; if (!(dgr.DataContext is RecipeStep recipeStep)) return; var param = recipeStep.FindParamByControlName(RecipeControlNames.STEP_NO); if (param is StepParam sp) { sp.IsChecked = true; } } private void RowOnUnselected(object sender, RoutedEventArgs e) { if (!(e.Source is ExDataGridRow dgr)) return; if (!(dgr.DataContext is RecipeStep recipeStep)) return; var param = recipeStep.FindParamByControlName(RecipeControlNames.STEP_NO); if (param is StepParam sp) { sp.IsChecked = false; } } private void ClearColumns() { dgCustom.Columns.Clear(); } /// /// 生成编辑器的列。 /// private void AddColumn(EditorDataGridTemplateColumnBase col) { // 设置ColumnHeaderTemplate和DataGridCellTemplate if (string.IsNullOrEmpty(col.StringCellTemplate) == false) col.CellTemplate = (DataTemplate)FindResource(col.StringCellTemplate); if (string.IsNullOrEmpty(col.StringCellEditingTemplate) == false) col.CellEditingTemplate = (DataTemplate)FindResource(col.StringCellEditingTemplate); if (string.IsNullOrEmpty(col.StringHeaderTemplate) == false) col.HeaderTemplate = (DataTemplate)FindResource(col.StringHeaderTemplate); // 通过Columns重建XDataGrid列 dgCustom.Columns.Add(col); } /// /// 滚动到并高亮指定的参数单元格。 /// /// public void FocusToParam(IParam param) { if (param == null) return; Recipe.Steps.ResetHighlight(); var row = param.RowOwner; var col = param.ColumnOwner; if (row != null && col != null) { dgCustom.ScrollIntoView(param.Parent, col); param.Highlight(); } } #endregion #region Events private void DgCustom_OnBeginningEdit(object sender, DataGridBeginningEditEventArgs e) { Debug.Assert(e.Row.Item is RecipeStep, "The content of row is not RecipeStep."); if (e.Row.Item is RecipeStep step) { var param = step[e.Column.DisplayIndex]; if (param is IValueParam && param.Permission != MenuPermissionEnum.MP_READ_WRITE || param.IsHideValue) { e.Cancel = true; // 如果单元格为只读权限、或设置了隐藏参数值,则提示不能编辑 var reason = ""; if (param.ColumnPermission != MenuPermissionEnum.MP_READ_WRITE) reason = "Recipe Row Access Denied!\r\n\r\nPlease check the RecipeRow Access Permission."; else if (param.StepPermission != MenuPermissionEnum.MP_READ_WRITE) reason = "Recipe Step Access Denied!\r\n\r\nPlease check the RecipeCol Access Permission."; else if (param.IsHideValue) reason = "Hidden Cell is denied editing."; else reason = "Cell Access Denied!\r\n\r\nPlease check the RecipeRow/RecipeCol Access Permission."; DialogBox.ShowError(reason); } } else e.Cancel = true; } private void DataGridCell_OnPreviewMouseDown(object sender, MouseButtonEventArgs e) { // 仅在编辑AccessPermissionWhitelist编辑模式下有效。 if (!Recipe.IsAccessibleWhitelistEditMode) return; if (sender is Border bdr && bdr.DataContext is Param param) { if (param.IsHighlighted) param.ResetHighlight(); else param.Highlight(); } } private void DgCustom_OnLoadingRow(object sender, DataGridRowEventArgs e) { e.Row.Selected += RowOnSelected; e.Row.Unselected += RowOnUnselected; if (sender is XDataGrid dg && e.Row.DataContext is RecipeStep step) { for (var i = 0; i < step.Count; i++) { step[i].RowOwner = e.Row; step[i].ColumnOwner = dg.Columns[i]; } } } private void DgCustom_OnUnloadingRow(object sender, DataGridRowEventArgs e) { e.Row.Selected -= RowOnSelected; e.Row.Unselected -= RowOnUnselected; } private void DgCustom_OnCellEditEnding(object sender, DataGridCellEditEndingEventArgs e) { if (e.Row.Item is RecipeStep step) { step.CalculateGasFlow(); step.Parent.Validate(); } } private void Expander_OnExpanded(object sender, RoutedEventArgs e) { if (sender is Expander expander && expander.DataContext is ExpanderColumn col) { foreach (var subCol in col.ChildColumns) { subCol.UpdateVisibility(); } } } private void Expander_OnCollapsed(object sender, RoutedEventArgs e) { if (sender is Expander expander && expander.DataContext is ExpanderColumn col) { foreach (var subCol in col.ChildColumns) { subCol.Visibility = Visibility.Collapsed; } } } /// /// 滚动DataGrid视图到指定的配方步骤。 /// /// public void ScrollToRecipeStep(RecipeStep step) { dgCustom.ScrollIntoView(step); } #endregion } }