65.9K
CodeProject 正在变化。 阅读更多。
Home

用于编辑具有两个字段的表的工具

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.96/5 (9投票s)

2004年5月24日

8分钟阅读

viewsIcon

47945

downloadIcon

627

本文演示了一个用于编辑具有两个字段的表的工具

引言

我通常与一些公司合作,为他们构建定制软件。在我参与的大部分项目中,我都遇到过在数据库管理一些表时的一些常见问题。特别地,在所有项目中,我都不得不处理一些只有两个字段的表。假设我们有一个带组合框的表单,用户需要在此输入城市名称,如果我们想存储一些城市供用户选择,我们通常通过创建一个带有两个字段(ID,city)的表来实现。而且,用户总是希望有一种方法来管理这个表(添加、修改、删除记录)。因此,在每个项目中,我都需要花费一些时间来创建用于管理这些表的表单。我决定创建一个工具来自动化与此表的交互。

存在一些问题,特别是由于我有时使用 SQL Server 数据库,有时使用 Access 数据库,而我不想为这种情况创建两个组件。我最终创建的工具是一个派生自 System.Windows.Forms.Form 的类,它只需要你想要操作的表的名称、该表中的字段名称及其长度,还需要一个 System.Data.IDbConnection,从而解决了多数据库的问题。它还有三个名为 AllowAddAllowUpdateAllowDelete 的属性,以防使用该表单的人不想允许用户添加、更新或删除任何记录,最后该表单有一对事件用于任何三种操作(添加、更新、删除):AddingrecAddedrecUpdatingrecUpdatedrecDeletingrecDeletedrec,因此开发人员可以在用户的任何操作中添加业务逻辑(允许或取消用户执行的任何操作,或进行一些特殊验证)。此表单支持本地化,并支持(英语(默认)、意大利语、法语、西班牙语、希腊语)。在未来的版本中,该表单还将支持直接打印表,而无需开发人员编写任何代码。

项目中唯一“特别”的代码是运行时检测连接类型(SqlConnectionOledbConnectionOdbcConnection)并相应地初始化 DataAdapters 和 Commands 的代码。这是表单(英文版)

以下是类的成员

public class twofieldform : System.Windows.Forms.Form
{
    private System.Windows.Forms.Button addbut;
    private System.Windows.Forms.Button updatebut;
    private System.Windows.Forms.Button delbut;
    private System.Windows.Forms.Button printbut;
    private System.Windows.Forms.Label textlabel;
    private System.Windows.Forms.Label originallabel;
    private System.Windows.Forms.Label titlelabel;
    private System.Windows.Forms.TextBox maintext;
    private System.Windows.Forms.DataGrid maingrid; 
    private string emftitlos; // The text of the form
    private string emffield; 
     // What is written in the the textlabel
    private string tablename; 
      // the name of the table that we manage
    private string mainfield; 
     // the name of the text field we 
     // want to manage within the table
    private int fieldlength; // the length of the field
    private System.Data.IDbConnection maincon;
    private System.Data.IDbDataAdapter mada;
    private System.Data.IDbCommand inscom;
    private System.Data.IDbCommand updcom;
    private System.Data.IDbCommand delcom;
    private System.Data.IDbCommand findid;
    private System.Data.DataSet tempset;
    private System.Data.DataTable maintable;

    //private printingdatagrids.SetTableSettings tableset;

    public event dbforms.twofieldformEventHandler Addingrec;
    public event dbforms.twofieldformEventHandler Addedrec;
    public event dbforms.twofieldformEventHandler Updatingrec;
    public event dbforms.twofieldformEventHandler Updatedrec;
    public event dbforms.twofieldformEventHandler Deletingrec;
    public event dbforms.twofieldformEventHandler Deletedrec;

