C# Winform 运行时轻松设计表单






4.78/5 (74投票s)
在运行时设计你的 Winform。无需编写任何代码,即可使用存储过程对你的表单执行 CRUD 操作。
*请观看我的 YouTube 视频链接,学习 我的运行时轻松设计表单。
引言
在我之前的文章 (运行时 Windows 表单设计)
我解释了如何在运行时设计表单。我将该程序扩展到了下一个高级版本,增加了更多功能。这是我制作的最好的项目之一。希望大家都喜欢。现在让我们看看我的运行时轻松设计表单有哪些新功能
我之前文章的局限性
在我之前的文章中,有一个局限性,即用户需要为绑定网格等事件设计和添加代码。
用户只能加载一个表单。无法向 SQL 查询添加参数。
运行时轻松设计表单的新功能
它具有我之前文章的所有功能,并增加了更多新的不同功能,例如:
1. 新程序分为 2 部分,第 1 部分是表单视图部分,用户可以通过选择树状视图中的菜单来查看所有动态创建的表单。
2. 第 2 部分表单设计部分用于动态添加/编辑/保存和打开表单。
3. 用户无需编写任何代码。
4. 易于设计表单。
5. 新功能提供了为表单选择存储过程的选项。
6. 用户可以选择参数并选择用于 SQL Server 存储过程的动态文本框控件。
7. 保存和打开表单。
运行时轻松设计表单软件应用程序的开发目的是使用面板控件设计你自己的表单,添加标签、按钮、文本框、DataGridView 等。用户可以选择用于该表单的存储过程,例如。现在用户可以设计一个简单的搜索表单,其中包含一个文本框、一个按钮和一个 Datagridview。为此,用户需要将文本框作为搜索参数传递给存储过程,并在单击按钮后,将结果最终绑定到 Datagridview。在表单设计屏幕上,用户可以添加文本框、标签、按钮和 DataGridView,然后从工具中单击 SQL 设置工具菜单。用户可以选择其搜索存储过程,并将控件分配给匹配的参数,然后保存表单。
在主表单中,用户现在可以在树状视图中查看他们保存的表单名称,当他们单击菜单时。他们保存的表单将显示出来。当用户单击按钮时,他们可以看到所有详细信息都将从数据库加载,使用他们分配给表单的存储过程。这个简单的 C# 应用程序允许用户添加
1. 创建新表单
2. 将表单保存为 XML 文件。
3. 从 XML 文件打开表单。
4. 剪切、复制和粘贴所有控件。
5. 删除所有控件。
6. 删除选定的控件。
7. 为表单添加/更改背景颜色(此处使用面板作为表单)。
8. 控件置于顶层和底层选项。
9. 添加标签控件,并使用属性窗口设计标签。
10. 添加按钮控件,并使用属性窗口设计按钮。
11. 添加复选框控件,并使用属性窗口设计复选框。
12. 添加按钮控件,并使用属性窗口设计按钮。
13. 添加组合框控件,并使用属性窗口设计组合框。
14. 添加DataGridView控件,并使用属性窗口设计 DataGridView。
15. 添加日期时间选择器控件,并使用属性窗口设计 DataTimePicker。
16. 添加面板控件,并使用属性窗口设计列表框。
17. 添加图片框控件,并使用属性窗口设计 PictureBox。
18. 添加单选按钮控件,并使用属性窗口设计 RadioButton。
19. 添加文本框控件,并使用属性窗口设计 TextBox。
20. SQL 设置这是此应用程序的新功能。现在用户无需在此处编写任何代码。你可以在数据库中编写存储过程,并通过SQL 设置工具菜单为你的表单选择存储过程。将控件属性分配给存储过程的参数。
21. 使用存储过程从数据库绑定/插入/编辑和删除数据。
新的轻松设计表单有 5 个部分用于在运行时设计你的表单。
第 1 部分 表单视图部分
第 2 部分 表单设计部分
第 3 部分为你的表单设置 SQL 存储过程
第 4 部分保存表单
第 5 部分打开现有表单进行编辑。
现在让我们一一详细介绍。
第 1 部分 表单视图部分:在下面的图片中,我用数字标出了每个部分。
1) 当用户单击“新建表单设计”按钮时,将打开一个表单,用户可以在其中添加、编辑、打开和保存表单。我们可以在表单设计部分看到更多详细信息。
2) 在树状视图中,我将列出所有已保存的表单名称。每当用户创建新表单并保存时。保存的文件名将列在 Treeview 中。当用户单击 Treeview 节点时,相关的表单将加载到右侧。
3) 这里举个例子,我们可以看到,现在用户从 Treeview 中点击了“NewItem”,并且相关的表单已经被加载。对于这个表单,我选择了 ItemInsert 存储过程,并将所有文本框控件作为参数传递给了 SP。
这是一个动画图片,更详细地解释了这一点。
第 2 部分 表单设计部分:
在此应用程序中,我们可以在左侧看到工具栏,用于向表单(此处为我们的面板)添加控件。中间是表单(面板),用户可以在其中添加和设计他们的控件。右侧是属性窗口,用于向选定的控件添加所有设计。
工具栏:这里我们可以看到可以运行时添加到表单的所有控件列表。其他功能,如创建新表单、保存表单、打开表单、剪切、复制和粘贴控件。最后,你可以看到的控件是 SQL 设置,这是一个非常重要的工具,用于将我们的表单控件与存储过程匹配,以执行我们的 CRUD 操作。
当用户单击“新建表单设计”按钮时,将打开表单设计。你可以参考上面的图片了解设计表单的外观。此表单在运行时创建动态表单中起主要作用。用户可以在此处添加新表单、编辑、打开和保存表单。在上面的图片中,我用数字标出了每个部分。
1) 在左侧,我们有工具菜单,用户可以在其中运行时向表单添加文本框、标签、按钮、面板、DataGridView 等,以设计表单。
2) 用户可以在运行时在此处设计他们的表单。用户可以拖放控件,调整控件大小。
3) 使用属性网格,用户可以更改每个选定控件的属性,如背景颜色、字体颜色、文本等。
4) 这是为表单选择存储过程以执行 CRUD 操作,并将 SP 的参数与控件匹配。我们将在第 3 部分中详细介绍。
运行时轻松设计表单将允许用户在运行时设计表单、打开和保存表单以及重用现有表单。用户可以从工具栏在运行时添加控件,设计他们的表单,选择存储过程,匹配并添加控件参数以执行诸如选择、插入、更新和删除之类的操作。
现在举例来说,用户可以添加一个 DataGridView 和一个 Button 控件,并在运行时使用文本框按 UserCode 和 UserName 搜索用户。
第 3 部分为你的表单设置 SQL 存储过程
设计完表单后,现在是时候将存储过程分配给我们的表单,并将控件参数传递给 SP 以执行 CRUD 操作了。
要设置存储过程并分配参数给 SP,请从工具栏单击 SQL 设置菜单。
当用户单击 SQL 设置时,将打开一个新表单,如下所示。在下面的图片中,我已经用数字标记了,让我们一一了解它们。
1) 在选择过程组合框中,我将显示我们数据库中的所有存储过程名称。请注意,在我们的应用程序 Bin 文件夹中,您可以找到“DBConnection.txt”文本文件,其中包含默认数据库连接字符串。您可以根据您的数据库服务器名称、数据库名称、SQL 用户 ID 和密码更改默认连接字符串。
2) 一旦为我们的表单选择了存储过程,就单击参数按钮。在这里我们可以看到,我已经选择了“USP_USER_SELECT”,它将在我们的表单中使用,用于搜索和绑定用户详细信息。
3) 当我们单击参数时,我将显示选定存储过程的所有参数。请注意,一切都将是动态的,所以用户不需要在这里添加任何参数。我将列出在存储过程中声明的所有参数。如果用户需要添加或删除任何参数,他们可以更新存储过程并在这里重新设计表单。
4) 选择 SQL 参数,然后单击向下箭头按钮。当用户单击向下箭头时,SQL 参数将显示在参数文本框中。
5) 在控件名称列表中,我将显示所有需要分配给每个 SQL 参数的文本框控件名称。
6) 同样在这里,选择适合 SQL 参数的控件,然后单击第 6 个向下按钮。控件名称将被添加到控件文本框中。
7) 一旦选择了 SQL 参数和控件,就一一单击添加按钮,以添加需要为我们的表单保存的最终 SQL 参数列表。
8) 这里将添加所选存储过程的所有 SQL 参数。
9) 确认列表,然后单击确定以保存表单。
这是一个用于用户搜索绑定的示例文件 gif 图片。
第 4 部分保存表单
在工具栏中,用户可以选择保存处理的表单,以便从主屏幕查看。你可以参考下面的图片。当用户单击“保存”工具栏按钮时,将显示主菜单名称列表,此处列出了所有以前保存的文件名(我将使用文件名作为菜单名在主屏幕 Treeview 中显示)。如果用户想将表单保存为现有菜单的子菜单,则用户可以选择主菜单名称并输入新的表单名称来保存。如果用户希望表单成为主菜单,则他可以选择组合框中的“选择”并输入新的表单名称。一旦表单保存。菜单将在主屏幕上添加带有新表单名称的选项。
注意:我将表单保存为 XML 文件。在应用程序根目录的 bin 文件夹中,您可以看到 2 个文件夹,“XMLFILE”和“XMLForms”。在“XMLFILE”文件夹中,您可以看到“NewFormNameList.XML”。在此文件中,我将存储所有将用于在主菜单 TreeView 中列出的文件名。
在“XMLForms”文件夹中,对于每个表单,您可以看到 2 个 XML 文件。一个 XML 文件我将用于存储包含所有属性的表单控件详细信息,另一个 XML 文件我将存储每个表单的存储过程名称以及参数详细信息。
这里您可以看到一个示例文件 ItemAdd XML 文件,其中包含所有控件信息以及位置、字体大小、字体颜色、背景颜色、图像等属性。
“ItemAdd_query.XML”这个 XML 文件包含表单存储过程名称、参数和控件名称的所有详细信息。
当用户输入现有表单名进行保存时,我将用新保存的版本覆盖现有表单。我将表单保存为 XML 文件。
第 5 部分打开现有表单进行编辑。
在工具栏中,用户可以选择打开现有表单进行修改。你可以参考下面的图片。当用户单击“打开”工具栏按钮时,将显示菜单名称列表(这是我们的表单名称)。用户可以从列表中选择需要打开进行修改的表单名称。如果用户知道表单名称,则可以直接在文本框中输入表单名称并单击“打开”来修改现有表单。
使用代码
本文是我之前文章 (运行时 Windows 表单设计) 的扩展。我主要重用了相同的代码部分,并进行了一些新的修改。表单设计部分与之前的文章相同,请参阅我之前的文章,了解如何在运行时添加控件以及执行拖放、调整大小、删除控件等操作。
在这部分代码中,让我们看看如何
1)将存储过程分配给表单并添加参数
在表单设计工具栏按钮的单击事件中,我将打开新表单以执行 SP 设置。
表单设计 SQL 设置工具栏单击事件
在 SQL 设置按钮单击事件中,我将获取设计表单的所有文本框控件名称,并将所有控件名称添加到列表中,并将控件名称列表传递给FrmParameterSetting
。
private void toolStripButton1_Click_1(object sender, EventArgs e)
{
ControlNames.Clear();
if (pnControls.Controls.Count > 0)
{
foreach (TextBox tb in pnControls.Controls.OfType<TextBox>())
{
ControlNames.Add(tb.Name.ToString());
}
FrmParameterSetting obj = new FrmParameterSetting(ControlNames);
// obj.ShowDialog();
if (obj.ShowDialog() == DialogResult.OK)
{
}
}
}
FormParameterSetting 表单加载事件
在FrmParameterSetting
表单加载事件中,我将获取所有存储过程名称并绑定到组合框。将所有控件名称列表绑定到列表视图以分配参数。
private void FrmParameterSetting_Load(object sender, EventArgs e)
{
LoadSPCopmbo();
listView2.Items.Clear();
foreach (string prime in ControlNames) // Loop through List with foreach.
{
ListViewItem lvi = new ListViewItem(prime);
this.listView2.Items.Add(lvi);
}
}
在LoadSPCopmbo
函数中,我将使用以下查询获取所有 SP 名称。在这里,我将查询传递给returnDataDatable
函数。
public void LoadSPCopmbo()
{
comboBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
comboBox1.AutoCompleteSource = AutoCompleteSource.ListItems;
String Query = "SELECT name FROM dbo.sysobjects WHERE (type = 'P') ORDER BY name";
DataTable dt = returnDataDatable(Query);
comboBox1.ValueMember = "name";
comboBox1.DisplayMember = "name";
comboBox1.DataSource = dt;
}
在“returnDataDatable
”中,我将从文本文件“DBConnection.txt
”读取连接字符串。正如我在本文前面解释过的,连接字符串将存储为应用程序根文件夹中的文本文件。
public DataTable returnDataDatable(String Query)
{
String ConnectionString = ReadConnectionString();
DataTable dt = new DataTable();
SqlConnection con = new SqlConnection(ConnectionString);
SqlCommand cmd = new SqlCommand(Query, con);
cmd.CommandType = CommandType.Text;
SqlDataAdapter sda = new SqlDataAdapter(cmd);
sda.Fill(dt);
return dt;
}
我将检查文本文件。如果文件存在于文件夹中,我将从文件中读取连接字符串;如果不存在,我将写入默认连接字符串。因此,请检查根文件夹中的“DBConnection.txt”,并根据你的系统数据库设置更改连接字符串。
private String ReadConnectionString()
{
string path = Application.StartupPath + @"\DBConnection.txt";
String connectionString = "";
if (!File.Exists(path))
{
using (StreamWriter tw = File.CreateText(path))
{
tw.WriteLine("Data Source=YOURDBServerName;Initial Catalog=YOURDBNAME;User id = YOURUSERNAME;password=YOURPASSWORD");
tw.Close();
}
}
else
{
TextReader tr = new StreamReader(path);
connectionString = tr.ReadLine();
tr.Close();
}
return connectionString;
}
当用户单击参数按钮时,我将绑定与所选存储过程相关的参数列表。在代码部分,您可以看到我使用了查询来获取所选存储过程的所有参数,并将最终结果绑定到列表。
FormParameterSetting 参数按钮单击事件
private void button2_Click(object sender, EventArgs e)
{
listView1.Items.Clear();
String Query = "SELECT p.name AS Name, t.name AS Type, p.max_length AS Length FROM sys.parameters AS p JOIN sys.types AS t ON t.user_type_id = p.user_type_id WHERE object_id = OBJECT_ID('" + txtSPNAME.Text.Trim().ToString() +"')";
DataTable dt = returnDataDatable(Query);
foreach (DataRow dr in dt.Rows)
{
ListViewItem lvi = new ListViewItem(dr["Name"].ToString());
lvi.SubItems.Add(dr["Type"].ToString());
lvi.SubItems.Add(dr["Length"].ToString());
this.listView1.Items.Add(lvi);
}
}
FormParameterSetting 确定按钮单击事件
在这里,我将获取所有最终的存储过程名称、参数列表以及参数列表的控件,并将结果绑定到公共列表类,以便在我们的表单设计中用于保存文件。
private void button5_Click(object sender, EventArgs e)
{
for (int i = 0; i < listView3.Items.Count; i++)
{
ShanuEasyFormDesign.Class.ControlList obj1 = new ShanuEasyFormDesign.Class.ControlList(txtSPNAME.Text.Trim(), listView3.Items[i].SubItems[0].Text.ToString(), listView3.Items[i].SubItems[1].Text.ToString());
ShanuEasyFormDesign.Class.ControlList.objDGVBind.Add(obj1);
}
}
表单设计保存:在保存按钮单击事件中,我将获取所有控件的详细信息以及属性,并将表单保存为 XML 文件。
// To Save as XML FIle
private void toolSaves_Click(object sender, EventArgs e)
{
if (pnControls.Controls.Count > 0)
{
frmSave obj = new frmSave();
// obj.ShowDialog();
if (obj.ShowDialog() == DialogResult.OK)
{
if (obj.SaveFileName != "")
{
String NewFileName_Query = Application.StartupPath + @"\XMLForms\" + obj.SaveFileName + "_Query.XML";
if(!File.Exists(NewFileName_Query))
{
if (ShanuEasyFormDesign.Class.ControlList.objDGVBind.Count <= 0)
{
MessageBox.Show("procedure and parameter need to be set before save");
}
}
SavetoXML(obj.SaveFileName);
}
}
}
}
表单打开:要打开现有表单。我将从选定的 XML 文件加载所有表单控件。
// to Open XML file as Form
private void toolOpens_Click(object sender, EventArgs e)
{
frmOpen obj = new frmOpen();
// obj.ShowDialog();
if (obj.ShowDialog() == DialogResult.OK)
{
if (obj.OpenFileName != "")
{
xmlFileName = Application.StartupPath + @"\XMLForms\" + obj.OpenFileName + ".XML";
xmlFileName_Query = Application.StartupPath + @"\XMLForms\" + obj.OpenFileName + "_Query.XML";
pnControls.Controls.Clear();
loadXMLFILE();
}
}
}
主表单 CRUD 操作:在主表单中,我们可以看到每个表单动态执行的 CRUD 操作。这是我用来动态执行此操作的代码。
TreeView 节点单击:当用户单击 TreeView 中的文件名时,我将读取相应的已保存 XML 文件,并将所有控件添加到面板中作为表单。
private void treeMenu_AfterSelect(object sender, TreeViewEventArgs e)
{
fileName = Application.StartupPath + @"\XMLForms\" + treeMenu.SelectedNode.Text + ".XML";
xmlFileName_Query = Application.StartupPath + @"\XMLForms\" + treeMenu.SelectedNode.Text + "_Query.XML";
pnlMain.Controls.Clear();
loadXMLFILE();
}
catch (Exception ex)
{
}
}
从 Xml 文件中,我将检查 Button 控件,并为此 Button 控件创建一个动态单击事件,如下所示。
case "System.Windows.Forms.Button":
{
System.Drawing.Color myBackColor = new System.Drawing.Color();
myBackColor = System.Drawing.ColorTranslator.FromHtml(gParam[8]);
Button ctrl = new Button();
//ctrl.Image = global::DragObject.Properties.Resources.Sunset;
ctrl.BackColor = myBackColor;
ctrl.Name = gParam[10];
ctrl.Location = new Point(System.Convert.ToInt32(gParam[1]), System.Convert.ToInt32(gParam[2]));
ctrl.Text = gParam[5];
ctrl.Size = new System.Drawing.Size(System.Convert.ToInt32(gParam[3]), System.Convert.ToInt32(gParam[4]));
if (gParam[11] == "Front")
{
ctrl.BringToFront();
}
else
{
ctrl.SendToBack();
}
ctrl.Click += new EventHandler(control_Click);
pnlMain.Controls.Add(ctrl);
}
在 Button单击事件中,我将从 XML 文件读取存储过程详细信息,并将最终结果绑定到 Datagridview。所有 CRUD 操作都将在该单击事件中进行管理。
private void control_Click(object sender, EventArgs e)
{
try
{
if (xmlFileName_Query != "")
{
XmlDocument xmlQuery = new XmlDocument();
xmlQuery.Load(xmlFileName_Query);
XmlNode xnQueryList = xmlQuery.SelectSingleNode("ShanuQuerySave");
int i = 0;
//XmlNode xnListQuery = xnList.SelectSingleNode("SQLQuery");
String txtBox1Name = "";
String ParameterName1 = "";
String ControltoBindName = "";
String Query = "";
foreach (XmlNode xn in xnQueryList)
{
Query = xn["ProcedureName"].InnerText;
}
DataTable dt = new DataTable();
String ConnectionString = ReadConnectionString();
SqlConnection con = new SqlConnection(ConnectionString);
SqlCommand cmd = new SqlCommand(Query, con);
cmd.CommandType = CommandType.StoredProcedure;
foreach (XmlNode xn in xnQueryList)
{
txtBox1Name = xn["cntrlName"].InnerText;
ParameterName1 = xn["ParameterName"].InnerText;
ControltoBindName = xn["cntrltoBind"].InnerText;
Query = xn["ProcedureName"].InnerText;
if (txtBox1Name != "")
{
Control control = returnTextBox(txtBox1Name);
if (control is TextBox)
{
txtBox1Name = control.Text.Trim().ToString();
}
}
cmd.Parameters.AddWithValue(ParameterName1, txtBox1Name);
}
SqlDataAdapter sda = new SqlDataAdapter(cmd);
sda.Fill(dt);
if (dt.Rows.Count > 0)
{
if (dt.Columns[0].ColumnName == "Result")
{
MessageBox.Show("Record :" + dt.Rows[0].ItemArray[0].ToString());
return;
}
}
if (ControltoBindName != "")
{
foreach (Control pnlCntl in pnlMain.Controls)
{
if (pnlCntl is DataGridView)
{
if (pnlCntl.Name == ControltoBindName)
{
DataGridView grid = (DataGridView)pnlCntl;
grid.DataSource = dt;
}
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
结论
注意:运行我的应用程序之前需要做的事情。
1) 连接字符串: 在运行应用程序之前,请更新应用程序根文件夹中的连接字符串以连接到你的本地 SQL Server 数据库。
我使用了 SQL Server 2008 R2 和 Visual Studio 2010。
2) 存储过程: 在附加的 zip 文件中,您可以找到名为“SQLScripts”的文件夹,其中包含所有表、数据库和存储过程创建脚本文件。请将所有脚本文件逐一运行到你的 SQL Server 数据库。
3) 运行程序
输出
1) 项目添加:我使用了下面的 SP 来插入项目详细信息。
注意:插入后,我将返回“已插入”的消息以确认。
-- Author : Shanu
-- Create date : 2015-02-05
-- Description : To Insert Item Master
-- Tables used : ItemMasters
-- Modifier : Shanu
-- Modify date : 2015-02-05
-- ============================================= -- exec USP_Item_Insert '',''
-- =============================================
ALTER PROCEDURE [dbo].[USP_Item_Insert]
(
@Item_Code VARCHAR(50) = '',
@Item_Name VARCHAR(50) = '',
@Price INT=0 ,
@TAX1 INT=0 ,
@Discount INT=0 ,
@Description VARCHAR(50) = '',
@USR_Name VARCHAR(50) = ''
)
AS
BEGIN
IF NOT EXISTS (SELECT * FROM ItemMasters WHERE Item_Code=@Item_Code and Item_Name=@Item_Name)
BEGIN
INSERT INTO [ItemMasters]
([Item_Code],[Item_Name],[Price],[TAX1],[Discount],[Description],[IN_DATE]
,[IN_USR_ID],[UP_DATE],[UP_USR_ID])
VALUES
(@Item_Code,@Item_Name,@Price,@TAX1,@Discount,@Description,GETDATE(),@USR_Name
,GETDATE(),@USR_Name)
select 'Inserted' as 'Result'
END
END
为了更清楚地理解,我添加了一个动画 gif 文件作为图片。
2) 项目编辑:我使用了下面的 SP 来更新项目详细信息。
注意:更新后,我将返回“已更新”的消息以确认。
-- Author : Shanu
-- Create date : 2015-02-05
-- Description : To Update Item Master
-- Tables used : ItemMasters
-- Modifier : Shanu
-- Modify date : 2015-02-05
-- ============================================= -- exec USP_Item_Update '',''
-- =============================================
ALTER PROCEDURE [dbo].[USP_Item_Update]
(
@Item_Code VARCHAR(50) = '',
@Item_Name VARCHAR(50) = '',
@Price INT=0 ,
@TAX1 INT=0 ,
@Discount INT=0 ,
@Description VARCHAR(50) = '',
@USR_Name VARCHAR(50) = ''
)
AS
BEGIN
UPDATE [ItemMasters]
SET [Item_Name]=@Item_Name,
[Price]=@Price,
[TAX1]=@TAX1,
[Discount]=@Discount,
[Description]=@Description,
[UP_DATE]=GETDATE(),
[UP_USR_ID]=@USR_Name
WHERE
Item_Code=@Item_Code
select 'Updated' as 'Result'
END
为了更清楚地理解,我添加了一个动画 gif 文件作为图片。
历史
ShanuEasyFormDesignV1.0 - 2015.04.21