目录一、实现效果二、效果图三、实现步骤1、创建窗体并设置窗体属性2、添加选项卡控件3、动态创建表单4、逻辑说明一、实现效果在选项卡里动态创建多个表单保存时获取所有表单的数据。1、打开一个新的窗体新窗体尺寸大小是当前电脑屏幕的80%不显示最大化/最小化窗口大小不能拖拉。2、选项卡点击保存按钮获取所有选项卡表单的内容。3、每个选项卡下都动态生成表单并且每个表单大小会跟随窗口大小自适应。表单创建及布局均使用代码实现不使用设计器的繁琐操作下面的代码可以直接复用方法都是封装好的可以添加多个表单。二、效果图三、实现步骤1、创建窗体并设置窗体属性创建窗体并命名为windowFixed。选中窗体设置属性AutoScaleModeFontFormBorderStyleFixedSingleMaximizeBoxFalseMinimizeBoxFalseStartPositionCenterScreen2、添加选项卡控件① 拖拽panel到窗体命名panelBottom设置Dock属性为Bottom并添加保存按钮命名为btnSave② 拖拽TabControl到窗体并命名为tabMain选中选项卡设置属性AppearanceFlatButtonsDockFillTabPages添加选项卡page注意顺序一定要先添加panelBottom再添加选项卡panelBottom不要放在选项卡里否则的话点击其他选项卡底部按钮就不会显示。3、动态创建表单using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Controls.Primitives; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; namespace test { public partial class windowFixed : Form { public windowFixed() { InitializeComponent(); // 调用表单初始化 InitForm(); } // 设置窗口初始状态 private void windowFixed_Load(object sender, EventArgs e) { // 1. 获取当前屏幕的工作区不包含底部任务栏 Rectangle screenArea Screen.PrimaryScreen.WorkingArea; // 2. 计算屏幕的 80% int targetWidth (int)(screenArea.Width * 0.8); int targetHeight (int)(screenArea.Height * 0.8); // 3. 赋值给当前窗体 this.Width targetWidth; this.Height targetHeight; // 4. 手动计算并在屏幕正中间显示 this.StartPosition FormStartPosition.Manual; this.Location new Point( screenArea.Left (screenArea.Width - this.Width) / 2, screenArea.Top (screenArea.Height - this.Height) / 2 ); } // 创建表单 // 初始化表单 private void InitForm() { // 遍历每一个选项卡给它们创建表单 foreach (TabPage page in tabMain.TabPages) { CreateForm(page); } } /// summary /// 动态创建表单 /// /summary /// param namepage/param private void CreateForm(TabPage page) { // 1. 创建 SplitContainer 分左右两列 SplitContainer split new SplitContainer(); split.Dock DockStyle.Fill; split.Orientation Orientation.Vertical; page.Controls.Add(split); split.SplitterDistance 400; // 2. 左侧流式布局 FlowLayoutPanel leftFlow new FlowLayoutPanel(); leftFlow.Dock DockStyle.Fill; leftFlow.FlowDirection FlowDirection.TopDown; leftFlow.WrapContents false; leftFlow.Padding new Padding(10); split.Panel1.Controls.Add(leftFlow); // 3. 右侧流式布局 FlowLayoutPanel rightFlow new FlowLayoutPanel(); rightFlow.Dock DockStyle.Fill; rightFlow.FlowDirection FlowDirection.TopDown; rightFlow.WrapContents false; rightFlow.Padding new Padding(10); split.Panel2.Controls.Add(rightFlow); // 4. 自适应事件 split.SizeChanged (s, e) { if (split.Width 100) { // 左右两侧比例为46 int targetDistance (int)(split.Width * 0.4); if (targetDistance split.Panel1MinSize targetDistance split.Width - split.Panel2MinSize) { split.SplitterDistance targetDistance; } } }; leftFlow.SizeChanged (s, e) ResizeRowsWidth(leftFlow); rightFlow.SizeChanged (s, e) ResizeRowsWidth(rightFlow); // 5. 填数据根据选项卡的名字Name填入不同的表单字段 switch (page.Name) { case baseInfo: AddInputRow(leftFlow, * HIS中药名称, HisName); AddInputWithButtonRow(leftFlow, * 简 拼, jp, 自动生成, BtnAutoGenerate_Click); AddComboRow(leftFlow, * 剂 型, cboDosage, new string[] { 中药配方颗粒, 中成药 }); AddRadioRow(leftFlow, * 性 别, sex, new string[] { 男, 女 }, 男); AddCheckBoxRow(leftFlow, 伴随症状, zhengzhuang, new string[] { 恶心, 呕吐, 头晕, 皮疹 }); AddMultilineRow(rightFlow, * 功 能, gongneng, 100); AddMultilineRow(rightFlow, * 主 治, zhuzhi, 120); break; case syz: AddMultilineRow(leftFlow, * 适应症, syz, 150); AddMultilineRow(rightFlow, * 禁忌说明, shuoming, 200); break; case jinji: AddMultilineRow(leftFlow, * 禁忌症, jinji, 250); break; } } // 辅助方法 // 辅助生成单行输入框 private void AddInputRow(FlowLayoutPanel parent, string labelText, string fieldName) { Panel row new Panel { Width parent.Width - 25, Height 35, Margin new Padding(0, 10, 0, 0) }; Label lbl new Label { Text labelText, Width 100, Dock DockStyle.Left, TextAlign ContentAlignment.MiddleRight }; TextBox txt new TextBox { Dock DockStyle.Fill, Margin new Padding(0, 8, 0, 0), Tag fieldName }; row.Controls.Add(txt); row.Controls.Add(lbl); parent.Controls.Add(row); } // 辅助生成带右侧按钮的单行输入框 private void AddInputWithButtonRow(FlowLayoutPanel parent, string labelText, string fieldName, string btnText, EventHandler btnClick) { // 1. 外层行容器 Panel row new Panel { Width parent.Width - 25, Height 35, Margin new Padding(0, 10, 0, 0) }; // 2. 标签 (靠左) Label lbl new Label { Text labelText, Width 100, Dock DockStyle.Left, TextAlign ContentAlignment.MiddleRight }; // 3. 按钮 (靠右) - 直接作为行容器的子控件 Button btn new Button(); btn.Text btnText; btn.Width 80; btn.Dock DockStyle.Right; // 直接 Dock 靠右不用套容器 btn.Margin new Padding(5, 5, 0, 5); // 设置 Margin 实现垂直居中上下缩进 5 像素 btn.BackColor Color.Chocolate; btn.ForeColor Color.White; btn.FlatStyle FlatStyle.Flat; btn.FlatAppearance.BorderSize 0; btn.Cursor Cursors.Hand; if (btnClick ! null) btn.Click btnClick; // 4. 输入框 (填满中间) TextBox txt new TextBox { Dock DockStyle.Fill, Margin new Padding(0, 8, 0, 0) , Tag fieldName }; // 5. 组装⚠️必须严格按照以下顺序Fill 最先加Left/Right 后加 row.Controls.Add(txt); // 首先让 TextBox 填满 row.Controls.Add(btn); // 然后把右侧空间切走给 Button row.Controls.Add(lbl); // 最后把左侧空间切走给 Label parent.Controls.Add(row); // 6. 自适应宽度 parent.SizeChanged (s, e) { row.Width parent.ClientSize.Width - 25; }; } // 辅助生成下拉框 private void AddComboRow(FlowLayoutPanel parent, string labelText, string fieldName, string[] items) { Panel row new Panel { Width parent.Width - 25, Height 35, Margin new Padding(0, 10, 0, 0) }; Label lbl new Label { Text labelText, Width 100, Dock DockStyle.Left, TextAlign ContentAlignment.MiddleRight }; ComboBox cbo new ComboBox(); cbo.Tag fieldName; cbo.Dock DockStyle.Fill; cbo.DropDownStyle ComboBoxStyle.DropDownList; cbo.Margin new Padding(0, 8, 0, 0); // 和 TextBox 保持一致的垂直间距 if (items ! null items.Length 0) { cbo.Items.AddRange(items); cbo.SelectedIndex 0; } row.Controls.Add(cbo); row.Controls.Add(lbl); parent.Controls.Add(row); } // 辅助生成多行输入框 private void AddMultilineRow(FlowLayoutPanel parent, string labelText, string fieldName, int height) { Panel row new Panel { Width parent.Width - 25, Height height 10, Margin new Padding(0, 10, 0, 0) }; Label lbl new Label { Text labelText, Width 100, Dock DockStyle.Left, TextAlign ContentAlignment.TopRight }; TextBox txt new TextBox { Multiline true, ScrollBars ScrollBars.Vertical, Dock DockStyle.Fill, Margin new Padding(0, 3, 0, 0) , Tag fieldName }; row.Controls.Add(txt); row.Controls.Add(lbl); parent.Controls.Add(row); } // 辅助生成单选框支持多个选项排在一行 private void AddRadioRow(FlowLayoutPanel parent, string labelText, string fieldName, string[] options, string defaultValue ) { // 1. 外层行容器 Panel row new Panel { Width parent.Width - 25, Height 35, Margin new Padding(0, 10, 0, 0) }; // 2. 标签 (靠左) Label lbl new Label { Text labelText, Width 100, Dock DockStyle.Left, TextAlign ContentAlignment.MiddleRight }; // 3. 选项容器 FlowLayoutPanel optionPanel new FlowLayoutPanel { Dock DockStyle.Fill, FlowDirection FlowDirection.LeftToRight, WrapContents false, Margin new Padding(0, 8, 0, 0) }; // 4. 循环创建单选框 if (options ! null) { foreach (var option in options) { RadioButton rb new RadioButton(); rb.Text option; rb.AutoSize true; rb.Margin new Padding(0, 0, 15, 0); // 核心给每个单选框打上相同的 Tag rb.Tag fieldName; // 如果传入了默认选中的值则勾选 if (option defaultValue) { rb.Checked true; } optionPanel.Controls.Add(rb); } } // 5. 组装⚠️注意添加顺序Fill 的最先加Left 的后加 row.Controls.Add(optionPanel); row.Controls.Add(lbl); parent.Controls.Add(row); } // 辅助生成多选框支持多个选项排在一行 private void AddCheckBoxRow(FlowLayoutPanel parent, string labelText, string fieldName, string[] options) { // 1. 外层行容器 Panel row new Panel { Width parent.Width - 25, Height 35, Margin new Padding(0, 10, 0, 0) }; // 2. 标签 (靠左) Label lbl new Label { Text labelText, Width 100, Dock DockStyle.Left, TextAlign ContentAlignment.MiddleRight }; // 3. 选项容器 (横向排列) FlowLayoutPanel optionPanel new FlowLayoutPanel { Dock DockStyle.Fill, FlowDirection FlowDirection.LeftToRight, WrapContents false, Margin new Padding(0, 8, 0, 0) }; // 4. 循环创建多选框 if (options ! null) { foreach (var option in options) { CheckBox cb new CheckBox(); cb.Text option; cb.AutoSize true; cb.Margin new Padding(0, 0, 15, 0); // 给每个多选框打上相同的 Tag cb.Tag fieldName; optionPanel.Controls.Add(cb); } } // 5. 组装 row.Controls.Add(optionPanel); row.Controls.Add(lbl); parent.Controls.Add(row); } // 统一调整流式面板里每一行的宽度 private void ResizeRowsWidth(FlowLayoutPanel parent) { foreach (Control ctrl in parent.Controls) { ctrl.Width parent.ClientSize.Width - 25; // 25 是留给滚动条的余量 } } private void BtnAutoGenerate_Click(object sender, EventArgs e) { MessageBox.Show(执行自动生成逻辑); } // 递归查找指定Tag的控件并返回它的值 --- 适用于单行输入框、多行输入框、下拉框、单选按钮 private string GetValueByTag(Control parent, string targetTag) { foreach (Control ctrl in parent.Controls) { if (ctrl.Tag ! null ctrl.Tag.ToString() targetTag) { if (ctrl is TextBox txt) return txt.Text; if (ctrl is ComboBox cbo) return cbo.Text; if (ctrl is RadioButton rb rb.Checked) return rb.Text; if (ctrl is CheckBox cb cb.Checked) return cb.Text; } // 如果没找到且控件有子控件继续深入查找 if (ctrl.HasChildren) { string result GetValueByTag(ctrl, targetTag); if (!string.IsNullOrEmpty(result)) return result; } } return ; // 没找到返回空字符串 } // 递归查找指定 Tag 的所有多选框并返回选中项的拼接字符串如 恶心,头晕--- 适用于多选框 private string GetCheckedBoxesValueByTag(Control parent, string targetTag) { Liststring checkedValues new Liststring(); // 内部递归函数 void FindCheckBoxes(Control ctrl) { foreach (Control child in ctrl.Controls) { // 如果找到了带有指定 Tag 的多选框且被选中了 if (child is CheckBox cb cb.Tag ! null cb.Tag.ToString() targetTag cb.Checked) { checkedValues.Add(cb.Text); } if (child.HasChildren) { FindCheckBoxes(child); } } } FindCheckBoxes(parent); // 将选中的值用逗号拼接如果没选就返回空字符串 return string.Join(,, checkedValues); } // 保存数据 /// summary /// 保存数据 /// /summary /// param namesender/param /// param namee/param private void btnSave_Click(object sender, EventArgs e) { // 用于存放所有提取出来的数据 Dictionarystring, string allData new Dictionarystring, string(); // 1.遍历所有选项卡 foreach (TabPage page in tabMain.TabPages) { // 2.根据选项卡的Name提取对应的值 switch (page.Name) { case baseInfo: allData[HisName] GetValueByTag(page, HisName); allData[jp] GetValueByTag(page, jp); allData[cboDosage] GetValueByTag(page, cboDosage); allData[sex] GetValueByTag(page, sex); allData[gongneng] GetValueByTag(page, gongneng); allData[zhuzhi] GetValueByTag(page, zhuzhi); // 复选框判断是否为空 allData[zhengzhuang] GetCheckedBoxesValueByTag(page, zhengzhuang); break; case syz: allData[syz] GetValueByTag(page, syz); allData[shuoming] GetValueByTag(page, shuoming); break; case jinji: allData[jinji] GetValueByTag(page, jinji); break; } } // 3.测试输出 string result 收集到的全部数据 \n; foreach (var kvp in allData) { result ${kvp.Key}:{kvp.Value}\n; } MessageBox.Show(result, 保存结果); } } }4、逻辑说明① 在windowFixed_Load事件中先设置窗口初始状态即窗口大小是当前电脑屏幕的80%。② 在构造函数中调用表单初始化。③ 由于可能会有多个选项卡所有这里使用循环遍历每一个选项卡并给他们创建表单。InitForm()方法里的tabMain是选项卡的名称一定要一致。④ 表单布局使用SplitContainerFlowLayoutPanel的方式控制表单布局使用SplitContainer将面板一分为二占用空间比例为4:6使用FlowLayoutPanel进行流式布局。⑤ 表单填充根据不同的选项卡名称设置对应选项卡下的表单并利用相关辅助方法设置表单的属性表单类型包括单行输入框、多行输入框、单选按钮、下拉框、复选框。⑥ 数据获取点击“保存”按钮时获取所有选项卡的值。如果添加其他表单直接在CreateForm()放在中的对应的选项卡下直接用代码添加即可。