/// /// Copyright (c) 2023 Sensus Slovensko a.s. /// using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms; using log4net; using Results.Entities; using TBF.Resources; namespace TBF.Rig.DataEntry.Uni { public partial class CycleBgEnForm : Form, GenericDevices.IHasCompleted { private static readonly ILog log = LogManager.GetLogger(typeof(CycleBgEnForm)); /// Arguments public readonly IList WaterMeters; readonly int lineSize; readonly Sz sz; readonly bool isLrOrder; /// true = controls obtain focus after ENTER key in left-right order, false = top-down order readonly IList commonItems; readonly IList colItems; readonly bool isEnd; readonly string formCloseKeys; /// Derived from arguments in the constructor readonly WaterMeter firstValidWM; readonly int wmsCount; readonly int linesCount; readonly int commonItemsCount; /// Size and layout related values readonly Font font; readonly int meterHeight; readonly int meterWidth; readonly int margin; readonly int spacing; readonly int buttonsWidth; readonly int hdrHeight; readonly int labelWid; readonly int checkBoxWid; /// UI elements readonly Label[] commonLabels; readonly ComboBox[] commonComboBoxes; readonly Label[] labels; /// Labels for water meter numbers readonly ComboBox[,] comboBoxes; /// Combo boxes for values in columns readonly CheckBox[] checkBoxes; /// Check boxes for water meter enable/disable readonly MultiPurposeBtnFunction multiPurposeButtonFn; bool multiPurposeButtonFlag; readonly LocalSettings ls; bool isHandlersEnabled; /// Initially false, set to true after ComboBoxes are filled with data /// Set to 'true' when the form closes public bool Completed { get { return completed; } } bool completed; /// /// Parameterless constructor for common functionality /// public CycleBgEnForm() { InitializeComponent(); ControlBox = false; completed = false; StartForceCloseHandler(); } /// /// Constructor /// /// Water meters /// Number of water meters in one (test bencch) line /// Form title /// Font size /// true = controls obtain focus in left-right order, forls = top-down order /// String containing keys to close this form /// Common items displayed in the upper part of the form /// Water meter items displayed in columns (in the matrix in the main part of the form) /// true = This form is displayed at the end of cycle public CycleBgEnForm(IList waterMeters, int lineSize, string title, Sz sz, bool isLrOrder, string formCloseKeys, IList commonItems, IList colItems, bool isEnd = false) : this() { /// Arguments this.WaterMeters = waterMeters; this.lineSize = lineSize; this.Text = !string.IsNullOrEmpty(title) ? title : string.Empty; this.sz = sz; this.isLrOrder = isLrOrder; this.formCloseKeys = !string.IsNullOrEmpty(formCloseKeys) ? formCloseKeys : string.Empty; this.commonItems = commonItems; this.colItems = colItems; this.isEnd = isEnd; /// Preserve column items for use in SummaryResults if (!isEnd) { /// cycle beginning DEItem.ClearSummaryColumns(); foreach (var it in colItems) if (it.Content != Ct.SerialNr) DEItem.AddSummaryColumn(it); } else { /// cycle end foreach (var it in colItems) if (it.Content != Ct.SerialNr) DEItem.AddSummaryColumn(it); } ls = Program.LocalSettings; /// Readonly variables derived from arguments commonItemsCount = (commonItems == null) ? 0 : commonItems.Count; wmsCount = (waterMeters == null) ? 0 : waterMeters.Count; linesCount = (wmsCount + Math.Max(1, lineSize) - 1) / Math.Max(1, lineSize); firstValidWM = (waterMeters != null) ? waterMeters.FirstOrDefault(x => (x != null && !x.Disabled)) : null; /// commonLabels = new Label[commonItemsCount]; commonComboBoxes = new ComboBox[commonItemsCount]; labels = new Label[wmsCount]; comboBoxes = new ComboBox[colItems.Count, wmsCount]; checkBoxes = new CheckBox[wmsCount]; /// /// Determine the multi purpose button function /// multiPurposeButton.Visible = false; multiPurposeButtonFn = MultiPurposeBtnFunction.None; int buttonsWidth = 350; for (int k = 0; k < colItems.Count; k++) { if ((colItems[k].Content == Ct.SerialNr || colItems[k].Content == Ct.SerialNrAux) && colItems[k].Action != Ac.LoadReadOnly) { multiPurposeButton.Visible = true; multiPurposeButton.Text = "Auto s/n"; multiPurposeButtonFn = MultiPurposeBtnFunction.AutoSN; buttonsWidth += 147; break; } if (colItems[k].Content == Ct.PrintLabel) { multiPurposeButton.Visible = true; multiPurposeButton.Text = string.Format("{0} ({1}/{2})", Strings.Print, Strings.yes, Strings.no); multiPurposeButtonFn = MultiPurposeBtnFunction.PrintLabelsOnOff; multiPurposeButtonFlag = false; buttonsWidth += 147; break; } } /// Layout related readonly variables derived from argument 'sz' switch (sz) { case Sz.S: font = new Font("Verdana", 12, FontStyle.Regular); meterHeight = 26; /// Height of a ComboBox control margin = 20; spacing = 8; this.buttonsWidth = buttonsWidth; hdrHeight = 120; labelWid = 31; checkBoxWid = 23; break; default: case Sz.M: font = new Font("Verdana", 14, FontStyle.Regular); meterHeight = 31; /// Height of a ComboBox control margin = 25; spacing = 10; this.buttonsWidth = buttonsWidth; hdrHeight = 140; labelWid = 34; checkBoxWid = 23; break; case Sz.L: font = new Font("Verdana", 18, FontStyle.Regular); meterHeight = 37; /// Height of a ComboBox control margin = 30; spacing = 12; this.buttonsWidth = buttonsWidth; hdrHeight = 160; labelWid = 40; checkBoxWid = 23; break; } meterWidth = labelWid + checkBoxWid + spacing; foreach (var ci in colItems) { meterWidth += ci.Width + spacing; } int tabIndex = 1; /// /// Group box with common items /// int groupBoxWidth = 0; int groupBoxHeight = 0; if (commonItemsCount > 0) { var groupBox = new GroupBox(); int labelsWidth = 0; for (int ix = 0; ix < commonItemsCount; ix++) { int oneLabelWidth = Convert.ToInt32(Graphics.FromImage(new Bitmap(1, 1)).MeasureString(commonItems[ix].Caption, font).Width); var oneLabel = new Label { Text = commonItems[ix].Caption, Font = font, Location = new Point(margin, (ix + 2) * spacing + ix * meterHeight), Size = new Size(oneLabelWidth + spacing, meterHeight), TextAlign = ContentAlignment.MiddleLeft, TabIndex = tabIndex++, Parent = groupBox, }; groupBox.Controls.Add(oneLabel); labelsWidth = Math.Max(labelsWidth, oneLabelWidth); } int combosWidth = 0; for (int ix = 0; ix < commonItemsCount; ix++) { var cb = new ComboBox { Name = string.Format("-1~{0}", ix + 1), Location = new Point(margin + spacing + labelsWidth, (ix + 2) * spacing + ix * meterHeight), Size = new Size(commonItems[ix].Width, meterHeight), Enabled = (commonItems[ix].Action != Ac.LoadReadOnly), Font = font, TabIndex = tabIndex++, Parent = groupBox, }; cb.SelectedIndexChanged += new System.EventHandler(comboBox_SelectedIndexChanged); cb.TextChanged += new System.EventHandler(comboBox_TextChanged); cb.KeyPress += new System.Windows.Forms.KeyPressEventHandler(comboBox_KeyPress); switch (ix) { case 0: AddHistoryToCombo(cb, isEnd ? ls.LastEnComm1 : ls.LastBgComm1); break; case 1: AddHistoryToCombo(cb, isEnd ? ls.LastEnComm2 : ls.LastBgComm2); break; case 2: AddHistoryToCombo(cb, isEnd ? ls.LastEnComm3 : ls.LastBgComm3); break; } commonComboBoxes[ix] = cb; groupBox.Controls.Add(cb); combosWidth = Math.Max(combosWidth, commonItems[ix].Width); } /// groupBox.Location = new Point(margin, margin - spacing); groupBoxWidth = labelsWidth + combosWidth + 2 * margin + spacing; groupBoxHeight = commonItemsCount * meterHeight + (commonItemsCount + 2) * spacing; groupBox.Size = new Size(groupBoxWidth, groupBoxHeight); groupBox.Font = font; groupBox.TabIndex = tabIndex++; groupBox.Parent = this; this.Controls.Add(groupBox); } /// /// Table /// int commonCheckBoxLeft = 0; int commonCheckBoxTop = 0; int columnsTop = Math.Max(hdrHeight, groupBoxHeight + 2 * margin + spacing); for (int j = 0; j < linesCount; j++) { /// Captions int capX = margin + j * (meterWidth + margin / 2) + labelWid + spacing; int capY = columnsTop - meterHeight; for (int k = 0; k < colItems.Count; k++) { int labelWidth = Convert.ToInt32(Graphics.FromImage(new Bitmap(1, 1)).MeasureString(colItems[k].Caption, font).Width); var label = new Label { Text = colItems[k].Caption, Location = new Point(capX, capY), Size = new Size(labelWidth + spacing, meterHeight), Font = font, TabIndex = tabIndex++, Parent = this, }; capX += colItems[k].Width + spacing; } /// Water meters for (int i = 0; i < lineSize; i++) { int wmPos0 = i + lineSize * j; if (wmPos0 > wmsCount) continue; int left = margin + j * (meterWidth + margin / 2); int top = columnsTop + i * (meterHeight + spacing); var label = new Label { Text = (wmPos0 + 1).ToString(), Location = new Point(left, top), Size = new Size(labelWid, meterHeight), Font = font, TextAlign = ContentAlignment.MiddleLeft, TabIndex = tabIndex++, Parent = this, }; left += labelWid + spacing / 2; labels[wmPos0] = label; this.Controls.Add(label); /// Columns for (int k = 0; k < colItems.Count; k++) { DEItem ci = colItems[k]; var comboBox = new ComboBox { Name = string.Format("{0}~{1}", k, wmPos0), Location = new Point(left, top), Size = new Size(ci.Width, meterHeight), Enabled = (ci.Action != Ac.LoadReadOnly && !waterMeters[wmPos0].Disabled), Font = font, TabIndex = tabIndex++, Parent = this, }; comboBox.SelectedIndexChanged += new System.EventHandler(this.comboBox_SelectedIndexChanged); comboBox.TextChanged += new System.EventHandler(this.comboBox_TextChanged); comboBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.comboBox_KeyPress); if (ci.Content == Ct.PrintLabel) { comboBox.Items.Add(Strings.yes); comboBox.Items.Add(Strings.no); } else { var lastVals = GetHistoryFromLS(isEnd, k); comboBox.Items.Add(lastVals.Length > wmPos0 ? lastVals[wmPos0] : string.Empty); /// History in drop-down menu } left += ci.Width + spacing; comboBoxes[k, wmPos0] = comboBox; this.Controls.Add(comboBox); } var checkBox = new CheckBox { Location = new Point(left, top + 6), Size = new Size(checkBoxWid, 23), TabIndex = tabIndex++, Parent = this, }; checkBoxes[wmPos0] = checkBox; this.Controls.Add(checkBox); if (commonCheckBoxLeft == 0) { commonCheckBoxLeft = left; commonCheckBoxTop = columnsTop - meterHeight; } } okButton.TabIndex = tabIndex++; clearButton.TabIndex = tabIndex++; var commonCheckBox = new CheckBox { Location = new Point(commonCheckBoxLeft, commonCheckBoxTop + 6), Size = new Size(checkBoxWid, 23), TabIndex = tabIndex++, Parent = this, }; commonCheckBox.CheckedChanged += new System.EventHandler(commonCheckBox_CheckedChanged); this.Controls.Add(commonCheckBox); } /// /// Adjust window size /// Rectangle screenRectangle = this.RectangleToScreen(this.ClientRectangle); int titleBarHeight = screenRectangle.Top - this.Top; Size = new Size(Math.Max(meterWidth * linesCount + margin * (linesCount + 1), margin + groupBoxWidth + buttonsWidth), titleBarHeight + columnsTop + lineSize * (meterHeight + spacing) + margin); Localize(); InitializeBoxes(); /// Set focus to the first enabled ComboBox bool activeControlFound = false; for (int ix = 0; ix < commonItemsCount; ix++) { if (commonComboBoxes[ix].Enabled) { ActiveControl = commonComboBoxes[ix]; activeControlFound = true; break; } } if (!activeControlFound) { /// Initialize k,j so that after the 1st increment k = 0 and j = 0 int k, j; if (isLrOrder) { k = -1; j = 0; } else { k = 0; j = -1; } /// Change focus if (FindNextEnabledCombo(comboBoxes, ref k, ref j, isLrOrder)) { ActiveControl = okButton; } else { ActiveControl = comboBoxes[k, j]; } } } /// /// Add combo box items from a history stored in a string array (obtained usually from LocalSettings) /// /// ComboBox to prepare /// String array with a history void AddHistoryToCombo(ComboBox comboBox, string[] history) { if (history != null) { for (int i = 0; i < history.Length; i++) comboBox.Items.Add(history[i]); } } /// /// Returns an array of strings from ls. /// Never returns null, null is converted to new string[0]. /// /// false = Beginning of cycle, true = End of cycle /// Column number (0-based) /// Array of strings string[] GetHistoryFromLS(bool isEnd, int k) { if (isEnd) { switch (k) { case 0: if (ls.LastEnColA == null) ls.LastEnColA = new string[0]; return ls.LastEnColA; case 1: if (ls.LastEnColB == null) ls.LastEnColB = new string[0]; return ls.LastEnColB; case 2: if (ls.LastEnColC == null) ls.LastEnColC = new string[0]; return ls.LastEnColC; case 3: if (ls.LastEnColD == null) ls.LastEnColD = new string[0]; return ls.LastEnColD; case 4: if (ls.LastEnColE == null) ls.LastEnColE = new string[0]; return ls.LastEnColE; default: return new string[0]; } } else { switch (k) { case 0: if (ls.LastBgColA == null) ls.LastBgColA = new string[0]; return ls.LastBgColA; case 1: if (ls.LastBgColB == null) ls.LastBgColB = new string[0]; return ls.LastBgColB; case 2: if (ls.LastBgColC == null) ls.LastBgColC = new string[0]; return ls.LastBgColC; case 3: if (ls.LastBgColD == null) ls.LastBgColD = new string[0]; return ls.LastBgColD; case 4: if (ls.LastBgColE == null) ls.LastBgColE = new string[0]; return ls.LastBgColE; default: return new string[0]; } } } void Localize() { okButton.Text = Strings.OkBtnText; clearButton.Text = Strings.ClearBtnText; } /// /// Initialize combo boxes state defined by related 'Action'. /// Clear check boxes. /// void InitializeBoxes() { isHandlersEnabled = false; /// Common combo boxes for (int ix = 0; ix < commonItemsCount; ix++) { switch (commonItems[ix].Action) { default: case Ac.Clear: commonComboBoxes[ix].Text = string.Empty; break; case Ac.HistoryLast: commonComboBoxes[ix].Text = (commonComboBoxes[ix].Items.Count > 0) ? commonComboBoxes[ix].Items[0].ToString() : string.Empty; break; case Ac.Load: case Ac.LoadReadOnly: commonComboBoxes[ix].Text = DEUtils.GetContent(commonItems[ix].Content, firstValidWM); break; case Ac.Set: commonComboBoxes[ix].Text = Strings.yes; break; } } /// Table for (int k = 0; k < colItems.Count; k++) { switch (colItems[k].Action) { default: case Ac.Clear: for (int i = 0; i < wmsCount; i++) comboBoxes[k, i].Text = string.Empty; break; case Ac.HistoryLast: for (int i = 0; i < wmsCount; i++) { comboBoxes[k, i].Text = comboBoxes[k, i].Items.Count > 0 ? comboBoxes[k, i].Items[0].ToString() : string.Empty; } break; case Ac.Load: case Ac.LoadReadOnly: for (int i = 0; i < wmsCount; i++) { comboBoxes[k, i].Text = DEUtils.GetContent(colItems[k].Content, WaterMeters[i]); } break; case Ac.Set: for (int i = 0; i < wmsCount; i++) { comboBoxes[k, i].Text = (WaterMeters[i] != null && !WaterMeters[i].Disabled) ? Strings.yes : Strings.no; } break; } } for (int i = 0; i < wmsCount; i++) { checkBoxes[i].Checked = isEnd ? (WaterMeters[i] != null && !WaterMeters[i].Disabled) : false; } isHandlersEnabled = true; } /// /// When one combo box is updated using drop-down menu, all combo boxes /// are updated by this function. /// void UpdateWMsFromBoxes() { for (int ix = 0; ix < commonItemsCount; ix++ ) { DEUtils.PutContent(commonItems[ix].Content, firstValidWM, commonComboBoxes[ix].Text); if (commonItems[ix].Action != Ac.LoadReadOnly) { if (isEnd) { switch (ix) { case 0: ls.UpdateHistory(commonComboBoxes[ix].Text, ref ls.LastEnComm1); break; case 1: ls.UpdateHistory(commonComboBoxes[ix].Text, ref ls.LastEnComm2); break; case 2: ls.UpdateHistory(commonComboBoxes[ix].Text, ref ls.LastEnComm3); break; } } else { switch (ix) { case 0: ls.UpdateHistory(commonComboBoxes[ix].Text, ref ls.LastBgComm1); break; case 1: ls.UpdateHistory(commonComboBoxes[ix].Text, ref ls.LastBgComm2); break; case 2: ls.UpdateHistory(commonComboBoxes[ix].Text, ref ls.LastBgComm3); break; } } } /// /// Propagate common (batch) values to water meters in case of /// Content.BatchAndWmOrder, Content.BatchAndWmRemark, Content.YearOfCalibration and Content.ArchivePath /// foreach (var wm in WaterMeters) { if (wm != null && !wm.Disabled) { switch (commonItems[ix].Content) { case Ct.BatchAndWmOrder: wm.PurchaseOrder = firstValidWM.Batch.PurchaseOrder; break; case Ct.BatchAndWmRemark: wm.Remark = firstValidWM.Batch.Remark; break; case Ct.YearOfCalibration: wm.YearOfProduction = firstValidWM.YearOfProduction; break; case Ct.ArchivePath: wm.ArchivePath = firstValidWM.ArchivePath; break; } } } } for (int k = 0; k < colItems.Count; k++) { /// Store values to water meters and prepare an array for a history in the LocalSettings var currentVals = new string[wmsCount]; for (int i = 0; i < wmsCount; i++) { DEUtils.PutContent(colItems[k].Content, WaterMeters[i], comboBoxes[k, i].Text); currentVals[i] = comboBoxes[k, i].Text; } /// Update history if (colItems[k].Action != Ac.LoadReadOnly) { if (isEnd) { switch (k) { case 0: ls.LastEnColA = currentVals; break; case 1: ls.LastEnColB = currentVals; break; case 2: ls.LastEnColC = currentVals; break; case 3: ls.LastEnColD = currentVals; break; case 4: ls.LastEnColE = currentVals; break; } } else { switch (k) { case 0: ls.LastBgColA = currentVals; break; case 1: ls.LastBgColB = currentVals; break; case 2: ls.LastBgColC = currentVals; break; case 3: ls.LastBgColD = currentVals; break; case 4: ls.LastBgColE = currentVals; break; } } } } for (int i = 0; i < wmsCount; i++) { if (WaterMeters[i] != null) WaterMeters[i].Disabled = !checkBoxes[i].Checked; } } private void commonCheckBox_CheckedChanged(object sender, EventArgs e) { bool state = (sender as CheckBox).Checked; for (int i = 0; i < wmsCount; i++) { checkBoxes[i].Checked = state; } } private void okButton_Click(object sender, EventArgs e) { UpdateWMsFromBoxes(); completed = true; Close(); } private void clearButton_Click(object sender, EventArgs e) { InitializeBoxes(); } private void multiPurposeButton_Click(object sender, EventArgs e) { if (multiPurposeButtonFn == MultiPurposeBtnFunction.None) return; if (multiPurposeButtonFn == MultiPurposeBtnFunction.AutoSN) { /// /// Find the 1st enabled water meter /// int firstIx; for (firstIx = 0; firstIx < wmsCount; firstIx++) { if (checkBoxes[firstIx].Checked) break; } if (firstIx == wmsCount) { return; /// There is not any enabled water meter } char[] digits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; /// /// Find a column with serial numbers /// for (int k = 0; k < colItems.Count; k++) { if ((colItems[k].Content == Ct.SerialNr || colItems[k].Content == Ct.SerialNrAux) && colItems[k].Action != Ac.LoadReadOnly) { /// /// Column with serial numbers found => perform an auto s/n assignment /// string firstSN = comboBoxes[k, firstIx].Text; int firstSnNr; int startIx = firstSN.IndexOfAny(digits); if (startIx >= 0) { int lastDigitPosPlus1 = startIx + 1; var listOfDigits = new List(digits); while (lastDigitPosPlus1 < firstSN.Length && listOfDigits.Contains(firstSN[lastDigitPosPlus1])) { lastDigitPosPlus1++; } int digitsCount = lastDigitPosPlus1 - startIx; if (int.TryParse(firstSN.Substring(startIx, digitsCount), out firstSnNr) && firstSnNr >= 0) { for (int ix = firstIx + 1; ix < wmsCount; ix++) { if (checkBoxes[ix].Checked) { firstSnNr++; string newSN = firstSnNr.ToString(); int len = newSN.Length; if (len <= digitsCount) { comboBoxes[k, ix].Text = firstSN.Substring(0, startIx + digitsCount - len) + newSN + firstSN.Substring(startIx + digitsCount); } else { comboBoxes[k, ix].Text = firstSN.Substring(0, startIx) + newSN + firstSN.Substring(startIx + digitsCount); ; } } } } } } } } if (multiPurposeButtonFn == MultiPurposeBtnFunction.PrintLabelsOnOff) { /// /// Find a column with Ct.PrintLabel /// for (int k = 0; k < colItems.Count; k++) { if (colItems[k].Content == Ct.PrintLabel) { for (int ix = 0; ix < wmsCount; ix++) { if (WaterMeters[ix] != null && !WaterMeters[ix].Disabled) { comboBoxes[k, ix].Text = multiPurposeButtonFlag ? Strings.yes : Strings.no; } } } } multiPurposeButtonFlag = !multiPurposeButtonFlag; } } private void comboBox_SelectedIndexChanged(object sndr, EventArgs e) { if (!isHandlersEnabled) return; string[] kj = (sndr as ComboBox).Name.Split(new char[] { '~' }); int k = int.Parse(kj[0]); int j = int.Parse(kj[1]); isHandlersEnabled = false; if (k >= 0 && colItems[k].Content != Ct.PrintLabel && comboBoxes[k, j].Text == comboBoxes[k, j].Items[0].ToString()) { for (int i = 0; i < wmsCount; i++) { comboBoxes[k, i].Text = comboBoxes[k, i].Items[0].ToString(); if (!string.IsNullOrEmpty(comboBoxes[k, i].Text)) checkBoxes[i].Checked = true; } } isHandlersEnabled = true; } private void comboBox_KeyPress(object sndr, KeyPressEventArgs e) { if (!isHandlersEnabled) return; string[] kj = (sndr as ComboBox).Name.Split(new char[] { '~' }); int k = int.Parse(kj[0]); int j = int.Parse(kj[1]); /// Close the form if any 'form close key' was pressed for (int ix = 0; ix < formCloseKeys.Length; ix++) { if (e.KeyChar == formCloseKeys[ix]) { okButton_Click(sndr, e); return; } } if (e.KeyChar == '\r') { if (k < 0) { for (int ix = j; ix < commonItemsCount; ix++) { if (commonComboBoxes[ix].Enabled) { commonComboBoxes[ix].Focus(); return; } } /// No next enabled common item found: /// Initialize k,j so that after the 1st increment k = 0 and j = 0 if (isLrOrder) { k = -1; j = 0; } else { k = 0; j = -1; } } /// Change focus if (FindNextEnabledCombo(comboBoxes, ref k, ref j, isLrOrder)) { okButton.Focus(); } else { comboBoxes[k, j].Focus(); } } } private void comboBox_TextChanged(object sndr, EventArgs e) { if (!isHandlersEnabled) return; string[] kj = (sndr as ComboBox).Name.Split(new char[] { '~' }); int k = int.Parse(kj[0]); int j = int.Parse(kj[1]); if (k >= 0) checkBoxes[j].Checked = true; } /// /// Find the next enabled combo box, update k and i. /// Return true (=pastTheEndOfArray) when there is no such next combo box. /// /// 2-dimensional array of combo boxes /// 0-nased column /// 0-based row /// Order of progress to the next combo: true = left-right, false = top-down /// true (=pastTheEndOfArray) when there is no such next combo box, otherwise false bool FindNextEnabledCombo(ComboBox[,] comboBoxes, ref int k, ref int i, bool isLrOrder) { bool pastTheEndOfArray = false; do { if (isLrOrder) { if (++k == comboBoxes.GetLength(0)) { k = 0; if (++i == comboBoxes.GetLength(1)) { i = 0; pastTheEndOfArray = true; break; } } } else { if (++i == comboBoxes.GetLength(1)) { i = 0; if (++k == comboBoxes.GetLength(0)) { k = 0; pastTheEndOfArray = true; break; } } } } while (!comboBoxes[k, i].Enabled); return pastTheEndOfArray; } #region Forced close handling public void StartForceCloseHandler() { UiBridge.Bridge.CloseModelessFormHandler += delegate(object sender, EventArgs args) { if (InvokeRequired) { Invoke(new EventHandler(OnForceClose), sender, args); } else OnForceClose(sender, args); }; } private void OnForceClose(object sender, EventArgs args) { DialogResult = DialogResult.Cancel; Close(); } #endregion } }