GPAL - Generally Positive Automation Library v1.0
GPAL The Fluent Automation LIbrary
Loading...
Searching...
No Matches
GPALForm.cs
1// =============================================================================
2// GPAL - Generally Positive Automation Library
3// Copyright © 2026 Software Decisions, Inc. All rights reserved.
4//
5// This file is part of GPAL.
6// Licensed under the Business Source License 1.1
7//
8// Primary development, architecture, and vision by Michael B. Vederman,
9// CEO of Software Decisions, Inc., Texas.
10//
11// Internal development maintained privately.
12// Public releases appear on GitHub: https://github.com/SoftwareDecisionsInc/GPAL.
13//
14// See LICENSE for full terms, including Additional Use Grant.
15// =============================================================================
16
17using OpenQA.Selenium;
18using System;
19using System.Collections.Generic;
20using System.Drawing;
21using System.Linq;
22using System.Text;
23using System.Threading.Tasks;
24using System.Windows.Forms;
25using System.Windows.Forms.DataVisualization.Charting;
27using static GenerallyPositive.Enums;
29
31{
64 {
91 public delegate CallIfStatus CallAfterFillInDelegate(GPALForm myForm, IGPALGrid<string> tokens, int tokenIdx);
92
93 private readonly System.Windows.Forms.ToolTip _toolTip = new System.Windows.Forms.ToolTip
94 {
95 AutoPopDelay = 5000,
96 InitialDelay = 500,
97 ShowAlways = true
98 };
99
116 public dynamic ToGPALObject()
117 {
118 return this;
119 }
120
124 public string FormName
125 {
126 get
127 {
128 return FormSettings.myForm.FormName;
129 }
130 internal set
131 {
132 FormSettings.myForm.FormName = value;
133 }
134 }
135
136 private FormSettings FormSettings { get; set; }
137
138 internal GPALForm()
139 {
140 FormSettings = new FormSettings();
141 FormSettings.myForm = new GPALFormForm()
142 {
143 ControlList = new List<GPALControl>(),
144 };
145
146 FormSettings.myForm.SuspendLayout();
147 FormSettings.myForm.Left = FormSettings.left;
148 FormSettings.myForm.Top = FormSettings.top;
149 FormSettings.myForm.Width = FormSettings.width;
150 FormSettings.myForm.Height = FormSettings.height;
151 FormSettings.myForm.InitialSize = new Size(FormSettings.width, FormSettings.height);
152
153 FormSettings.myForm.AutoSize = true;
154
155 Size _initialPreferredSize = FormSettings.myForm.Size;
156
157 FormSettings.TableLayoutPanel = new TableLayoutPanel()
158 {
159 //Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right,
160 Dock = DockStyle.Fill,
161 AutoSize = false,
162 AutoScroll = true,
163 };
164 FormSettings.myForm.Controls.Add(FormSettings.TableLayoutPanel);
165
166 System.Windows.Forms.Application.EnableVisualStyles();
167 }
168
169 #region <Settings>
170 internal GPALFormForm Form
171 {
172 get
173 {
174 return FormSettings.myForm;
175 }
176 }
177 internal List<GPALControl> ControlList
178 {
179 get
180 {
181 return FormSettings.myForm.ControlList;
182 }
183 }
190 {
191 FormSettings.width = width;
192 FormSettings.myForm.Width = width;
193 // ensure the right edge is on-screen, but the left edge could be off-screen if the form is too wide
194 // todo - how to handle out of bounds settings?
195 if (FormSettings.left > (Screen.PrimaryScreen.Bounds.Width - width))
196 FormSettings.left = Screen.PrimaryScreen.Bounds.Width - width - 2;
197
198 FormSettings.myForm.InitialSize = new Size(FormSettings.myForm.Width, FormSettings.myForm.Height);
199
200 return this;
201 }
202
208 {
209 FormSettings.height = height;
210 FormSettings.myForm.Height = height;
211 // ensure the bottom is on-screen, but this could put the grab bar off-screen
212 if (FormSettings.top > (Screen.PrimaryScreen.Bounds.Height - height))
213 FormSettings.top = Screen.PrimaryScreen.Bounds.Height - height - 2;
214 else
215 FormSettings.top = (Screen.PrimaryScreen.Bounds.Height - height) / 2; // center unless overridden by subsequent Top
216
217 FormSettings.myForm.InitialSize = new Size(FormSettings.myForm.Width, FormSettings.myForm.Height);
218
219 return this;
220 }
221
227 {
228 FormSettings.top = top;
229 FormSettings.myForm.Top = top;
230 return this;
231 }
232
238 {
239 FormSettings.left = left;
240 FormSettings.myForm.Left = left;
241 return this;
242 }
243
246 public int Left => FormSettings.myForm.Left;
250 public int Top => FormSettings.myForm.Top;
254 public int Width => FormSettings.myForm.Width;
258 public int Height => FormSettings.myForm.Height;
266 public IAllowFormSettingsAndActions CallAfterFillIn(GPALForm.CallAfterFillInDelegate callAfterFillIn)
267 {
268 FormSettings.myForm.CallAfterFillIn = callAfterFillIn;
269 return this;
270 }
271 #endregion <Settings>
272 #region <Forms Actions>
288 {
289 return gPALControl switch
290 {
291 GPALButton btn => AddButton(btn),
292 GPALChart ch => AddChart(ch),
293 GPALComboBox cb => AddComboBox(cb),
294 GPALDataGridView dgv => AddDataGridView(dgv),
295 GPALDateTimePicker dtp => AddDateTimePicker(dtp),
296 GPALCheckbox chk => AddCheckbox(chk),
297 GPALFileSelector fs => AddFileSelector(fs),
298 GPALInput inp => AddInput(inp),
299 GPALLabel lbl => AddLabel(lbl),
300 GPALListView lv => AddListView(lv),
301 GPALMenuBar mb => AddMenuBar(mb),
302 GPALNumericUpDown nud => AddNumericUpDown(nud),
303 GPALProgressBar pb => AddProgressBar(pb),
304 GPALRadioButton rb => AddRadioButton(rb),
305 GPALRichTextBox rtb => AddRichTextBox(rtb),
306 GPALStatusStrip ss => AddStatusStrip(ss),
307 GPALToolbar tb => AddToolbar(tb),
308 GPALTab tab => AddTab(tab),
309 GPALSplitter sp => AddSplitter(sp),
310 GPALGroupBox gb => AddGroupBox(gb),
311 GPALTableLayoutPanel tlp => AddTableLayoutPanel(tlp),
312 GPALTextArea ta => AddTextArea(ta),
313 GPALTreeView tv => AddTreeView(tv),
314
315 _ => this // or throw new NotSupportedException(...)
316 };
317 }
318
324 {
325 FormSettings.myForm.FormTitle = title;
326 FormSettings.myForm.Text = title;
327 return this;
328 }
329
337 {
338 FormSettings.myForm.InputDatabase = inputDatabase;
339 DatabaseHelper.TokenizeDatabase(inputDatabase);
340 FormHelper.FillInWithTokens(this, new List<IGPALGrid<string>> { inputDatabase.Tokens }, WriteMode.Overwrite);
341 return this;
342 }
343
351 {
352 FormSettings.myForm.InputDatabase = inputDatabase;
353 DatabaseHelper.TokenizeDatabase(inputDatabase);
354 FormHelper.FillInWithTokens(this, new List<IGPALGrid<string>> { inputDatabase.Tokens }, WriteMode.Append);
355 return this;
356 }
357
365 {
366 FormSettings.myForm.InputDatabase = inputDatabase;
367 DatabaseHelper.TokenizeDatabase(inputDatabase);
368 FormHelper.FillInWithTokens(this, new List<IGPALGrid<string>> { inputDatabase.Tokens }, WriteMode.Insert);
369 return this;
370 }
371
379 {
380 FormSettings.myForm.InputFile = inputFile;
381 if (false == FileHelper.TokenizeFile(inputFile))
382 {
383 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $"Unable to tokenize file [{inputFile.Filename}]. Continuing.", inputFile, GPALObjectType.GPALFile);
384 return this;
385 }
386 FormHelper.FillInWithTokens(this, ((IGPALFileInternal)inputFile).TokenList, WriteMode.Overwrite);
387 return this;
388 }
389
397 {
398 FormSettings.myForm.InputFile = inputFile;
399 if (false == FileHelper.TokenizeFile(inputFile))
400 {
401 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $"Unable to tokenize file [{inputFile.Filename}]. Continuing.", inputFile, GPALObjectType.GPALFile);
402 return this;
403 }
404 FormHelper.FillInWithTokens(this, ((IGPALFileInternal)inputFile).TokenList, WriteMode.Append);
405 return this;
406 }
407
415 {
416 FormSettings.myForm.InputFile = inputFile;
417 if (false == FileHelper.TokenizeFile(inputFile))
418 {
419 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $"Unable to tokenize file [{inputFile.Filename}]. Continuing.", inputFile, GPALObjectType.GPALFile);
420 return this;
421 }
422 FormHelper.FillInWithTokens(this, ((IGPALFileInternal)inputFile).TokenList, WriteMode.Insert);
423 return this;
424 }
425
432 public IAllowFormSettingsAndActions FillInFrom(IGPALGrid<string> inputGrid)
433 {
434 FormHelper.FillInWithTokens(this, new List<IGPALGrid<string>> { inputGrid }, WriteMode.Overwrite);
435 return this;
436 }
437
444 public IAllowFormSettingsAndActions AppendFrom(IGPALGrid<string> inputGrid)
445 {
446 FormHelper.FillInWithTokens(this, new List<IGPALGrid<string>> { inputGrid }, WriteMode.Append);
447 return this;
448 }
449
456 public IAllowFormSettingsAndActions InsertFrom(IGPALGrid<string> inputGrid)
457 {
458 FormHelper.FillInWithTokens(this, new List<IGPALGrid<string>> { inputGrid }, WriteMode.Insert);
459 return this;
460 }
461 // a workflow raised from a button is still the form's guest, and a guest that fails should not take the
462 // house down with it. windows forms funnels everything thrown on the ui thread through here, so this is
463 // the one place to say so: publish it, leave the form standing, let whoever pressed the button read the
464 // log tab and press it again
465 private static bool workflowFailureHandled = false;
466
467 private void KeepFormAliveOnWorkflowFailure()
468 {
469 if (true == workflowFailureHandled) return;
470
471 workflowFailureHandled = true;
472
473 // only the subscription. the mode cannot be set once a control exists on the thread, and by the time
474 // a form is shown they all do. windows forms routes to a handler when one is attached, which is this
475 System.Windows.Forms.Application.ThreadException += (sender, args) =>
476 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $"The form kept going after [{args.Exception?.GetType().Name}]", null, GPALObjectType.GPALForm, args.Exception);
477 }
478
485 {
486 KeepFormAliveOnWorkflowFailure();
487 SizeForm();
488 FormSettings.myForm.ResumeLayout();
489 if (true == FormSettings.myForm.Visible)
490 FormSettings.myForm.Hide();
491 FormSettings.myForm.ShowDialog();
492
493 return this;
494 }
495
516 {
517 KeepFormAliveOnWorkflowFailure();
518 SizeForm();
519 FormSettings.myForm.ResumeLayout();
520 if (true == FormSettings.myForm.Visible)
521 FormSettings.myForm.Hide();
522 FormSettings.myForm.Show();
523
524 return this;
525 }
526
557 {
558 FormSettings.myForm.Hide();
559 return this;
560 }
561 #endregion <Forms Actions>
562 #region Helpers
568 private void ApplyToolTip(GPALControl gpalControl, Control winControl)
569 {
570 if (!string.IsNullOrWhiteSpace(gpalControl.ToolTipText))
571 {
572 FormSettings.ToolTip.SetToolTip(winControl, gpalControl.ToolTipText);
573 if (gpalControl.ToolTipAutoPopDelayMs != 5000)
574 FormSettings.ToolTip.AutoPopDelay = gpalControl.ToolTipAutoPopDelayMs;
575 }
576 }
580 private void SizeForm()
581 {
582 int tmpHeight = 30;
583
584 if (0 == FormSettings.height)
585 {
586 foreach (GPALControl controlEntry in FormSettings.myForm.ControlList)
587 {
588 switch (controlEntry.ControlType)
589 {
590 case FormControlType.TextArea:
591 case FormControlType.RichTextBox:
592 tmpHeight += 54;
593 break;
594
595 default:
596 tmpHeight += 30;
597 break;
598 }
599 }
600 FormSettings.height = tmpHeight;
601
602 if (0 < FormSettings.width)
603 FormSettings.myForm.Width = FormSettings.width;
604 if (0 < FormSettings.height)
605 FormSettings.myForm.Height = FormSettings.height;
606 }
607
608 // apply explicit positioning regardless of whether width/height were set,
609 // otherwise StartPosition defaults to WindowsDefaultLocation and Left/Top are ignored
610 if (0 < FormSettings.top)
611 {
612 FormSettings.myForm.StartPosition = FormStartPosition.Manual;
613 FormSettings.myForm.Top = FormSettings.top;
614 }
615 if (0 < FormSettings.left)
616 {
617 FormSettings.myForm.StartPosition = FormStartPosition.Manual;
618 FormSettings.myForm.Left = FormSettings.left;
619 }
620 }
621 #endregion Helpers
622 #region Add Controls
628 private IAllowFormSettingsAndActions AddButton(GPALButton gPALButton)
629 {
630 FormSettings.myForm.ControlList.Add(gPALButton);
631 Button button = new Button
632 {
633 Name = gPALButton.Name,
634 Text = gPALButton.Text,
635 // AutoSize = true,
636 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom,
637 Enabled = gPALButton.Enabled,
638 Height = 10 < gPALButton.Height ? gPALButton.Height : 20,
639 };
640
641 gPALButton.WindowsControl = button;
642
643 foreach (var (evt, handler) in gPALButton.Callbacks)
644 {
645 if (handler == null) continue; // skip invalid
646
647 switch (evt)
648 {
649 case ControlEventType.DoubleClick:
650 button.DoubleClick += (EventHandler)handler;
651 break;
652
653 case ControlEventType.Default:
654 case ControlEventType.Click:
655 button.Click += (EventHandler)handler;
656 break;
657 // ignore unsupported
658 }
659 }
660
661 FormSettings.TableLayoutPanel.Controls.Add(button);
662
663 if (true == gPALButton.IsDefault)
664 {
665 FormSettings.myForm.AcceptButton = button;
666 button.DialogResult = DialogResult.OK;
667 button.TabIndex = 0;
668 }
669
670 if (!string.IsNullOrWhiteSpace(gPALButton.ToolTipText))
671 ApplyToolTip(gPALButton, button);
672
673 return this;
674 }
680 private IAllowFormSettingsAndActions AddChart(GPALChart gPALChart)
681 {
682 FormSettings.myForm.ControlList.Add(gPALChart);
683 FormSettings.TableLayoutPanel.Controls.Add((Chart)gPALChart.WindowsControl);
684 ((Chart)gPALChart.WindowsControl).Height = 10 < gPALChart.Height ? gPALChart.Height : 150;
685
686 Chart chart = gPALChart.WindowsControl;
687
688 foreach (var (evt, handler) in gPALChart.Callbacks)
689 {
690 if (handler == null) continue; // skip invalid
691
692 switch (evt)
693 {
694 case ControlEventType.DragDrop:
695 chart.DragDrop += (DragEventHandler)handler;
696 break;
697
698 case ControlEventType.DragEnter:
699 chart.DragEnter += (DragEventHandler)handler;
700 break;
701
702 case ControlEventType.DragLeave:
703 chart.DragLeave += (EventHandler)handler;
704 break;
705
706 case ControlEventType.DragOver:
707 chart.DragOver += (DragEventHandler)handler;
708 break;
709
710 case ControlEventType.DoubleClick:
711 chart.DoubleClick += (EventHandler)handler;
712 break;
713
714 case ControlEventType.Default:
715 case ControlEventType.Click:
716 chart.Click += (EventHandler)handler;
717 break;
718 // ignore unsupported
719 }
720 }
721
722 if (!string.IsNullOrWhiteSpace(gPALChart.ToolTipText))
723 ApplyToolTip(gPALChart, chart);
724
725 return this;
726 }
732 private IAllowFormSettingsAndActions AddCheckbox(GPALCheckbox gPALCheckbox)
733 {
734 FormSettings.myForm.ControlList.Add(gPALCheckbox);
735 CheckBox checkBox = new CheckBox
736 {
737 Name = gPALCheckbox.Name,
738 Text = gPALCheckbox.Text,
739 AutoSize = true,
740 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
741 Checked = gPALCheckbox.Checked,
742 Enabled = gPALCheckbox.Enabled,
743 Height = 10 < gPALCheckbox.Height ? gPALCheckbox.Height : 20,
744 };
745 gPALCheckbox.WindowsControl = checkBox;
746
747 // has to be in place for proper presentation, user can also hook into this for their purposes
748 checkBox.CheckedChanged += new EventHandler(delegate (Object sender, EventArgs a) { gPALCheckbox.Checked = ((CheckBox)sender).Checked; }); // copy changed text up to control
749
750 foreach (var (evt, handler) in gPALCheckbox.Callbacks)
751 {
752 if (handler == null) continue; // skip invalid
753
754 switch (evt)
755 {
756 case ControlEventType.Default:
757 case ControlEventType.CheckedChanged:
758 checkBox.CheckedChanged += (EventHandler)handler;
759 break;
760
761 case ControlEventType.DoubleClick:
762 checkBox.DoubleClick += (EventHandler)handler;
763 break;
764
765 case ControlEventType.Click:
766 checkBox.Click += (EventHandler)handler;
767 break;
768 // ignore unsupported
769 }
770 }
771
772 FormSettings.TableLayoutPanel.Controls.Add(checkBox);
773
774 if (!string.IsNullOrWhiteSpace(gPALCheckbox.ToolTipText))
775 ApplyToolTip(gPALCheckbox, checkBox);
776
777 return this;
778 }
784 // the first thing on the list that can actually be chosen, for when there is nothing to fall back to
785 private static int FirstEnabledIndex(ComboBox combo, GPALComboBox gpalCombo)
786 {
787 int retVal = -1;
788
789 for (int index = 0; index < combo.Items.Count && -1 == retVal; index++)
790 if (false == gpalCombo.disabledItems.Contains(combo.Items[index].ToString()))
791 retVal = index;
792
793 return retVal;
794 }
795
796 private IAllowFormSettingsAndActions AddComboBox(GPALComboBox gpalCombo)
797 {
798 FormSettings.myForm.ControlList.Add(gpalCombo);
799
800 var combo = new ComboBox
801 {
802 DropDownStyle = ComboBoxStyle.DropDownList,
803 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
804 Height = 10 < gpalCombo.Height ? gpalCombo.Height : 20,
805 };
806
807 // populate items
808 foreach (string item in gpalCombo.items)
809 {
810 combo.Items.Add(item);
811 }
812
813 // restore selection if set — by name takes priority, then by index
814 if (!string.IsNullOrEmpty(gpalCombo.selectedItem))
815 {
816 int idx = combo.Items.IndexOf(gpalCombo.selectedItem);
817 if (0 <= idx) combo.SelectedIndex = idx;
818 }
819 else if (0 <= gpalCombo.selectedIndex && gpalCombo.selectedIndex < combo.Items.Count)
820 {
821 combo.SelectedIndex = gpalCombo.selectedIndex;
822 }
823
824 // a disabled item is drawn greyed and refuses to be chosen. owner-draw is the only way windows
825 // forms lets one entry of a dropdown say no, and it draws the ordinary way for everything else
826 combo.DrawMode = DrawMode.OwnerDrawFixed;
827
828 combo.DrawItem += (sender, args) =>
829 {
830 if (0 > args.Index) return;
831
832 string text = combo.Items[args.Index].ToString();
833 bool disabled = true == gpalCombo.disabledItems.Contains(text);
834
835 // the highlight belongs to something choosable, so a disabled row keeps the plain background
836 if (false == disabled)
837 args.DrawBackground();
838 else
839 using (SolidBrush background = new SolidBrush(SystemColors.Window))
840 args.Graphics.FillRectangle(background, args.Bounds);
841
842 using (SolidBrush foreground = new SolidBrush(disabled ? SystemColors.GrayText : args.ForeColor))
843 args.Graphics.DrawString(text, args.Font, foreground, args.Bounds);
844
845 if (false == disabled) args.DrawFocusRectangle();
846 };
847
848 // put it back on the last thing that was allowed. SelectedIndexChanged fires for every route in,
849 // including the keyboard and code, so this is the one place that catches them all
850 combo.SelectedIndexChanged += (sender, args) =>
851 {
852 if (0 > combo.SelectedIndex) return;
853
854 string text = combo.Items[combo.SelectedIndex].ToString();
855
856 if (false == gpalCombo.disabledItems.Contains(text))
857 {
858 gpalCombo.lastEnabledIndex = combo.SelectedIndex;
859 return;
860 }
861
862 GPAL.PublishSimpleEvent(Enums.GPALEventType.CAUTION, $"[{text}] does not apply to what is selected, so it cannot be chosen", null, Enums.GPALObjectType.GPALForm);
863
864 combo.SelectedIndex = 0 <= gpalCombo.lastEnabledIndex && gpalCombo.lastEnabledIndex < combo.Items.Count
865 ? gpalCombo.lastEnabledIndex
866 : FirstEnabledIndex(combo, gpalCombo);
867 };
868
869 gpalCombo.lastEnabledIndex = combo.SelectedIndex;
870
871 gpalCombo.WindowsControl = combo;
872
873 foreach (var (evt, handler) in gpalCombo.Callbacks)
874 {
875 if (handler == null) continue; // skip invalid
876
877 switch (evt)
878 {
879 case ControlEventType.Default:
880 case ControlEventType.SelectedIndexChanged:
881 combo.SelectedIndexChanged += (EventHandler)handler;
882 break;
883
884 case ControlEventType.ValueChanged:
885 combo.SelectedValueChanged += (EventHandler)handler;
886 break;
887
888 case ControlEventType.Click:
889 combo.Click += (EventHandler)handler;
890 break;
891 // ignore unsupported
892 }
893 }
894
895 // Apply tooltip if set
896 if (false == string.IsNullOrEmpty(gpalCombo.ToolTipText))
897 ApplyToolTip(gpalCombo, combo);
898
899 FormSettings.TableLayoutPanel.Controls.Add(combo);
900
901 return this;
902 }
908 private IAllowFormSettingsAndActions AddDataGridView(GPALDataGridView gPALDataGridView)
909 {
910 FormSettings.myForm.ControlList.Add(gPALDataGridView);
911
912 DataGridView dgv = new DataGridView
913 {
914 Name = gPALDataGridView.Name,
915 ReadOnly = gPALDataGridView.ReadOnly,
916 AutoGenerateColumns = gPALDataGridView.AutoGenerateColumns,
917 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right,
918 Enabled = gPALDataGridView.IsEnabled,
919 AllowUserToAddRows = false, // usually not wanted in dashboards
920 RowHeadersVisible = false, // optional – cleaner look
921 AutoSize = true,
922 Height = 10 < gPALDataGridView.Height ? gPALDataGridView.Height : 20,
923 };
924
925 // Add explicit columns if supplied
926 foreach (string colName in gPALDataGridView.ColumnNames)
927 {
928 dgv.Columns.Add(colName, colName);
929 }
930
931 gPALDataGridView.WindowsControl = dgv;
932
933 // Wire callbacks (same pattern as before)
934 foreach (var (evt, handler) in gPALDataGridView.Callbacks)
935 {
936 if (handler == null) continue;
937
938 switch (evt)
939 {
940 case ControlEventType.Default:
941 case ControlEventType.CellValueChanged:
942 case ControlEventType.Change:
943 dgv.CellValueChanged += (DataGridViewCellEventHandler)handler;
944 break;
945
946 case ControlEventType.DoubleClick:
947 dgv.DoubleClick += (EventHandler)handler;
948 break;
949
950 case ControlEventType.Click:
951 dgv.Click += (EventHandler)handler;
952 break;
953 // ignore unsupported
954 }
955 }
956
957 // Populate from grid if already set during fluent chain
958 gPALDataGridView.SyncGridToControl(); // ← important line
959
960 FormSettings.TableLayoutPanel.Controls.Add(dgv);
961
962 if (!string.IsNullOrWhiteSpace(gPALDataGridView.ToolTipText))
963 ApplyToolTip(gPALDataGridView, dgv);
964
965 return this;
966 }
972 private IAllowFormSettingsAndActions AddDateTimePicker(GPALDateTimePicker gPALDateTimePicker)
973 {
974 FormSettings.myForm.ControlList.Add(gPALDateTimePicker);
975
976 DateTimePicker dtp = new DateTimePicker
977 {
978 Name = gPALDateTimePicker.Name,
979 Value = gPALDateTimePicker.Value,
980 Format = gPALDateTimePicker.Format,
981 CustomFormat = gPALDateTimePicker.CustomFormat,
982 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
983 Enabled = gPALDateTimePicker.IsEnabled,
984 Height = 10 < gPALDateTimePicker.Height ? gPALDateTimePicker.Height : 10,
985 };
986
987 gPALDateTimePicker.WindowsControl = dtp;
988
989 foreach (var (evt, handler) in gPALDateTimePicker.Callbacks)
990 {
991 if (handler == null) continue; // skip invalid
992
993 switch (evt)
994 {
995 case ControlEventType.Default:
996 case ControlEventType.ValueChanged:
997 case ControlEventType.Change:
998 dtp.ValueChanged += (EventHandler)handler;
999 break;
1000
1001 case ControlEventType.Click:
1002 dtp.Click += (EventHandler)handler;
1003 break;
1004 // ignore unsupported
1005 }
1006 }
1007
1008 FormSettings.TableLayoutPanel.Controls.Add(dtp);
1009
1010 if (!string.IsNullOrWhiteSpace(gPALDateTimePicker.ToolTipText))
1011 ApplyToolTip(gPALDateTimePicker, dtp);
1012
1013 return this;
1014 }
1020 private IAllowFormSettingsAndActions AddFileSelector(GPALFileSelector gPALFileSelector)
1021 {
1022 FormSettings.myForm.ControlList.Add(gPALFileSelector);
1023
1024 if (gPALFileSelector._compact)
1025 return AddFileSelectorCompact(gPALFileSelector);
1026
1027 Button browseButton = new Button
1028 {
1029 Name = gPALFileSelector.Name + "_Browse",
1030 Text = "Browse...",
1031 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
1032 Width = 80,
1033 Enabled = gPALFileSelector.IsEnabled
1034 };
1035
1036 TextBox displayBox = new TextBox
1037 {
1038 Name = gPALFileSelector.Name + "_Path",
1039 ReadOnly = true,
1040 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
1041 Text = "(click Browse to select)",
1042 Enabled = gPALFileSelector.IsEnabled
1043 };
1044
1045 TableLayoutPanel container = new TableLayoutPanel
1046 {
1047 ColumnCount = 2,
1048 RowCount = 1,
1049 Dock = DockStyle.Top, // full width, hugs a single row of content (no tall default panel gap)
1050 // same reason as the compact selector below: auto-sizing would measure the percent column from
1051 // the path sitting in the box, and a long one puts a horizontal scrollbar on the whole form
1052 Height = Math.Max(displayBox.PreferredHeight, browseButton.Height) + 4,
1053 Margin = new Padding(0)
1054 };
1055 container.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
1056 container.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 90));
1057 container.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
1058 container.Controls.Add(browseButton, 0, 0);
1059 container.Controls.Add(displayBox, 1, 0);
1060
1061 gPALFileSelector.WindowsControl = container;
1062
1063 browseButton.Click += (s, e) =>
1064 {
1065 string[] selected = OpenBrowseDialog(gPALFileSelector);
1066 bool cancelled = selected == null || selected.Length == 0;
1067 gPALFileSelector.NotifySelection(cancelled, selected);
1068 displayBox.Text = cancelled ? "(selection cancelled)" : string.Join("; ", selected);
1069 };
1070
1071 FormSettings.TableLayoutPanel.Controls.Add(container);
1072
1073 if (!string.IsNullOrWhiteSpace(gPALFileSelector.ToolTipText))
1074 ApplyToolTip(gPALFileSelector, container);
1075
1076 return this;
1077 }
1078
1079 private IAllowFormSettingsAndActions AddFileSelectorCompact(GPALFileSelector gPALFileSelector)
1080 {
1081 PlaceholderTextBox textBox = new PlaceholderTextBox
1082 {
1083 Name = gPALFileSelector.Name + "_Path",
1084 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
1085 Enabled = gPALFileSelector.IsEnabled,
1086 PlaceholderText = gPALFileSelector._placeholder
1087 };
1088
1089 Button dotButton = new Button
1090 {
1091 Name = gPALFileSelector.Name + "_Browse",
1092 Text = "...",
1093 Width = 28,
1094 Anchor = AnchorStyles.Top | AnchorStyles.Right,
1095 Enabled = gPALFileSelector.IsEnabled
1096 };
1097
1098 TableLayoutPanel container = new TableLayoutPanel
1099 {
1100 ColumnCount = 2,
1101 RowCount = 1,
1102 Dock = DockStyle.Top, // full width, hugs a single row of content (no tall default panel gap)
1103 // no AutoSize. docking top already takes the width from the form, while auto-sizing measures the
1104 // percent column from what is in it, so a long path in the box asks the form to be wider than it
1105 // is and leaves a horizontal scrollbar that resizing never clears
1106 Height = Math.Max(textBox.PreferredHeight, dotButton.Height) + 4,
1107 Margin = new Padding(0)
1108 };
1109 container.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
1110 container.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
1111 container.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 32));
1112 container.Controls.Add(textBox, 0, 0);
1113 container.Controls.Add(dotButton, 1, 0);
1114
1115 // WindowsControl points to the text box so SyncDisplay() and .Text access work naturally
1116 gPALFileSelector.WindowsControl = textBox;
1117
1118 textBox.TextChanged += (s, e) => gPALFileSelector._selectedPath = textBox.Text;
1119
1120 dotButton.Click += (s, e) =>
1121 {
1122 string[] selected = OpenBrowseDialog(gPALFileSelector);
1123 bool cancelled = selected == null || selected.Length == 0;
1124 if (!cancelled)
1125 textBox.Text = selected[0];
1126 gPALFileSelector.NotifySelection(cancelled, selected);
1127 };
1128
1129 FormSettings.TableLayoutPanel.Controls.Add(container);
1130
1131 if (!string.IsNullOrWhiteSpace(gPALFileSelector.ToolTipText))
1132 ApplyToolTip(gPALFileSelector, container);
1133
1134 return this;
1135 }
1136
1137 private static string[] OpenBrowseDialog(GPALFileSelector gPALFileSelector)
1138 {
1139 if (gPALFileSelector._selectFolder)
1140 {
1141 using (FolderBrowserDialog fbd = new FolderBrowserDialog())
1142 {
1143 fbd.Description = gPALFileSelector._title;
1144 fbd.SelectedPath = gPALFileSelector._initialDirectory;
1145 return fbd.ShowDialog() == DialogResult.OK ? new[] { fbd.SelectedPath } : null;
1146 }
1147 }
1148 else
1149 {
1150 using (OpenFileDialog ofd = new OpenFileDialog())
1151 {
1152 ofd.Title = gPALFileSelector._title;
1153 ofd.InitialDirectory = gPALFileSelector._initialDirectory;
1154 ofd.Filter = gPALFileSelector._filter;
1155 ofd.Multiselect = gPALFileSelector._multiSelect;
1156 return ofd.ShowDialog() == DialogResult.OK ? ofd.FileNames : null;
1157 }
1158 }
1159 }
1165 private IAllowFormSettingsAndActions AddInput(GPALInput gPALInput)
1166 {
1167 FormSettings.myForm.ControlList.Add(gPALInput);
1168 PlaceholderTextBox input = new PlaceholderTextBox
1169 {
1170 Name = gPALInput.Name,
1171 Text = gPALInput.Text,
1172 AutoSize = true,
1173 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
1174 Enabled = gPALInput.Enabled,
1175 Height = 10 < gPALInput.Height ? gPALInput.Height : 20,
1176 };
1177
1178 if (!string.IsNullOrEmpty(gPALInput.PlaceHolder))
1179 input.PlaceholderText = gPALInput.PlaceHolder;
1180
1181 if (true == gPALInput.InputIsPassword)
1182 input.PasswordChar = '\u25CF'; // bullet
1183
1184 if (true == gPALInput.IsReadOnly)
1185 input.ReadOnly = true;
1186
1187 gPALInput.WindowsControl = input;
1188
1189 input.Text = gPALInput.Text; // somehow text is getting truncated to one char from above
1190
1191 input.TextChanged += new EventHandler(delegate (Object sender, EventArgs a) { gPALInput.Text = ((TextBox)sender).Text; }); // copy changed text up to control
1192
1193 foreach (var (evt, handler) in gPALInput.Callbacks)
1194 {
1195 if (handler == null) continue; // skip invalid
1196
1197 switch (evt)
1198 {
1199 case ControlEventType.Default:
1200 case ControlEventType.TextChanged:
1201 input.TextChanged += (EventHandler)handler;
1202 break;
1203
1204 case ControlEventType.DoubleClick:
1205 input.DoubleClick += (EventHandler)handler;
1206 break;
1207
1208 case ControlEventType.Click:
1209 input.Click += (EventHandler)handler;
1210 break;
1211
1212 case ControlEventType.KeyDown:
1213 input.KeyDown += (KeyEventHandler)handler;
1214 break;
1215 // ignore unsupported
1216 }
1217 }
1218
1219 FormSettings.TableLayoutPanel.Controls.Add(input);
1220
1221 if (!string.IsNullOrWhiteSpace(gPALInput.ToolTipText))
1222 ApplyToolTip(gPALInput, input);
1223
1224 return this;
1225 }
1244 private void FitLabelHeight(Label label)
1245 {
1246 if (0 < label.Width)
1247 {
1248 Size measured = TextRenderer.MeasureText(label.Text, label.Font, new Size(label.Width, int.MaxValue),
1249 TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);
1250
1251 int wanted = 18 > measured.Height ? 18 : measured.Height;
1252
1253 if (label.Height != wanted)
1254 label.Height = wanted;
1255 }
1256 }
1257
1258 private IAllowFormSettingsAndActions AddLabel(GPALLabel gPALLabel)
1259 {
1260 FormSettings.myForm.ControlList.Add(gPALLabel);
1261 Label label = new Label
1262 {
1263 Name = gPALLabel.Name,
1264 Text = gPALLabel.Text,
1265 Font = gPALLabel.FontType,
1266 TextAlign = gPALLabel.TextAlignment,
1267 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
1268 };
1269 label.Font = new Font(label.Font.FontFamily, 10f, FontStyle.Bold);
1270
1271 label.AutoSize = false;
1272 label.Height = 18 < gPALLabel.Height ? gPALLabel.Height : 18;
1273
1274 // a Label wraps its text and then clips every line below the first, which is how the end of a long
1275 // caption goes missing. no height was asked for, so the label takes the height its text needs at
1276 // whatever width the layout gives it, and takes it again each time that width changes
1277 if (18 >= gPALLabel.Height)
1278 label.SizeChanged += (sender, args) => FitLabelHeight(label);
1279
1280 gPALLabel.WindowsControl = label;
1281
1282 foreach (var (evt, handler) in gPALLabel.Callbacks)
1283 {
1284 if (handler == null) continue; // skip invalid
1285
1286 switch (evt)
1287 {
1288 case ControlEventType.DoubleClick:
1289 label.DoubleClick += (EventHandler)handler;
1290 break;
1291
1292 case ControlEventType.Default:
1293 case ControlEventType.Click:
1294 label.Click += (EventHandler)handler;
1295 break;
1296 // ignore unsupported
1297 }
1298 }
1299
1300 FormSettings.TableLayoutPanel.Controls.Add(label);
1301
1302 if (!string.IsNullOrWhiteSpace(gPALLabel.ToolTipText))
1303 ApplyToolTip(gPALLabel, label);
1304
1305 return this;
1306 }
1312 private IAllowFormSettingsAndActions AddListView(GPALListView gPALListView)
1313 {
1314 FormSettings.myForm.ControlList.Add(gPALListView);
1315
1316 ListView listView = new ListView
1317 {
1318 Name = gPALListView.Name,
1319 View = gPALListView.ViewMode,
1320 FullRowSelect = gPALListView.FullRowSelect,
1321 MultiSelect = gPALListView.MultiSelect,
1322 CheckBoxes = gPALListView.checkBoxes,
1323 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right,
1324 Enabled = gPALListView.IsEnabled,
1325 Height = 10 < gPALListView.Height ? gPALListView.Height: 150,
1326 };
1327
1328 // Add columns
1329 foreach (string header in gPALListView.Columns)
1330 {
1331 listView.Columns.Add(header, -2, HorizontalAlignment.Left); // auto-size
1332 }
1333
1334 // a greyed row is not a selectable one. ListView has no cancellable selection event, so the
1335 // selection is taken back off as soon as it lands
1336 listView.ItemSelectionChanged += (sender, args) =>
1337 {
1338 if (true == args.IsSelected && SystemColors.GrayText == args.Item.ForeColor)
1339 args.Item.Selected = false;
1340 };
1341
1342 gPALListView.WindowsControl = listView;
1343
1344 foreach (var (evt, handler) in gPALListView.Callbacks)
1345 {
1346 if (handler == null) continue; // skip invalid
1347
1348 switch (evt)
1349 {
1350 case ControlEventType.Default:
1351 case ControlEventType.SelectedIndexChanged:
1352 case ControlEventType.Change:
1353 listView.SelectedIndexChanged += (EventHandler)handler;
1354 break;
1355
1356 case ControlEventType.DoubleClick:
1357 listView.DoubleClick += (EventHandler)handler;
1358 break;
1359
1360 case ControlEventType.Click:
1361 listView.Click += (EventHandler)handler;
1362 break;
1363
1364 // checking a row is not selecting one, and a list with checkboxes is usually asking
1365 // about the checks rather than the highlight
1366 case ControlEventType.ItemChecked:
1367 listView.ItemChecked += (ItemCheckedEventHandler)handler;
1368 break;
1369 // ignore unsupported
1370 }
1371 }
1372
1373 FormSettings.TableLayoutPanel.Controls.Add(listView);
1374
1375 if (gPALListView.Sortable)
1376 {
1377 var comparer = new ListViewItemComparer();
1378 listView.ListViewItemSorter = comparer;
1379 listView.ColumnClick += (s, e) =>
1380 {
1381 if (comparer.Column == e.Column)
1382 comparer.Order = comparer.Order == SortOrder.Ascending
1383 ? SortOrder.Descending : SortOrder.Ascending;
1384 else
1385 {
1386 comparer.Column = e.Column;
1387 comparer.Order = SortOrder.Ascending;
1388 }
1389 listView.Sort();
1390 };
1391 }
1392
1393 if (gPALListView.ColumnWeights.Count > 0)
1394 {
1395 var weights = gPALListView.ColumnWeights;
1396 double total = 0;
1397 foreach (var w in weights) total += w;
1398 if (total <= 0) total = 1;
1399
1400 void applyWeights(object s, EventArgs e)
1401 {
1402 int available = listView.ClientSize.Width - SystemInformation.VerticalScrollBarWidth;
1403 if (available <= 0) return;
1404 for (int i = 0; i < weights.Count && i < listView.Columns.Count; i++)
1405 listView.Columns[i].Width = (int)(available * weights[i] / total);
1406 }
1407
1408 listView.Resize += applyWeights;
1409 listView.HandleCreated += applyWeights;
1410 }
1411
1412 if (!string.IsNullOrWhiteSpace(gPALListView.ToolTipText))
1413 ApplyToolTip(gPALListView, listView);
1414
1415 return this;
1416 }
1422 private IAllowFormSettingsAndActions AddNumericUpDown(GPALNumericUpDown gPALNumericUpDown)
1423 {
1424 FormSettings.myForm.ControlList.Add(gPALNumericUpDown);
1425
1426 NumericUpDown nud = new NumericUpDown
1427 {
1428 Name = gPALNumericUpDown.Name,
1429 Value = gPALNumericUpDown.Value,
1430 Minimum = gPALNumericUpDown.Minimum,
1431 Maximum = gPALNumericUpDown.Maximum,
1432 Increment = gPALNumericUpDown.Increment,
1433 DecimalPlaces = gPALNumericUpDown.DecimalPlaces,
1434 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
1435 Enabled = gPALNumericUpDown.IsEnabled,
1436 Height = 10 < gPALNumericUpDown.Height ? gPALNumericUpDown.Height : 20,
1437 };
1438
1439 gPALNumericUpDown.WindowsControl = nud;
1440
1441 foreach (var (evt, handler) in gPALNumericUpDown.Callbacks)
1442 {
1443 if (handler == null) continue; // skip invalid
1444
1445 switch (evt)
1446 {
1447 case ControlEventType.Default:
1448 case ControlEventType.ValueChanged:
1449 case ControlEventType.Change:
1450 nud.ValueChanged += (EventHandler)handler;
1451 break;
1452
1453 case ControlEventType.Click:
1454 nud.Click += (EventHandler)handler;
1455 break;
1456 // ignore unsupported
1457 }
1458 }
1459
1460 FormSettings.TableLayoutPanel.Controls.Add(nud);
1461
1462 if (!string.IsNullOrWhiteSpace(gPALNumericUpDown.ToolTipText))
1463 ApplyToolTip(gPALNumericUpDown, nud);
1464
1465 return this;
1466 }
1472 private IAllowFormSettingsAndActions AddProgressBar(GPALProgressBar gPALProgressBar)
1473 {
1474 FormSettings.myForm.ControlList.Add(gPALProgressBar);
1475
1476 ProgressBar progressBar = new ProgressBar
1477 {
1478 Name = gPALProgressBar.Name,
1479 Minimum = gPALProgressBar.Minimum,
1480 Maximum = gPALProgressBar.Maximum,
1481 Value = gPALProgressBar.Value,
1482 Style = gPALProgressBar.Style,
1483 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
1484 Enabled = gPALProgressBar.IsEnabled,
1485 Height = 10 < gPALProgressBar.Height ? gPALProgressBar.Height : 20,
1486 };
1487
1488 gPALProgressBar.WindowsControl = progressBar;
1489
1490 foreach (var (evt, handler) in gPALProgressBar.Callbacks)
1491 {
1492 if (handler == null) continue; // skip invalid
1493
1494 switch (evt)
1495 {
1496 case ControlEventType.Click:
1497 progressBar.Click += (EventHandler)handler;
1498 break;
1499
1500 // ignore unsupported
1501 }
1502 }
1503
1504 FormSettings.TableLayoutPanel.Controls.Add(progressBar);
1505
1506 if (!string.IsNullOrWhiteSpace(gPALProgressBar.ToolTipText))
1507 ApplyToolTip(gPALProgressBar, progressBar);
1508
1509 return this;
1510 }
1511
1517 private IAllowFormSettingsAndActions AddRadioButton(GPALRadioButton gPALRadioButton)
1518 {
1519 FormSettings.myForm.ControlList.Add(gPALRadioButton);
1520 RadioButton radioButton = new RadioButton
1521 {
1522 Name = gPALRadioButton.Name,
1523 Text = gPALRadioButton.Text,
1524 AutoSize = true,
1525 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
1526 Checked = gPALRadioButton.Checked,
1527 Enabled = gPALRadioButton.Enabled,
1528 Height = 10 < gPALRadioButton.Height ? gPALRadioButton.Height : 20,
1529 };
1530 gPALRadioButton.WindowsControl = radioButton;
1531 radioButton.CheckedChanged += new EventHandler(delegate (Object sender, EventArgs a) { gPALRadioButton.Checked = ((RadioButton)sender).Checked; }); // copy changed text up to control
1532
1533 foreach (var (evt, handler) in gPALRadioButton.Callbacks)
1534 {
1535 if (handler == null) continue; // skip invalid
1536
1537 switch (evt)
1538 {
1539 case ControlEventType.Default:
1540 case ControlEventType.CheckedChanged:
1541 radioButton.CheckedChanged += (EventHandler)handler;
1542 break;
1543
1544 case ControlEventType.Click:
1545 radioButton.Click += (EventHandler)handler;
1546 break;
1547 // ignore unsupported
1548 }
1549 }
1550
1551 FormSettings.TableLayoutPanel.Controls.Add(radioButton);
1552
1553 if (!string.IsNullOrWhiteSpace(gPALRadioButton.ToolTipText))
1554 ApplyToolTip(gPALRadioButton, radioButton);
1555
1556 return this;
1557 }
1563 private IAllowFormSettingsAndActions AddStatusStrip(GPALStatusStrip gPALStatusStrip)
1564 {
1565 FormSettings.myForm.ControlList.Add(gPALStatusStrip);
1566
1567 StatusStrip statusStrip = new StatusStrip
1568 {
1569 Name = gPALStatusStrip.Name,
1570 Dock = DockStyle.Bottom,
1571 SizingGrip = false,
1572 Enabled = gPALStatusStrip.IsEnabled,
1573 Height = 10 < gPALStatusStrip.Height ? gPALStatusStrip.Height : 20,
1574 };
1575
1576 ToolStripStatusLabel statusLabel = new ToolStripStatusLabel
1577 {
1578 Text = gPALStatusStrip.Text,
1579 Spring = true,
1580 TextAlign = ContentAlignment.MiddleLeft,
1581 Height = 10 < gPALStatusStrip.Height ? gPALStatusStrip.Height : 20,
1582 };
1583
1584 statusStrip.Items.Add(statusLabel);
1585
1586 gPALStatusStrip.WindowsControl = statusStrip;
1587
1588 foreach (var (evt, handler) in gPALStatusStrip.Callbacks)
1589 {
1590 if (handler == null) continue; // skip invalid
1591
1592 switch (evt)
1593 {
1594 case ControlEventType.Click:
1595 case ControlEventType.Default:
1596 statusStrip.ItemClicked += (sender, e) => ((EventHandler)handler)(sender, e);
1597 break;
1598
1599 // ignore unsupported
1600 }
1601 }
1602
1603 // StatusStrip is added directly to the form (not TableLayoutPanel)
1604 FormSettings.myForm.Controls.Add(statusStrip);
1605
1606 if (!string.IsNullOrWhiteSpace(gPALStatusStrip.ToolTipText))
1607 ApplyToolTip(gPALStatusStrip, statusStrip);
1608
1609 return this;
1610 }
1616 private IAllowFormSettingsAndActions AddMenuBar(GPALMenuBar gPALMenuBar)
1617 {
1618 FormSettings.myForm.ControlList.Add(gPALMenuBar);
1619
1620 MenuStrip menuStrip = new MenuStrip
1621 {
1622 Name = gPALMenuBar.Name,
1623 Enabled = gPALMenuBar.IsEnabled,
1624 };
1625
1626 var topMenus = new System.Collections.Generic.Dictionary<string, ToolStripMenuItem>();
1627 foreach (var (topMenu, item, isSeparator) in gPALMenuBar.menuEntries)
1628 {
1629 string menuKey = string.IsNullOrWhiteSpace(topMenu) ? "" : topMenu;
1630 if (!topMenus.TryGetValue(menuKey, out ToolStripMenuItem topItem))
1631 {
1632 topItem = new ToolStripMenuItem(menuKey);
1633 topMenus[menuKey] = topItem;
1634 menuStrip.Items.Add(topItem);
1635 }
1636
1637 if (isSeparator)
1638 {
1639 topItem.DropDownItems.Add(new ToolStripSeparator());
1640 continue;
1641 }
1642
1643 ToolStripMenuItem menuItem = new ToolStripMenuItem(item.Text);
1644 if (item.Image != null)
1645 menuItem.Image = item.Image;
1646 if (item.ShortCutKey != System.Windows.Forms.Keys.None)
1647 {
1648 menuItem.ShortcutKeys = item.ShortCutKey;
1649 menuItem.ShowShortcutKeys = true;
1650 }
1651 foreach (var (evt, handler) in item.Callbacks)
1652 if (handler is EventHandler clickHandler)
1653 menuItem.Click += clickHandler;
1654 if (!string.IsNullOrWhiteSpace(item.ToolTipText))
1655 menuItem.ToolTipText = item.ToolTipText;
1656
1657 // what WithEnabled said before the form existed, applied now that the item does
1658 menuItem.Enabled = item.IsEnabled;
1659 item.WindowsControl = menuItem;
1660 topItem.DropDownItems.Add(menuItem);
1661 }
1662
1663 gPALMenuBar.WindowsControl = menuStrip;
1664
1665 // MenuStrip docks to the top of the form (not the TableLayoutPanel)
1666 FormSettings.myForm.Controls.Add(menuStrip);
1667 FormSettings.myForm.MainMenuStrip = menuStrip;
1668
1669 // A top-docked MenuStrip must sit above any top-docked toolbar. Docked siblings fill from the
1670 // edge in reverse child-index order, so pushing the menu to the highest index keeps it outermost.
1671 FormSettings.myForm.Controls.SetChildIndex(menuStrip, FormSettings.myForm.Controls.Count - 1);
1672
1673 return this;
1674 }
1681 private IAllowFormSettingsAndActions AddToolbar(GPALToolbar gPALToolbar)
1682 {
1683 FormSettings.myForm.ControlList.Add(gPALToolbar);
1684
1685 ToolStrip toolStrip = new ToolStrip
1686 {
1687 Name = gPALToolbar.Name,
1688 Enabled = gPALToolbar.IsEnabled,
1689 };
1690
1691 foreach (var (item, isSeparator) in gPALToolbar.toolbarEntries)
1692 {
1693 if (isSeparator)
1694 {
1695 toolStrip.Items.Add(new ToolStripSeparator());
1696 continue;
1697 }
1698
1699 ToolStripButton button = new ToolStripButton(item.Text);
1700 if (item.Image != null)
1701 {
1702 button.Image = item.Image;
1703 button.DisplayStyle = string.IsNullOrWhiteSpace(item.Text)
1704 ? ToolStripItemDisplayStyle.Image
1705 : ToolStripItemDisplayStyle.ImageAndText;
1706 }
1707 else
1708 button.DisplayStyle = ToolStripItemDisplayStyle.Text;
1709
1710 foreach (var (evt, handler) in item.Callbacks)
1711 if (handler is EventHandler clickHandler)
1712 button.Click += clickHandler;
1713 if (!string.IsNullOrWhiteSpace(item.ToolTipText))
1714 button.ToolTipText = item.ToolTipText;
1715
1716 button.Enabled = item.IsEnabled;
1717 item.WindowsControl = button;
1718 toolStrip.Items.Add(button);
1719 }
1720
1721 gPALToolbar.WindowsControl = toolStrip;
1722
1723 // ToolStrip docks to the top of the form (not the TableLayoutPanel), below any MenuStrip.
1724 FormSettings.myForm.Controls.Add(toolStrip);
1725 if (FormSettings.myForm.MainMenuStrip != null)
1726 FormSettings.myForm.Controls.SetChildIndex(FormSettings.myForm.MainMenuStrip, FormSettings.myForm.Controls.Count - 1);
1727
1728 return this;
1729 }
1735 private IAllowFormSettingsAndActions AddTab(GPALTab gPALTab)
1736 {
1737 if (null == FormSettings.TabControl)
1738 {
1739 FormSettings.TabControl = new TabControl
1740 {
1741 Name = "tabControl",
1742 AutoSize = false,
1743 //Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom,
1744 Dock = DockStyle.Fill,
1745 Multiline = true,
1746
1747 };
1748
1749 // a disabled TabPage greys its contents and is still selectable, so the tab stays reachable
1750 // and lands on a page nothing can be done with. refusing the switch is what disables a tab
1751 FormSettings.TabControl.Selecting += (sender, args) =>
1752 {
1753 // nothing selected yet means the pages are still being added, and refusing that would
1754 // leave the control showing no tab at all
1755 if (null == FormSettings.TabControl.SelectedTab) return;
1756
1757 if (null != args.TabPage && false == args.TabPage.Enabled) args.Cancel = true;
1758 };
1759
1760 FormSettings.TableLayoutPanel.Controls.Add(FormSettings.TabControl);
1761 }
1762
1763 TabPage tabPage = new TabPage
1764 {
1765 Name = gPALTab.Name,
1766 Text = gPALTab.Text,
1767 AutoSize = false,
1768 //Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom,
1769 Dock = DockStyle.Fill,
1770 Enabled = gPALTab.Enabled
1771 };
1772
1773 TableLayoutPanel tmpTableLayoutPanel = new TableLayoutPanel
1774 {
1775 //Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right,
1776 Dock = DockStyle.Fill,
1777 AutoSize = false,
1778
1779 // the same as the form's own panel. without it a tab holding more than fits simply hides the
1780 // rest, with no scrollbar to say so, and the controls at the bottom may as well not be there
1781 AutoScroll = true
1782 };
1783
1784 gPALTab.WindowsControl = tabPage;
1785
1786 FormSettings.myForm.ControlList.Add(gPALTab);
1787
1788 foreach (var (evt, handler) in gPALTab.Callbacks)
1789 {
1790 if (handler == null) continue; // skip invalid
1791
1792 switch (evt)
1793 {
1794 case ControlEventType.Scroll:
1795 tabPage.Scroll += (ScrollEventHandler)handler;
1796 break;
1797
1798 case ControlEventType.Click:
1799 // the page's own click, which is the blank area around the controls on it. rarely what
1800 // anyone means by clicking a tab, which is why it has to be asked for by name
1801 tabPage.Click += (EventHandler)handler;
1802 break;
1803
1804 case ControlEventType.Default:
1805 case ControlEventType.SelectedIndexChanged:
1806 case ControlEventType.Change:
1807 // being the tab on show is what a tab is for, so it is the one you get without saying.
1808 // a TabPage has no event for it: the control that owns the pages has one, and it names
1809 // the page it moved to, so each tab hears only about itself
1810 FormSettings.TabControl.Selected += (sender, args) =>
1811 {
1812 if (args.TabPage == tabPage)
1813 ((EventHandler)handler)(gPALTab, args);
1814 };
1815 break;
1816 // ignore unsupported
1817 }
1818 }
1819
1820 tabPage.Controls.Add(tmpTableLayoutPanel);
1821 FormSettings.TabControl.Controls.Add(tabPage);
1822 FormSettings.TableLayoutPanel = tmpTableLayoutPanel;
1823
1824 if (!string.IsNullOrWhiteSpace(gPALTab.ToolTipText))
1825 ApplyToolTip(gPALTab, tabPage);
1826
1827 return this;
1828 }
1836 private IAllowFormSettingsAndActions AddSplitter(GPALSplitter gPALSplitter)
1837 {
1838 FormSettings.myForm.ControlList.Add(gPALSplitter);
1839
1840 SplitContainer sc = new SplitContainer
1841 {
1842 // a vertical bar gives left and right panes, a horizontal one top and bottom
1843 Orientation = true == gPALSplitter.IsHorizontalSplit ? Orientation.Vertical : Orientation.Horizontal,
1844 // full width, and only as tall as its contents need. Docking it to fill would leave it with
1845 // whatever height its row happened to give it and then it clips what is inside. The height is
1846 // measured once the panes are filled, below, unless .WithHeight already said what it should be
1847 Dock = DockStyle.Top,
1848 Height = 10 < gPALSplitter.Height ? gPALSplitter.Height : 0,
1849 AutoScaleMode = AutoScaleMode.Inherit
1850 };
1851
1852 if (true == gPALSplitter.IsPercentage)
1853 {
1854 double pct = gPALSplitter.Percentage;
1855
1856 // a proportion has to be reapplied as the form resizes, where a pixel distance stays put
1857 sc.SizeChanged += (s, e) =>
1858 {
1859 int along = true == gPALSplitter.IsHorizontalSplit ? sc.Width : sc.Height;
1860
1861 if (along > sc.Panel1MinSize + sc.Panel2MinSize + sc.SplitterWidth)
1862 sc.SplitterDistance = (int)(along * pct);
1863 };
1864 }
1865 else
1866 sc.SplitterDistance = gPALSplitter.Dimension;
1867
1868 gPALSplitter.WindowsControl = sc;
1869
1870 TableLayoutPanel firstPane = MakeSplitPaneTlp();
1871 TableLayoutPanel secondPane = MakeSplitPaneTlp();
1872
1873 sc.Panel1.Controls.Add(firstPane);
1874 sc.Panel2.Controls.Add(secondPane);
1875 FormSettings.TableLayoutPanel.Controls.Add(sc);
1876
1877 FillContainer(gPALSplitter.FormControls, firstPane, secondPane, gPALSplitter.Name);
1878
1879 // a SplitContainer cannot size itself to what is inside it, so it is measured here instead of being
1880 // given a number someone guessed. Side by side panes are as tall as the taller one; stacked panes are
1881 // as tall as both plus the bar. .WithHeight overrides all of it
1882 if (10 >= gPALSplitter.Height)
1883 {
1884 // asking a pane for its PreferredSize hands back the height it already has, because it is docked to
1885 // fill the pane it is in, so it can only ever agree with the splitter it was measured from. what is
1886 // actually wanted is the height of the controls in it, which is what this adds up
1887 Func<TableLayoutPanel, int> contentHeight = pane =>
1888 {
1889 int total = pane.Padding.Top + pane.Padding.Bottom;
1890
1891 foreach (Control child in pane.Controls)
1892 total += child.Height + child.Margin.Top + child.Margin.Bottom;
1893
1894 return total;
1895 };
1896
1897 sc.Height = true == gPALSplitter.IsHorizontalSplit
1898 ? Math.Max(contentHeight(firstPane), contentHeight(secondPane))
1899 : contentHeight(firstPane) + contentHeight(secondPane) + sc.SplitterWidth;
1900 }
1901
1902 if (!string.IsNullOrWhiteSpace(gPALSplitter.ToolTipText))
1903 ApplyToolTip(gPALSplitter, sc);
1904
1905 return this;
1906 }
1907
1912 private IAllowFormSettingsAndActions AddGroupBox(GPALGroupBox gPALGroupBox)
1913 {
1914 FormSettings.myForm.ControlList.Add(gPALGroupBox);
1915
1916 GroupBox gb = new GroupBox
1917 {
1918 Name = gPALGroupBox.Name,
1919 Text = gPALGroupBox.Text,
1920 Enabled = gPALGroupBox.IsEnabled,
1921 // full width and only as tall as what is inside. Docking it to fill hands its size to the parent,
1922 // which is what stops a box growing to show its own controls.
1923 // NOTE: DockStyle is not a flags enum, so Top | Bottom is Left rather than both
1924 Dock = DockStyle.Top,
1925 AutoSize = true,
1926 AutoSizeMode = AutoSizeMode.GrowAndShrink,
1927 };
1928
1929 // the panel inside has to measure its contents too, or the box is asked to grow around something that
1930 // never reports a size. A splitter pane is the opposite case and fills, so this is not MakeSplitPaneTlp
1931 TableLayoutPanel inside = new TableLayoutPanel
1932 {
1933 Dock = DockStyle.Top,
1934 //AutoSize = true,
1935 AutoSizeMode = AutoSizeMode.GrowAndShrink,
1936 ColumnCount = 1,
1937 };
1938
1939 inside.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
1940 gb.Controls.Add(inside);
1941 FormSettings.TableLayoutPanel.Controls.Add(gb);
1942
1943 gPALGroupBox.WindowsControl = gb;
1944
1945 FillContainer(gPALGroupBox.FormControls, inside, null, gPALGroupBox.Name);
1946
1947 if (!string.IsNullOrWhiteSpace(gPALGroupBox.ToolTipText))
1948 ApplyToolTip(gPALGroupBox, gb);
1949
1950 return this;
1951 }
1952
1964 private void FillContainer(List<object> formControls, TableLayoutPanel firstPane, TableLayoutPanel secondPane, string name)
1965 {
1966 TableLayoutPanel outerPanel = FormSettings.TableLayoutPanel;
1967 TabControl outerTabControl = FormSettings.TabControl;
1968 TableLayoutPanel current = firstPane;
1969 bool markerSeen = false;
1970
1971 FormSettings.TabControl = null;
1972
1973 foreach (object formControl in formControls)
1974 {
1975 if (formControl is GPALSpliter)
1976 {
1977 if (null == secondPane)
1978 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $"[{name}] has no second pane, so the pane marker does nothing. Everything added stays in the one panel.");
1979 else if (true == markerSeen)
1980 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $"[{name}] has more than one pane marker and a splitter has only two panes, so everything after the first marker stays in the second.");
1981 else
1982 {
1983 current = secondPane;
1984 markerSeen = true;
1985 }
1986
1987 continue;
1988 }
1989
1990 FormSettings.TableLayoutPanel = current;
1991 FormSettings.TabControl = null;
1992 WithFormControl((GPALControl)formControl);
1993 }
1994
1995 FormSettings.TableLayoutPanel = outerPanel;
1996 FormSettings.TabControl = outerTabControl;
1997 }
1998
1999 private TableLayoutPanel MakeSplitPaneTlp()
2000 {
2001 var tlp = new TableLayoutPanel
2002 {
2003 Dock = DockStyle.Fill,
2004 AutoSize = false,
2005 ColumnCount = 1,
2006 };
2007 tlp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
2008 return tlp;
2009 }
2010
2016 private IAllowFormSettingsAndActions AddTableLayoutPanel(GPALTableLayoutPanel gPALTableLayoutPanel)
2017 {
2018 FormSettings.myForm.ControlList.Add(gPALTableLayoutPanel);
2019 TableLayoutPanel tableLayoutPanel = new TableLayoutPanel
2020 {
2021 Name = gPALTableLayoutPanel.Name,
2022 GrowStyle = gPALTableLayoutPanel.GrowStyle,
2023 AutoScroll = false,
2024 ColumnCount = gPALTableLayoutPanel.ColumnCount,
2025 RowCount = gPALTableLayoutPanel.RowCount
2026 // todo : more settings
2027 };
2028
2029 if (!string.IsNullOrWhiteSpace(gPALTableLayoutPanel.ToolTipText))
2030 ApplyToolTip(gPALTableLayoutPanel, tableLayoutPanel);
2031
2032 return this;
2033 }
2034
2040 private IAllowFormSettingsAndActions AddTextArea(GPALTextArea gPALTextArea)
2041 {
2042 FormSettings.myForm.ControlList.Add(gPALTextArea);
2043 PlaceholderTextBox textBox = new PlaceholderTextBox
2044 {
2045 Name = gPALTextArea.Name,
2046 Text = gPALTextArea.Text,
2047 Multiline = true,
2048 WordWrap = true,
2049 ScrollBars = ScrollBars.Both,
2050 Height = 10 < gPALTextArea.Height ? gPALTextArea.Height : 150,
2051 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right,
2052 Enabled = gPALTextArea.Enabled
2053 };
2054 if (true == gPALTextArea.IsReadOnly)
2055 textBox.ReadOnly = true;
2056
2057 if (!string.IsNullOrEmpty(gPALTextArea.PlaceHolder))
2058 textBox.PlaceholderText = gPALTextArea.PlaceHolder;
2059
2060 gPALTextArea.WindowsControl = textBox;
2061 textBox.Text = gPALTextArea.Text; // somehow text is getting truncated to one char from above
2062
2063 textBox.TextChanged += new EventHandler(delegate (Object sender, EventArgs a) { gPALTextArea.Text = ((TextBox)sender).Text; }); // copy changed text up to control
2064
2065 foreach (var (evt, handler) in gPALTextArea.Callbacks)
2066 {
2067 if (handler == null) continue; // skip invalid
2068
2069 switch (evt)
2070 {
2071 case ControlEventType.Default:
2072 case ControlEventType.TextChanged:
2073 textBox.TextChanged += (EventHandler)handler;
2074 break;
2075
2076 case ControlEventType.Click:
2077 textBox.Click += (EventHandler)handler;
2078 break;
2079 // ignore unsupported
2080 }
2081 }
2082
2083 FormSettings.TableLayoutPanel.Controls.Add(textBox);
2084
2085 if (!string.IsNullOrWhiteSpace(gPALTextArea.ToolTipText))
2086 ApplyToolTip(gPALTextArea, textBox);
2087
2088 return this;
2089 }
2096 private IAllowFormSettingsAndActions AddRichTextBox(GPALRichTextBox gPALRichTextBox)
2097 {
2098 FormSettings.myForm.ControlList.Add(gPALRichTextBox);
2099 RichTextBox richTextBox = new RichTextBox
2100 {
2101 Name = gPALRichTextBox.Name,
2102 Multiline = true,
2103 WordWrap = true,
2104 ReadOnly = gPALRichTextBox.IsReadOnly,
2105 ScrollBars = RichTextBoxScrollBars.Both,
2106 Height = 10 < gPALRichTextBox.Height ? gPALRichTextBox.Height : 150,
2107 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right,
2108 Enabled = gPALRichTextBox.Enabled
2109 };
2110 gPALRichTextBox.WindowsControl = richTextBox;
2111 GPALRichTextBox.RenderMarkdown(richTextBox, gPALRichTextBox.Text);
2112
2113 FormSettings.TableLayoutPanel.Controls.Add(richTextBox);
2114
2115 if (!string.IsNullOrWhiteSpace(gPALRichTextBox.ToolTipText))
2116 ApplyToolTip(gPALRichTextBox, richTextBox);
2117
2118 return this;
2119 }
2125 private IAllowFormSettingsAndActions AddTreeView(GPALTreeView gPALTreeView)
2126 {
2127 FormSettings.myForm.ControlList.Add(gPALTreeView);
2128
2129 TreeView tv = new TreeView
2130 {
2131 Name = gPALTreeView.Name,
2132 CheckBoxes = gPALTreeView.CheckBoxes,
2133 ShowLines = gPALTreeView.ShowLines,
2134 ShowPlusMinus = gPALTreeView.ShowPlusMinus,
2135 Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right,
2136 Enabled = gPALTreeView.IsEnabled,
2137 Height = 10 < gPALTreeView.Height ? gPALTreeView.Height : 150,
2138 };
2139
2140 // BeforeSelect can refuse outright, so a greyed node never becomes the selected one
2141 tv.BeforeSelect += (sender, args) =>
2142 {
2143 if (null != args.Node && SystemColors.GrayText == args.Node.ForeColor) args.Cancel = true;
2144 };
2145
2146 gPALTreeView.WindowsControl = tv;
2147
2148 foreach (var (evt, handler) in gPALTreeView.Callbacks)
2149 {
2150 if (handler == null) continue; // skip invalid
2151
2152 switch (evt)
2153 {
2154 case ControlEventType.Default:
2155 case ControlEventType.AfterSelect:
2156 case ControlEventType.Change:
2157 tv.AfterSelect += (TreeViewEventHandler)handler;
2158 break;
2159
2160 case ControlEventType.DoubleClick:
2161 tv.DoubleClick += (EventHandler)handler;
2162 break;
2163
2164 case ControlEventType.Click:
2165 tv.Click += (EventHandler)handler;
2166 break;
2167 // ignore unsupported
2168 }
2169 }
2170
2171 FormSettings.TableLayoutPanel.Controls.Add(tv);
2172
2173 if (!string.IsNullOrWhiteSpace(gPALTreeView.ToolTipText))
2174 ApplyToolTip(gPALTreeView, tv);
2175
2176 return this;
2177 }
2178 #endregion <private>
2179 }
2180
2181 internal class ListViewItemComparer : System.Collections.IComparer
2182 {
2183 public int Column { get; set; } = 0;
2184 public SortOrder Order { get; set; } = SortOrder.Ascending;
2185
2186 public int Compare(object x, object y)
2187 {
2188 var lx = (ListViewItem)x;
2189 var ly = (ListViewItem)y;
2190
2191 string sx = Column == 0 ? lx.Text
2192 : Column < lx.SubItems.Count ? lx.SubItems[Column].Text : string.Empty;
2193 string sy = Column == 0 ? ly.Text
2194 : Column < ly.SubItems.Count ? ly.SubItems[Column].Text : string.Empty;
2195
2196 int result;
2197
2198 // Numeric (strip currency/percent symbols and commas)
2199 string nx = sx.Replace(",", "").Replace("$", "").Replace("%", "").Trim();
2200 string ny = sy.Replace(",", "").Replace("$", "").Replace("%", "").Trim();
2201 if (double.TryParse(nx, out double dx) && double.TryParse(ny, out double dy))
2202 result = dx.CompareTo(dy);
2203 // Date
2204 else if (System.DateTime.TryParse(sx, out System.DateTime dtx) &&
2205 System.DateTime.TryParse(sy, out System.DateTime dty))
2206 result = dtx.CompareTo(dty);
2207 // String
2208 else
2209 result = string.Compare(sx, sy, StringComparison.OrdinalIgnoreCase);
2210
2211 return Order == SortOrder.Ascending ? result : -result;
2212 }
2213 }
2214}
2215
File-side plumbing behind the fluent chain: writing a unit of work's data out in a delimited format,...
Definition FileHelper.cs:55
Class to define database usage. Currently only used for input from a table, sql or stored procedure....
GPAL File object instantied with GPAL.File Used to load tokens into a GPALGrid [rows/columns].
Definition GPALFile.cs:36
A GPAL button instantiated with GPAL.Button for use on GPAL forms. Callback EventHandler is invoked ...
bool Enabled
Runtime access to Enable/Disable the button.
A GPAL chart instantiated with GPAL.Chart for use on GPAL forms. Callback EventHandler is invoked on...
A GPAL checkbox instantiateds with GPAL,Checkbox for use on GPAL forms. Callback EventHandler is inv...
bool Enabled
Runtime access to Enable/Disable the checkbox.
bool Checked
Runtime access to un/check the checkbox. Setting this writes through to the live control (if realized...
A dropdown list (ComboBox) for selecting one option from a predefined set of choices....
Base class for all GPAL form controls.
int Height
The height of the control, in pixels. 0 [default] lets the control size itself.
ContentAlignment TextAlignment
The alignment currently in use.
string Text
For text based controls, get or set the text in the control. Setting this writes through to the live...
string Name
Control name, only used for Information and Exception channel messages. NOTE: Use ....
Font FontType
The font currently being used.
FormControlType ControlType
The control type of WindowsControl. Set when the control is instantiated with GPAL.
dynamic WindowsControl
Underlying Windows form control, Button, Checkbox, Textbox, etc. NOTE: Use GPAL to instantiate form ...
A grid control instantiated with GPAL.DataGridView for use on GPAL forms, displaying the contents of ...
IReadOnlyList< string > ColumnNames
The column header names set via WithColumns(string[]).
bool AutoGenerateColumns
Whether columns are auto-generated when no column names were provided, as set via WithAutoGenerateCol...
A date/time picker control instantiated with GPAL.DateTimePicker for use on GPAL forms.
string CustomFormat
The custom display format string, as set via WithCustomFormat(string).
DateTime Value
Runtime access to the date/time. Reads the live control once the form is up, so the date the user pic...
DateTimePickerFormat Format
The display format, as set via WithFormat(DateTimePickerFormat).
A file/folder selector control instantiated with GPAL.FileSelector for use on GPAL forms....
Form object that contains the fluent methods to create interactive forms. Instatiated using GPAL....
Definition GPALForm.cs:64
int Left
The current left coordinate of the form, in pixels.
Definition GPALForm.cs:246
IAllowFormSettingsAndActions FillInFrom(GPALDatabase inputDatabase)
Fill in (overwrite) the forms inputs using tokens derived from the database. Used after ....
Definition GPALForm.cs:336
string FormName
The name of your form Currently not used, but will be used for Information and Exceptional channel me...
Definition GPALForm.cs:125
IAllowFormSettingsAndActions AppendFrom(GPALFile inputFile)
Append to the end of the forms inputs using tokens derived from the file. Used after ....
Definition GPALForm.cs:396
IAllowFormSettingsAndActions WithTitle(string title)
The title to display at the top of your form.
Definition GPALForm.cs:323
IAllowFormSettingsAndActions CallAfterFillIn(GPALForm.CallAfterFillInDelegate callAfterFillIn)
Add a handler to call after a row of tokens is consumed and after all inputs are filled in with data....
Definition GPALForm.cs:266
delegate CallIfStatus CallAfterFillInDelegate(GPALForm myForm, IGPALGrid< string > tokens, int tokenIdx)
Delegate callback for the CallAfterFillIn EventHandler which will be invoked after each row of tokens...
int Height
The current height of the form, in pixels.
Definition GPALForm.cs:258
IAllowFormSettingsAndActions WithWidth(int width)
Define the initial width of your GPAL form.
Definition GPALForm.cs:189
IAllowFormActionsAndFillIn Show()
Show the GPAL form so it may be automated by tokens. Use ShowDialog when the form workflow ends....
Definition GPALForm.cs:515
IAllowFormSettingsAndActions InsertFrom(IGPALGrid< string > inputGrid)
Insert at the beginning of the forms inputs using tokens derived from the GPALGrid....
Definition GPALForm.cs:456
dynamic ToGPALObject()
Return a GPALForm so that GPAL.Form does not have to be cast.
Definition GPALForm.cs:116
int Width
The current width of the form, in pixels.
Definition GPALForm.cs:254
IAllowFormSettingsAndActions WithHeight(int height)
Define the initial height of your GPAL form.
Definition GPALForm.cs:207
IAllowFormSettingsAndActions InsertFrom(GPALDatabase inputDatabase)
Insert at the beginning of the forms inputs using tokens derived from the database....
Definition GPALForm.cs:364
IAllowFormActions ShowDialog()
Show the GPAL form as a modal. Use LAST in a form workflow. NOTE: This blocks workflows and FillIn m...
Definition GPALForm.cs:484
IAllowFormSettingsAndActions FillInFrom(IGPALGrid< string > inputGrid)
Fill in (overwrite) the forms inputs using tokens derived from the GPALGrid. Used after ....
Definition GPALForm.cs:432
IAllowFormSettingsAndActions AppendFrom(GPALDatabase inputDatabase)
Append to the end of the forms inputs using tokens derived from the database. Used after ....
Definition GPALForm.cs:350
IAllowFormSettingsAndActions WithLeft(int left)
Define the initial coordinate of the left side of your GPAL form.
Definition GPALForm.cs:237
IAllowFormSettingsAndActions FillInFrom(GPALFile inputFile)
Fill in (overwrite) the forms inputs using tokens derived from the file. Used after ....
Definition GPALForm.cs:378
IAllowFormActions Hide()
Hide the GPAL form. This will essentially terminate the program if .ShowDialog() is the last method i...
Definition GPALForm.cs:556
IAllowFormSettingsAndActions WithFormControl(GPALControl gPALControl)
Add a form control (GPALButton, GPALChart, etc.) to a GPALForm. Controls are displayed on one line i...
Definition GPALForm.cs:287
IAllowFormSettingsAndActions InsertFrom(GPALFile inputFile)
Insert at the beginning of the forms inputs using tokens derived from the file. Used after ....
Definition GPALForm.cs:414
IAllowFormSettingsAndActions AppendFrom(IGPALGrid< string > inputGrid)
Append to the end of the forms inputs using tokens derived from the GPALGrid. Used after ....
Definition GPALForm.cs:444
IAllowFormSettingsAndActions WithTop(int top)
Define the initial coordinate of the top side of your GPAL form.
Definition GPALForm.cs:226
int Top
The current top coordinate of the form, in pixels.
Definition GPALForm.cs:250
A titled box around a set of controls, instantiated with GPAL.GroupBox and laid out like any other co...
A GPAL single line input instantiated with GPAL.Input for use on GPAL forms. Callback EventHandler i...
bool Enabled
Runtime access to Enable/Disable the input.
A GPAL lable instantiated with GPAL.Label for use on GPAL forms. Callback EventHandler is invoked on...
A list view control capable of displaying data in Details, List, LargeIcon, SmallIcon,...
bool Sortable
Whether column-click sorting is enabled.
IReadOnlyList< string > Columns
The column header names set via WithColumns(string[]).
IReadOnlyList< double > ColumnWeights
Proportional column weights set via WithColumnWeight(double).
View ViewMode
The view mode set via WithView(ListViewMode), mapped to the underlying WinForms View.
bool MultiSelect
Whether multi-select is enabled, as set via WithMultiSelect(bool).
bool FullRowSelect
Whether full-row selection is enabled, as set via WithFullRowSelect(bool).
A menu bar docked to the top of a GPAL form. Open a top-level menu with WithMenu(string),...
A numeric up/down spinner control instantiated with GPAL.NumericUpDown for use on GPAL forms.
decimal Minimum
The minimum allowed value, as set via WithMinimum(decimal).
int DecimalPlaces
The number of decimal places displayed, as set via WithDecimalPlaces(int).
decimal Maximum
The maximum allowed value, as set via WithMaximum(decimal).
decimal Increment
The spinner increment, as set via WithIncrement(decimal).
decimal Value
Runtime access to the value. Reads the live control once the form is up, so a value the user spun to ...
A progress indicator control that shows completion percentage or indeterminate activity....
int Maximum
The maximum progress value, as set via WithMaximum(int).
ProgressBarStyle Style
The current visual style. Setting this writes through to the live control (if realized),...
int Value
The current progress value, clamped to Minimum/Maximum. Setting this writes through to the live contr...
int Minimum
The minimum progress value, as set via WithMinimum(int).
A GPAL radiobutton instantiated with GPAL.RadioButton for use GPAL forms. Callback EventHandler is i...
bool Enabled
Runtime access to Enable/Disable the radiobutton.
bool Checked
Runtime access to un/check the radiobutton. Setting this writes through to the live control (if reali...
A read-only display control for rendering simple Markdown - headings (#, ##, ###),...
new string Text
The Markdown source text. Setting this re-renders headings, bold spans, and bullet/numbered lists (wi...
Two panes with a bar between them, instantiated with GPAL.Splitter and laid out like any other contro...
A status bar (usually docked at the bottom of the form) that displays real-time messages,...
new string Text
The status text. Blank or whitespace falls back to "Ready". Setting this writes through to the first ...
A GPAL tab instantiated with GPAL.Tab for use on GPAL forms. Callback EventHandler is invoked on the...
A table layout panel instantiated with GPAL.TableLayoutPanel for use on GPAL forms,...
TableLayoutPanelGrowStyle GrowStyle
The direction in which the panel automatically adds new rows or columns when it runs out of space,...
int ColumnCount
The number of columns in the panel, mirroring the underlying TableLayoutPanel.ColumnCount.
A GPAL multiline textare instantiated with GPAL.TextArea for use on GPAL forms. Callback EventHandle...
A toolbar docked below the menu bar, containing GPALBarItem buttons and optional separators.
A tree view control instantiated with GPAL.TreeView for use on GPAL forms.
bool ShowLines
Whether lines are drawn between sibling nodes and their parent, as set via WithShowLines(bool).
bool ShowPlusMinus
Whether plus/minus expand buttons are displayed, as set via WithShowPlusMinus(bool).
bool CheckBoxes
Whether checkboxes are displayed next to each tree node, as set via WithCheckBoxes(bool).
Everything starts here, all the GPAL controls and global settings using fluent syntax are here....
Definition GPAL.cs:49
static void PublishSimpleEvent(GPALEventType gPALEventType, string msg, dynamic gPALObject=null, Enums.GPALObjectType gPALObjectType=GPALObjectType.None, Exception ex=null)
Publish a message to either the information channel or exception channel (if exception passed in) Pub...
Definition GPAL.cs:2406