Author SHA1 Message Date
redlineoandClaude Sonnet 5 b42815aa2a Fix add/remove template buttons overlapping the list (Dock add-order bug)
WinForms docks child controls in the REVERSE of their Controls.Add()
order - the last control added gets docked first (claims its edge
space first), and earlier-added controls lay out against whatever
remains. pnlTemplateButtons (Dock=Top) was added before lstTemplates
(Dock=Fill), so Fill was processed first against the full, unclaimed
panel area and expanded to cover the whole thing - overlapping the
button strip instead of leaving room for it. Swapped the add order
(Fill first, Top second) so the button strip actually carves out its
band before Fill claims what's left.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 13:04:48 +03:00
redlineoandClaude Sonnet 5 f1c67669fd Fix template list becoming unclickable with many entries via Dock layout
lstTemplates was a direct child of tabTemplates with an explicit pixel
Size, computed once against this dev machine's tab-strip height under
its own DPI/font scale. On the operator's real machine, adding enough
templates pushed rows past the control's actual bottom edge -
maximizing the window didn't help, confirming it wasn't a screen-space
problem but a stale size assumption baked into the designer file (the
same root cause as the earlier add/remove-button visibility bug).

Moved the button strip + list into their own panel (pnlTemplateList),
with the buttons Dock=Top and lstTemplates Dock=Fill - the list's
bounds are now computed by the layout engine against the real
container at runtime, with no absolute size to be wrong about,
regardless of DPI/font scale/window size.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 12:39:49 +03:00
@@ -35,6 +35,8 @@ namespace BulkArchiveMailer.AddIn.UI
private System.Windows.Forms.Label lblFromAccount; private System.Windows.Forms.Label lblFromAccount;
private System.Windows.Forms.ComboBox cboFromAccount; private System.Windows.Forms.ComboBox cboFromAccount;
private System.Windows.Forms.Panel pnlTemplateList;
private System.Windows.Forms.Panel pnlTemplateButtons;
private System.Windows.Forms.ListBox lstTemplates; private System.Windows.Forms.ListBox lstTemplates;
private System.Windows.Forms.Button btnAddTemplate; private System.Windows.Forms.Button btnAddTemplate;
private System.Windows.Forms.Button btnRemoveTemplate; private System.Windows.Forms.Button btnRemoveTemplate;
@@ -80,6 +82,8 @@ namespace BulkArchiveMailer.AddIn.UI
this.lblFromAccount = new System.Windows.Forms.Label(); this.lblFromAccount = new System.Windows.Forms.Label();
this.cboFromAccount = new System.Windows.Forms.ComboBox(); this.cboFromAccount = new System.Windows.Forms.ComboBox();
this.pnlTemplateList = new System.Windows.Forms.Panel();
this.pnlTemplateButtons = new System.Windows.Forms.Panel();
this.lstTemplates = new System.Windows.Forms.ListBox(); this.lstTemplates = new System.Windows.Forms.ListBox();
this.btnAddTemplate = new System.Windows.Forms.Button(); this.btnAddTemplate = new System.Windows.Forms.Button();
this.btnRemoveTemplate = new System.Windows.Forms.Button(); this.btnRemoveTemplate = new System.Windows.Forms.Button();
@@ -105,6 +109,8 @@ namespace BulkArchiveMailer.AddIn.UI
this.tabControl.SuspendLayout(); this.tabControl.SuspendLayout();
this.tabGeneral.SuspendLayout(); this.tabGeneral.SuspendLayout();
this.tabTemplates.SuspendLayout(); this.tabTemplates.SuspendLayout();
this.pnlTemplateList.SuspendLayout();
this.pnlTemplateButtons.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
@@ -213,9 +219,7 @@ namespace BulkArchiveMailer.AddIn.UI
this.tabTemplates.Text = "Шаблоны"; this.tabTemplates.Text = "Шаблоны";
this.tabTemplates.Padding = new System.Windows.Forms.Padding(12); this.tabTemplates.Padding = new System.Windows.Forms.Padding(12);
this.tabTemplates.UseVisualStyleBackColor = true; this.tabTemplates.UseVisualStyleBackColor = true;
this.tabTemplates.Controls.Add(this.btnAddTemplate); this.tabTemplates.Controls.Add(this.pnlTemplateList);
this.tabTemplates.Controls.Add(this.btnRemoveTemplate);
this.tabTemplates.Controls.Add(this.lstTemplates);
this.tabTemplates.Controls.Add(this.lblTplDisplayName); this.tabTemplates.Controls.Add(this.lblTplDisplayName);
this.tabTemplates.Controls.Add(this.txtTplDisplayName); this.tabTemplates.Controls.Add(this.txtTplDisplayName);
this.tabTemplates.Controls.Add(this.lblTplFileSuffix); this.tabTemplates.Controls.Add(this.lblTplFileSuffix);
@@ -230,50 +234,86 @@ namespace BulkArchiveMailer.AddIn.UI
this.tabTemplates.Controls.Add(this.txtTplBody); this.tabTemplates.Controls.Add(this.txtTplBody);
this.tabTemplates.Controls.Add(this.lblTplHint); this.tabTemplates.Controls.Add(this.lblTplHint);
// btnAddTemplate / btnRemoveTemplate // pnlTemplateList
// //
// Deliberately placed ABOVE lstTemplates (not below it) so // Left-column container for the template picker (buttons +
// their visibility never depends on how much vertical room is // list). Its own outer Location/Size/Anchor still use fixed
// left over at the bottom of the tab page after the list - // pixel coordinates relative to the tab page, exactly like
// that computation (tab-strip height under the operator's // before - that outer placement was never the problem. What
// actual DPI/font scale, which this code cannot know at design // is Dock-based from here down is everything INSIDE this
// time) is exactly what went wrong before: lstTemplates ran all // panel, which is the part that actually broke on a real
// the way to the tab page's real bottom edge with only ~6px to // machine: lstTemplates used to be a direct child of
// spare for the buttons beneath it, which was enough on some // tabTemplates with an explicit Size, so its bottom edge (and
// machines and not on others. Anchored Top|Left (fixed // therefore its scrollability/clickability once many
// distance from the tab page's top-left corner) rather than // templates were added) depended on a hand-computed pixel
// Bottom, they are visible as long as the tab page itself is // height that was only ever verified on the dev machine's own
// visible at all - not merely as long as it is "tall enough". // DPI/font scale. Docking removes that computation entirely -
this.btnAddTemplate.Location = new System.Drawing.Point(15, 20); // see pnlTemplateButtons/lstTemplates below.
this.pnlTemplateList.Location = new System.Drawing.Point(15, 20);
this.pnlTemplateList.Size = new System.Drawing.Size(190, 467);
this.pnlTemplateList.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
// Dock order matters here, and it is the OPPOSITE of what looks
// intuitive: WinForms lays out docked children in the REVERSE
// of their Controls.Add() order - the LAST control added is
// docked FIRST (closest to the parent's edge), and each
// earlier-added control is then laid out against whatever
// client area is left over. So lstTemplates (Dock=Fill) must be
// added FIRST, and pnlTemplateButtons (Dock=Top) must be added
// SECOND/last, so that the Top strip is actually the one that
// gets docked last (i.e. first in effect) and carves its 35px
// band out of the top; Fill then only gets whatever remains
// below it. Adding them in the other order (as this used to be)
// makes Fill get laid out first, against the FULL panel client
// area (nothing has carved out the top strip yet), so it
// expands to cover the entire panel and visually overlaps
// pnlTemplateButtons - exactly the "buttons overlap the list"
// bug reported on the real machine.
this.pnlTemplateList.Controls.Add(this.lstTemplates);
this.pnlTemplateList.Controls.Add(this.pnlTemplateButtons);
// pnlTemplateButtons
//
// Fixed-height strip pinned to the top of pnlTemplateList.
// btnAddTemplate/btnRemoveTemplate live inside it at a fixed
// offset from ITS top-left corner (0,0)/(95,0) - a small,
// never-resized container, so plain Location placement here
// carries none of the earlier risk (that risk only existed
// when the same fixed coordinates were relative to the whole,
// variable-height tab page).
this.pnlTemplateButtons.Dock = System.Windows.Forms.DockStyle.Top;
this.pnlTemplateButtons.Size = new System.Drawing.Size(190, 35);
this.pnlTemplateButtons.Controls.Add(this.btnAddTemplate);
this.pnlTemplateButtons.Controls.Add(this.btnRemoveTemplate);
// btnAddTemplate
this.btnAddTemplate.Location = new System.Drawing.Point(0, 0);
this.btnAddTemplate.Size = new System.Drawing.Size(90, 27); this.btnAddTemplate.Size = new System.Drawing.Size(90, 27);
this.btnAddTemplate.Text = "+ Добавить"; this.btnAddTemplate.Text = "+ Добавить";
this.btnAddTemplate.UseVisualStyleBackColor = true; this.btnAddTemplate.UseVisualStyleBackColor = true;
this.btnAddTemplate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)));
this.btnAddTemplate.Click += new System.EventHandler(this.btnAddTemplate_Click); this.btnAddTemplate.Click += new System.EventHandler(this.btnAddTemplate_Click);
// btnRemoveTemplate // btnRemoveTemplate
this.btnRemoveTemplate.Location = new System.Drawing.Point(115, 20); this.btnRemoveTemplate.Location = new System.Drawing.Point(95, 0);
this.btnRemoveTemplate.Size = new System.Drawing.Size(90, 27); this.btnRemoveTemplate.Size = new System.Drawing.Size(90, 27);
this.btnRemoveTemplate.Text = "Удалить"; this.btnRemoveTemplate.Text = "Удалить";
this.btnRemoveTemplate.UseVisualStyleBackColor = true; this.btnRemoveTemplate.UseVisualStyleBackColor = true;
this.btnRemoveTemplate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)));
this.btnRemoveTemplate.Click += new System.EventHandler(this.btnRemoveTemplate_Click); this.btnRemoveTemplate.Click += new System.EventHandler(this.btnRemoveTemplate_Click);
// lstTemplates // lstTemplates
// //
// Starts below the two buttons now (y=55, was y=20) and still // Dock=Fill: always occupies exactly whatever area of
// stretches down to the bottom of the tab page (Anchor // pnlTemplateList is left below pnlTemplateButtons, computed
// Top|Bottom|Left) - only its OWN bottom edge is at risk of // by the layout engine at every resize/DPI - not a hand-picked
// being visually tight against the tab page's real bottom // pixel height that can end up too short (and therefore
// edge if the assumed content height is off, which just means // effectively unscrollable/unclickable for items past the cut
// slightly less of the list is visible at once (it already has // -off) on a machine with different DPI/font scaling than the
// a scrollbar - IntegralHeight = false - so this degrades // one this was last tested on. IntegralHeight = false just
// gracefully) rather than an entire control disappearing. // means the list doesn't force its height to a whole multiple
this.lstTemplates.Location = new System.Drawing.Point(15, 55); // of the item height - irrelevant to whether Fill can "escape"
this.lstTemplates.Size = new System.Drawing.Size(190, 432); // the panel, which it structurally cannot.
this.lstTemplates.Dock = System.Windows.Forms.DockStyle.Fill;
this.lstTemplates.IntegralHeight = false; this.lstTemplates.IntegralHeight = false;
this.lstTemplates.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.lstTemplates.SelectedIndexChanged += new System.EventHandler(this.lstTemplates_SelectedIndexChanged); this.lstTemplates.SelectedIndexChanged += new System.EventHandler(this.lstTemplates_SelectedIndexChanged);
// lblTplDisplayName // lblTplDisplayName
@@ -399,6 +439,8 @@ namespace BulkArchiveMailer.AddIn.UI
this.tabControl.ResumeLayout(false); this.tabControl.ResumeLayout(false);
this.tabGeneral.ResumeLayout(false); this.tabGeneral.ResumeLayout(false);
this.tabGeneral.PerformLayout(); this.tabGeneral.PerformLayout();
this.pnlTemplateButtons.ResumeLayout(false);
this.pnlTemplateList.ResumeLayout(false);
this.tabTemplates.ResumeLayout(false); this.tabTemplates.ResumeLayout(false);
this.tabTemplates.PerformLayout(); this.tabTemplates.PerformLayout();
this.ResumeLayout(false); this.ResumeLayout(false);