From c1d10f22f575c83f2f15a6ecde42e772a59c2e59 Mon Sep 17 00:00:00 2001 From: cyf Date: Mon, 31 Aug 2026 18:03:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E4=B8=AA=E4=BA=BA?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E5=8F=8A=E8=A1=A8=E6=A0=BC=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 插件库/Lskj.Business/Impl/SystemInfo.cs | 17 +- 插件库/Lskj.Control/BandedGridControlEx.cs | 235 +++++++++++ 插件库/Lskj.Control/FrmExport.cs | 8 +- 插件库/Lskj.Control/GridControlEx.cs | 42 +- 插件库/Lskj.Control/Model/GridExtend.cs | 4 + 插件库/Lskj.Control/ModuleGridDetailEx.cs | 10 + 插件库/Lskj.Control/TabControlEx.cs | 10 + .../Control/MainPanelControlEx2.Designer.cs | 367 +++++++++++++++--- .../Lskj.Main/FrmPersonalSetting.Designer.cs | 171 ++++++++ 插件库/Lskj.Main/FrmPersonalSetting.cs | 251 ++++++++++++ 插件库/Lskj.Main/Lskj.Main.csproj | 6 + 11 files changed, 1033 insertions(+), 88 deletions(-) create mode 100644 插件库/Lskj.Main/FrmPersonalSetting.Designer.cs create mode 100644 插件库/Lskj.Main/FrmPersonalSetting.cs diff --git a/插件库/Lskj.Business/Impl/SystemInfo.cs b/插件库/Lskj.Business/Impl/SystemInfo.cs index b8296d6..68196be 100644 --- a/插件库/Lskj.Business/Impl/SystemInfo.cs +++ b/插件库/Lskj.Business/Impl/SystemInfo.cs @@ -342,12 +342,17 @@ namespace Lskj.Business Instance.IsSpecialAuditSave = item.Table.Columns.Contains("IsSpecialAuditSave") && !string.IsNullOrEmpty(item["IsSpecialAuditSave"] + "") ? "1".Equals(item["IsSpecialAuditSave"] + "") : false; Instance.SuppressImportSuccessTip = item.Table.Columns.Contains("SuppressImportSuccessTip") && !string.IsNullOrEmpty(item["SuppressImportSuccessTip"] + "") ? "1".Equals(item["SuppressImportSuccessTip"] + "") : false; - string newMainValue = item.Table.Columns.Contains("NewMain") ? (item["NewMain"] + "").Trim() : ""; - Instance.NewMain = "1".Equals(newMainValue) || "true".Equals(newMainValue, StringComparison.OrdinalIgnoreCase); + Instance.NewMain = item.Table.Columns.Contains("NewMain") && !string.IsNullOrEmpty(item["NewMain"] + "") ? "1".Equals(item["NewMain"] + "") : false; if (Instance.NewMain) { Instance.MainLeftShowMode = "1"; Instance.DefaultMainTag = 4; + Instance.MainTopColor = item.Table.Columns.Contains("MainTopColor") && !string.IsNullOrEmpty(item["MainTopColor"] + "") ? item["MainTopColor"] + "" : ""; + Instance.MainLeftSelectColor = item.Table.Columns.Contains("MainLeftSelectColor") && !string.IsNullOrEmpty(item["MainLeftSelectColor"] + "") ? item["MainLeftSelectColor"] + "" : ""; + Instance.MainLeftColor = item.Table.Columns.Contains("MainLeftColor") && !string.IsNullOrEmpty(item["MainLeftColor"] + "") ? item["MainLeftColor"] + "" : ""; + Instance.MainTopFontSize = item.Table.Columns.Contains("MainTopFontSize") && !string.IsNullOrEmpty(item["MainTopFontSize"] + "") ? Convert.ToInt32(item["MainTopFontSize"] + "") : 0; + Instance.MainLeftFontSize = item.Table.Columns.Contains("MainLeftFontSize") && !string.IsNullOrEmpty(item["MainLeftFontSize"] + "") ? Convert.ToInt32(item["MainLeftFontSize"] + "") : 0; + } } /// @@ -1203,13 +1208,17 @@ namespace Lskj.Business /// public string MainLeftColor; /// + /// 新版界面左侧选中背景色 + /// + public string MainLeftSelectColor; + /// /// 新版界面顶部字体大小 /// - public string MainTopFontSize; + public int MainTopFontSize; /// /// 新版界面左侧字体大小 /// - public string MainLeftFontSize; + public int MainLeftFontSize; } } diff --git a/插件库/Lskj.Control/BandedGridControlEx.cs b/插件库/Lskj.Control/BandedGridControlEx.cs index 13fc52c..ab9cb05 100644 --- a/插件库/Lskj.Control/BandedGridControlEx.cs +++ b/插件库/Lskj.Control/BandedGridControlEx.cs @@ -122,6 +122,8 @@ namespace Lskj.Control this.bandedGridView.PopupMenuShowing += new DevExpress.XtraGrid.Views.Grid.PopupMenuShowingEventHandler(this.OnGridViewPopupMenuShowing); this.bandedGridView.Click += new EventHandler(OnBandedGridView_Click); this.bandedGridView.DoubleClick += new EventHandler(OnBandedGridViewDoubleClick); + // 多表头的操作列使用 BandedGridView,复用基类的禁编按钮点击处理。 + this.bandedGridView.MouseDown += GridView_MouseDown; this.bandedGridView.CustomDrawGroupRow += new RowObjectCustomDrawEventHandler(OnGridCustomDrawGroupRow); this.bandedGridView.LeftCoordChanged += OnBandedLeftCoordChanged; this.SetGridRowHeightAndFont(); @@ -133,6 +135,236 @@ namespace Lskj.Control this.bandedGridView.ShowingEditor += GridView_ShowingEditor; } + /// + /// 多表头创建或重建操作列。 + /// 操作列不参与业务列配置,只挂到空白一级表头的最右侧。 + /// + protected override void TryInitRightMenuBtnEdit(bool rebuild) + { + if (!this.gridColumnsInitialized || this.Model == null || + (this.CustomColumKey ?? string.Empty).Contains("BaseLeftGridView_")) + { + return; + } + + BandedGridColumn oldColumn = this.bandedGridView.Columns["RightMenuBtnEdit"]; + bool showButtonColumn = this.rightMenuButtonTable != null && + this.rightMenuButtonTable.Rows.Cast().Any(row => + (row.Table.Columns.Contains("IcoName") && + !string.IsNullOrWhiteSpace(row["IcoName"] + "")) || + (row.Table.Columns.Contains("showtoolbar") && + "1".Equals(row["showtoolbar"] + ""))); + if (!showButtonColumn) + { + if (oldColumn != null) + { + this.RemoveRightMenuButtonColumn(oldColumn); + } + return; + } + + if (oldColumn != null) + { + if (!rebuild) + { + return; + } + this.RemoveRightMenuButtonColumn(oldColumn); + } + + this.InitRightMenuBtnEdit(this.rightMenuButtonTable); + DataTable dataSource = this.GridControl == null ? null : this.GridControl.DataSource as DataTable; + if (dataSource != null && !dataSource.Columns.Contains("RightMenuBtnEdit")) + { + dataSource.Columns.Add("RightMenuBtnEdit"); + } + } + + /// + /// 删除旧操作列,同时保持空白一级表头下其他列的现有宽度。 + /// + private void RemoveRightMenuButtonColumn(BandedGridColumn oldColumn) + { + GridBand ownerBand = oldColumn.OwnerBand; + int oldColumnWidth = oldColumn.Width; + int originalBandWidth = ownerBand == null ? 0 : ownerBand.Width; + Dictionary originalColumnWidths = ownerBand == null + ? new Dictionary() + : ownerBand.Columns.Cast() + .Where(column => column != oldColumn) + .ToDictionary(column => column, column => column.Width); + + this.bandedGridView.BeginUpdate(); + try + { + this.bandedGridView.Columns.Remove(oldColumn); + if (ownerBand == null) + { + return; + } + + if (ownerBand.Columns.Count == 0) + { + this.bandedGridView.Bands.Remove(ownerBand); + } + else + { + ownerBand.Width = Math.Max(0, originalBandWidth - oldColumnWidth); + foreach (KeyValuePair item in originalColumnWidths) + { + item.Key.Width = item.Value; + } + } + } + finally + { + this.bandedGridView.EndUpdate(); + } + } + + /// + /// 创建多表头操作列,并将其放到空白一级表头的最右侧。 + /// + protected override void InitRightMenuBtnEdit(DataTable rightMenuTab) + { + if (rightMenuTab == null) + { + return; + } + + BandedGridColumn gridColumn = new BandedGridColumn(); + gridColumn.FieldName = "RightMenuBtnEdit"; + gridColumn.Name = "RightMenuBtnEdit"; + gridColumn.Caption = "操作列"; + gridColumn.Visible = true; + gridColumn.OptionsColumn.AllowEdit = true; + + RepositoryItemButtonEdit itemButtonEdit = new RepositoryItemButtonEdit(); + itemButtonEdit.Buttons.Clear(); + itemButtonEdit.AppearanceDisabled.Options.UseTextOptions = true; + itemButtonEdit.AppearanceDisabled.TextOptions.HAlignment = HorzAlignment.Center; + itemButtonEdit.TextEditStyle = TextEditStyles.HideTextEditor; + itemButtonEdit.ButtonsStyle = BorderStyles.UltraFlat; + itemButtonEdit.BorderStyle = BorderStyles.NoBorder; + itemButtonEdit.ButtonClick += new ButtonPressedEventHandler(OnItemButtonEdit_ButtonClick); + + int columnWidth = 0; + if (rightMenuTab.Columns.Contains("IcoName")) + { + DataRow[] rightMenuButtons = rightMenuTab.Select() + .Where(n => !string.IsNullOrEmpty(n["IcoName"] + "")) + .ToArray(); + int pictureWidth = 30; + foreach (DataRow rightMenu in rightMenuButtons) + { + GridRightMenuModel menuModel = new GridRightMenuModel(rightMenu); + if (string.IsNullOrWhiteSpace(menuModel.MenuName)) + { + continue; + } + + EditorButton editorButton = new EditorButton(); + editorButton.Appearance.BackColor = Color.Transparent; + if (!string.IsNullOrEmpty(menuModel.IcoName)) + { + try + { + Image image = GetRightMenuButtonImage(menuModel.IcoName); + if (image != null && image.Width > 30) + { + pictureWidth = image.Width; + } + editorButton.Image = image; + } + catch (Exception) + { + } + } + editorButton.ToolTip = menuModel.MenuName; + editorButton.Kind = ButtonPredefines.Glyph; + editorButton.Width = pictureWidth; + editorButton.Tag = menuModel; + itemButtonEdit.Buttons.Add(editorButton); + } + columnWidth = rightMenuButtons.Length * pictureWidth; + } + + if (rightMenuTab.Columns.Contains("showtoolbar")) + { + DataRow[] rightMenuTextButtons = rightMenuTab.Select() + .Where(n => "1".Equals(n["showtoolbar"] + "")) + .ToArray(); + foreach (DataRow rightMenu in rightMenuTextButtons) + { + GridRightMenuModel menuModel = new GridRightMenuModel(rightMenu); + if (string.IsNullOrWhiteSpace(menuModel.MenuName)) + { + continue; + } + + EditorButton editorButton = new EditorButton(); + editorButton.Caption = menuModel.MenuName + ""; + editorButton.ToolTip = menuModel.MenuName; + editorButton.Kind = ButtonPredefines.Glyph; + editorButton.Width = this.CalculateTextWidth(editorButton.Caption, editorButton.Appearance.Font); + editorButton.Tag = menuModel; + editorButton.Appearance.BackColor = Color.FromArgb(240, 240, 240); + editorButton.Appearance.BorderColor = Color.FromArgb(180, 180, 180); + editorButton.Appearance.ForeColor = Color.FromArgb(50, 50, 50); + editorButton.Appearance.Options.UseBackColor = true; + editorButton.Appearance.Options.UseBorderColor = true; + editorButton.Appearance.Options.UseForeColor = true; + itemButtonEdit.Buttons.Add(editorButton); + columnWidth += editorButton.Width; + } + } + + gridColumn.ColumnEdit = itemButtonEdit; + gridColumn.MaxWidth = columnWidth; + gridColumn.MinWidth = columnWidth; + gridColumn.Width = columnWidth; + + GridBand emptyBand = this.bandedGridView.Bands.Cast() + .FirstOrDefault(x => string.IsNullOrEmpty(x.Caption)); + if (emptyBand == null) + { + emptyBand = new GridBand(); + emptyBand.Caption = ""; + emptyBand.AppearanceHeader.Options.UseFont = true; + emptyBand.AppearanceHeader.Font = new Font("宋体", 9, FontStyle.Bold); + emptyBand.AppearanceHeader.Options.UseTextOptions = true; + emptyBand.AppearanceHeader.TextOptions.HAlignment = HorzAlignment.Center; + this.bandedGridView.Bands.Add(emptyBand); + } + + int originalBandWidth = emptyBand.Width; + Dictionary originalColumnWidths = emptyBand.Columns + .Cast() + .ToDictionary(column => column, column => column.Width); + + this.bandedGridView.BeginUpdate(); + try + { + emptyBand.Columns.Add(gridColumn); + this.bandedGridView.Columns.Add(gridColumn); + gridColumn.ColVIndex = emptyBand.Columns.Count - 1; + + // DevExpress 默认保持 Band 总宽度,新增列时会压缩同一 Band 下的原列。 + // 恢复原列宽,并单独为操作列扩展一级表头宽度。 + emptyBand.Width = originalBandWidth + columnWidth; + foreach (KeyValuePair item in originalColumnWidths) + { + item.Key.Width = item.Value; + } + gridColumn.Width = columnWidth; + } + finally + { + this.bandedGridView.EndUpdate(); + } + this.bandedGridView.OptionsBehavior.EditorShowMode = EditorShowMode.MouseDown; + } + /// @@ -574,6 +806,7 @@ namespace Lskj.Control protected void AddBandColumns(DataTable table, bool readOnly = true) { if (table == null) return; + this.gridColumnsInitialized = false; this.InitGridColumsTab = table; this.mControlList.Clear(); List bandFieldstr = new List(); @@ -844,6 +1077,8 @@ namespace Lskj.Control // 加载数据源 //BindDataSource(); this.SetCustomColumns();//加载多表头自定义列 + this.gridColumnsInitialized = true; + this.TryInitRightMenuBtnEdit(false); //多表头冻结 foreach (BandedGridColumn item in FrozenColumns) diff --git a/插件库/Lskj.Control/FrmExport.cs b/插件库/Lskj.Control/FrmExport.cs index fae2a18..0912fd3 100644 --- a/插件库/Lskj.Control/FrmExport.cs +++ b/插件库/Lskj.Control/FrmExport.cs @@ -178,6 +178,12 @@ namespace Lskj.Control foreach (GridColumn col in gridView.Columns) { + if (col.FieldName.Equals("RightMenuBtnEdit", StringComparison.OrdinalIgnoreCase)) + { + // 操作列仅用于界面按钮,不应进入导出结果。 + hasBoolean = true; + continue; + } if ((col.ColumnEdit != null && col.ColumnEdit.GetType() == typeof(RepositoryItemCheckEdit)) || (col.ColumnType != null && col.ColumnType.Name.ToLower() == "boolean")) { @@ -209,7 +215,7 @@ namespace Lskj.Control foreach (BandedGridColumn col in gridView.Columns) { GridColumnModel gridModel = col.Tag as GridColumnModel; - if (col.Visible) + if (col.Visible && !col.FieldName.Equals("RightMenuBtnEdit", StringComparison.OrdinalIgnoreCase)) { BandedGridColumn newCol = new BandedGridColumn(); newCol.Tag = col.Tag; diff --git a/插件库/Lskj.Control/GridControlEx.cs b/插件库/Lskj.Control/GridControlEx.cs index 9523c9c..e4cecce 100644 --- a/插件库/Lskj.Control/GridControlEx.cs +++ b/插件库/Lskj.Control/GridControlEx.cs @@ -184,8 +184,8 @@ namespace Lskj.Control /// /// 外部设置的右键菜单数据,用于创建每行操作列。 /// - private DataTable rightMenuButtonTable; - private bool gridColumnsInitialized; + protected DataTable rightMenuButtonTable; + protected bool gridColumnsInitialized; /// /// 新版模块选择框缓存。同一关联模块的单选和多选界面分别复用。 @@ -5649,8 +5649,8 @@ namespace Lskj.Control /// internal void SetRightMenuButtonTable(DataTable table) { - // 树表格和多表头表格原来不在此处创建操作列,维持原有行为。 - if (this is TreeGridControlEx || this is BandedGridControlEx) + // 树表格仍由原有逻辑处理,不创建操作列。 + if (this is TreeGridControlEx) { return; } @@ -5659,7 +5659,7 @@ namespace Lskj.Control this.TryInitRightMenuBtnEdit(true); } - private void TryInitRightMenuBtnEdit(bool rebuild) + protected virtual void TryInitRightMenuBtnEdit(bool rebuild) { if (!this.gridColumnsInitialized || this.Model == null || this.CustomColumKey.Contains("BaseLeftGridView_")) @@ -5689,7 +5689,7 @@ namespace Lskj.Control } } - private void InitRightMenuBtnEdit(DataTable rightMenuTab) + protected virtual void InitRightMenuBtnEdit(DataTable rightMenuTab) { GridColumn gridColumn = new GridColumn(); gridColumn.FieldName = "RightMenuBtnEdit"; @@ -9771,7 +9771,7 @@ namespace Lskj.Control /// /// /// - private void OnItemButtonEdit_ButtonClick(object sender, ButtonPressedEventArgs e) + protected void OnItemButtonEdit_ButtonClick(object sender, ButtonPressedEventArgs e) { EditorButton editorButton = e.Button as EditorButton; GridRightMenuModel menuModel = editorButton.Tag as GridRightMenuModel; @@ -9779,8 +9779,8 @@ namespace Lskj.Control if (toolStripMenuItem != null) { bool allowClick = true; - int[] mSelectRows = this.gridView.GetSelectedRows(); - DataRow mSelectRow = this.gridView.GetDataRow(mSelectRows[0]); + int[] mSelectRows = this.GridView.GetSelectedRows(); + DataRow mSelectRow = this.GridView.GetDataRow(mSelectRows[0]); if (menuModel != null) { if (menuModel.PrivilegeOper.Length > 1 && !menuModel.PrivilegeOper.Contains(ERPInfo.Instance.UserName + ",")) @@ -9797,13 +9797,13 @@ namespace Lskj.Control if (this.gridViewRightMenu.ControlObj != null) cond = this.gridViewRightMenu.ControlObj.ReplaceParentControlValue(menuModel.MenuCond); cond = ReplaceHelper.ReplaceRowParam(mSelectRow, menuModel.MenuCond); - if (this.gridView != null) + if (this.GridView != null) { - GridCell[] cells = this.gridView.GetSelectedCells(); + GridCell[] cells = this.GridView.GetSelectedCells(); if (cells != null && cells.Length > 0 && cond.Contains("{COLUMN_")) { GridCell cell = cells[0]; - DataRow focusedRow = this.gridView.GetFocusedDataRow(); + DataRow focusedRow = this.GridView.GetFocusedDataRow(); string colValue = focusedRow == null || !focusedRow.Table.Columns.Contains(cell.Column.Name) ? "" : focusedRow[cell.Column.Name] + ""; cond = cond.ReplaceColumnParam(cell.Column.Name, cell.Column.Caption, colValue); } @@ -11113,7 +11113,7 @@ namespace Lskj.Control return Math.Max(textWidth + padding, 40); } - private Image GetRightMenuButtonImage(string imageName) + protected Image GetRightMenuButtonImage(string imageName) { if (string.IsNullOrWhiteSpace(imageName)) { @@ -11177,7 +11177,7 @@ namespace Lskj.Control /// /// /// - private void GridView_MouseDown(object sender, MouseEventArgs e) + protected void GridView_MouseDown(object sender, MouseEventArgs e) { if (e.Button != MouseButtons.Left) return; @@ -11190,7 +11190,7 @@ namespace Lskj.Control RepositoryItemButtonEdit repo = hitInfo.Column.ColumnEdit as RepositoryItemButtonEdit; if (!hitInfo.Column.FieldName.Equals("RightMenuBtnEdit")) return; int rowHandle = hitInfo.RowHandle; - this.gridView.SelectRowHandler(rowHandle); + view.SelectRowHandler(rowHandle); int buttonIndex = this.GetClickedButtonIndex(repo, view, rowHandle, hitInfo.Column, e.Location, hitInfo.Column.Width); if (buttonIndex != -1) @@ -11207,7 +11207,7 @@ namespace Lskj.Control /// 执行点击的右键按钮 /// /// - private void RightButtonClick(EditorButton editorButton) + protected void RightButtonClick(EditorButton editorButton) { GridRightMenuModel menuModel = editorButton.Tag as GridRightMenuModel; @@ -11215,8 +11215,8 @@ namespace Lskj.Control if (toolStripMenuItem != null) { bool allowClick = true; - int[] mSelectRows = this.gridView.GetSelectedRows(); - DataRow mSelectRow = this.gridView.GetDataRow(mSelectRows[0]); + int[] mSelectRows = this.GridView.GetSelectedRows(); + DataRow mSelectRow = this.GridView.GetDataRow(mSelectRows[0]); if (menuModel != null) { if (menuModel.PrivilegeOper.Length > 1 && !menuModel.PrivilegeOper.Contains(ERPInfo.Instance.UserName + ",")) @@ -11233,13 +11233,13 @@ namespace Lskj.Control if (this.gridViewRightMenu.ControlObj != null) cond = this.gridViewRightMenu.ControlObj.ReplaceParentControlValue(menuModel.MenuCond); cond = ReplaceHelper.ReplaceRowParam(mSelectRow, menuModel.MenuCond); - if (this.gridView != null) + if (this.GridView != null) { - GridCell[] cells = this.gridView.GetSelectedCells(); + GridCell[] cells = this.GridView.GetSelectedCells(); if (cells != null && cells.Length > 0 && cond.Contains("{COLUMN_")) { GridCell cell = cells[0]; - DataRow focusedRow = this.gridView.GetFocusedDataRow(); + DataRow focusedRow = this.GridView.GetFocusedDataRow(); string colValue = focusedRow == null || !focusedRow.Table.Columns.Contains(cell.Column.Name) ? "" : focusedRow[cell.Column.Name] + ""; cond = cond.ReplaceColumnParam(cell.Column.Name, cell.Column.Caption, colValue); } diff --git a/插件库/Lskj.Control/Model/GridExtend.cs b/插件库/Lskj.Control/Model/GridExtend.cs index a7031ee..d64415b 100644 --- a/插件库/Lskj.Control/Model/GridExtend.cs +++ b/插件库/Lskj.Control/Model/GridExtend.cs @@ -622,6 +622,8 @@ namespace Lskj.Control.Model } GridColumnModel model = col.Tag as GridColumnModel; + // 操作列是运行时创建的界面列,不参与个性化保存。 + if (model == null) continue; DataRow a = customTable.Rows.Cast().FirstOrDefault(x => col.Name.Equals(x["fieldName"] + "", StringComparison.OrdinalIgnoreCase)); string tempSql = a == null ? string.Format(@"insert into {8}(formkey,fieldname,username,orderid,isvisible,operatorid,operatorName,operatedate,fieldWidth,IfFixColumn,FieldText {11}) values('{0}','{1}','{2}','{3}','{4}','{5}','{6}',getdate(),'{7}','{9}','{10}' {12});", key, col.FieldName, col.Caption, col.VisibleIndex, Convert.ToInt32(col.Visible), ERPInfo.Instance.UserId, ERPInfo.Instance.UserName, col.Width, ResourceKeys.SettingTableName, col.OwnerBand != null ? col.OwnerBand.Fixed == DevExpress.XtraGrid.Columns.FixedStyle.Left ? 1 : 0 : 0, model.FieldText, saveFieldName, fieldValue) : string.Format(@"update {6} set orderid='{0}',fieldWidth='{1}',isvisible={2},IfFixColumn='{7}' {8} where formkey='{3}' and fieldname='{4}' and operatorid='{5}';", @@ -1146,6 +1148,8 @@ namespace Lskj.Control.Model gridControl.gridViewRightMenu = rightMenu; rightMenu.InitRightMenus(gridControl, table, model, control, menu); rightMenu.SetRightCallback(handler); + // 右键菜单创建完成后再生成多表头操作列,确保按钮数据完整。 + gridControl.SetRightMenuButtonTable(table); } /// /// 说明:设置图标右键菜单 diff --git a/插件库/Lskj.Control/ModuleGridDetailEx.cs b/插件库/Lskj.Control/ModuleGridDetailEx.cs index bf78a5a..3b2e6ad 100644 --- a/插件库/Lskj.Control/ModuleGridDetailEx.cs +++ b/插件库/Lskj.Control/ModuleGridDetailEx.cs @@ -358,6 +358,11 @@ namespace Lskj.Control tcButtom.OnDetailRightCallback += OnGridViewRightCallBack; } + if (this.SysModel.RefreshTheInterface) + { + + } + this.tcButtom.Model = this.Model; this.tcButtom.DrgaParentPrimaryKey = this.SysModel.ParmaryKey; this.tcButtom.IsDragDetail = this.IsDragDetail; @@ -387,6 +392,11 @@ namespace Lskj.Control tcButtom.OnDetailRightCallback += OnGridViewRightCallBack; } + if (this.SysModel.RefreshTheInterface) + { + + } + this.tcButtom.Model = this.Model; this.tcButtom.DrgaParentPrimaryKey = this.SysModel.ParmaryKey; this.tcButtom.IsDragDetail = this.IsDragDetail; diff --git a/插件库/Lskj.Control/TabControlEx.cs b/插件库/Lskj.Control/TabControlEx.cs index 8a8cdb4..e1fd8c1 100644 --- a/插件库/Lskj.Control/TabControlEx.cs +++ b/插件库/Lskj.Control/TabControlEx.cs @@ -142,6 +142,13 @@ namespace Lskj.Control /// public event EventHandler BillDetailRightGridCallBack; + /// + /// 新版左右结构下,左侧或者右侧的表格数据改变后,刷新主表数据(主表刷新后会刷新当前明细或者全部明细) + /// + /// The sender. + public event EventHandler OnDataChangeExecution; + + /// /// 明细页签与主模块对应关系 /// @@ -181,6 +188,9 @@ namespace Lskj.Control /// 页签名(判断是否有重复的左右结构) /// public string DetaiName = string.Empty; + + + public TabControlEx() { InitializeComponent(); diff --git a/插件库/Lskj.Main/Control/MainPanelControlEx2.Designer.cs b/插件库/Lskj.Main/Control/MainPanelControlEx2.Designer.cs index 4d5ca08..acb23ba 100644 --- a/插件库/Lskj.Main/Control/MainPanelControlEx2.Designer.cs +++ b/插件库/Lskj.Main/Control/MainPanelControlEx2.Designer.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; +using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; @@ -12,6 +13,7 @@ using System.Windows.Forms; using DevExpress.XtraTab; using DevExpress.XtraTreeList; using Lskj.Business; +using Lskj.Main; using Lskj.Model; using WinFormsControl = System.Windows.Forms.Control; @@ -26,10 +28,15 @@ namespace Lskj.Main.Control private static readonly Color ShortcutBorderColor = Color.FromArgb(221, 227, 234); private System.ComponentModel.IContainer components = null; private MainPanelControlEx _legacyPanel; + private MainTabsNativeWindow _mainTabsNativeWindow; protected override void Dispose(bool disposing) { - if (disposing && (components != null)) components.Dispose(); + if (disposing) + { + if (_mainTabsNativeWindow != null) _mainTabsNativeWindow.Dispose(); + if (components != null) components.Dispose(); + } base.Dispose(disposing); } @@ -62,35 +69,37 @@ namespace Lskj.Main.Control #region 新版主界面外观 private void ApplyWpfShellStyle() { + MainAppearanceSettings appearance = MainAppearanceSettingsStore.Load(); + Color headerColor = GetConfiguredColor(appearance.TopMenuBackground, HeaderColor); Panel header = FindControl("plTop"); Panel headerLeft = FindControl("pl_top_left"); Panel headerCenter = FindControl("pl_top_center"); Panel headerRight = FindControl("pl_top_right"); Panel headerSeparator = FindControl("panel3"); MenuStrip topMenu = FindControl("menuMain"); - if (header != null) { header.Height = 50; header.BackColor = HeaderColor; } + if (header != null) { header.Height = 50; header.BackColor = headerColor; } if (headerLeft != null) { headerLeft.Width = 176; - headerLeft.BackColor = HeaderColor; + headerLeft.BackColor = headerColor; headerLeft.BackgroundImage = null; AddBrandVisual(headerLeft); } - if (headerCenter != null) { headerCenter.BackColor = HeaderColor; headerCenter.Padding = new Padding(0, 9, 0, 0); } + if (headerCenter != null) { headerCenter.BackColor = headerColor; headerCenter.Padding = new Padding(0, 9, 0, 0); } // 右上角继续使用旧版控件、按钮和位置,仅统一顶部背景色。 - if (headerRight != null) { headerRight.BackColor = HeaderColor; headerRight.BackgroundImage = null; } + if (headerRight != null) { headerRight.BackColor = headerColor; headerRight.BackgroundImage = null; } // 旧版 Logo 与菜单之间有 19px 的装饰分隔面板;WPF 顶部栏是连续布局, // 保留该控件会在两块蓝色之间形成突兀的深色竖条。 if (headerSeparator != null) { headerSeparator.Width = 0; - headerSeparator.BackColor = HeaderColor; + headerSeparator.BackColor = headerColor; headerSeparator.BackgroundImage = null; } StyleSearchControls(); - if (topMenu != null) StyleTopNavigation(topMenu); + if (topMenu != null) StyleTopNavigation(topMenu, headerColor, appearance.TopMenuFontSize); StyleMainTabs(); - StyleSidebar(); + StyleSidebar(appearance); StyleWorkspaceAndShortcuts(); RememberStyledNavigation(); } @@ -147,8 +156,13 @@ namespace Lskj.Main.Control Color tabHover = Color.White; Color tabBorder = Color.FromArgb(199, 211, 228); tabs.PaintStyleName = "Flat"; + tabs.BorderStyle = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder; + tabs.BorderStylePage = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder; tabs.HeaderLocation = DevExpress.XtraTab.TabHeaderLocation.Top; tabs.HeaderAutoFill = DevExpress.Utils.DefaultBoolean.False; + tabs.MultiLine = DevExpress.Utils.DefaultBoolean.False; + tabs.ShowHeaderFocus = DevExpress.Utils.DefaultBoolean.False; + tabs.MaxTabPageWidth = 220; tabs.TabPageWidth = 0; tabs.BackColor = tabStrip; tabs.Appearance.BackColor = tabStrip; @@ -162,18 +176,69 @@ namespace Lskj.Main.Control tabs.AppearancePage.HeaderHotTracked.Font = tabs.AppearancePage.Header.Font; tabs.AppearancePage.HeaderDisabled.Font = tabs.AppearancePage.Header.Font; tabs.Paint -= MainTabs_Paint; - tabs.Paint += MainTabs_Paint; + if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) + tabs.Paint += MainTabs_Paint; + else + AttachMainTabsNativePainter(tabs); + tabs.ControlAdded -= MainTabs_ControlAdded; + tabs.ControlAdded += MainTabs_ControlAdded; - // 功能导航页签要与左侧 176px 目录栏严格对齐。DevExpress 旧版 - // 页签宽度按文本自适应,因此用不可见的全角空格补足首个页签宽度, - // 其它页签仍保持按内容自适应,并通过边框清晰分隔。 + // WPF 首页页签固定 176px,其余页签为 92~220px。直接使用每页的 + // TabPageWidth,避免用全角空格补宽后在不同字体/DPI 下发生偏移。 XtraTabPage first = tabs.TabPages.Cast().FirstOrDefault(p => p.Name == "xtpFirst"); - if (first != null) first.Text = "功能导航" + new string('\u3000', 10); + if (first != null) first.Text = "功能导航"; + ApplyWpfTabWidths(tabs); + } + + private void MainTabs_ControlAdded(object sender, ControlEventArgs e) + { + XtraTabControl tabs = sender as XtraTabControl; + if (tabs == null || !(e.Control is XtraTabPage)) return; + ApplyWpfTabWidths(tabs); + tabs.Invalidate(); + } + + private void AttachMainTabsNativePainter(XtraTabControl tabs) + { + if (_mainTabsNativeWindow == null) + _mainTabsNativeWindow = new MainTabsNativeWindow(); + _mainTabsNativeWindow.Attach(tabs); + } + + private static void ApplyWpfTabWidths(XtraTabControl tabs) + { + Font font = tabs.AppearancePage.Header.Font ?? tabs.Font; + foreach (XtraTabPage page in tabs.TabPages) + { + if (page.Name == "xtpFirst") + { + page.TabPageWidth = 167; + continue; + } + if (page.Name == "xtpWorktop" || page.Name == "xtpDesktop") + { + page.TabPageWidth = 83; + continue; + } + + int textWidth = TextRenderer.MeasureText(page.Text ?? string.Empty, font, + Size.Empty, TextFormatFlags.NoPadding | TextFormatFlags.SingleLine).Width; + // WPF:左右文字留白 20px,关闭按钮及间隔 20px。 + // DevExpress 15.2 会在 TabPageWidth 外再增加 9px 头部外框; + // 预先扣除该值,使最终可见宽度与 WPF 的 92~220px 一致。 + int visualWidth = Math.Max(92, Math.Min(220, textWidth + 40)); + page.TabPageWidth = Math.Max(1, visualWidth - 9); + } } private void MainTabs_Paint(object sender, PaintEventArgs e) { XtraTabControl tabs = sender as XtraTabControl; + if (tabs != null) DrawMainTabs(tabs, e.Graphics); + } + + private static void DrawMainTabs(XtraTabControl tabs, Graphics graphics) + { DevExpress.XtraTab.IXtraTab tabInterface = tabs as DevExpress.XtraTab.IXtraTab; DevExpress.XtraTab.ViewInfo.BaseTabControlViewInfo viewInfo = tabInterface == null ? null : tabInterface.ViewInfo; @@ -181,47 +246,85 @@ namespace Lskj.Main.Control Rectangle header = viewInfo.HeaderInfo.Bounds; if (header.Width <= 0 || header.Height <= 0) return; - Color stripColor = Color.FromArgb(243, 245, 248); - Color borderColor = Color.FromArgb(199, 211, 228); - int lastRight = header.Left; + Color stripColor = Color.FromArgb(243, 245, 248); // #F3F5F8 + Color normalColor = Color.FromArgb(248, 250, 252); // #F8FAFC + Color normalBorder = Color.FromArgb(199, 211, 228); // #C7D3E4 + Color selectedBorder = Color.FromArgb(199, 211, 228); // #C7D3E4 + Color normalText = Color.FromArgb(79, 95, 120); // #4F5F78 + Color selectedText = Color.FromArgb(7, 88, 255); // #0758FF + int dividerY = viewInfo.PageClientBounds.Top > 0 + ? viewInfo.PageClientBounds.Top - 1 + : header.Bottom - 1; + dividerY = Math.Max(header.Top, Math.Min(tabs.ClientSize.Height - 1, dividerY)); + + // 先覆盖整个页签带,彻底消除 DevExpress 当前皮肤留下的渐变、圆角和焦点边框。 + using (Brush stripBrush = new SolidBrush(stripColor)) + graphics.FillRectangle(stripBrush, 0, header.Top, + tabs.ClientSize.Width, Math.Max(1, dividerY - header.Top + 1)); foreach (DevExpress.XtraTab.ViewInfo.BaseTabPageViewInfo page in viewInfo.HeaderInfo.VisiblePages) { Rectangle bounds = page.Bounds; if (bounds.Width <= 0 || bounds.Height <= 0) continue; - lastRight = Math.Max(lastRight, bounds.Right); + XtraTabPage tabPage = page.Page as XtraTabPage; + bool home = tabPage != null && tabPage.Name == "xtpFirst"; + bool selected = page.Page == viewInfo.SelectedTabPage; + bool hover = page.IsHotState && !selected; + Color backColor = home + ? (hover ? Color.White : stripColor) + : (selected || hover ? Color.White : normalColor); + Color borderColor = selected ? selectedBorder : normalBorder; + + // 相邻页签首尾紧贴,不再保留页签栏底色间隔;每个页签仍然 + // 单独绘制完整矩形边框,因此无间距时也能保持清晰边界。 + int surfaceLeft = bounds.Left; + int surfaceRight = bounds.Right; + int surfaceTop = header.Top + 1; + int surfaceBottom = Math.Max(surfaceTop, dividerY - 1); + Rectangle surface = Rectangle.FromLTRB(surfaceLeft, surfaceTop, + Math.Max(surfaceLeft + 1, surfaceRight), surfaceBottom + 1); + using (Brush brush = new SolidBrush(backColor)) + graphics.FillRectangle(brush, surface); using (Pen pen = new Pen(borderColor)) { - // 明确绘制页签左右分隔及顶部边线,避免 DevExpress 不同皮肤 - // 忽略 Appearance.BorderColor 后多个白色页签连成一片。 - e.Graphics.DrawLine(pen, bounds.Left, bounds.Top, bounds.Left, bounds.Bottom - 1); - e.Graphics.DrawLine(pen, bounds.Right - 1, bounds.Top, bounds.Right - 1, bounds.Bottom - 1); - e.Graphics.DrawLine(pen, bounds.Left, bounds.Top, bounds.Right - 1, bounds.Top); + // 为每个页签单独绘制完整矩形。WPF 通过相邻边框、页签间距 + // 和底部 Divider 共同形成矩形;WinForms 直接绘制四边可避免 + // DevExpress 皮肤把多个未选中页签合并成一个整体。 + graphics.DrawRectangle(pen, surface.Left, surface.Top, + Math.Max(0, surface.Width - 1), Math.Max(0, surface.Height - 1)); + } + + Rectangle closeBounds = page.ControlBox; + int textLeft = surface.Left + (home ? 34 : 12); + int textRight = closeBounds.Width > 0 ? closeBounds.Left - 6 : surface.Right - 8; + Rectangle textBounds = new Rectangle(textLeft, surface.Top, + Math.Max(1, textRight - textLeft), surface.Height); + Color textColor = !home && selected ? selectedText : normalText; + TextRenderer.DrawText(graphics, page.Page.Text, tabs.AppearancePage.Header.Font, + textBounds, textColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter | + TextFormatFlags.SingleLine | TextFormatFlags.EndEllipsis | TextFormatFlags.NoPrefix); + + if (closeBounds.Width > 0 && closeBounds.Height > 0) + { + bool closeHot = closeBounds.Contains(tabs.PointToClient(WinFormsControl.MousePosition)); + if (closeHot) + { + using (Brush brush = new SolidBrush(Color.FromArgb(234, 242, 251))) + graphics.FillRectangle(brush, closeBounds); + } + int cx = closeBounds.Left + closeBounds.Width / 2; + int cy = closeBounds.Top + closeBounds.Height / 2; + using (Pen pen = new Pen(Color.FromArgb(127, 143, 163))) + { + graphics.DrawLine(pen, cx - 3, cy - 3, cx + 3, cy + 3); + graphics.DrawLine(pen, cx + 3, cy - 3, cx - 3, cy + 3); + } } } - // WPF 页签后方是浅灰色页签带,而不是与内容区相同的纯白色。 - Rectangle buttons = viewInfo.HeaderInfo.ButtonsBounds; - int emptyRight = buttons.Width > 0 ? buttons.Left : header.Right; - if (lastRight < emptyRight) - { - using (Brush brush = new SolidBrush(stripColor)) - e.Graphics.FillRectangle(brush, lastRight, header.Top, emptyRight - lastRight, header.Height); - } - // WPF 在整个 WorkspaceTabStrip 底部单独覆盖一条 1px 的 #C7D3E4 - // 分隔线。不能沿用 HeaderInfo.Bounds.Bottom:旧版 DevExpress 中该位置 - // 仍可能属于页签头内部,线条会与页签背景混在一起。这里按内容区顶部定位, - // 横跨控件全宽,并增加一条极浅下沿,使 WinForms 在不同 DPI 下仍有边界感。 - Rectangle pageClient = tabs.PageClientBounds; - int dividerY = pageClient.Top > 0 ? pageClient.Top - 1 : header.Bottom - 1; - dividerY = Math.Max(0, Math.Min(tabs.ClientSize.Height - 1, dividerY)); - using (Pen pen = new Pen(borderColor)) - e.Graphics.DrawLine(pen, 0, dividerY, tabs.ClientSize.Width - 1, dividerY); - if (dividerY + 1 < tabs.ClientSize.Height) - { - using (Pen pen = new Pen(Color.FromArgb(237, 242, 247))) - e.Graphics.DrawLine(pen, 0, dividerY + 1, tabs.ClientSize.Width - 1, dividerY + 1); - } + // 与 WPF WorkspaceTabStripDivider 一致,最上层覆盖 1px #C7D3E4。 + using (Pen pen = new Pen(selectedBorder)) + graphics.DrawLine(pen, 0, dividerY, tabs.ClientSize.Width - 1, dividerY); } private static void ConfigureTabAppearance(DevExpress.Utils.AppearanceObject appearance, Color backColor, Color foreColor, Color borderColor) @@ -253,16 +356,16 @@ namespace Lskj.Main.Control } } - private void StyleTopNavigation(MenuStrip menu) + private void StyleTopNavigation(MenuStrip menu, Color headerColor, int topFontSize) { - menu.Renderer = new WpfMenuRenderer(HeaderColor, () => + menu.Renderer = new WpfMenuRenderer(headerColor, () => { // 每次绘制都重新读取当前导航,支持旧版双击或其它入口切换目录。 return FindCurrentTopMenu(menu) ?? _selectedTopMenu; }); - menu.BackColor = HeaderColor; + menu.BackColor = headerColor; menu.ForeColor = Color.FromArgb(242, 246, 255); - menu.Font = new Font("微软雅黑", 10F); + menu.Font = new Font("微软雅黑", GetConfiguredFontSize(topFontSize, 9F)); menu.Dock = DockStyle.Fill; menu.Location = Point.Empty; menu.Padding = new Padding(4, 0, 4, 0); @@ -416,34 +519,38 @@ namespace Lskj.Main.Control if (oldTitle != null) oldTitle.Visible = false; } - private void StyleSidebar() + private void StyleSidebar(MainAppearanceSettings appearance) { Panel sidebar = FindControl("pl_center_left"); if (sidebar == null) return; + Color sidebarColor = GetConfiguredColor(appearance.SidebarBackground, SidebarColor); + Color sidebarSelectedColor = GetConfiguredColor(appearance.SidebarSelectedBackground, SidebarSelectedColor); + Color sidebarHoverColor = GetHoverColor(sidebarColor); + float sidebarFontSize = GetConfiguredFontSize(appearance.LeftMenuFontSize, 12F); // 新版侧栏对应旧程序的 MainLeftShowMode=1(分组树菜单)配置。 bool useGroupedSidebar = IsGroupedSidebarMode(); - sidebar.Width = 176; sidebar.Padding = Padding.Empty; sidebar.BackColor = SidebarColor; sidebar.AutoScroll = true; + sidebar.Width = 176; sidebar.Padding = Padding.Empty; sidebar.BackColor = sidebarColor; sidebar.AutoScroll = true; if (!useGroupedSidebar) return; foreach (TreeList tree in FindControls(sidebar)) { - tree.Dock = DockStyle.Fill; tree.RowHeight = 44; tree.BackColor = SidebarColor; - tree.Appearance.Empty.BackColor = SidebarColor; tree.Appearance.Empty.Options.UseBackColor = true; - tree.Appearance.Row.BackColor = SidebarColor; tree.Appearance.Row.ForeColor = Color.FromArgb(235, 255, 255, 255); - tree.Appearance.Row.Font = new Font("微软雅黑", 10F); tree.Appearance.Row.Options.UseBackColor = true; + tree.Dock = DockStyle.Fill; tree.RowHeight = 44; tree.BackColor = sidebarColor; + tree.Appearance.Empty.BackColor = sidebarColor; tree.Appearance.Empty.Options.UseBackColor = true; + tree.Appearance.Row.BackColor = sidebarColor; tree.Appearance.Row.ForeColor = Color.FromArgb(235, 255, 255, 255); + tree.Appearance.Row.Font = new Font("微软雅黑", sidebarFontSize); tree.Appearance.Row.Options.UseBackColor = true; tree.Appearance.Row.Options.UseForeColor = true; tree.Appearance.Row.Options.UseFont = true; - tree.Appearance.FocusedRow.BackColor = SidebarSelectedColor; tree.Appearance.FocusedRow.ForeColor = Color.White; + tree.Appearance.FocusedRow.BackColor = sidebarSelectedColor; tree.Appearance.FocusedRow.ForeColor = Color.White; tree.Appearance.FocusedRow.Options.UseBackColor = true; tree.Appearance.FocusedRow.Options.UseForeColor = true; - tree.Appearance.HideSelectionRow.BackColor = SidebarSelectedColor; tree.Appearance.HideSelectionRow.ForeColor = Color.White; + tree.Appearance.HideSelectionRow.BackColor = sidebarSelectedColor; tree.Appearance.HideSelectionRow.ForeColor = Color.White; tree.Appearance.HideSelectionRow.Options.UseBackColor = true; tree.Appearance.HideSelectionRow.Options.UseForeColor = true; tree.OptionsView.ShowColumns = false; tree.OptionsView.ShowIndicator = false; tree.OptionsView.ShowHorzLines = false; tree.OptionsView.ShowVertLines = false; } foreach (MenuBar menuBar in FindControls(sidebar)) { - Button button = menuBar.GetButton(); menuBar.BackColor = SidebarColor; menuBar.Height = 44; - button.Dock = DockStyle.Fill; button.BackColor = SidebarColor; button.BackgroundImage = null; + Button button = menuBar.GetButton(); menuBar.BackColor = sidebarColor; menuBar.Height = 44; + button.Dock = DockStyle.Fill; button.BackColor = sidebarColor; button.BackgroundImage = null; button.FlatStyle = FlatStyle.Flat; button.FlatAppearance.BorderSize = 0; - button.FlatAppearance.MouseOverBackColor = Color.FromArgb(74, 80, 108); button.FlatAppearance.MouseDownBackColor = SidebarSelectedColor; - button.ForeColor = Color.FromArgb(235, 255, 255, 255); button.Font = new Font("微软雅黑", 10F); + button.FlatAppearance.MouseOverBackColor = sidebarHoverColor; button.FlatAppearance.MouseDownBackColor = sidebarSelectedColor; + button.ForeColor = Color.FromArgb(235, 255, 255, 255); button.Font = new Font("微软雅黑", sidebarFontSize); button.TextAlign = ContentAlignment.MiddleLeft; button.Padding = new Padding(20, 0, 12, 0); button.Click -= SidebarButton_Click; button.Click += SidebarButton_Click; @@ -454,11 +561,14 @@ namespace Lskj.Main.Control { Button selected = sender as Button; if (selected == null) return; + MainAppearanceSettings appearance = MainAppearanceSettingsStore.Load(); + Color sidebarColor = GetConfiguredColor(appearance.SidebarBackground, SidebarColor); + Color sidebarSelectedColor = GetConfiguredColor(appearance.SidebarSelectedBackground, SidebarSelectedColor); Panel sidebar = FindControl("pl_center_left"); foreach (Button button in FindControls