diff --git a/插件库/Lskj.Business/Impl/BaseModuleImpl.cs b/插件库/Lskj.Business/Impl/BaseModuleImpl.cs index 0c8eea2..cfda06c 100644 --- a/插件库/Lskj.Business/Impl/BaseModuleImpl.cs +++ b/插件库/Lskj.Business/Impl/BaseModuleImpl.cs @@ -1059,6 +1059,28 @@ namespace Lskj.Business.Impl table.Columns["tagid"].ColumnName = "nullable"; } + if (!table.Columns.Contains("resultfields") && HasExistsColumn("p_systemwordbooktab", "resultfields")) + { + table.Columns.Add("resultfields", typeof(string)); + DataTable resultFieldsTable = SqlHelper.ExecuteDataTable( + "select id,isnull(resultfields,'') resultfields from p_systemwordbooktab where tab=@modid", + new SqlParameter[] { new SqlParameter("@modid", menuCode) }); + + Dictionary resultFieldsById = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (DataRow row in resultFieldsTable.Rows) + { + resultFieldsById[row["id"] + ""] = row["resultfields"] + ""; + } + foreach (DataRow row in table.Rows) + { + string resultFields; + if (table.Columns.Contains("id") && resultFieldsById.TryGetValue(row["id"] + "", out resultFields)) + { + row["resultfields"] = resultFields; + } + } + } + return table; } /// @@ -1272,7 +1294,73 @@ namespace Lskj.Business.Impl p[1].Value = menuCode; p[2].Value = ERPInfo.Instance.UserName; - return SqlHelper.ExecuteDataSet(CommandType.StoredProcedure, "p_getControlLocation", "temp", p).Tables[0]; + DataTable table = SqlHelper.ExecuteDataSet( + CommandType.StoredProcedure, + "p_getControlLocation", + "temp", + p).Tables[0]; + AppendExtendedReturnResultFields(table, menuCode); + return table; + } + + /// + /// p_getControlLocation 的旧版本不返回 resultfields。 + /// 仅当窗体包含 180/181 扩展返回控件时按模块补取,普通窗体不增加查询。 + /// + private static void AppendExtendedReturnResultFields(DataTable table, string menuCode) + { + if (table == null || table.Rows.Count == 0 || + !table.Columns.Contains("id") || !table.Columns.Contains("fieldTypeId")) + { + return; + } + + List extendedRows = new List(); + bool allConfigured = table.Columns.Contains("resultfields"); + foreach (DataRow row in table.Rows) + { + int fieldType; + if (!int.TryParse(row["fieldTypeId"] + "", out fieldType) || + (fieldType != 180 && fieldType != 181)) + { + continue; + } + + extendedRows.Add(row); + if (!table.Columns.Contains("resultfields") || + string.IsNullOrWhiteSpace(row["resultfields"] + "")) + { + allConfigured = false; + } + } + + if (extendedRows.Count == 0 || allConfigured) return; + if (!HasExistsColumn("p_systemwordbooktab", "resultfields")) return; + + if (!table.Columns.Contains("resultfields")) + { + table.Columns.Add("resultfields", typeof(string)); + } + + DataTable resultFieldsTable = SqlHelper.ExecuteDataTable( + "select id,isnull(resultfields,'') resultfields " + + "from p_systemwordbooktab where tab=@modid", + new SqlParameter[] { new SqlParameter("@modid", menuCode) }); + Dictionary resultFieldsById = + new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (DataRow row in resultFieldsTable.Rows) + { + resultFieldsById[row["id"] + ""] = row["resultfields"] + ""; + } + + foreach (DataRow row in extendedRows) + { + string resultFields; + if (resultFieldsById.TryGetValue(row["id"] + "", out resultFields)) + { + row["resultfields"] = resultFields; + } + } } /// /// 说明:获取添加界面多标签 @@ -1436,4 +1524,4 @@ namespace Lskj.Business.Impl } } -} \ No newline at end of file +} diff --git a/插件库/Lskj.Business/Impl/BillImpl.cs b/插件库/Lskj.Business/Impl/BillImpl.cs index 792a748..5ce7a09 100644 --- a/插件库/Lskj.Business/Impl/BillImpl.cs +++ b/插件库/Lskj.Business/Impl/BillImpl.cs @@ -461,6 +461,8 @@ namespace Lskj.Business.Impl sumCond += ",a.ColumnAnnotation "; if (dataTable.Columns.Contains("TitleColor")) sumCond += ",a.TitleColor "; + if (dataTable.Columns.Contains("resultfields")) + sumCond += ",a.resultfields "; string columsSql = string.Format(@"SELECT DISTINCT isnull(CASE WHEN ISNULL(b.userList,'')='' AND ISNULL(a.PrivilegeView,'')<>'' OR isnull(isVisible,0)=1 THEN 0 ELSE width END,0) width, @@ -559,6 +561,8 @@ namespace Lskj.Business.Impl sumCond += ",a.ColumnAnnotation "; if (dataTable.Columns.Contains("TitleColor")) sumCond += ",a.TitleColor "; + if (dataTable.Columns.Contains("resultfields")) + sumCond += ",a.resultfields "; string columsSql = string.Format(@"SELECT DISTINCT isnull(CASE WHEN ISNULL(b.userList,'')='' AND ISNULL(a.PrivilegeView,'')<>'' OR isnull(isVisible,0)=1 THEN 0 ELSE width END,0) width, diff --git a/插件库/Lskj.Control/AutoGridLookUp/ExtendedReturnSearchPopup.cs b/插件库/Lskj.Control/AutoGridLookUp/ExtendedReturnSearchPopup.cs new file mode 100644 index 0000000..4d541a2 --- /dev/null +++ b/插件库/Lskj.Control/AutoGridLookUp/ExtendedReturnSearchPopup.cs @@ -0,0 +1,233 @@ +using DevExpress.XtraEditors; +using DevExpress.XtraGrid; +using DevExpress.XtraGrid.Views.Grid; +using DevExpress.XtraGrid.Views.Grid.ViewInfo; +using DevExpress.Utils; +using System; +using System.Windows.Forms; + +namespace Lskj.Control +{ + /// + /// 181 扩展搜索专用弹窗。查询文本只存在于本控件中,不参与业务列绑定。 + /// + internal sealed class ExtendedReturnSearchPopup : UserControl + { + private readonly TableLayoutPanel searchPanel; + private readonly TextEdit searchEdit; + private readonly SimpleButton queryButton; + private readonly SimpleButton clearButton; + private readonly GridControl resultGrid; + private readonly GridView resultView; + + public event EventHandler SearchRequested; + public event EventHandler ClearRequested; + public event EventHandler ResultSelected; + + public ExtendedReturnSearchPopup() + { + searchPanel = new TableLayoutPanel(); + searchEdit = new TextEdit(); + queryButton = new SimpleButton(); + clearButton = new SimpleButton(); + resultGrid = new GridControl(); + resultView = new GridView(resultGrid); + + SuspendLayout(); + searchPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(searchEdit.Properties)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(resultGrid)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(resultView)).BeginInit(); + + searchPanel.Dock = DockStyle.Top; + searchPanel.Height = 36; + searchPanel.Padding = new Padding(4); + searchPanel.ColumnCount = 3; + searchPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); + searchPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 72F)); + searchPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 72F)); + searchPanel.RowCount = 1; + searchPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); + searchPanel.GrowStyle = TableLayoutPanelGrowStyle.FixedSize; + + queryButton.Dock = DockStyle.Fill; + queryButton.Margin = new Padding(4, 0, 0, 0); + queryButton.Text = "查询"; + queryButton.TabIndex = 1; + queryButton.Click += QueryButton_Click; + + clearButton.Dock = DockStyle.Fill; + clearButton.Margin = new Padding(4, 0, 0, 0); + clearButton.Text = "清空"; + clearButton.TabIndex = 2; + clearButton.Click += ClearButton_Click; + + searchEdit.Dock = DockStyle.Fill; + searchEdit.Margin = new Padding(0); + searchEdit.TabIndex = 0; + searchEdit.Properties.AutoHeight = false; + searchEdit.Properties.NullValuePrompt = "输入查询内容后按回车"; + searchEdit.Properties.NullValuePromptShowForEmptyValue = true; + searchEdit.KeyDown += SearchEdit_KeyDown; + + searchPanel.Controls.Add(searchEdit, 0, 0); + searchPanel.Controls.Add(queryButton, 1, 0); + searchPanel.Controls.Add(clearButton, 2, 0); + + resultGrid.Dock = DockStyle.Fill; + resultGrid.MainView = resultView; + resultGrid.ViewCollection.AddRange(new DevExpress.XtraGrid.Views.Base.BaseView[] { resultView }); + + resultView.GridControl = resultGrid; + resultView.OptionsBehavior.Editable = false; + resultView.OptionsSelection.EnableAppearanceFocusedCell = false; + resultView.OptionsView.ShowGroupPanel = false; + resultView.OptionsView.ShowIndicator = true; + resultView.OptionsView.ColumnAutoWidth = false; + resultView.IndicatorWidth = 40; + resultView.CustomDrawRowIndicator += ResultView_CustomDrawRowIndicator; + resultView.MouseDown += ResultView_MouseDown; + resultView.KeyDown += ResultView_KeyDown; + + Controls.Add(resultGrid); + Controls.Add(searchPanel); + Name = "ExtendedReturnSearchPopup"; + Size = new System.Drawing.Size(420, 240); + + ((System.ComponentModel.ISupportInitialize)(resultView)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(resultGrid)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(searchEdit.Properties)).EndInit(); + searchPanel.ResumeLayout(false); + ResumeLayout(false); + } + + public GridControl ResultGrid + { + get { return resultGrid; } + } + + public GridView ResultView + { + get { return resultView; } + } + + public string SearchText + { + get { return (searchEdit.Text ?? string.Empty).Trim(); } + } + + public void PrepareForOpen() + { + searchEdit.Text = string.Empty; + resultGrid.DataSource = null; + } + + public void FocusSearchEditor() + { + if (!searchEdit.CanFocus) return; + + searchEdit.Focus(); + searchEdit.SelectionStart = searchEdit.Text.Length; + searchEdit.SelectionLength = 0; + } + + private void SearchEdit_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Down) + { + e.Handled = true; + e.SuppressKeyPress = true; + FocusResultRow(false); + return; + } + if (e.KeyCode == Keys.Up) + { + e.Handled = true; + e.SuppressKeyPress = true; + FocusResultRow(true); + return; + } + if (e.KeyCode != Keys.Enter) return; + + e.Handled = true; + e.SuppressKeyPress = true; + RaiseSearchRequested(); + } + + private void FocusResultRow(bool focusLastRow) + { + if (resultView.DataRowCount <= 0) return; + + int visibleIndex = focusLastRow ? resultView.DataRowCount - 1 : 0; + int rowHandle = resultView.GetVisibleRowHandle(visibleIndex); + if (rowHandle < 0) return; + + resultGrid.Focus(); + resultView.FocusedRowHandle = rowHandle; + resultView.SelectRow(rowHandle); + } + + private void QueryButton_Click(object sender, EventArgs e) + { + RaiseSearchRequested(); + } + + private void RaiseSearchRequested() + { + if (SearchRequested != null) + { + SearchRequested(this, EventArgs.Empty); + } + } + + private void ClearButton_Click(object sender, EventArgs e) + { + if (ClearRequested != null) + { + ClearRequested(this, EventArgs.Empty); + } + } + + private void ResultView_CustomDrawRowIndicator(object sender, RowIndicatorCustomDrawEventArgs e) + { + if (e.Info == null || !e.Info.IsRowIndicator || e.RowHandle < 0) return; + + e.Appearance.TextOptions.HAlignment = HorzAlignment.Center; + e.Info.DisplayText = (e.RowHandle + 1).ToString(); + } + + private void ResultView_MouseDown(object sender, MouseEventArgs e) + { + if (e.Button != MouseButtons.Left) return; + + GridHitInfo hitInfo = resultView.CalcHitInfo(e.Location); + if (!hitInfo.InRow && !hitInfo.InRowCell) return; + + resultView.FocusedRowHandle = hitInfo.RowHandle; + if (ResultSelected != null) + { + ResultSelected(this, EventArgs.Empty); + } + } + + private void ResultView_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Up && + resultView.FocusedRowHandle == resultView.GetVisibleRowHandle(0)) + { + e.Handled = true; + e.SuppressKeyPress = true; + FocusSearchEditor(); + return; + } + if (e.KeyCode != Keys.Enter) return; + + e.Handled = true; + e.SuppressKeyPress = true; + if (ResultSelected != null) + { + ResultSelected(this, EventArgs.Empty); + } + } + } +} diff --git a/插件库/Lskj.Control/AutoGridLookUp/LabelExtendedReturnSearchEdit.cs b/插件库/Lskj.Control/AutoGridLookUp/LabelExtendedReturnSearchEdit.cs new file mode 100644 index 0000000..e72130e --- /dev/null +++ b/插件库/Lskj.Control/AutoGridLookUp/LabelExtendedReturnSearchEdit.cs @@ -0,0 +1,816 @@ +using DevExpress.Utils; +using DevExpress.XtraEditors; +using DevExpress.XtraEditors.Controls; +using DevExpress.XtraGrid.Columns; +using Lskj.Business.Impl; +using Lskj.Control.Model; +using Lskj.Core; +using Lskj.Util; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Drawing; +using System.Linq; +using System.Threading; +using System.Windows.Forms; + +namespace Lskj.Control +{ + /// + /// MyControl 中的 181 扩展搜索控件。业务实际值与界面显示文本相互独立, + /// 数据源仅在弹出框打开或用户执行查询时按需访问。 + /// + public sealed class LabelExtendedReturnSearchEdit : BaseUserControl + { + private const int MaxRows = 100; + private const int DefaultPopupWidth = 520; + private const int DefaultPopupHeight = 300; + private const int MinimumPopupWidth = 360; + + private readonly Panel labelPanel; + private readonly Label titleLabel; + private readonly Panel editorPanel; + private readonly PopupContainerEdit popupEdit; + private readonly PopupContainerControl popupControl; + private readonly ExtendedReturnSearchPopup popup; + private readonly object schemaSyncRoot = new object(); + private readonly object querySyncRoot = new object(); + + private string actualValue = string.Empty; + private string schemaSql = string.Empty; + private DataTable schema; + private SearchRequest pendingSearch; + private bool queryWorkerRunning; + private int queryVersion; + private bool disposed; + + private sealed class SearchRequest + { + public int Version; + public string SourceSql; + public string Keyword; + public string ConnectionString; + } + + private sealed class QueryResult + { + public DataTable Table; + public Exception Error; + } + + public LabelExtendedReturnSearchEdit() + { + labelPanel = new Panel(); + titleLabel = new Label(); + editorPanel = new Panel(); + popupEdit = new PopupContainerEdit(); + popupControl = new PopupContainerControl(); + popup = new ExtendedReturnSearchPopup(); + + // MyControl 的坐标和尺寸均来自配置值,保持与现有 LabelTextEdit 一致, + // 禁止 UserControl 在高 DPI/大字体环境下再次缩放配置宽度。 + AutoScaleMode = AutoScaleMode.None; + + SuspendLayout(); + labelPanel.SuspendLayout(); + editorPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(popupEdit.Properties)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(popupControl)).BeginInit(); + popupControl.SuspendLayout(); + + labelPanel.BackColor = Color.Transparent; + labelPanel.Dock = DockStyle.Left; + labelPanel.Width = 40; + labelPanel.Controls.Add(titleLabel); + + titleLabel.AutoSize = true; + titleLabel.BackColor = Color.Transparent; + titleLabel.Location = new Point(5, 4); + titleLabel.Text = "名称"; + + editorPanel.BackColor = Color.Transparent; + editorPanel.Dock = DockStyle.Fill; + editorPanel.Controls.Add(popupEdit); + + popupEdit.Dock = DockStyle.Fill; + popupEdit.Properties.AutoHeight = false; + popupEdit.Properties.AllowNullInput = DefaultBoolean.True; + popupEdit.Properties.NullText = string.Empty; + popupEdit.Properties.TextEditStyle = TextEditStyles.DisableTextEditor; + popupEdit.Properties.PopupSizeable = false; + popupEdit.Properties.PopupResizeMode = ResizeMode.Default; + popupEdit.Properties.PopupBorderStyle = PopupBorderStyles.Flat; + popupEdit.Properties.ShowPopupShadow = true; + popupEdit.Properties.PopupControl = popupControl; + popupEdit.Properties.Buttons.Clear(); + popupEdit.Properties.Buttons.Add(new EditorButton(ButtonPredefines.Combo)); + popupEdit.QueryPopUp += PopupEdit_QueryPopUp; + popupEdit.Popup += PopupEdit_Popup; + popupEdit.Closed += PopupEdit_Closed; + + popup.Dock = DockStyle.Fill; + popupControl.Controls.Add(popup); + popup.SearchRequested += Popup_SearchRequested; + popup.ClearRequested += Popup_ClearRequested; + popup.ResultSelected += Popup_ResultSelected; + + Controls.Add(editorPanel); + Controls.Add(labelPanel); + Name = "LabelExtendedReturnSearchEdit"; + Size = new Size(249, 21); + Disposed += LabelExtendedReturnSearchEdit_Disposed; + + popupControl.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(popupControl)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(popupEdit.Properties)).EndInit(); + editorPanel.ResumeLayout(false); + labelPanel.ResumeLayout(false); + labelPanel.PerformLayout(); + ResumeLayout(false); + } + + public Model.MyControl ControlObj { get; set; } + + public PopupContainerEdit TextEdit + { + get { return popupEdit; } + } + + /// + /// 保存到当前业务字段的实际 ID。 + /// + public string EditValue + { + get { return actualValue ?? string.Empty; } + set + { + actualValue = value ?? string.Empty; + RefreshDisplayText(); + } + } + + /// + /// BaseUserControl 的 EditText 仍用于常规赋值入口;读取时返回界面显示文本, + /// MyControl.GetControlValue 对 181 会显式读取 EditValue。 + /// + public override string EditText + { + get { return popupEdit.Text ?? string.Empty; } + set { EditValue = value; } + } + + public override string LabelText + { + get { return titleLabel.Text; } + set + { + titleLabel.Text = value ?? string.Empty; + if (FontSize > 0) + { + labelPanel.AutoSize = false; + titleLabel.AutoSize = false; + titleLabel.Dock = DockStyle.Fill; + titleLabel.TextAlign = ContentAlignment.MiddleLeft; + labelPanel.Width = Math.Max(0, (value ?? string.Empty).Length * GetCharWidth()); + GetCharWidthMultilingual(titleLabel, titleLabel.Text, labelPanel); + } + else + { + labelPanel.Width = titleLabel.Width + PaddingLeft; + } + } + } + + public override float FontSize + { + get { return base.FontSize; } + set + { + base.FontSize = value; + if (value <= 0) return; + + titleLabel.Font = new Font(titleLabel.Font.FontFamily, value); + popupEdit.Font = new Font(popupEdit.Font.FontFamily, value); + popup.ResultView.Appearance.Row.Font = new Font("微软雅黑", value); + } + } + + public override string NullText + { + get { return popupEdit.Properties.NullValuePrompt; } + set + { + popupEdit.Properties.NullValuePromptShowForEmptyValue = !string.IsNullOrEmpty(value); + popupEdit.Properties.NullValuePrompt = value ?? string.Empty; + base.NullText = value; + } + } + + public override bool ReadOnly + { + get { return base.ReadOnly; } + set + { + base.ReadOnly = value; + popupEdit.Properties.ReadOnly = value; + titleLabel.ForeColor = value + ? ReadOnlyLabelForceColor + : Required ? RequiredLabelForceColor : DefaultLabelForceColor; + } + } + + public override bool Required + { + get { return base.Required; } + set + { + base.Required = value; + if (value) titleLabel.ForeColor = RequiredLabelForceColor; + } + } + + public override Color BackgroundColor + { + get { return popupEdit.Properties.Appearance.BackColor; } + set { popupEdit.Properties.Appearance.BackColor = value; } + } + + public override Color ForeColor + { + get { return popupEdit.Properties.Appearance.ForeColor; } + set { popupEdit.Properties.Appearance.ForeColor = value; } + } + + public override bool ContentBold + { + get { return popupEdit.Properties.Appearance.Font.Bold; } + set + { + Font oldFont = popupEdit.Properties.Appearance.Font; + popupEdit.Properties.Appearance.Font = new Font( + oldFont.FontFamily, + oldFont.Size, + value ? FontStyle.Bold : FontStyle.Regular); + } + } + + public override bool IsEmpty() + { + return Model != null && Model.IsEmpty && string.IsNullOrWhiteSpace(EditValue); + } + + public override bool IsUpdate() + { + return Model == null || Model.Text == null || + !Model.Text.Equals(EditValue, StringComparison.OrdinalIgnoreCase); + } + + /// + /// 从 ResultFields 中 TextMember 对应的业务控件读取预存显示文本。 + /// + public void RefreshDisplayText() + { + string displayText = string.Empty; + try + { + string displayField = GetDisplayTargetField(); + if (ControlObj != null && !string.IsNullOrWhiteSpace(displayField)) + { + BaseUserControl displayControl = ControlObj.FindControl(displayField); + if (displayControl != null && !object.ReferenceEquals(displayControl, this)) + { + displayText = displayControl.EditText ?? string.Empty; + } + } + } + catch + { + // 初始化配置尚未完整时保持为空,弹出时再给出明确配置错误。 + } + popupEdit.EditValue = displayText; + } + + private void PopupEdit_QueryPopUp(object sender, System.ComponentModel.CancelEventArgs e) + { + try + { + ValidateBusinessConfiguration(); + Size popupSize = GetPopupSize(); + popup.Size = popupSize; + popupControl.Size = popupSize; + popupEdit.Properties.PopupFormSize = popupSize; + popupEdit.Properties.PopupFormMinSize = popupSize; + popup.PrepareForOpen(); + + if (!string.IsNullOrWhiteSpace(EditValue)) + { + StartSearch(EditValue); + } + else + { + CancelSearch(); + } + } + catch (Exception ex) + { + e.Cancel = true; + MessageUtil.Show("扩展搜索配置错误:" + ex.Message); + } + } + + private void PopupEdit_Popup(object sender, EventArgs e) + { + try + { + popup.BeginInvoke(new MethodInvoker(popup.FocusSearchEditor)); + } + catch (ObjectDisposedException) + { + } + catch (InvalidOperationException) + { + } + } + + private void PopupEdit_Closed(object sender, ClosedEventArgs e) + { + CancelSearch(); + popup.ResultGrid.DataSource = null; + RefreshDisplayText(); + } + + private void Popup_SearchRequested(object sender, EventArgs e) + { + try + { + ValidateBusinessConfiguration(); + string keyword = popup.SearchText; + if (string.IsNullOrWhiteSpace(keyword)) + { + CancelSearch(); + popup.ResultGrid.DataSource = null; + return; + } + StartSearch(keyword); + } + catch (Exception ex) + { + MessageUtil.Show("扩展搜索配置错误:" + ex.Message); + } + } + + private void Popup_ClearRequested(object sender, EventArgs e) + { + try + { + IList mappings = ValidateClearConfiguration(); + List> values = + new List>(); + HashSet targetFields = + new HashSet(StringComparer.OrdinalIgnoreCase); + + values.Add(new KeyValuePair(Model.FieldName, string.Empty)); + targetFields.Add(Model.FieldName); + foreach (ExtendedReturnFieldMapping mapping in mappings) + { + if (targetFields.Add(mapping.TargetField)) + { + values.Add(new KeyValuePair( + mapping.TargetField, + string.Empty)); + } + } + + ControlObj.ApplyExtendedReturnValues(values); + popupEdit.ClosePopup(); + } + catch (Exception ex) + { + MessageUtil.Show("扩展搜索清空失败:" + ex.Message); + } + } + + private void Popup_ResultSelected(object sender, EventArgs e) + { + try + { + DataRow selectedRow = popup.ResultView.GetFocusedDataRow(); + if (selectedRow == null) return; + + IList mappings = ValidateBusinessConfiguration(); + ValidateSourceColumns(selectedRow.Table.Columns, mappings); + + List> values = new List>(); + foreach (ExtendedReturnFieldMapping mapping in mappings) + { + KeyValuePair mappedValue = new KeyValuePair( + mapping.TargetField, + selectedRow[mapping.SourceField]); + if (string.Equals(mapping.TargetField, Model.FieldName, StringComparison.OrdinalIgnoreCase)) + { + values.Insert(0, mappedValue); + } + else + { + values.Add(mappedValue); + } + } + + bool containsActualField = mappings.Any(item => + string.Equals(item.TargetField, Model.FieldName, StringComparison.OrdinalIgnoreCase)); + if (!containsActualField) + { + values.Insert(0, new KeyValuePair( + Model.FieldName, + selectedRow[Model.ValueMember])); + } + + ApplyValues(values); + RefreshDisplayText(); + popupEdit.ClosePopup(); + } + catch (Exception ex) + { + MessageUtil.Show("扩展搜索返回值失败:" + ex.Message); + } + } + + private void ApplyValues(IList> values) + { + ControlObj.ApplyExtendedReturnValues(values); + } + + private IList ValidateClearConfiguration() + { + if (Model == null) throw new InvalidOperationException("扩展返回控件配置不存在。"); + if (ControlObj == null) throw new InvalidOperationException("扩展返回控件未关联 MyControl。"); + + IList mappings = + ExtendedReturnSupport.ParseResultFields(Model.ResultFields); + if (mappings.Count == 0) + { + throw new InvalidOperationException("resultfields 不能为空。"); + } + if (ControlObj.FindControl(Model.FieldName) == null) + { + throw new InvalidOperationException( + string.Format("MyControl 中不存在业务控件“{0}”。", Model.FieldName)); + } + foreach (ExtendedReturnFieldMapping mapping in mappings) + { + if (ControlObj.FindControl(mapping.TargetField) == null) + { + throw new InvalidOperationException( + string.Format("MyControl 中不存在业务控件“{0}”。", mapping.TargetField)); + } + } + return mappings; + } + + private IList ValidateBusinessConfiguration() + { + if (Model == null) throw new InvalidOperationException("扩展返回控件配置不存在。"); + if (ControlObj == null) throw new InvalidOperationException("扩展返回控件未关联 MyControl。"); + if (string.IsNullOrWhiteSpace(Model.SourceSql)) + { + throw new InvalidOperationException("搜索数据源 SQL 不能为空。"); + } + if (string.IsNullOrWhiteSpace(Model.ValueMember)) + { + throw new InvalidOperationException("fieldsqlid 不能为空。"); + } + if (string.IsNullOrWhiteSpace(Model.TextMember)) + { + throw new InvalidOperationException("fieldsqlname 不能为空。"); + } + if (string.Equals(Model.ValueMember, Model.TextMember, StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException("fieldsqlid 与 fieldsqlname 必须配置为不同的返回列。"); + } + + IList mappings = ExtendedReturnSupport.ParseResultFields(Model.ResultFields); + if (mappings.Count == 0) + { + throw new InvalidOperationException("resultfields 不能为空。"); + } + + int displayMappingCount = mappings.Count(item => + string.Equals(item.SourceField, Model.TextMember, StringComparison.OrdinalIgnoreCase)); + if (displayMappingCount == 0) + { + throw new InvalidOperationException( + string.Format("resultfields 未配置显示字段“{0}”的业务控件映射。", Model.TextMember)); + } + if (displayMappingCount > 1) + { + throw new InvalidOperationException( + string.Format("resultfields 中显示字段“{0}”只能映射到一个业务控件。", Model.TextMember)); + } + + ExtendedReturnFieldMapping actualMapping = mappings.FirstOrDefault(item => + string.Equals(item.TargetField, Model.FieldName, StringComparison.OrdinalIgnoreCase)); + if (actualMapping != null && + !string.Equals(actualMapping.SourceField, Model.ValueMember, StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException( + string.Format("实际值控件“{0}”必须映射到 fieldsqlid 指定的返回列“{1}”。", + Model.FieldName, + Model.ValueMember)); + } + + foreach (ExtendedReturnFieldMapping mapping in mappings) + { + if (ControlObj.FindControl(mapping.TargetField) == null) + { + throw new InvalidOperationException( + string.Format("MyControl 中不存在业务控件“{0}”。", mapping.TargetField)); + } + } + return mappings; + } + + private void ValidateSourceColumns( + DataColumnCollection columns, + IEnumerable mappings) + { + if (columns == null || columns.Count == 0) + { + throw new InvalidOperationException("扩展搜索数据源没有返回任何列。"); + } + if (!columns.Contains(Model.ValueMember)) + { + throw new InvalidOperationException( + string.Format("返回数据不存在值字段“{0}”。", Model.ValueMember)); + } + if (!columns.Contains(Model.TextMember)) + { + throw new InvalidOperationException( + string.Format("返回数据不存在显示字段“{0}”。", Model.TextMember)); + } + foreach (ExtendedReturnFieldMapping mapping in mappings) + { + if (!columns.Contains(mapping.SourceField)) + { + throw new InvalidOperationException( + string.Format("返回数据不存在映射源字段“{0}”。", mapping.SourceField)); + } + } + } + + private string GetDisplayTargetField() + { + if (Model == null) return string.Empty; + IList mappings = ExtendedReturnSupport.ParseResultFields(Model.ResultFields); + return ExtendedReturnSupport.FindTargetField(mappings, Model.TextMember); + } + + private Size GetPopupSize() + { + int width = Model != null && Model.LookUpWidth > 0 + ? Model.LookUpWidth + : DefaultPopupWidth; + width = Math.Max(width, MinimumPopupWidth); + return new Size(width, DefaultPopupHeight); + } + + private string BuildSourceSql() + { + string sourceSql = Model.SourceSql; + if (ControlObj != null) + { + sourceSql = ControlObj.ReplaceControlValue(sourceSql); + } + sourceSql = BaseImpl.GetDefaultValue(sourceSql); + return ReplaceHelper.ReplaceParam(sourceSql); + } + + private void StartSearch(string keyword) + { + SearchRequest request = new SearchRequest(); + request.Version = Interlocked.Increment(ref queryVersion); + request.SourceSql = BuildSourceSql(); + request.Keyword = keyword; + request.ConnectionString = SqlHelper._connection.ConnectionString; + + bool startWorker = false; + lock (querySyncRoot) + { + pendingSearch = request; + if (!queryWorkerRunning) + { + queryWorkerRunning = true; + startWorker = true; + } + } + if (startWorker) + { + ThreadPool.QueueUserWorkItem(ProcessSearchQueue); + } + } + + private void ProcessSearchQueue(object state) + { + while (true) + { + SearchRequest request; + lock (querySyncRoot) + { + request = pendingSearch; + pendingSearch = null; + if (request == null) + { + queryWorkerRunning = false; + return; + } + } + + QueryResult result = Query( + request.SourceSql, + request.Keyword, + request.ConnectionString); + DeliverResult(request.Version, result); + } + } + + private QueryResult Query(string sourceSql, string keyword, string connectionString) + { + QueryResult result = new QueryResult(); + try + { + DataTable sourceSchema = GetSchema(sourceSql, connectionString); + IList mappings = ExtendedReturnSupport.ParseResultFields(Model.ResultFields); + ValidateSourceColumns(sourceSchema.Columns, mappings); + string searchSql = ExtendedReturnSupport.BuildSearchSql(sourceSql, sourceSchema.Columns, MaxRows); + result.Table = ExecuteQuery( + searchSql, + ExtendedReturnSupport.BuildLikeParameterValue(keyword), + connectionString); + } + catch (Exception ex) + { + result.Error = ex; + } + return result; + } + + private DataTable GetSchema(string sourceSql, string connectionString) + { + lock (schemaSyncRoot) + { + if (schema != null && string.Equals(schemaSql, sourceSql, StringComparison.Ordinal)) + { + return schema; + } + + schema = ExecuteQuery(ExtendedReturnSupport.BuildStructureSql(sourceSql), null, connectionString); + schemaSql = sourceSql; + return schema; + } + } + + private static DataTable ExecuteQuery( + string commandText, + string keywordParameterValue, + string connectionString) + { + DataTable table = new DataTable(); + using (DbConnection connection = SqlHelper.dbFactory.CreateConnection()) + using (DbCommand command = SqlHelper.dbFactory.CreateCommand()) + using (DbDataAdapter adapter = SqlHelper.dbFactory.CreateDataAdapter()) + { + connection.ConnectionString = connectionString; + command.Connection = connection; + command.CommandText = commandText; + command.CommandType = CommandType.Text; + command.CommandTimeout = SqlHelper.CommandTimeout; + + if (keywordParameterValue != null) + { + DbParameter parameter = SqlHelper.dbFactory.CreateParameter(); + parameter.ParameterName = ExtendedReturnSupport.SearchParameterName; + parameter.DbType = DbType.String; + parameter.Size = 4000; + parameter.Value = keywordParameterValue; + command.Parameters.Add(parameter); + } + + adapter.SelectCommand = command; + connection.Open(); + adapter.Fill(table); + } + return table; + } + + private void DeliverResult(int version, QueryResult result) + { + if (disposed || !IsHandleCreated) return; + + MethodInvoker deliver = new MethodInvoker(delegate + { + if (disposed || IsDisposed || version != queryVersion || !popupEdit.IsPopupOpen) return; + if (result.Error != null) + { + MessageUtil.Show("扩展搜索查询失败:" + result.Error.Message); + return; + } + + popup.ResultGrid.DataSource = result.Table; + ConfigureResultColumns(result.Table); + }); + + try + { + BeginInvoke(deliver); + } + catch (ObjectDisposedException) + { + } + catch (InvalidOperationException) + { + } + } + + private void ConfigureResultColumns(DataTable table) + { + if (table == null) return; + + IList mappings = ExtendedReturnSupport.ParseResultFields(Model.ResultFields); + popup.ResultView.Columns.Clear(); + foreach (DataColumn dataColumn in table.Columns) + { + GridColumn column = new GridColumn(); + column.Name = column.FieldName = dataColumn.ColumnName; + column.Caption = GetColumnCaption(dataColumn.ColumnName, mappings); + column.Visible = !dataColumn.ColumnName.StartsWith("_", StringComparison.Ordinal); + popup.ResultView.Columns.Add(column); + } + + string[] configuredWidths = string.IsNullOrWhiteSpace(Model.LookUpFieldsWidth) + ? null + : Model.LookUpFieldsWidth.Trim().TrimEnd(',').Split(','); + int visibleIndex = 0; + foreach (GridColumn column in popup.ResultView.Columns) + { + if (!column.Visible) continue; + + int width; + if (configuredWidths != null && visibleIndex < configuredWidths.Length && + int.TryParse(configuredWidths[visibleIndex], out width)) + { + column.Width = width; + } + else + { + int captionWidth = GraphicsText.GetTextWidth(column.Caption); + int valueWidth = CalcMaxColumnWidth(table, column.FieldName); + column.Width = Math.Max(captionWidth, valueWidth); + } + visibleIndex++; + } + } + + private string GetColumnCaption( + string sourceField, + IEnumerable mappings) + { + string targetField = ExtendedReturnSupport.FindTargetField(mappings, sourceField); + if (string.IsNullOrWhiteSpace(targetField)) return sourceField; + + BaseUserControl targetControl = ControlObj.FindControl(targetField); + if (targetControl != null && !string.IsNullOrWhiteSpace(targetControl.LabelText)) + { + return targetControl.LabelText; + } + return targetField; + } + + private static int CalcMaxColumnWidth(DataTable table, string fieldName) + { + int maxWidth = 0; + foreach (DataRow row in table.Rows) + { + object value = row[fieldName]; + int width = GraphicsText.GetTextWidth( + value == null || value == DBNull.Value ? string.Empty : value + string.Empty); + if (width > maxWidth) maxWidth = width; + } + return maxWidth; + } + + private void CancelSearch() + { + Interlocked.Increment(ref queryVersion); + lock (querySyncRoot) + { + pendingSearch = null; + } + } + + private void LabelExtendedReturnSearchEdit_Disposed(object sender, EventArgs e) + { + disposed = true; + CancelSearch(); + } + } +} diff --git a/插件库/Lskj.Control/GridControlEx.ExtendedReturn.cs b/插件库/Lskj.Control/GridControlEx.ExtendedReturn.cs new file mode 100644 index 0000000..9c3cc91 --- /dev/null +++ b/插件库/Lskj.Control/GridControlEx.ExtendedReturn.cs @@ -0,0 +1,1301 @@ +using DevExpress.Utils; +using DevExpress.XtraEditors; +using DevExpress.XtraEditors.Controls; +using DevExpress.XtraEditors.Repository; +using DevExpress.XtraGrid; +using DevExpress.XtraGrid.Columns; +using DevExpress.XtraGrid.Views.Base; +using DevExpress.XtraGrid.Views.Grid; +using Lskj.Business.Impl; +using Lskj.Control.Model; +using Lskj.Control.MultiModelLookUp; +using Lskj.Core; +using Lskj.Util; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Data.Common; +using System.Drawing; +using System.Linq; +using System.Threading; +using System.Windows.Forms; + +namespace Lskj.Control +{ + public partial class GridControlEx + { + private const int ExtendedLookupMaxRows = 100; + private const int ExtendedLookupDefaultPopupWidth = 520; + private const int ExtendedLookupDefaultPopupHeight = 300; + private const int ExtendedLookupMinimumPopupWidth = 360; + + private readonly object mExtendedReturnSyncRoot = new object(); + private readonly Dictionary mExtendedReturnSearchEditors = + new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary mExtendedReturnPopupContexts = + new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary mExtendedReturnSchemas = + new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary mExtendedReturnDisplayFields = + new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary mExtendedReturnQueryStates = + new Dictionary(StringComparer.OrdinalIgnoreCase); + private bool mExtendedReturnDisplayEventAttached; + private bool mExtendedReturnEditEventAttached; + private bool mExtendedReturnDisposeEventAttached; + private int mExtendedReturnSchemaGeneration; + + private sealed class ExtendedLookupRequestState + { + public int Version; + public int DeliveredVersion; + public bool IsRunning; + public ExtendedLookupRequest PendingRequest; + public System.Threading.Timer DebounceTimer; + } + + private sealed class ExtendedReturnPopupContext + { + public GridColumnModel Model; + public ExtendedReturnSearchPopup Popup; + public PopupContainerEdit OwnerEdit; + public DataRow BusinessRow; + } + + private sealed class ExtendedLookupRequest + { + public int Version; + public DateTime DueTime; + public string FieldName; + public string Keyword; + public string SourceSql; + public string ConnectionString; + public GridColumnModel Model; + public PopupContainerEdit Editor; + public ExtendedReturnPopupContext PopupContext; + public DataRow BusinessRow; + } + + private sealed class ExtendedLookupQueryResult + { + public DataTable Table; + public Exception Error; + } + + /// + /// 初始化扩展返回类型。180 仅创建模块选择按钮,181 仅创建空搜索编辑器, + /// 两种类型均不在表格初始化时访问业务数据源。 + /// + private void InitExtendedReturn(GridColumn column, GridColumnModel model) + { + if (column == null || model == null) return; + + CacheExtendedReturnDisplayField(model); + AttachExtendedReturnDisplayEvent(); + AttachExtendedReturnDisposeEvent(); + if (model.FieldType == ControlType.LabModuleSelectReturnIdExtended) + { + InitExtendedModuleReturn(column, model); + } + else if (model.FieldType == ControlType.LabSearchReturnIdExtended) + { + InitExtendedSearchReturn(column, model); + } + } + + private void AttachExtendedReturnDisplayEvent() + { + if (mExtendedReturnDisplayEventAttached) return; + + this.GridView.CustomColumnDisplayText += GridView_CustomColumnDisplayText_ExtendedReturn; + mExtendedReturnDisplayEventAttached = true; + } + + private void AttachExtendedReturnDisposeEvent() + { + if (mExtendedReturnDisposeEventAttached) return; + + this.Disposed += GridControlEx_Disposed_ExtendedReturn; + mExtendedReturnDisposeEventAttached = true; + } + + private void GridControlEx_Disposed_ExtendedReturn(object sender, EventArgs e) + { + DisposeExtendedReturnRequestTimers(); + } + + private void InitExtendedModuleReturn(GridColumn column, GridColumnModel model) + { + RepositoryItemButtonEdit buttonEdit = new RepositoryItemButtonEdit(); + buttonEdit.NullText = string.Empty; + buttonEdit.AllowNullInput = DefaultBoolean.True; + buttonEdit.TextEditStyle = TextEditStyles.DisableTextEditor; + buttonEdit.Tag = model; + buttonEdit.Buttons.Clear(); + buttonEdit.Buttons.Add(new EditorButton(ButtonPredefines.Ellipsis)); + buttonEdit.ButtonClick += ExtendedModuleReturn_ButtonClick; + buttonEdit.CustomDisplayText += ExtendedModuleReturn_CustomDisplayText; + + this.gridControl.RepositoryItems.Add(buttonEdit); + column.ColumnEdit = buttonEdit; + column.OptionsColumn.ReadOnly = true; + column.FilterMode = ColumnFilterMode.DisplayText; + this.mControlList.Add(model); + } + + private void InitExtendedSearchReturn(GridColumn column, GridColumnModel model) + { + RepositoryItemPopupContainerEdit searchEdit = CreateExtendedReturnSearchEdit(model); + RepositoryItemTextEdit displayEdit = new RepositoryItemTextEdit(); + displayEdit.ReadOnly = true; + + this.gridControl.RepositoryItems.Add(displayEdit); + this.gridControl.RepositoryItems.Add(searchEdit); + column.ColumnEdit = displayEdit; + column.FilterMode = ColumnFilterMode.DisplayText; + mExtendedReturnSearchEditors[model.FieldName] = searchEdit; + this.mControlList.Add(model); + + if (!mExtendedReturnEditEventAttached) + { + this.GridView.CustomRowCellEditForEditing += GridView_CustomRowCellEditForEditing_ExtendedReturn; + this.GridView.RowCellClick += GridView_RowCellClick_ExtendedReturn; + mExtendedReturnEditEventAttached = true; + } + } + + private RepositoryItemPopupContainerEdit CreateExtendedReturnSearchEdit(GridColumnModel model) + { + Size popupSize = GetExtendedReturnPopupSize(model); + ExtendedReturnSearchPopup popup = new ExtendedReturnSearchPopup(); + popup.Dock = DockStyle.Fill; + popup.Size = popupSize; + if (model.FontSize > 0) + { + popup.ResultView.Appearance.Row.Font = new Font("微软雅黑", model.FontSize); + } + + PopupContainerControl popupControl = new PopupContainerControl(); + popupControl.Controls.Add(popup); + popupControl.Size = popupSize; + + RepositoryItemPopupContainerEdit searchEdit = new RepositoryItemPopupContainerEdit(); + searchEdit.NullText = string.Empty; + searchEdit.AllowNullInput = DefaultBoolean.True; + searchEdit.TextEditStyle = TextEditStyles.DisableTextEditor; + searchEdit.PopupSizeable = false; + searchEdit.PopupResizeMode = ResizeMode.Default; + searchEdit.PopupBorderStyle = PopupBorderStyles.Flat; + searchEdit.ShowPopupShadow = true; + searchEdit.PopupFormSize = popupSize; + searchEdit.PopupFormMinSize = popupSize; + searchEdit.PopupControl = popupControl; + searchEdit.Buttons.Clear(); + searchEdit.Buttons.Add(new EditorButton(ButtonPredefines.Combo)); + searchEdit.Tag = model; + searchEdit.QueryPopUp += ExtendedReturnSearch_QueryPopUp; + searchEdit.Popup += ExtendedReturnSearch_Popup; + searchEdit.Closed += ExtendedReturnSearch_Closed; + searchEdit.QueryDisplayText += ExtendedReturnSearch_QueryDisplayText; + + ExtendedReturnPopupContext context = new ExtendedReturnPopupContext + { + Model = model, + Popup = popup + }; + popupControl.Tag = context; + popup.Tag = context; + popup.SearchRequested += ExtendedReturnSearch_SearchRequested; + popup.ClearRequested += ExtendedReturnSearch_ClearRequested; + popup.ResultSelected += ExtendedReturnSearch_ResultSelected; + mExtendedReturnPopupContexts[model.FieldName] = context; + return searchEdit; + } + + private static Size GetExtendedReturnPopupSize(GridColumnModel model) + { + int width = model != null && model.LookUpWidth > 0 + ? model.LookUpWidth + : ExtendedLookupDefaultPopupWidth; + width = Math.Max(width, ExtendedLookupMinimumPopupWidth); + return new Size(width, ExtendedLookupDefaultPopupHeight); + } + + /// + /// 表格展示仍绑定实际业务字段,显示文本直接取同一业务行中预存的翻译列。 + /// + private void GridView_CustomColumnDisplayText_ExtendedReturn(object sender, CustomColumnDisplayTextEventArgs e) + { + try + { + GridColumnModel model = e.Column == null ? null : e.Column.Tag as GridColumnModel; + if (!IsExtendedReturnType(model)) return; + if (e.ListSourceRowIndex < 0) return; + + string displayField = GetExtendedReturnDisplayField(model); + if (string.IsNullOrWhiteSpace(displayField)) return; + + object displayValue = this.GridView.GetListSourceRowCellValue(e.ListSourceRowIndex, displayField); + e.DisplayText = displayValue == null || displayValue == DBNull.Value ? string.Empty : displayValue + string.Empty; + } + catch + { + // 配置尚未完善时回退显示实际值,避免表格绘制阶段反复弹窗。 + } + } + + private void ExtendedModuleReturn_CustomDisplayText(object sender, CustomDisplayTextEventArgs e) + { + try + { + RepositoryItemButtonEdit editor = sender as RepositoryItemButtonEdit; + GridColumnModel model = editor == null ? null : editor.Tag as GridColumnModel; + DataRow row = this.GridView.GetFocusedDataRow(); + string displayField = GetExtendedReturnDisplayField(model); + if (row == null || string.IsNullOrWhiteSpace(displayField) || !row.Table.Columns.Contains(displayField)) return; + + e.DisplayText = row[displayField] == DBNull.Value ? string.Empty : row[displayField] + string.Empty; + } + catch + { + } + } + + private static bool IsExtendedReturnType(GridColumnModel model) + { + return model != null && + (model.FieldType == ControlType.LabModuleSelectReturnIdExtended || + model.FieldType == ControlType.LabSearchReturnIdExtended); + } + + private void CacheExtendedReturnDisplayField(GridColumnModel model) + { + try + { + IList mappings = ExtendedReturnSupport.ParseResultFields(model.ResultFields); + mExtendedReturnDisplayFields[model.FieldName] = ExtendedReturnSupport.FindTargetField(mappings, model.TextMember); + } + catch + { + mExtendedReturnDisplayFields[model.FieldName] = string.Empty; + } + } + + private string GetExtendedReturnDisplayField(GridColumnModel model) + { + if (model == null) return string.Empty; + string displayField; + if (mExtendedReturnDisplayFields.TryGetValue(model.FieldName, out displayField)) return displayField; + + IList mappings = ExtendedReturnSupport.ParseResultFields(model.ResultFields); + displayField = ExtendedReturnSupport.FindTargetField(mappings, model.TextMember); + mExtendedReturnDisplayFields[model.FieldName] = displayField; + return displayField; + } + + private void GridView_CustomRowCellEditForEditing_ExtendedReturn(object sender, CustomRowCellEditEventArgs e) + { + GridColumnModel model = e.Column == null ? null : e.Column.Tag as GridColumnModel; + if (model == null || model.FieldType != ControlType.LabSearchReturnIdExtended) return; + + RepositoryItemPopupContainerEdit searchEdit; + if (!mExtendedReturnSearchEditors.TryGetValue(model.FieldName, out searchEdit)) return; + + try + { + DataRow row = this.GridView.GetDataRow(e.RowHandle); + ValidateExtendedReturnBusinessConfiguration(model, row); + ExtendedReturnPopupContext context = GetExtendedReturnPopupContext(model.FieldName); + context.BusinessRow = row; + context.OwnerEdit = null; + context.Popup.ResultGrid.DataSource = null; + e.RepositoryItem = searchEdit; + } + catch (Exception ex) + { + MessageUtil.Show("扩展返回字段配置错误:" + ex.Message); + } + } + + private ExtendedReturnPopupContext GetExtendedReturnPopupContext(string fieldName) + { + ExtendedReturnPopupContext context; + if (!mExtendedReturnPopupContexts.TryGetValue(fieldName, out context)) + { + throw new InvalidOperationException(string.Format("扩展搜索列“{0}”未初始化弹出结果容器。", fieldName)); + } + return context; + } + + private void GridView_RowCellClick_ExtendedReturn(object sender, RowCellClickEventArgs e) + { + GridColumnModel model = e.Column == null ? null : e.Column.Tag as GridColumnModel; + if (e.Button != MouseButtons.Left || model == null || + model.FieldType != ControlType.LabSearchReturnIdExtended || + !e.Column.OptionsColumn.AllowEdit) return; + + this.GridView.FocusedRowHandle = e.RowHandle; + this.GridView.FocusedColumn = e.Column; + this.GridView.ShowEditor(); + + PopupContainerEdit edit = this.GridView.ActiveEditor as PopupContainerEdit; + if (edit == null) return; + + MethodInvoker showPopup = new MethodInvoker(delegate + { + if (this.IsDisposed || edit.IsDisposed || + !object.ReferenceEquals(this.GridView.ActiveEditor, edit) || edit.IsPopupOpen) return; + edit.ShowPopup(); + }); + try + { + this.BeginInvoke(showPopup); + } + catch (ObjectDisposedException) + { + } + catch (InvalidOperationException) + { + } + } + + private void ExtendedReturnSearch_QueryPopUp(object sender, CancelEventArgs e) + { + PopupContainerEdit edit = sender as PopupContainerEdit; + GridColumnModel model = edit == null ? null : edit.Properties.Tag as GridColumnModel; + if (model == null) return; + + try + { + ExtendedReturnPopupContext context = GetExtendedReturnPopupContext(model.FieldName); + context.OwnerEdit = edit; + context.BusinessRow = this.GridView.GetFocusedDataRow(); + ValidateExtendedReturnBusinessConfiguration(model, context.BusinessRow); + Size popupSize = GetExtendedReturnPopupSize(model); + context.Popup.Size = popupSize; + edit.Properties.PopupControl.Size = popupSize; + edit.Properties.PopupFormSize = popupSize; + edit.Properties.PopupFormMinSize = popupSize; + + string actualValueText = GetExtendedReturnBusinessValueText(context); + context.Popup.PrepareForOpen(); + if (actualValueText.Length > 0) + { + StartExtendedReturnSearch(context, actualValueText); + } + else + { + CancelExtendedReturnSearch(model.FieldName); + } + + CurrentOperColumnKey = Model.ModuleCode + "_" + model.FieldName; + CurrentOperModel = model; + CurrentOperGridView = this.GridView; + } + catch (Exception ex) + { + e.Cancel = true; + MessageUtil.Show("扩展搜索配置错误:" + ex.Message); + } + } + + private void ExtendedReturnSearch_Popup(object sender, EventArgs e) + { + PopupContainerEdit edit = sender as PopupContainerEdit; + GridColumnModel model = edit == null ? null : edit.Properties.Tag as GridColumnModel; + if (model == null) return; + + ExtendedReturnPopupContext context = GetExtendedReturnPopupContext(model.FieldName); + try + { + context.Popup.BeginInvoke(new MethodInvoker(context.Popup.FocusSearchEditor)); + } + catch (ObjectDisposedException) + { + } + catch (InvalidOperationException) + { + } + } + + private void ExtendedReturnSearch_QueryDisplayText(object sender, QueryDisplayTextEventArgs e) + { + RepositoryItemPopupContainerEdit repository = sender as RepositoryItemPopupContainerEdit; + GridColumnModel model = repository == null ? null : repository.Tag as GridColumnModel; + if (model == null) return; + + ExtendedReturnPopupContext context; + if (!mExtendedReturnPopupContexts.TryGetValue(model.FieldName, out context)) return; + e.DisplayText = GetExtendedReturnBusinessDisplayText(context); + } + + private string GetExtendedReturnBusinessDisplayText(ExtendedReturnPopupContext context) + { + if (context == null || context.Model == null) return string.Empty; + + DataRow row = context.BusinessRow ?? this.GridView.GetFocusedDataRow(); + string displayField = GetExtendedReturnDisplayField(context.Model); + if (row == null || string.IsNullOrWhiteSpace(displayField) || + !row.Table.Columns.Contains(displayField)) return string.Empty; + + object displayValue = row[displayField]; + return displayValue == null || displayValue == DBNull.Value + ? string.Empty + : displayValue + string.Empty; + } + + private string GetExtendedReturnBusinessValueText(ExtendedReturnPopupContext context) + { + if (context == null || context.Model == null) return string.Empty; + + DataRow row = context.BusinessRow ?? this.GridView.GetFocusedDataRow(); + if (row == null || !row.Table.Columns.Contains(context.Model.FieldName)) return string.Empty; + + object actualValue = row[context.Model.FieldName]; + return actualValue == null || actualValue == DBNull.Value + ? string.Empty + : actualValue + string.Empty; + } + + private void ExtendedReturnSearch_SearchRequested(object sender, EventArgs e) + { + ExtendedReturnSearchPopup popup = sender as ExtendedReturnSearchPopup; + ExtendedReturnPopupContext context = popup == null ? null : popup.Tag as ExtendedReturnPopupContext; + if (context == null) return; + + try + { + StartExtendedReturnSearch(context, popup.SearchText); + } + catch (Exception ex) + { + MessageUtil.Show("扩展搜索配置错误:" + ex.Message); + } + } + + private void StartExtendedReturnSearch(ExtendedReturnPopupContext context, string keyword) + { + if (context == null || context.Model == null || context.OwnerEdit == null) return; + + keyword = (keyword ?? string.Empty).Trim(); + CancelExtendedReturnSearch(context.Model.FieldName); + context.Popup.ResultGrid.DataSource = null; + if (keyword.Length == 0) return; + + ValidateExtendedReturnBusinessConfiguration(context.Model, context.BusinessRow); + string sourceSql = ResolveExtendedReturnSourceSql(context.Model, context.BusinessRow); + QueueExtendedReturnSearch(context.OwnerEdit, context, sourceSql, keyword); + } + + private void ExtendedReturnSearch_ClearRequested(object sender, EventArgs e) + { + ExtendedReturnSearchPopup popup = sender as ExtendedReturnSearchPopup; + ExtendedReturnPopupContext context = popup == null ? null : popup.Tag as ExtendedReturnPopupContext; + if (context == null || context.OwnerEdit == null) return; + + try + { + ClearExtendedReturnFromPopup(context); + } + catch (Exception ex) + { + MessageUtil.Show("扩展返回字段配置错误:" + ex.Message); + } + } + + private void ExtendedReturnSearch_ResultSelected(object sender, EventArgs e) + { + ExtendedReturnSearchPopup popup = sender as ExtendedReturnSearchPopup; + ExtendedReturnPopupContext context = popup == null ? null : popup.Tag as ExtendedReturnPopupContext; + if (context == null || context.OwnerEdit == null || + !HasCurrentExtendedReturnResult(context.Model.FieldName)) return; + + DataRow selectedRow = popup.ResultView.GetFocusedDataRow(); + if (selectedRow == null || context.BusinessRow == null) return; + + string error; + if (!TryApplyExtendedReturnMappings(context.BusinessRow, selectedRow, context.Model, out error)) + { + MessageUtil.Show("扩展返回字段配置错误:" + error); + return; + } + + CancelExtendedReturnSearch(context.Model.FieldName); + SetExtendedReturnEditorBusinessValue(context); + context.OwnerEdit.ClosePopup(); + this.GridView.PostEditor(); + this.GridView.UpdateCurrentRow(); + this.GridView.RefreshRow(this.GridView.FocusedRowHandle); + } + + private string ResolveExtendedReturnSourceSql(GridColumnModel model, DataRow row) + { + string sqlValue = model == null ? string.Empty : model.SqlSource; + if (this.ParentControl != null) + { + sqlValue = this.ParentControl.ReplaceParentControlValue(sqlValue); + } + sqlValue = (sqlValue ?? string.Empty).Replace("#", string.Empty); + + if (row != null) + { + sqlValue = ReplaceHelper.ReplaceRowParam(row, sqlValue); + } + sqlValue = BaseImpl.GetDefaultValue(sqlValue); + return ReplaceHelper.ReplaceParam(sqlValue); + } + + private static void ValidateExtendedReturnMembers(GridColumnModel model) + { + if (model == null) throw new InvalidOperationException("扩展返回列配置不存在。"); + if (string.IsNullOrWhiteSpace(model.ValueMember)) + { + throw new InvalidOperationException("fieldsqlid 不能为空。"); + } + if (string.IsNullOrWhiteSpace(model.TextMember)) + { + throw new InvalidOperationException("fieldsqlname 不能为空。"); + } + if (string.Equals(model.ValueMember, model.TextMember, StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException("fieldsqlid 与 fieldsqlname 必须配置为不同的返回列。"); + } + } + + private static IList ValidateExtendedReturnBusinessConfiguration( + GridColumnModel model, + DataRow businessRow) + { + ValidateExtendedReturnMembers(model); + if (businessRow == null) + { + throw new InvalidOperationException("当前业务行不存在。"); + } + if (!businessRow.Table.Columns.Contains(model.FieldName)) + { + throw new InvalidOperationException(string.Format("业务表不存在实际值字段“{0}”。", model.FieldName)); + } + + IList mappings = ExtendedReturnSupport.ParseResultFields(model.ResultFields); + if (mappings.Count == 0) + { + throw new InvalidOperationException("resultfields 不能为空。"); + } + int displayMappingCount = mappings.Count( + item => string.Equals(item.SourceField, model.TextMember, StringComparison.OrdinalIgnoreCase)); + if (displayMappingCount == 0) + { + throw new InvalidOperationException( + string.Format("resultfields 未配置显示字段“{0}”的业务表映射。", model.TextMember)); + } + if (displayMappingCount > 1) + { + throw new InvalidOperationException( + string.Format("resultfields 中显示字段“{0}”只能映射到一个业务表字段。", model.TextMember)); + } + ExtendedReturnFieldMapping boundFieldMapping = mappings.FirstOrDefault( + item => string.Equals(item.TargetField, model.FieldName, StringComparison.OrdinalIgnoreCase)); + if (boundFieldMapping != null && + !string.Equals(boundFieldMapping.SourceField, model.ValueMember, StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException( + string.Format( + "实际值字段“{0}”必须映射到 fieldsqlid 指定的返回列“{1}”。", + model.FieldName, + model.ValueMember)); + } + foreach (ExtendedReturnFieldMapping mapping in mappings) + { + if (!businessRow.Table.Columns.Contains(mapping.TargetField)) + { + throw new InvalidOperationException(string.Format("业务表不存在字段“{0}”。", mapping.TargetField)); + } + } + return mappings; + } + + private static void ValidateExtendedReturnSourceColumns( + DataColumnCollection columns, + GridColumnModel model, + IEnumerable mappings) + { + if (columns == null || columns.Count == 0) + { + throw new InvalidOperationException("扩展返回数据源没有返回任何列。"); + } + if (!columns.Contains(model.ValueMember)) + { + throw new InvalidOperationException(string.Format("返回数据不存在值字段“{0}”。", model.ValueMember)); + } + if (!columns.Contains(model.TextMember)) + { + throw new InvalidOperationException(string.Format("返回数据不存在显示字段“{0}”。", model.TextMember)); + } + foreach (ExtendedReturnFieldMapping mapping in mappings) + { + if (!columns.Contains(mapping.SourceField)) + { + throw new InvalidOperationException(string.Format("返回数据不存在映射源字段“{0}”。", mapping.SourceField)); + } + } + } + + private void QueueExtendedReturnSearch( + PopupContainerEdit edit, + ExtendedReturnPopupContext context, + string sourceSql, + string keyword) + { + GridColumnModel model = context.Model; + ExtendedLookupRequest request; + lock (mExtendedReturnSyncRoot) + { + ExtendedLookupRequestState state = GetExtendedLookupState(model.FieldName); + state.Version++; + request = new ExtendedLookupRequest + { + Version = state.Version, + DueTime = DateTime.UtcNow, + FieldName = model.FieldName, + Keyword = keyword, + SourceSql = sourceSql, + ConnectionString = SqlHelper._connection.ConnectionString, + Model = model, + Editor = edit, + PopupContext = context, + BusinessRow = context.BusinessRow + }; + state.PendingRequest = request; + state.DeliveredVersion = 0; + if (state.DebounceTimer == null) + { + state.DebounceTimer = new System.Threading.Timer( + ExtendedReturnDebounceElapsed, + model.FieldName, + 0, + Timeout.Infinite); + } + else + { + state.DebounceTimer.Change(0, Timeout.Infinite); + } + } + } + + private ExtendedLookupRequestState GetExtendedLookupState(string fieldName) + { + ExtendedLookupRequestState state; + if (!mExtendedReturnQueryStates.TryGetValue(fieldName, out state)) + { + state = new ExtendedLookupRequestState(); + mExtendedReturnQueryStates[fieldName] = state; + } + return state; + } + + private void ExtendedReturnDebounceElapsed(object stateValue) + { + string fieldName = stateValue as string; + if (string.IsNullOrWhiteSpace(fieldName)) return; + + // System.Threading.Timer 已在线程池回调中执行;直接进入每列串行状态机, + // 避免搜索请求占用旧表格初始化共用的 SchedulerUtil 队列。 + StartExtendedReturnWorker(fieldName); + } + + private void StartExtendedReturnWorker(string fieldName) + { + ExtendedLookupRequest request; + lock (mExtendedReturnSyncRoot) + { + ExtendedLookupRequestState state = GetExtendedLookupState(fieldName); + if (state.IsRunning || state.PendingRequest == null) return; + + TimeSpan remainingDelay = state.PendingRequest.DueTime - DateTime.UtcNow; + if (remainingDelay.TotalMilliseconds > 0) + { + state.DebounceTimer.Change( + (int)Math.Ceiling(remainingDelay.TotalMilliseconds), + Timeout.Infinite); + return; + } + + state.IsRunning = true; + request = state.PendingRequest; + state.PendingRequest = null; + } + + RunExtendedReturnWorker(request); + } + + private void RunExtendedReturnWorker(ExtendedLookupRequest request) + { + while (request != null) + { + lock (mExtendedReturnSyncRoot) + { + ExtendedLookupRequestState currentState = GetExtendedLookupState(request.FieldName); + if (currentState.Version != request.Version) + { + request = TakeReadyExtendedReturnRequest(currentState); + continue; + } + } + + ExtendedLookupQueryResult result = QueryExtendedReturn(request); + bool deliverResult = false; + ExtendedLookupRequest nextRequest = null; + lock (mExtendedReturnSyncRoot) + { + ExtendedLookupRequestState state = GetExtendedLookupState(request.FieldName); + if (state.Version == request.Version && state.PendingRequest == null) + { + deliverResult = true; + state.IsRunning = false; + } + else + { + nextRequest = TakeReadyExtendedReturnRequest(state); + } + } + + if (deliverResult) + { + BeginInvokeExtendedReturnResult(request, result); + } + request = nextRequest; + } + } + + /// + /// 仅在 pending 已到防抖截止时间时交给当前工作线程;否则释放工作线程并重新启动定时器。 + /// 调用方必须持有 mExtendedReturnSyncRoot。 + /// + private static ExtendedLookupRequest TakeReadyExtendedReturnRequest(ExtendedLookupRequestState state) + { + ExtendedLookupRequest pendingRequest = state.PendingRequest; + if (pendingRequest == null) + { + state.IsRunning = false; + return null; + } + + TimeSpan remainingDelay = pendingRequest.DueTime - DateTime.UtcNow; + if (remainingDelay.TotalMilliseconds > 0) + { + state.IsRunning = false; + if (state.DebounceTimer != null) + { + state.DebounceTimer.Change( + (int)Math.Ceiling(remainingDelay.TotalMilliseconds), + Timeout.Infinite); + } + return null; + } + + state.PendingRequest = null; + return pendingRequest; + } + + private ExtendedLookupQueryResult QueryExtendedReturn(ExtendedLookupRequest request) + { + ExtendedLookupQueryResult result = new ExtendedLookupQueryResult(); + try + { + DataTable schema = GetExtendedReturnSchema(request.FieldName, request.SourceSql, request.ConnectionString); + IList mappings = ExtendedReturnSupport.ParseResultFields(request.Model.ResultFields); + ValidateExtendedReturnSourceColumns(schema.Columns, request.Model, mappings); + string searchSql = ExtendedReturnSupport.BuildSearchSql(request.SourceSql, schema.Columns, ExtendedLookupMaxRows); + result.Table = ExecuteExtendedReturnQuery( + searchSql, + ExtendedReturnSupport.BuildLikeParameterValue(request.Keyword), + request.ConnectionString); + } + catch (Exception ex) + { + result.Error = ex; + } + return result; + } + + private DataTable GetExtendedReturnSchema(string fieldName, string sourceSql, string connectionString) + { + int schemaGeneration; + lock (mExtendedReturnSyncRoot) + { + DataTable cachedSchema; + if (mExtendedReturnSchemas.TryGetValue(fieldName, out cachedSchema)) return cachedSchema; + schemaGeneration = mExtendedReturnSchemaGeneration; + } + + DataTable schema = ExecuteExtendedReturnQuery(ExtendedReturnSupport.BuildStructureSql(sourceSql), null, connectionString); + lock (mExtendedReturnSyncRoot) + { + DataTable cachedSchema; + if (mExtendedReturnSchemas.TryGetValue(fieldName, out cachedSchema)) return cachedSchema; + if (schemaGeneration == mExtendedReturnSchemaGeneration) + { + mExtendedReturnSchemas[fieldName] = schema; + } + } + return schema; + } + + private static DataTable ExecuteExtendedReturnQuery( + string commandText, + string keywordParameterValue, + string connectionString) + { + DataTable table = new DataTable(); + using (DbConnection connection = SqlHelper.dbFactory.CreateConnection()) + using (DbCommand command = SqlHelper.dbFactory.CreateCommand()) + using (DbDataAdapter adapter = SqlHelper.dbFactory.CreateDataAdapter()) + { + connection.ConnectionString = connectionString; + command.Connection = connection; + command.CommandText = commandText; + command.CommandType = CommandType.Text; + command.CommandTimeout = SqlHelper.CommandTimeout; + + if (keywordParameterValue != null) + { + DbParameter parameter = SqlHelper.dbFactory.CreateParameter(); + parameter.ParameterName = ExtendedReturnSupport.SearchParameterName; + parameter.DbType = DbType.String; + parameter.Size = 4000; + parameter.Value = keywordParameterValue; + command.Parameters.Add(parameter); + } + + adapter.SelectCommand = command; + connection.Open(); + adapter.Fill(table); + } + return table; + } + + private void BeginInvokeExtendedReturnResult(ExtendedLookupRequest request, ExtendedLookupQueryResult result) + { + if (this.IsDisposed || !this.IsHandleCreated) return; + + MethodInvoker deliver = new MethodInvoker(delegate + { + if (this.IsDisposed || request.Editor == null || request.Editor.IsDisposed) return; + if (!object.ReferenceEquals(this.GridView.ActiveEditor, request.Editor)) return; + if (this.GridView.FocusedColumn == null || + !string.Equals(this.GridView.FocusedColumn.FieldName, request.FieldName, StringComparison.OrdinalIgnoreCase)) return; + if (!object.ReferenceEquals(this.GridView.GetFocusedDataRow(), request.BusinessRow)) return; + + lock (mExtendedReturnSyncRoot) + { + ExtendedLookupRequestState state = GetExtendedLookupState(request.FieldName); + if (state.Version != request.Version) return; + } + + if (result.Error != null) + { + MessageUtil.Show("扩展搜索查询失败:" + result.Error.Message); + return; + } + + lock (mExtendedReturnSyncRoot) + { + ExtendedLookupRequestState state = GetExtendedLookupState(request.FieldName); + if (state.Version != request.Version) return; + state.DeliveredVersion = request.Version; + } + request.PopupContext.Popup.ResultGrid.DataSource = result.Table; + ConfigureExtendedReturnViewColumns(request.PopupContext, result.Table); + if (request.Editor.Focused && !request.Editor.IsPopupOpen) + { + request.Editor.ShowPopup(); + } + }); + + try + { + this.BeginInvoke(deliver); + } + catch (ObjectDisposedException) + { + // 控件关闭期间丢弃后台结果。 + } + catch (InvalidOperationException) + { + // 句柄销毁或尚不可用时丢弃后台结果。 + } + } + + private void ConfigureExtendedReturnViewColumns(ExtendedReturnPopupContext context, DataTable table) + { + if (context == null || context.Popup == null || table == null || context.Model == null) return; + + GridColumnModel model = context.Model; + GridView resultView = context.Popup.ResultView; + IList mappings = ExtendedReturnSupport.ParseResultFields(model.ResultFields); + resultView.Columns.Clear(); + foreach (DataColumn dataColumn in table.Columns) + { + GridColumn column = new GridColumn(); + column.Name = column.FieldName = dataColumn.ColumnName; + column.Caption = GetExtendedReturnColumnCaption(dataColumn.ColumnName, mappings); + column.Visible = !dataColumn.ColumnName.StartsWith("_", StringComparison.Ordinal); + resultView.Columns.Add(column); + } + + string[] configuredWidths = string.IsNullOrWhiteSpace(model.LookUpFieldsWidth) + ? null + : model.LookUpFieldsWidth.Trim().TrimEnd(',').Split(','); + int visibleIndex = 0; + foreach (GridColumn column in resultView.Columns) + { + if (!column.Visible) continue; + + int width; + if (configuredWidths != null && visibleIndex < configuredWidths.Length && + int.TryParse(configuredWidths[visibleIndex], out width)) + { + column.Width = width; + } + else + { + int captionWidth = GraphicsText.GetTextWidth(column.Caption); + int valueWidth = CalcMaxColumnWidth(table, column.FieldName); + column.Width = Math.Max(captionWidth, valueWidth); + } + visibleIndex++; + } + } + + private string GetExtendedReturnColumnCaption( + string sourceField, + IEnumerable mappings) + { + string targetField = ExtendedReturnSupport.FindTargetField(mappings, sourceField); + if (string.IsNullOrWhiteSpace(targetField)) return sourceField; + + GridColumn businessColumn = this.GridView.Columns.FirstOrDefault( + item => string.Equals(item.FieldName, targetField, StringComparison.OrdinalIgnoreCase)); + if (businessColumn != null && !string.IsNullOrWhiteSpace(businessColumn.Caption)) + { + return businessColumn.Caption; + } + return targetField; + } + + private void ClearExtendedReturnFromPopup(ExtendedReturnPopupContext context) + { + GridColumnModel model = context.Model; + CancelExtendedReturnSearch(model.FieldName); + string error; + if (!TryClearExtendedReturnMappings(context.BusinessRow, model, out error)) + { + throw new InvalidOperationException(error); + } + + context.OwnerEdit.EditValue = null; + context.Popup.ResultGrid.DataSource = null; + if (context.OwnerEdit.IsPopupOpen) context.OwnerEdit.ClosePopup(); + this.GridView.PostEditor(); + this.GridView.UpdateCurrentRow(); + this.GridView.RefreshRow(this.GridView.FocusedRowHandle); + } + + private void SetExtendedReturnEditorBusinessValue(ExtendedReturnPopupContext context) + { + if (context == null || context.OwnerEdit == null || context.Model == null || + context.BusinessRow == null || + !context.BusinessRow.Table.Columns.Contains(context.Model.FieldName)) return; + + object value = context.BusinessRow[context.Model.FieldName]; + context.OwnerEdit.EditValue = value == null || value == DBNull.Value ? null : value; + } + + private void ExtendedReturnSearch_Closed(object sender, ClosedEventArgs e) + { + PopupContainerEdit edit = sender as PopupContainerEdit; + if (edit == null) return; + + GridColumnModel model = edit.Properties.Tag as GridColumnModel; + if (model == null) return; + + try + { + ExtendedReturnPopupContext context = GetExtendedReturnPopupContext(model.FieldName); + CancelExtendedReturnSearch(model.FieldName); + SetExtendedReturnEditorBusinessValue(context); + context.Popup.ResultGrid.DataSource = null; + this.GridView.RefreshRow(this.GridView.FocusedRowHandle); + } + catch (Exception ex) + { + MessageUtil.Show("扩展返回字段配置错误:" + ex.Message); + } + } + + private void CancelExtendedReturnSearch(string fieldName) + { + lock (mExtendedReturnSyncRoot) + { + ExtendedLookupRequestState state = GetExtendedLookupState(fieldName); + state.Version++; + state.DeliveredVersion = 0; + state.PendingRequest = null; + if (state.DebounceTimer != null) + { + state.DebounceTimer.Change(Timeout.Infinite, Timeout.Infinite); + } + } + } + + private bool HasCurrentExtendedReturnResult(string fieldName) + { + lock (mExtendedReturnSyncRoot) + { + ExtendedLookupRequestState state = GetExtendedLookupState(fieldName); + return state.DeliveredVersion != 0 && state.DeliveredVersion == state.Version; + } + } + + private bool TryApplyExtendedReturnMappings( + DataRow businessRow, + DataRow selectedRow, + GridColumnModel model, + out string error) + { + error = string.Empty; + if (businessRow == null || selectedRow == null || model == null) + { + error = "业务行或选中行不存在。"; + return false; + } + + IList mappings; + try + { + mappings = ValidateExtendedReturnBusinessConfiguration(model, businessRow); + } + catch (Exception ex) + { + error = ex.Message; + return false; + } + List> stagedValues = new List>(); + foreach (ExtendedReturnFieldMapping mapping in mappings) + { + if (!businessRow.Table.Columns.Contains(mapping.TargetField)) + { + error = string.Format("业务表不存在字段“{0}”。", mapping.TargetField); + return false; + } + if (!selectedRow.Table.Columns.Contains(mapping.SourceField)) + { + error = string.Format("查询结果不存在字段“{0}”。", mapping.SourceField); + return false; + } + stagedValues.Add(new KeyValuePair(mapping.TargetField, selectedRow[mapping.SourceField])); + } + + if (!mappings.Any(item => string.Equals(item.TargetField, model.FieldName, StringComparison.OrdinalIgnoreCase))) + { + if (!businessRow.Table.Columns.Contains(model.FieldName)) + { + error = string.Format("业务表不存在实际值字段“{0}”。", model.FieldName); + return false; + } + if (string.IsNullOrWhiteSpace(model.ValueMember) || !selectedRow.Table.Columns.Contains(model.ValueMember)) + { + error = string.Format("查询结果不存在值字段“{0}”。", model.ValueMember); + return false; + } + stagedValues.Add(new KeyValuePair(model.FieldName, selectedRow[model.ValueMember])); + } + + return TryCommitExtendedReturnValues(businessRow, stagedValues, out error); + } + + private static bool IsExtendedReturnEmptyValue(object value) + { + return value == null || value == DBNull.Value || string.IsNullOrWhiteSpace(value + string.Empty); + } + + private static bool TryClearExtendedReturnMappings( + DataRow businessRow, + GridColumnModel model, + out string error) + { + error = string.Empty; + IList mappings; + try + { + mappings = ValidateExtendedReturnBusinessConfiguration(model, businessRow); + } + catch (Exception ex) + { + error = ex.Message; + return false; + } + + List> stagedValues = new List>(); + HashSet targetFields = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (ExtendedReturnFieldMapping mapping in mappings) + { + targetFields.Add(mapping.TargetField); + } + targetFields.Add(model.FieldName); + + foreach (string targetField in targetFields) + { + DataColumn column = businessRow.Table.Columns[targetField]; + if (!column.AllowDBNull) + { + error = string.Format("业务字段“{0}”不允许为空,不能清空扩展选择结果。", targetField); + return false; + } + stagedValues.Add(new KeyValuePair(targetField, DBNull.Value)); + } + return TryCommitExtendedReturnValues(businessRow, stagedValues, out error); + } + + private void ExtendedModuleReturn_ButtonClick(object sender, ButtonPressedEventArgs e) + { + ButtonEdit buttonEdit = sender as ButtonEdit; + GridColumnModel model = buttonEdit == null ? null : buttonEdit.Properties.Tag as GridColumnModel; + if (model == null) return; + + try + { + DataRow businessRow = this.GridView.GetFocusedDataRow(); + IList mappings = ValidateExtendedReturnBusinessConfiguration(model, businessRow); + + using (FrmModelLookUp2 modelLookUp = new FrmModelLookUp2(model.addModuleld, true)) + { + modelLookUp.ConfigureColumnMode = model.ConfigureColumnMode; + modelLookUp.ValueField = model.ValueMember; + modelLookUp.TextField = model.TextMember; + modelLookUp.SourceSQL = model.SqlSource; + modelLookUp.IsType = ControlType.LabSelectReturnIdNew; + modelLookUp.ValueMember = model.ValueMember; + modelLookUp.Tag = model; + modelLookUp.EditText = buttonEdit.Text; + modelLookUp.EditValue = buttonEdit.EditValue + string.Empty; + modelLookUp.CurrentOperColumnKey = Model.ModuleCode + "_" + model.FieldName; + + string sourceSql = modelLookUp.SysModel.MenuSql.Replace("#", string.Empty) + model.SplicingConditions; + if (this.ParentControl != null) + { + sourceSql = this.ParentControl.ReplaceControlValue(sourceSql); + } + sourceSql = ReplaceHelper.ReplaceRowParam(this.GridView.GetFocusedDataRow(), sourceSql); + modelLookUp.NewSourceSQL = sourceSql; + modelLookUp.InitControls(modelLookUp.UnionModuleCodel); + ValidateExtendedReturnSourceColumns(modelLookUp.DataSource.Columns, model, mappings); + + if (modelLookUp.ShowDialog() != DialogResult.OK) return; + + string error; + if (!TryApplyExtendedReturnMappings(businessRow, modelLookUp.GetSelectedResultRow(), model, out error)) + { + MessageUtil.Show("扩展返回字段配置错误:" + error); + return; + } + + buttonEdit.EditValue = businessRow[model.FieldName]; + this.GridView.PostEditor(); + this.GridView.UpdateCurrentRow(); + this.GridView.RefreshRow(this.GridView.FocusedRowHandle); + } + } + catch (Exception ex) + { + MessageUtil.Show("扩展模块选择失败:" + ex.Message); + } + } + + /// + /// 先在同结构的脱离行上完成全部类型转换,所有值都合法后再写入真实业务行。 + /// + private static bool TryCommitExtendedReturnValues( + DataRow businessRow, + IList> stagedValues, + out string error) + { + error = string.Empty; + try + { + DataTable validationTable = businessRow.Table.Clone(); + DataRow validationRow = validationTable.NewRow(); + foreach (KeyValuePair stagedValue in stagedValues) + { + validationRow[stagedValue.Key] = stagedValue.Value == null ? DBNull.Value : stagedValue.Value; + } + + Dictionary originalValues = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (KeyValuePair stagedValue in stagedValues) + { + originalValues[stagedValue.Key] = businessRow[stagedValue.Key]; + } + + try + { + foreach (KeyValuePair stagedValue in stagedValues) + { + businessRow[stagedValue.Key] = validationRow[stagedValue.Key]; + } + } + catch + { + foreach (KeyValuePair originalValue in originalValues) + { + businessRow[originalValue.Key] = originalValue.Value; + } + throw; + } + return true; + } + catch (Exception ex) + { + error = "返回值无法写入业务字段:" + ex.Message; + return false; + } + } + + /// + /// 刷新控件数据源时只清空扩展搜索的架构和请求状态,不会重新加载业务数据源。 + /// + private void ResetExtendedReturnState() + { + DisposeExtendedReturnRequestTimers(); + lock (mExtendedReturnSyncRoot) + { + mExtendedReturnSchemas.Clear(); + mExtendedReturnSchemaGeneration++; + } + + foreach (ExtendedReturnPopupContext context in mExtendedReturnPopupContexts.Values) + { + context.Popup.ResultGrid.DataSource = null; + } + } + + private void DisposeExtendedReturnRequestTimers() + { + lock (mExtendedReturnSyncRoot) + { + foreach (ExtendedLookupRequestState state in mExtendedReturnQueryStates.Values) + { + state.Version++; + state.DeliveredVersion = 0; + state.PendingRequest = null; + if (state.DebounceTimer != null) + { + state.DebounceTimer.Change(Timeout.Infinite, Timeout.Infinite); + state.DebounceTimer.Dispose(); + state.DebounceTimer = null; + } + } + } + } + } +} diff --git a/插件库/Lskj.Control/GridControlEx.cs b/插件库/Lskj.Control/GridControlEx.cs index 8d7db63..c3b8bef 100644 --- a/插件库/Lskj.Control/GridControlEx.cs +++ b/插件库/Lskj.Control/GridControlEx.cs @@ -192,30 +192,6 @@ namespace Lskj.Control /// public Dictionary> mControlSourceDic = new Dictionary>(); /// - /// 字典搜索框数据源缓存,只用于弹出下拉框时临时绑定,避免表格滚动时控件持有大数据源。 - /// - private Dictionary mSpecialReturnSourceDic = new Dictionary(); - /// - /// 字典搜索框后台加载任务缓存。 - /// - private Dictionary> mSpecialReturnSourceTaskDic = new Dictionary>(); - /// - /// 字典搜索框显示文本缓存,表格显示时按单元格值快速转换为显示文本。 - /// - private Dictionary> mSpecialReturnDisplayDic = new Dictionary>(); - /// - /// 字典搜索框编辑器缓存,进入编辑状态时再替换为搜索框控件。 - /// - private Dictionary mSpecialReturnEditDic = new Dictionary(); - /// - /// 字典搜索框显示文本事件是否已绑定。 - /// - private bool mSpecialReturnDisplayEventAttached = false; - /// - /// 字典搜索框编辑器替换事件是否已绑定。 - /// - private bool mSpecialReturnEditEventAttached = false; - /// /// 计算值 /// public decimal customSum; @@ -1019,7 +995,7 @@ namespace Lskj.Control return; } - if (!string.IsNullOrEmpty(model.SqlSource) && !ControlType.IsNotLoadData(model.FieldType) && !IsSpecialReturnBox(model.FieldType)) + if (!string.IsNullOrEmpty(model.SqlSource) && !ControlType.IsNotLoadData(model.FieldType)) { Task sourceTask = new Task(() => { @@ -1215,11 +1191,9 @@ namespace Lskj.Control case ControlType.LabModuleAddRowsText: InitModuleReturnLine(column, model); break; - case ControlType.DictionarySearchBoxToId: - case ControlType.DictionarySearchBoxToText: - case ControlType.DictionarySearchBoxToIdParam: - case ControlType.DictionarySearchBoxToTextParam: - InitSpecialReturn(column, model); + case ControlType.LabModuleSelectReturnIdExtended: + case ControlType.LabSearchReturnIdExtended: + InitExtendedReturn(column, model); break; default: break; @@ -1231,569 +1205,6 @@ namespace Lskj.Control } } - /// - /// 初始化字典搜索框。表格显示阶段使用文本编辑器,进入编辑状态时再切换为搜索框。 - /// - private void InitSpecialReturn(GridColumn column, GridColumnModel model) - { - RepositoryItemGridLookUpEdit searchEdit = CreateSpecialReturnSearchEdit(model); - RepositoryItemTextEdit textEdit = new RepositoryItemTextEdit(); - - gridControl.RepositoryItems.Add(textEdit); - gridControl.RepositoryItems.Add(searchEdit); - column.ColumnEdit = textEdit; - - mSpecialReturnEditDic[model.FieldName] = searchEdit; - this.mControlList.Add(model); - StartSpecialReturnSourceTask(model); - - if (!mSpecialReturnDisplayEventAttached) - { - this.gridView.CustomColumnDisplayText += GridView_CustomColumnDisplayText_SpecialReturn; - mSpecialReturnDisplayEventAttached = true; - } - if (!mSpecialReturnEditEventAttached) - { - this.gridView.CustomRowCellEditForEditing += GridView_CustomRowCellEditForEditing_SpecialReturn; - mSpecialReturnEditEventAttached = true; - } - - } - - /// - /// 判断是否为字典搜索框类型。 - /// - private static bool IsSpecialReturnBox(int fieldType) - { - return fieldType == ControlType.DictionarySearchBoxToId - || fieldType == ControlType.DictionarySearchBoxToText - || fieldType == ControlType.DictionarySearchBoxToIdParam - || fieldType == ControlType.DictionarySearchBoxToTextParam; - } - - /// - /// 判断字典搜索框是否保存显示文本。 - /// - private static bool IsSpecialReturnTextBox(int fieldType) - { - return fieldType == ControlType.DictionarySearchBoxToText - || fieldType == ControlType.DictionarySearchBoxToTextParam; - } - - /// - /// 判断字典搜索框是否为带参数类型。 - /// - private static bool IsSpecialReturnParamBox(int fieldType) - { - return fieldType == ControlType.DictionarySearchBoxToIdParam - || fieldType == ControlType.DictionarySearchBoxToTextParam; - } - - /// - /// 创建字典搜索框编辑器。 - /// - private RepositoryItemGridLookUpEdit CreateSpecialReturnSearchEdit(GridColumnModel model) - { - RepositoryItemGridLookUpEdit searchEdit = new RepositoryItemGridLookUpEdit(); - if (model.FontSize > 0) searchEdit.View.Appearance.Row.Font = new Font("微软雅黑", model.FontSize); - searchEdit.View.OptionsView.ShowIndicator = false; - searchEdit.View.OptionsView.ColumnAutoWidth = false; - searchEdit.PopupSizeable = true; - searchEdit.PopupResizeMode = ResizeMode.Default; - searchEdit.AllowFocused = true; - searchEdit.ImmediatePopup = true; - searchEdit.ShowFooter = false; - searchEdit.NullText = ""; - searchEdit.TextEditStyle = TextEditStyles.Standard; - searchEdit.PopupBorderStyle = PopupBorderStyles.Flat; - searchEdit.AllowNullInput = DefaultBoolean.False; - searchEdit.ShowPopupShadow = true; - searchEdit.DisplayMember = model.TextMember; - searchEdit.ValueMember = IsSpecialReturnTextBox(model.FieldType) ? model.TextMember : model.ValueMember; - searchEdit.Tag = model; - searchEdit.View.Tag = searchEdit; - searchEdit.View.Appearance.HeaderPanel.TextOptions.HAlignment = HorzAlignment.Center; - searchEdit.View.PopupMenuShowing += new PopupMenuShowingEventHandler(OnAutoSearchEditPopupMenuShowing); - searchEdit.KeyDown += OnsearchEditKeyDown; - searchEdit.QueryPopUp += SpecialReturnEdit_QueryPopUp; - searchEdit.CloseUp += SpecialReturnEdit_CloseUp; - searchEdit.Closed += SpecialReturnEdit_Closed; - searchEdit.CustomDisplayText += SpecialReturnEdit_CustomDisplayText; - searchEdit.EditValueChanging += gridLookUpEdit1_EditValueChanging; - searchEdit.View.CustomDrawFilterPanel += OnCustomDrawFilterPanel; - - GridColumn valueColumn = new GridColumn(); - valueColumn.Name = valueColumn.FieldName = model.ValueMember; - valueColumn.Caption = "编码"; - valueColumn.Visible = model.ValueMember.Substring(0, 1) != "_"; - - GridColumn textColumn = new GridColumn(); - textColumn.Name = textColumn.FieldName = model.TextMember; - textColumn.Caption = "名称"; - textColumn.Visible = model.TextMember.Substring(0, 1) != "_"; - - searchEdit.View.Columns.AddRange(new GridColumn[] { valueColumn, textColumn }); - return searchEdit; - } - - /// - /// 字典搜索框显示文本事件,表格滚动显示时只查字典,不绑定下拉框大数据源。 - /// - private void GridView_CustomColumnDisplayText_SpecialReturn(object sender, CustomColumnDisplayTextEventArgs e) - { - try - { - GridColumnModel model = e.Column.Tag as GridColumnModel; - if (model == null || !IsSpecialReturnBox(model.FieldType)) return; - - string value = e.Value + ""; - if (string.IsNullOrWhiteSpace(value)) return; - - string displayText = string.Empty; - bool sourceReady = EnsureSpecialReturnSourceReady(model); - if (TryGetSpecialReturnDisplayText(model, value, out displayText)) - { - e.DisplayText = displayText; - } - else if (sourceReady) - { - e.DisplayText = string.Empty; - } - } - catch - { - } - } - - /// - /// 字典搜索框编辑器替换事件,只有进入编辑状态时才使用搜索框控件。 - /// - private void GridView_CustomRowCellEditForEditing_SpecialReturn(object sender, CustomRowCellEditEventArgs e) - { - GridColumnModel model = e.Column.Tag as GridColumnModel; - if (model == null || !IsSpecialReturnBox(model.FieldType)) return; - if (mSpecialReturnEditDic.ContainsKey(model.FieldName)) - { - RepositoryItemGridLookUpEdit searchEdit = mSpecialReturnEditDic[model.FieldName]; - BindSpecialReturnCurrentValue(searchEdit, model, this.gridView.GetRowCellValue(e.RowHandle, e.Column)); - e.RepositoryItem = searchEdit; - } - } - - /// - /// 字典搜索框编辑态显示文本事件,避免编辑器未绑定数据源时当前值显示为空。 - /// - private void SpecialReturnEdit_CustomDisplayText(object sender, CustomDisplayTextEventArgs e) - { - RepositoryItemGridLookUpEdit edit = sender as RepositoryItemGridLookUpEdit; - if (edit == null || e.Value == null) return; - - GridColumnModel model = edit.Tag as GridColumnModel; - if (model == null || !IsSpecialReturnBox(model.FieldType)) return; - - string value = e.Value + ""; - if (string.IsNullOrWhiteSpace(value)) return; - - string displayText = string.Empty; - bool sourceReady = EnsureSpecialReturnSourceReady(model); - if (TryGetSpecialReturnDisplayText(model, value, out displayText)) - { - e.DisplayText = displayText; - } - else if (sourceReady) - { - e.DisplayText = string.Empty; - } - } - - /// - /// 字典搜索框弹出事件。普通类型使用缓存数据源,带参数类型按当前行条件重新查询。 - /// - private void SpecialReturnEdit_QueryPopUp(object sender, CancelEventArgs e) - { - try - { - GridLookUpEdit edit = sender as GridLookUpEdit; - if (edit == null) return; - GridColumnModel model = edit.Properties.Tag as GridColumnModel; - if (model == null) return; - - CurrentOperColumnKey = Model.ModuleCode + "_" + model.FieldName; - CurrentOperModel = model; - - DataTable table = IsSpecialReturnParamBox(model.FieldType) ? GetSpecialReturnParamSourceTable(model) : GetSpecialReturnSourceTable(model); - if (IsSpecialReturnParamBox(model.FieldType)) - { - AddSpecialReturnManualData(model, table); - } - edit.Properties.DataSource = table; - AddSpecialReturnViewColumns(edit.Properties, table, model); - SetSpecialReturnPopupWidth(edit.Properties, table, model); - CacheSpecialReturnDisplayValues(model, table); - } - catch (Exception ex) - { - string Message = ErrorMessage.PromptErrorMessage(ex, Model.ModuleCode); - MessageUtil.Show(Message, ex.Message); - } - } - - /// - /// 字典搜索框关闭选择事件,把本次选择结果补入显示字典。 - /// - private void SpecialReturnEdit_CloseUp(object sender, CloseUpEventArgs e) - { - try - { - GridLookUpEdit edit = sender as GridLookUpEdit; - if (edit == null) return; - GridColumnModel model = edit.Properties.Tag as GridColumnModel; - if (model == null || e.Value == null) return; - - DataTable table = edit.Properties.DataSource as DataTable; - if (table == null) return; - - string value = e.Value + ""; - string valueMember = IsSpecialReturnTextBox(model.FieldType) ? model.TextMember : model.ValueMember; - if (!table.Columns.Contains(valueMember) || !table.Columns.Contains(model.TextMember)) return; - - DataRow row = table.Rows.Cast().FirstOrDefault(item => (item[valueMember] + "").Equals(value)); - if (row != null) - { - GetSpecialReturnDisplayDic(model)[value] = row[model.TextMember] + ""; - } - - edit.EditValue = e.Value; - GridColumn column = this.gridView.Columns[model.FieldName]; - if (column != null) - { - this.gridView.SetFocusedRowCellValue(column, e.Value); - } - this.gridView.PostEditor(); - this.gridView.UpdateCurrentRow(); - } - catch - { - } - } - - /// - /// 字典搜索框关闭后清空临时数据源,避免编辑器长期持有大量数据。 - /// - private void SpecialReturnEdit_Closed(object sender, ClosedEventArgs e) - { - GridLookUpEdit edit = sender as GridLookUpEdit; - if (edit != null) - { - edit.Properties.DataSource = null; - } - } - - /// - /// 获取字典搜索框初始化数据源,用于普通弹出和显示字典缓存。 - /// - private DataTable GetSpecialReturnSourceTable(GridColumnModel model) - { - string key = model.FieldName; - if (mSpecialReturnSourceDic.ContainsKey(key)) - { - return mSpecialReturnSourceDic[key]; - } - if (mSpecialReturnSourceTaskDic.ContainsKey(key)) - { - DataTable taskTable = mSpecialReturnSourceTaskDic[key].Result; - mSpecialReturnSourceDic[key] = taskTable; - CacheSpecialReturnDisplayValues(model, taskTable); - return taskTable; - } - - DataTable table = BaseImpl.GetDataTableResult(model.SqlSource); - mSpecialReturnSourceDic[key] = table; - CacheSpecialReturnDisplayValues(model, table); - return table; - } - - /// - /// 获取带参数字典搜索框弹出数据源,逻辑与原带参数搜索框弹出查询保持一致。 - /// - private DataTable GetSpecialReturnParamSourceTable(GridColumnModel model) - { - DataRow[] dataRows = this.GetViewFocusedDataRows(); - DataRow rowItem = dataRows.Count() > 0 ? dataRows[0] : this.gridView.GetFocusedDataRow(); - if (rowItem == null && this.ParentControl == null) - { - return GetSpecialReturnSourceTable(model); - } - - string sqlValue = model.SqlSource; - if (this.ParentControl != null) - sqlValue = this.ParentControl.ReplaceParentControlValue(sqlValue); - - sqlValue = sqlValue.Replace("#", ""); - - if (rowItem != null) - { - sqlValue = ReplaceHelper.ReplaceRowParam(rowItem, sqlValue); - } - - return MainImpl.GetDataTableResult(sqlValue); - } - - /// - /// 带参数字典搜索框保存文本时,把手动新增的数据追加到本次弹出数据源。 - /// - private void AddSpecialReturnManualData(GridColumnModel model, DataTable table) - { - if (model == null || table == null || !model.SearchBoxAddition || !IsSpecialReturnTextBox(model.FieldType)) return; - if (!this.ManuallyAddData.ContainsKey(model.FieldName)) return; - - List dataRows = this.ManuallyAddData[model.FieldName]; - foreach (DataRow row in dataRows) - { - DataRow newrow = table.NewRow(); - newrow.ItemArray = row.ItemArray; - table.Rows.Add(newrow); - } - } - - /// - /// 后台加载字典搜索框数据源并建立显示字典。 - /// - private void StartSpecialReturnSourceTask(GridColumnModel model) - { - if (model == null || string.IsNullOrWhiteSpace(model.SqlSource) || mSpecialReturnSourceTaskDic.ContainsKey(model.FieldName)) return; - - Task sourceTask = new Task(() => - { - return BaseImpl.GetDataTableResult(model.SqlSource); - }); - sourceTask.ContinueWith(task => - { - if (task.Status != TaskStatus.RanToCompletion || task.Result == null) return; - if (this.IsDisposed || !this.IsHandleCreated) return; - this.BeginInvoke(new MethodInvoker(delegate () - { - if (this.IsDisposed) return; - mSpecialReturnSourceDic[model.FieldName] = task.Result; - CacheSpecialReturnDisplayValues(model, task.Result); - gridView.Invalidate(); - })); - }); - mSpecialReturnSourceTaskDic.Add(model.FieldName, sourceTask); - sourceTask.Start(DataTableExtend.SchedulerUtil); - } - - /// - /// 获取字段对应的显示文本字典。 - /// - private Dictionary GetSpecialReturnDisplayDic(GridColumnModel model) - { - if (!mSpecialReturnDisplayDic.ContainsKey(model.FieldName)) - { - mSpecialReturnDisplayDic[model.FieldName] = new Dictionary(); - } - return mSpecialReturnDisplayDic[model.FieldName]; - } - - /// - /// 确认字典搜索框数据源是否已加载;后台任务已完成但回调未执行时,在这里补建显示字典。 - /// - private bool EnsureSpecialReturnSourceReady(GridColumnModel model) - { - if (model == null) return false; - if (mSpecialReturnSourceDic.ContainsKey(model.FieldName)) return true; - - Task sourceTask = null; - if (!mSpecialReturnSourceTaskDic.TryGetValue(model.FieldName, out sourceTask)) return false; - if (!sourceTask.IsCompleted) return false; - - if (sourceTask.Status == TaskStatus.RanToCompletion && sourceTask.Result != null) - { - mSpecialReturnSourceDic[model.FieldName] = sourceTask.Result; - CacheSpecialReturnDisplayValues(model, sourceTask.Result); - } - return true; - } - - /// - /// 把数据源中的值和显示文本缓存到字典中。 - /// - private void CacheSpecialReturnDisplayValues(GridColumnModel model, DataTable table) - { - if (table == null || !table.Columns.Contains(model.ValueMember) || !table.Columns.Contains(model.TextMember)) return; - - Dictionary displayDic = GetSpecialReturnDisplayDic(model); - foreach (DataRow row in table.Rows) - { - string value = IsSpecialReturnTextBox(model.FieldType) ? row[model.TextMember] + "" : row[model.ValueMember] + ""; - if (!string.IsNullOrWhiteSpace(value) && !displayDic.ContainsKey(value)) - { - displayDic.Add(value, row[model.TextMember] + ""); - } - } - } - - /// - /// 根据单元格保存值获取显示文本。 - /// - private string GetSpecialReturnDisplayText(GridColumnModel model, string value) - { - Dictionary displayDic = GetSpecialReturnDisplayDic(model); - string[] values = value.Trim(',').Split(','); - StringBuilder result = new StringBuilder(); - foreach (string item in values) - { - string key = (item + "").Trim(); - if (string.IsNullOrWhiteSpace(key)) continue; - - string displayText = string.Empty; - result.Append(displayDic.TryGetValue(key, out displayText) ? displayText : key); - result.Append(","); - } - return result.ToString().TrimEnd(','); - } - - /// - /// 严格按字典获取显示文本,字典中不存在时返回false。 - /// - private bool TryGetSpecialReturnDisplayText(GridColumnModel model, string value, out string displayText) - { - displayText = string.Empty; - Dictionary displayDic = GetSpecialReturnDisplayDic(model); - string[] values = value.Trim(',').Split(','); - StringBuilder result = new StringBuilder(); - foreach (string item in values) - { - string key = (item + "").Trim(); - if (string.IsNullOrWhiteSpace(key)) continue; - - string itemText = string.Empty; - if (!displayDic.TryGetValue(key, out itemText)) return false; - - result.Append(itemText); - result.Append(","); - } - - displayText = result.ToString().TrimEnd(','); - return !string.IsNullOrWhiteSpace(displayText); - } - - /// - /// 进入编辑态时只给当前有效值绑定一行临时数据,避免未弹出下拉框时显示为空。 - /// - private void BindSpecialReturnCurrentValue(RepositoryItemGridLookUpEdit searchEdit, GridColumnModel model, object cellValue) - { - if (searchEdit == null || model == null || cellValue == null) return; - - string value = cellValue + ""; - if (string.IsNullOrWhiteSpace(value)) return; - - string displayText = string.Empty; - bool sourceReady = EnsureSpecialReturnSourceReady(model); - if (!TryGetSpecialReturnDisplayText(model, value, out displayText)) - { - if (sourceReady) - { - searchEdit.DataSource = null; - return; - } - displayText = value; - } - - DataTable table = new DataTable(); - if (!table.Columns.Contains(model.ValueMember)) table.Columns.Add(model.ValueMember); - if (!table.Columns.Contains(model.TextMember)) table.Columns.Add(model.TextMember); - - DataRow row = table.NewRow(); - row[model.ValueMember] = IsSpecialReturnTextBox(model.FieldType) ? displayText : value; - row[model.TextMember] = displayText; - table.Rows.Add(row); - - searchEdit.DataSource = table; - } - - /// - /// 根据弹出数据源补充搜索框显示列。 - /// - private void AddSpecialReturnViewColumns(RepositoryItemGridLookUpEdit searchEdit, DataTable table, GridColumnModel model) - { - if (searchEdit == null || table == null) return; - - foreach (DataColumn dcol in table.Columns) - { - if (string.IsNullOrEmpty(dcol.ColumnName) || dcol.ColumnName.Substring(0, 1) == "_") continue; - if (searchEdit.View.Columns.FirstOrDefault(item => item.FieldName == dcol.ColumnName) != null) continue; - - GridColumn otherColumn = new GridColumn(); - otherColumn.Name = otherColumn.FieldName = otherColumn.Caption = dcol.ColumnName; - otherColumn.Visible = true; - searchEdit.View.Columns.Add(otherColumn); - } - } - - /// - /// 设置字典搜索框弹出宽度,规则与普通自动搜索框保持一致。 - /// - private void SetSpecialReturnPopupWidth(RepositoryItemGridLookUpEdit searchEdit, DataTable table, GridColumnModel model) - { - if (searchEdit == null || table == null || model == null) return; - - int popWidth = 0; - string[] columnsWidth = !string.IsNullOrWhiteSpace(model.LookUpFieldsWidth) ? model.LookUpFieldsWidth.Trim().TrimEnd(',').Split(',') : null; - if (columnsWidth != null && columnsWidth.Length > 0) - { - int columnWidthIndex = 0; - foreach (DataColumn dcol in table.Columns) - { - if (!string.IsNullOrEmpty(dcol.ColumnName) && dcol.ColumnName.Substring(0, 1) != "_") - { - GridColumn gCol = searchEdit.View.Columns.FirstOrDefault(x => x.FieldName == dcol.ColumnName); - if (gCol != null) - { - int width = 0; - if (columnWidthIndex < columnsWidth.Length) - { - string widthStr = columnsWidth[columnWidthIndex]; - width = int.TryParse(widthStr, out width) ? width : GraphicsText.GetTextWidth(gCol.Caption); - } - else - { - width = GraphicsText.GetTextWidth(gCol.Caption); - int maxWidth = CalcMaxColumnWidth(table, gCol.Name); - width = width > maxWidth ? width : maxWidth; - } - gCol.Width = width; - popWidth += gCol.Width; - } - } - columnWidthIndex++; - } - } - else - { - foreach (DataColumn dcol in table.Columns) - { - if (string.IsNullOrEmpty(dcol.ColumnName) || dcol.ColumnName.Substring(0, 1) == "_") continue; - GridColumn gCol = searchEdit.View.Columns.FirstOrDefault(x => x.FieldName == dcol.ColumnName); - if (gCol != null) - { - int columnWidth = GraphicsText.GetTextWidth(gCol.Caption); - gCol.Width = CalcMaxColumnWidth(table, gCol.Name); - gCol.Width = columnWidth > gCol.Width ? columnWidth : gCol.Width; - popWidth += gCol.Width; - } - } - } - - if (model.LookUpWidth > 0) - { - popWidth = model.LookUpWidth + 5; - } - searchEdit.PopupFormSize = new Size(popWidth, searchEdit.PopupFormSize.Height); - } - - - /// /// 说明:设置列格式化 /// 创建人:龚宇超 @@ -2113,7 +1524,6 @@ namespace Lskj.Control { if (this.DisableFieldSources && item.isVislble) continue; if (ControlType.IsNotLoadData(item.FieldType)) continue; - if (IsSpecialReturnBox(item.FieldType)) continue; if (UpdateColumnRefresh && !string.IsNullOrWhiteSpace(UpdateColumns) && !UpdateColumns.Split(',').Contains(item.FieldName)) continue; if (!string.IsNullOrWhiteSpace(item.SqlSource)) { @@ -5230,11 +4640,6 @@ namespace Lskj.Control if (SystemInfo.Instance.DisableCellAutoFiltering && edit.Properties != null) edit.Properties.AutoComplete = false;//设置是否启用自动完成功能 RepositoryItemGridLookUpEdit searchEdit = edit.Properties; DataTable dt = (searchEdit.DataSource as DataTable); - GridColumnModel model = searchEdit.Tag as GridColumnModel; - if (dt == null && model != null && IsSpecialReturnBox(model.FieldType)) - { - return; - } BeginInvoke(new MethodInvoker(delegate () { FilterLookup(sender, e.NewValue + ""); @@ -9933,16 +9338,7 @@ namespace Lskj.Control { this.mLoading = false; mControlSourceDic.Clear(); - mSpecialReturnSourceDic.Clear(); - mSpecialReturnSourceTaskDic.Clear(); - mSpecialReturnDisplayDic.Clear(); - foreach (GridColumnModel model in mControlList) - { - if (IsSpecialReturnBox(model.FieldType)) - { - StartSpecialReturnSourceTask(model); - } - } + ResetExtendedReturnState(); SetColumnsSource(UpdateColumnRefresh); } diff --git a/插件库/Lskj.Control/Lskj.Control.csproj b/插件库/Lskj.Control/Lskj.Control.csproj index 046ed56..093f543 100644 --- a/插件库/Lskj.Control/Lskj.Control.csproj +++ b/插件库/Lskj.Control/Lskj.Control.csproj @@ -708,6 +708,12 @@ UserControl + + UserControl + + + UserControl + AutoGridPopup.cs @@ -1161,6 +1167,10 @@ UserControl + + GridControlEx.cs + UserControl + GridControlEx.cs @@ -1263,6 +1273,7 @@ + diff --git a/插件库/Lskj.Control/Model/ControlModel.cs b/插件库/Lskj.Control/Model/ControlModel.cs index 89ebf3d..4701c92 100644 --- a/插件库/Lskj.Control/Model/ControlModel.cs +++ b/插件库/Lskj.Control/Model/ControlModel.cs @@ -139,6 +139,10 @@ namespace Lskj.Control.Model /// The text member. public string TextMember { get; set; } /// + /// 扩展返回字段映射,格式为“业务控件字段=返回数据字段”。 + /// + public string ResultFields { get; set; } + /// /// Gets or sets the source SQL. /// /// The source SQL. diff --git a/插件库/Lskj.Control/Model/ControlType.cs b/插件库/Lskj.Control/Model/ControlType.cs index 427c274..444377c 100644 --- a/插件库/Lskj.Control/Model/ControlType.cs +++ b/插件库/Lskj.Control/Model/ControlType.cs @@ -493,21 +493,13 @@ namespace Lskj.Control.Model /// - /// 快速搜索框返回id(字典模式) + /// 模块选择返回ID-扩展 /// - public const int DictionarySearchBoxToId = 181; + public const int LabModuleSelectReturnIdExtended = 180; /// - /// 快速搜索框返回text(字典模式) + /// 搜索返回ID-扩展 /// - public const int DictionarySearchBoxToText = 182; - /// - /// 快速搜索框返回id 带参数(字典模式) - /// - public const int DictionarySearchBoxToIdParam = 183; - /// - /// 快速搜索框返回text 带参数(字典模式) - /// - public const int DictionarySearchBoxToTextParam = 184; + public const int LabSearchReturnIdExtended = 181; /// /// 说明:是否为Value类型 @@ -628,6 +620,8 @@ namespace Lskj.Control.Model fieldType == LabPhone || fieldType == LabSelectReturnIdNew || fieldType == LabSelectReturnTextNew || + fieldType == LabModuleSelectReturnIdExtended || + fieldType == LabSearchReturnIdExtended || fieldType == LabApiBtuton|| fieldType == LabModuleAddRowsID || fieldType == LabModuleAddRowsText; diff --git a/插件库/Lskj.Control/Model/ExtendedReturnSupport.cs b/插件库/Lskj.Control/Model/ExtendedReturnSupport.cs new file mode 100644 index 0000000..b6aca77 --- /dev/null +++ b/插件库/Lskj.Control/Model/ExtendedReturnSupport.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; +using System.Text.RegularExpressions; + +namespace Lskj.Control.Model +{ + internal sealed class ExtendedReturnFieldMapping + { + public ExtendedReturnFieldMapping(string targetField, string sourceField) + { + TargetField = targetField; + SourceField = sourceField; + } + + public string TargetField { get; private set; } + + public string SourceField { get; private set; } + } + + internal static class ExtendedReturnSupport + { + internal const string SearchAlias = "LS_ExtendedLookup"; + internal const string SearchParameterName = "@lookupKeyword"; + + public static IList ParseResultFields(string resultFields) + { + List mappings = new List(); + if (string.IsNullOrWhiteSpace(resultFields)) return mappings; + + HashSet targetFields = new HashSet(StringComparer.OrdinalIgnoreCase); + string[] items = resultFields.Split(new char[] { ';', ';' }); + foreach (string rawItem in items) + { + string item = (rawItem ?? string.Empty).Trim(); + if (item.Length == 0) continue; + + int separatorIndex = item.IndexOf('='); + if (separatorIndex <= 0 || separatorIndex == item.Length - 1 || item.IndexOf('=', separatorIndex + 1) >= 0) + { + throw new FormatException(string.Format("返回字段配置“{0}”格式错误,应为“业务表字段=返回数据字段”。", item)); + } + + string targetField = item.Substring(0, separatorIndex).Trim(); + string sourceField = item.Substring(separatorIndex + 1).Trim(); + if (targetField.Length == 0 || sourceField.Length == 0) + { + throw new FormatException(string.Format("返回字段配置“{0}”存在空字段名。", item)); + } + if (!targetFields.Add(targetField)) + { + throw new FormatException(string.Format("返回字段配置中的业务表字段“{0}”重复。", targetField)); + } + + mappings.Add(new ExtendedReturnFieldMapping(targetField, sourceField)); + } + return mappings; + } + + public static string FindTargetField(IEnumerable mappings, string sourceField) + { + if (mappings == null || string.IsNullOrWhiteSpace(sourceField)) return string.Empty; + + foreach (ExtendedReturnFieldMapping mapping in mappings) + { + if (string.Equals(mapping.SourceField, sourceField, StringComparison.OrdinalIgnoreCase)) + { + return mapping.TargetField; + } + } + return string.Empty; + } + + public static string BuildStructureSql(string sourceSql) + { + return string.Format("select top 0 * from ({0}) as [{1}]", NormalizeSourceSql(sourceSql), SearchAlias); + } + + public static string BuildSearchSql(string sourceSql, DataColumnCollection columns, int maxRows) + { + if (columns == null || columns.Count == 0) + { + throw new InvalidOperationException("搜索 SQL 没有返回任何可查询列。"); + } + if (maxRows <= 0) + { + throw new ArgumentOutOfRangeException("maxRows", "最大返回行数必须大于零。"); + } + + string alias = QuoteIdentifier(SearchAlias); + StringBuilder builder = new StringBuilder(); + builder.AppendFormat("select top ({0}) * from ({1}) as {2} where ", maxRows, NormalizeSourceSql(sourceSql), alias); + + for (int i = 0; i < columns.Count; i++) + { + if (i > 0) builder.Append(" or "); + builder.Append("convert(nvarchar(4000), "); + builder.Append(alias); + builder.Append('.'); + builder.Append(QuoteIdentifier(columns[i].ColumnName)); + builder.Append(") like "); + builder.Append(SearchParameterName); + builder.Append(" escape N'\\'"); + } + return builder.ToString(); + } + + public static string BuildLikeParameterValue(string keyword) + { + string value = keyword ?? string.Empty; + value = value.Replace("\\", "\\\\"); + value = value.Replace("%", "\\%"); + value = value.Replace("_", "\\_"); + value = value.Replace("[", "\\["); + return "%" + value + "%"; + } + + private static string NormalizeSourceSql(string sourceSql) + { + if (string.IsNullOrWhiteSpace(sourceSql)) + { + throw new InvalidOperationException("搜索数据源 SQL 不能为空。"); + } + + string normalized = sourceSql.Trim(); + if (normalized.EndsWith(";", StringComparison.Ordinal)) + { + normalized = normalized.Substring(0, normalized.Length - 1).TrimEnd(); + } + if (normalized.IndexOf(';') >= 0) + { + throw new InvalidOperationException("扩展搜索只支持一条可组合的 SELECT 语句。"); + } + if (!Regex.IsMatch(normalized, @"^select\b", RegexOptions.IgnoreCase)) + { + throw new InvalidOperationException("扩展搜索数据源必须是可作为派生表使用的 SELECT 语句。"); + } + return normalized; + } + + private static string QuoteIdentifier(string identifier) + { + if (string.IsNullOrWhiteSpace(identifier)) + { + throw new InvalidOperationException("搜索 SQL 返回了空列名。"); + } + return "[" + identifier.Replace("]", "]]") + "]"; + } + } +} diff --git a/插件库/Lskj.Control/Model/GridColumnModel.cs b/插件库/Lskj.Control/Model/GridColumnModel.cs index 19098ff..35af329 100644 --- a/插件库/Lskj.Control/Model/GridColumnModel.cs +++ b/插件库/Lskj.Control/Model/GridColumnModel.cs @@ -57,6 +57,7 @@ namespace Lskj.Control.Model private bool _frozenFlag; private int _lookUpWidth; private string _lookUpFieldsWidth; + private string _resultFields; #endregion @@ -326,6 +327,10 @@ namespace Lskj.Control.Model /// public string LookUpFieldsWidth { get { return this._lookUpFieldsWidth; } } /// + /// 扩展选择结果映射,格式:业务表字段=返回数据字段;业务表字段1=返回数据字段1。 + /// + public string ResultFields { get { return this._resultFields; } } + /// /// 动态列 /// public bool isDynamic; @@ -459,6 +464,7 @@ namespace Lskj.Control.Model } this.addModuleResult = item.Table.Columns.Contains("addModuleResult") ? item["addModuleResult"] + "" : string.Empty; + this._resultFields = item.Table.Columns.Contains("resultfields") ? item["resultfields"] + "" : string.Empty; this.addModuleld = item.Table.Columns.Contains("addModuleId") ? item["addModuleId"] + "" : string.Empty; this.unionCompare = item.Table.Columns.Contains("UnionCompare") ? item["UnionCompare"] + "" : string.Empty; this.isExportColumns = item.Table.Columns.Contains("isExportColumns") && !string.IsNullOrWhiteSpace(item["isExportColumns"] + "") ? "1".Equals(item["isExportColumns"] + "") : false; diff --git a/插件库/Lskj.Control/Model/MyControl.cs b/插件库/Lskj.Control/Model/MyControl.cs index 2f1d084..d05bd53 100644 --- a/插件库/Lskj.Control/Model/MyControl.cs +++ b/插件库/Lskj.Control/Model/MyControl.cs @@ -136,6 +136,11 @@ namespace Lskj.Control.Model /// 下拉框、搜索框控件对象 /// public List mControlList = new List(); + private readonly List mExtendedReturnSearchControls = + new List(); + private readonly List> mExtendedReturnPendingChanges = + new List>(); + private int mExtendedReturnBatchDepth; /// /// 数据源任务对象 /// @@ -2253,6 +2258,10 @@ namespace Lskj.Control.Model LabelMultiAutoTextEdit4 moduleReturnsIdNew = ctr as LabelMultiAutoTextEdit4; value = ControlType.LabSelectReturnIdNew == model.FieldType ? moduleReturnsIdNew.EditValue : moduleReturnsIdNew.EditText; break; + case ControlType.LabSearchReturnIdExtended: + LabelExtendedReturnSearchEdit extendedReturnSearch = ctr as LabelExtendedReturnSearchEdit; + value = extendedReturnSearch == null ? string.Empty : extendedReturnSearch.EditValue; + break; case ControlType.DynamicallyGeneratedSql: LabelMultiAutoTextEdit3 moduleSqlGenerated = ctr as LabelMultiAutoTextEdit3; value = moduleSqlGenerated.EditText; @@ -2679,6 +2688,7 @@ namespace Lskj.Control.Model } } + RefreshExtendedReturnDisplayControls(); foreach (ControlModel model in this.ControlModels) { BaseUserControl control = FindControl(model); @@ -3710,6 +3720,14 @@ namespace Lskj.Control.Model } pictureEdit.TextEdit.Image = image; } + else if (baseControl is LabelExtendedReturnSearchEdit) + { + LabelExtendedReturnSearchEdit extendedReturnSearch = baseControl as LabelExtendedReturnSearchEdit; + extendedReturnSearch.EditValue = controlValue == null || controlValue == DBNull.Value + ? string.Empty + : controlValue + ""; + baseControl.Model.Text = extendedReturnSearch.EditValue; + } else if (baseControl.Model.FieldType == 7 && !"****".Equals(controlValue)) { string dfmt = baseControl.Model.DataFormat; @@ -3750,6 +3768,113 @@ namespace Lskj.Control.Model //baseControl.Parent.Focus(); } } + RefreshExtendedReturnDisplayControls(); + } + + /// + /// 用户从扩展搜索弹窗选择或清空时,批量写入所有映射字段。 + /// 中间值改变事件会延迟到全部字段提交成功后执行,且不改变原始值基线。 + /// + public void ApplyExtendedReturnValues(IList> values) + { + if (values == null || values.Count == 0) return; + if (mExtendedReturnBatchDepth > 0) + { + throw new InvalidOperationException("扩展返回字段正在批量赋值,不能重复执行。"); + } + + List> normalizedValues = + new List>(); + Dictionary originalValues = + new Dictionary(StringComparer.OrdinalIgnoreCase); + Dictionary originalModelTexts = + new Dictionary(); + HashSet targetFields = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (KeyValuePair value in values) + { + string targetField = (value.Key ?? string.Empty).Trim(); + if (targetField.Length == 0) + { + throw new InvalidOperationException("扩展返回字段名称不能为空。"); + } + if (!targetFields.Add(targetField)) + { + throw new InvalidOperationException( + string.Format("扩展返回字段“{0}”重复。", targetField)); + } + + BaseUserControl targetControl = FindControl(targetField); + if (targetControl == null) + { + throw new InvalidOperationException( + string.Format("MyControl 中不存在业务控件“{0}”。", targetField)); + } + + object normalizedValue = value.Value == null || value.Value == DBNull.Value + ? string.Empty + : value.Value; + normalizedValues.Add(new KeyValuePair(targetField, normalizedValue)); + originalValues[targetField] = GetControlValue(targetField); + originalModelTexts[targetControl] = targetControl.Model == null + ? null + : targetControl.Model.Text; + } + + bool success = false; + mExtendedReturnPendingChanges.Clear(); + mExtendedReturnBatchDepth++; + try + { + foreach (KeyValuePair value in normalizedValues) + { + SetControlValue(value.Key, value.Value); + } + RestoreExtendedReturnModelTexts(originalModelTexts); + RefreshExtendedReturnDisplayControls(true); + success = true; + } + catch + { + foreach (KeyValuePair originalValue in originalValues) + { + SetControlValue(originalValue.Key, originalValue.Value); + } + RestoreExtendedReturnModelTexts(originalModelTexts); + RefreshExtendedReturnDisplayControls(true); + throw; + } + finally + { + mExtendedReturnBatchDepth--; + if (!success) mExtendedReturnPendingChanges.Clear(); + } + + FlushExtendedReturnPendingChanges(); + } + + private static void RestoreExtendedReturnModelTexts( + IDictionary originalModelTexts) + { + foreach (KeyValuePair item in originalModelTexts) + { + if (item.Key != null && item.Key.Model != null) + { + item.Key.Model.Text = item.Value; + } + } + } + + private void RefreshExtendedReturnDisplayControls(bool force = false) + { + if (mExtendedReturnBatchDepth > 0 && !force) return; + foreach (LabelExtendedReturnSearchEdit control in mExtendedReturnSearchControls) + { + if (control != null && !control.IsDisposed && control.Model != null) + { + control.RefreshDisplayText(); + } + } } /// @@ -4152,6 +4277,7 @@ namespace Lskj.Control.Model model.Sign = item.Table.Columns.Contains("TM_tagID") && !string.IsNullOrEmpty(item["TM_tagID"] + "") ? Convert.ToInt32(item["TM_tagID"] + "") : 0; model.TextMember = item["lookupResult"] + ""; model.ValueMember = item["lookupKeyField"] + ""; + model.ResultFields = item.Table.Columns.Contains("resultfields") ? item["resultfields"] + "" : string.Empty; if (!dataCaches.GetValue(item, "LookupSql", out string lookupSql)) { @@ -4239,28 +4365,6 @@ namespace Lskj.Control.Model model.FieldType = 161; model.IsRadio = true; } - //快速搜索框在控件中转成普通搜索框 - if (model.FieldType == 181 || model.FieldType == 182 || model.FieldType == 183 || model.FieldType == 184) - { - switch (model.FieldType) - { - case ControlType.DictionarySearchBoxToId: - model.FieldType = 5; - break; - case ControlType.DictionarySearchBoxToText: - model.FieldType = 6; - break; - case ControlType.DictionarySearchBoxToIdParam: - model.FieldType = 15; - break; - case ControlType.DictionarySearchBoxToTextParam: - model.FieldType = 16; - break; - - } - - } - return model; } /// @@ -4830,6 +4934,18 @@ namespace Lskj.Control.Model if (!isLoadBorder) moduleReturnsIdNew.TextEdit.BorderStyle = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder; baseControl = moduleReturnsIdNew; break; + case ControlType.LabSearchReturnIdExtended: + LabelExtendedReturnSearchEdit extendedReturnSearch = new LabelExtendedReturnSearchEdit(); + extendedReturnSearch.ControlObj = this; + mExtendedReturnSearchControls.Add(extendedReturnSearch); + extendedReturnSearch.TextEdit.KeyDown += new KeyEventHandler(OnTextEditKeyDown); + extendedReturnSearch.TextEdit.TextChanged += new EventHandler(OnTextEditTextChanged); + if (!isLoadBorder) + { + extendedReturnSearch.TextEdit.BorderStyle = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder; + } + baseControl = extendedReturnSearch; + break; case ControlType.DynamicallyGeneratedSql: LabelMultiAutoTextEdit3 moduleSqlGenerated = new LabelMultiAutoTextEdit3(); // moduleSqlGenerated.SetDataSource(); @@ -5884,6 +6000,49 @@ namespace Lskj.Control.Model /// The source of the event. /// The instance containing the event data. protected void OnTextEditTextChanged(object sender, EventArgs e) + { + if (mExtendedReturnBatchDepth > 0) + { + QueueExtendedReturnPendingChange(sender, e); + return; + } + ProcessTextEditTextChanged(sender, e); + } + + private void QueueExtendedReturnPendingChange(object sender, EventArgs e) + { + System.Windows.Forms.Control changedControl = sender as System.Windows.Forms.Control; + BaseUserControl baseControl = changedControl == null + ? null + : changedControl.ToBaseUserControl(); + foreach (KeyValuePair pendingChange in mExtendedReturnPendingChanges) + { + System.Windows.Forms.Control pendingControl = + pendingChange.Key as System.Windows.Forms.Control; + BaseUserControl pendingBaseControl = pendingControl == null + ? null + : pendingControl.ToBaseUserControl(); + if (baseControl != null && object.ReferenceEquals(baseControl, pendingBaseControl)) + { + return; + } + } + mExtendedReturnPendingChanges.Add( + new KeyValuePair(sender, e ?? EventArgs.Empty)); + } + + private void FlushExtendedReturnPendingChanges() + { + KeyValuePair[] pendingChanges = + mExtendedReturnPendingChanges.ToArray(); + mExtendedReturnPendingChanges.Clear(); + foreach (KeyValuePair pendingChange in pendingChanges) + { + ProcessTextEditTextChanged(pendingChange.Key, pendingChange.Value); + } + } + + private void ProcessTextEditTextChanged(object sender, EventArgs e) { BaseUserControl baseControl = (sender as System.Windows.Forms.Control).ToBaseUserControl(); this.SetUnionAncCalcControl(baseControl); diff --git a/插件库/Lskj.Control/MultiModelLookUp/FrmModelLookUp2.cs b/插件库/Lskj.Control/MultiModelLookUp/FrmModelLookUp2.cs index 1c7019b..28842e6 100644 --- a/插件库/Lskj.Control/MultiModelLookUp/FrmModelLookUp2.cs +++ b/插件库/Lskj.Control/MultiModelLookUp/FrmModelLookUp2.cs @@ -156,6 +156,14 @@ namespace Lskj.Control.MultiModelLookUp /// public Dictionary ModuleResultDic = new Dictionary(); /// + /// 单选模式最后选中的原始数据行,供需要按动态列名返回多字段的调用方使用。 + /// + public DataRow SelectedResultRow { get; private set; } + public DataRow GetSelectedResultRow() + { + return SelectedResultRow ?? this.gcMain.GridView.GetFocusedDataRow(); + } + /// /// 返回多行的集合(模块返回添加行 167 168) /// public List EditValueList = new List(); @@ -855,10 +863,12 @@ namespace Lskj.Control.MultiModelLookUp { EditValue = string.Empty; EditText = string.Empty; + SelectedResultRow = null; ModuleResultDic.Clear(); DataRow dr = this.gcMain.GridView.GetFocusedDataRow(); if (dr != null) { + SelectedResultRow = dr; this.EditValue = dr[ValueMember] + ""; this.EditText = dr[TextField] + "";