    private System.ComponentModel.Container components = null;
    public bool AllowAdd
    {
       get
       {
           return this.addbut.Enabled;
       }
       set
       {
           this.addbut.Enabled=value;
       }
    }
    public bool AllowUpdate
    {
       get
       {
           return this.updatebut.Enabled;
       }
       set
       {
           this.updatebut.Enabled=value;
       }
    }
    public bool AllowDelete
    {
       get
       {
           return this.delbut.Enabled;
       }
       set
       {
           this.delbut.Enabled=value;
       }
    }
    public bool AllowPrint
    {
       get
       {
           return this.printbut.Enabled;
       }
       set
       {
           this.printbut.Enabled=value;
       }
} …

首先是声明表单中的所有控件。originallabel 是一个特殊的标签,仅用于数据绑定,以便程序可以理解用户是否想修改记录。控件声明之后是 emftitlos,它是写在表单的 Text 属性和 titlelabel 中的字符串,emffield 是写在 textlabel 中的文本(程序还会在 emffield 前面添加一个“:”),tablename 是我们要管理的数据库表的名称,mainfield 是表中的字段名称,fieldlengthmanfield 的长度。

表单的限制是数据库中的表必须有一个 ID 字段,该字段必须是 int 类型并且是 autonumber

在此之后是 maincon,这是表单用于连接到数据库的连接,mada 是用于将所有字段填充到本地数据表的 DataAdapter,inscomupdcomdelcom 是用于从数据库添加、更新、删除记录的命令,findid 是用于查找新添加记录的 ID 的命令,以便我们可以直接将新记录添加到本地数据表中,而无需合并表或重新加载整个表,tempset 是一个临时使用的数据集,这是因为 IDbDataAdapter 接口的 Fill 函数存在限制,最后 maintable 是一个包含表数据的 Data Table。接下来是所有事件的声明,它们都是 twofieldformEventHandler 类型,我将稍后解释。还有属性的声明,这些属性都非常容易理解。

在项目中,除了 twofieldform.cs 文件之外,还有一个名为 twofieldformevent.cs 的文件,其中包含事件的代码。

public class twofieldformEventArgs : System.EventArgs
{
    public bool Cancel;
    public int ID;
    public string newvalue;
    public string oldvalue;
    public string errormessage;
    public twofieldformEventArgs()
    {
       Cancel=false;       
    }
}

public delegate void twofieldformEventHandler(
  object sender, twofieldformEventArgs e);

首先是所有事件中使用的委托,名为 twofieldformEventHandler,它接受两个参数:sender,即触发事件的对象;e,类型为 twofieldformEventArgstwofieldformEventArgs 是一个有 5 个成员的类,六个事件以不同的方式使用这些成员。在 AddingrecUpdatingrecDeletingrec 这三个事件中,如果开发人员愿意,可以通过将 cancel 设置为 true 并将 errormessage 设置为表单将显示的 a message 来取消操作。我们来解释所有情况:

  1. Addingrec:在这种情况下,我们只使用 newvalue,并将其设置为用户想要插入的 mainfield 的值。
  2. Addedrec:如果程序员没有将 cancel 设置为 true,表单会将新记录添加到数据库,并将 ID 字段设置为记录的新 ID。
  3. Updatingrec:在这种情况下,表单将 oldvalue 设置为我们要更改的记录的 mainfield 的值,将 ID 设置为它的 ID,将 newvalue 设置为用户输入的值。
  4. Updatedrec:如果程序员没有将 cancel 设置为 true,表单将使用与 Updatingrec 相同的参数触发 Updatedrec 事件。
  5. Deletingrec:在这里,表单将 oldvalue 设置为用户想要删除的记录的 mainfield 的值,并将 ID 设置为它的 ID。
  6. Deletedrec:最后,如果程序员没有取消操作,表单将使用相同的参数触发 Deleterecrec 事件。

之后是类的构造函数。

public twofieldform(System.Data.IDbConnection thecon,
 string emfanisimostitlos,string emfanisimopedio,
 string thetablename,string mainpedio,int flength)
{
    welcome();
    InitializeComponent();
    emftitlos = emfanisimostitlos;
    emffield=emfanisimopedio;
    tablename=thetablename;
    mainfield = mainpedio;
    fieldlength=flength;
    maincon=thecon;
    this.Text= this.emftitlos;
    this.titlelabel.Text=this.emftitlos;
this.textlabel.Text=this.emffield+":";
    this.maintext.MaxLength=fieldlength;
    preparecommandsandadapters();
    tempset = new System.Data.DataSet();
    try
    {
       mada.Fill(tempset);
       maintable = tempset.Tables[0];
       maintable.TableName="mt";
    }
    catch (System.Exception a)
    {
       MessageBox.Show(a.Message);
    }
    checkifmainfieldexist();
    System.Data.DataColumn[] pk = new System.Data.DataColumn[1];
    pk[0]=maintable.Columns["ID"];
    maintable.PrimaryKey=pk;
}

构造函数接受 emftitlosemffieldtablenamemainfieldfieldlength 的值作为参数,再加上一个 IDbConnection,这是开发人员想要用来连接到数据库的连接。第一行执行 welcome 函数,它只是一个本地化测试(如果你愿意,可以省略它),之后是 InitializeComponent(),这是设计器的函数。这个函数中唯一有点奇怪的是 originallabel 的位置,它位于数据网格下方,所以用户看不到它(代码写得不好,但没时间做得更好,抱歉 J)。之后是一些设置 Text 属性和 UI 的代码,然后执行 preparecommandsandadapters()

preparecommandsandadapters 的第一部分如下:

private void preparecommandsandadapters()
{
    System.Data.IDbDataParameter newpar;
    if (this.maincon is System.Data.SqlClient.SqlConnection)
    {
mada = new System.Data.SqlClient.SqlDataAdapter(
 "SELECT * FROM "+
 this.tablename,(System.Data.SqlClient.SqlConnection)maincon);
 
inscom = new System.Data.SqlClient.SqlCommand(
 "INSERT INTO "+this.tablename+"("+this.mainfield+") 
 VALUES(@mainfield)",(System.Data.SqlClient.SqlConnection)maincon);
 
       newpar = new System.Data.SqlClient.SqlParameter();
       newpar.ParameterName="@mainfield";
       newpar.DbType=System.Data.DbType.String;
       newpar.Size=this.fieldlength;
       inscom.Parameters.Add(newpar);
       updcom = new System.Data.SqlClient.SqlCommand(
 "UPDATE "+this.tablename+" SET "+this.mainfield+
 "=@mainfield WHERE ID=@ID",(System.Data.SqlClient.SqlConnection)maincon);
       newpar = new System.Data.SqlClient.SqlParameter();
       newpar.ParameterName="@mainfield";
       newpar.DbType=System.Data.DbType.String;
       newpar.Size=this.fieldlength;
       updcom.Parameters.Add(newpar);
       newpar = new System.Data.SqlClient.SqlParameter();
       newpar.ParameterName="@ID";
       newpar.DbType=System.Data.DbType.Int32;
       newpar.Size=this.fieldlength;
       updcom.Parameters.Add(newpar);
          
       delcom = new System.Data.SqlClient.SqlCommand(
 "DELETE FROM "+this.tablename+" WHERE ID=@ID",
 (System.Data.SqlClient.SqlConnection)maincon);
       newpar = new System.Data.SqlClient.SqlParameter();
       newpar.ParameterName="@ID";
       newpar.DbType=System.Data.DbType.Int32;
       delcom.Parameters.Add(newpar);
       findid = new System.Data.SqlClient.SqlCommand(
 "SELECT ID FROM "+this.tablename+" WHERE "+this.mainfield+
 "=@mainfield",(System.Data.SqlClient.SqlConnection)maincon);
       newpar = new System.Data.SqlClient.SqlParameter();
       newpar.ParameterName="@mainfield";
       newpar.DbType=System.Data.DbType.String;
       newpar.Size=this.fieldlength;
       findid.Parameters.Add(newpar);
    }
}

之后是 oledb 和 odbc 的相同代码。代码使用 is 运算符检测 maincon 是否为 sqlconnection,并为 sql 初始化所有适配器和命令。另一个要点是,我没有使用命令的 parameters 属性的 Add 函数来在一行中插入参数,我不得不初始化一个对象并设置所有属性来做到这一点,因为在 IdbCommand 接口中,parameters 集合只是一个简单的列表,没有 add 函数。在构造函数中的 preparecommandsandadapters 之后,我初始化了 tempset 并使用 IdbDataAdapter 接口的 Fill 函数填充它,我使用数据集而不是直接使用我的 maintable 成员的原因是,接口的 fill 函数只支持这种模式,并在数据集中创建一个名为“Table”的表,之后我获取该表到 maintable 并更改其名称。此外,使用 checkifmainfieldexist 函数,我对 mainfield 和 ID 字段的存在进行了一些检查,以及表是否有 2 列或更多列,然后我在构造函数中设置主键。

private void checkifmainfieldexist()
{
    System.Resources.ResourceManager resources = 
     new System.Resources.ResourceManager(typeof(twofieldform));
    bool mainfieldexist = false;
    bool IDexist = false;
    foreach (System.Data.DataColumn a in maintable.Columns)
    {
       if (a.ColumnName == this.mainfield) mainfieldexist = true;
       if (a.ColumnName == "ID") IDexist=true;
    }
    if (mainfieldexist==false) 
    {
       throw new Exception(resources.GetString("error1"));
    }
    if (IDexist==false) 
    {
       throw new Exception(resources.GetString("error2"));
    }
    if (maintable.Columns.Count>2)
    {
       throw new Exception(resources.GetString("error3"));
    }
}

我们继续 twofieldform_Load 事件。

private void sqltwofieldform_Load(object sender, System.EventArgs e)
{           
    System.Windows.Forms.DataGridTableStyle maintablestyle = 
     new System.Windows.Forms.DataGridTableStyle();
    maintablestyle.MappingName = "mt";
    System.Windows.Forms.DataGridColumnStyle maincolumnstyle = 
     new System.Windows.Forms.DataGridTextBoxColumn();
    maincolumnstyle.HeaderText=this.emffield;
    maincolumnstyle.MappingName=this.mainfield;
    maincolumnstyle.ReadOnly=true;
       maintablestyle.GridColumnStyles.Add(maincolumnstyle);
    maingrid.TableStyles.Add(maintablestyle);
    maingrid.DataSource = maintable;
    maingrid.DataBindings.Add("Tag",maintable,"ID");
       originallabel.DataBindings.Add(
       "Text",maintable,this.mainfield)       
}

在此事件中,我根据 mainfield 构建了一个数据网格的表样式,并将其添加到数据网格的 TableStyles 集合中。之后,我设置了 maingrid 的数据源并设置了各种数据绑定。originallabel 的数据绑定主要用于知道用户在从网格中选择项目后是否在文本框中进行了任何更改。

现在我将描述用户可以执行的三种可能操作之一。我更喜欢描述更新,因为它稍微复杂一些,另外两种操作以类似的方式完成。对于每种操作,我都有按钮事件和一个实际执行 IdbCommand 的方法。

private void updaterec(int ID,string newvalue)
{
    try
    {
       maincon.Open();
 ((System.Data.IDbDataParameter)updcom.Parameters["@id"]).Value=ID;
 ((System.Data.IDbDataParameter)updcom.Parameters[
  "@mainfield"]).Value=newvalue;
       updcom.ExecuteNonQuery();
       maincon.Close();
    }
    catch (System.Exception a)
    {
       MessageBox.Show(a.Message);
    }
}

我认为该函数非常简单,首先我为命令的参数赋值,然后执行它,所有这些都在 try catch 语句中完成,我转换了 updcom.parameters[“@ID”],因为正如我所说的 IdbCommand.Parameters 集合是一个简单的对象集合。现在是更新按钮的点击事件。

private void updatebut_Click(object sender, System.EventArgs e)
{
   string newv = maintext.Text.ToString();
    System.Data.DataRow[] exrows= maintable.Select(
this.mainfield+"='"+newv+"'");
    if (exrows.Length>0)
    {
       MessageBox.Show("The record allready exist",
"Message",System.Windows.Forms.MessageBoxButtons.OK);
       return;
    }
    else
    {
       System.Data.DataRow therow= 
maintable.Rows.Find(maingrid.Tag.ToString());
       dbforms.twofieldformEventArgs ea = 
new dbforms.twofieldformEventArgs();
       ea.newvalue=newv;
       ea.oldvalue=therow[this.mainfield].ToString();
       ea.ID=int.Parse(maingrid.Tag.ToString());
       if (this.Updatingrec!=null)
       {
           this.Updatingrec(this,ea);
       }
       if (ea.Cancel==false)
       {
           updaterec(int.Parse(maingrid.Tag.ToString()),newv);  
           if(this.Updatedrec!=null)
           {
              this.Updatedrec(this,ea);
           }
              therow[this.mainfield]=newv;
              maintable.AcceptChanges();
           }
           else
           {
 MessageBox.Show(ea.errormessage,this.emftitlos,
  System.Windows.Forms.MessageBoxButtons.OK);
              return;
           }
    }
}     

首先,newv 是一个包含 mainfield 新值的字符串变量,然后我检查该值是否已存在于表中,然后找到用户要更改的行。我使用 find 方法,因为我已经将 ID 设置为数据表的主键,然后我创建一个 twofieldformeventargs 类,其中包含更新事件所需的所有数据,并触发它。要触发事件,您必须先检查它,因为它是一个委托,看看它是否为 null。如果类中的 cancel 变量为 false,我将在数据库中更新(使用 updaterec 方法)并触发下一个更新事件。我还直接更改本地 maintable。如果为 true,我将取消操作并显示带有错误消息字符串的消息框。

结论

我认为上面的代码片段可以极大地帮助一些程序员,他们经常在应用程序中使用这个辅助表,而无需每次需要表时都面对相同的烦人代码。

© . All rights reserved.