原文:ImplementingDynamicItemTemplates
By:ScottWatermasysk
Published:4/10/2002
翻译:Drason
模版控件能让用户几乎不用花费任何时间就创建出复杂的用户界面.Asp.net有很多控件都使用了模版技术(DataGrid就是一个例子).而这些控件都工作得很好,通常,模版可以被保存为ascx文件以增加复用性.很有可能,事前你是不知道你的控件是怎么布局的,而且你需要动态的添加一些模版以应付不同的事件.
使用模版的另一个优势,就是它们能动态的添加到你的控件里面去.这样的话,你可以事先设计好模版,然后通过简单的几行代码就添加到你的控件中.
下面这篇文章就要告诉你如何如何一步步的添加一个动态的ItemTemplate和EditItemTemplate到DataGrid中.另外,还会告诉你怎么获取和更新用户对EditItemTemplate所做的改变.例子将会是很简单的.然后,我很快就会在TripleASP上面正式发布一个改进后的TableEditor版本.这个版本将更好的说明如何使用动态模版.
ITempalte的实现
为了能动态的添加ItemTemplate和EditItemTemplate,我们需要创建2个类来实现ITemplate的接口(Interface).第一个类是GenericItem.这个类的主要工作就是:取数据源的列名,创建一个文本控件(literalcontral),为这个文本控件赋值,最后把这个文本控件加到父控件(在这里父控件就是DataGrid了).
到目前为止还是很顺利.在继续下面的讨论之前,我们来看看代码和完成的步骤.
usingSystem;
usingSystem.Web;
usingSystem.Data;usingSystem.Web.UI;
usingSystem.Web.UI.WebControls;
namespaceTripleASP.ItemTemplates
{
///<summary>
///SummarydescriptionforGenericItem.
///</summary>
publicclassGenericItem:ITemplate
{
privatestringcolumn;
//privateboolvalidate;
publicGenericItem(stringcolumn)
{
this.column=column;
}
publicvoidInstantiateIn(Controlcontainer)
{
Literall=newLiteral();
l.DataBinding+=newEventHandler(this.BindData);
container.Controls.Add(l);
}
publicvoidBindData(objectsender,EventArgse)
{
Literall=(Literal)sender;
DataGridItemcontainer=(DataGridItem)l.NamingContainer;
l.Text=((DataRowView)container.DataItem)[column].ToString();
}
}
}
正如你看到的,GenericItem类实现了ITemplate的接口(interface).因为我们是实现接口,所以必须包括InstantiateIn这个方法.这个方法是用来定义所有子控件和模版所属的控件对象的.在这个方法里面,我们创建了一个新的Literal控件来保存DataGrid的单元值.接着,我们添加了DataBinding事件处理函数.这个事件处理函数实际上就是在DataGrid绑定数据的时候,把单元值放到Literal控件的Text属性中.最后,把这个Literal控件加入到控件的容器集合中.很简单吧?
动态EditItemTemplate
动态EditItemTemplate类ValidateEditItem跟GenericItem很类似,但是有3个地方不同.
第一个不同的地方是,我们添加的是Textbox控件而不是Literal控件.这样的话,在编辑模式下,用户可以做任何修改.
第二个不同的地方,你会发现我们会显式地命名控件.这会使我们能够获取更新事件中的任何数据变化.
最后一个不同,你会看到一个跟Textbox相联系的RequiredFieldValidator控件.这是可选的.但是,这的确让你知道有些事是可以这样做的.
下面就是ValidateEditItem的代码:
usingSystem;
usingSystem.Data;
usingSystem.Web.UI;
usingSystem.Web.UI.WebControls;
usingSystem.Web;
namespaceTripleASP.ItemTemplates
{
///<summary>
///SummarydescriptionforValidateEditItem.
///</summary>
publicclassValidateEditItem:ITemplate
{
privatestringcolumn;
publicValidateEditItem(stringcolumn)
{
this.column=column;
}
publicvoidInstantiateIn(Controlcontainer)
{
TextBoxtb=newTextBox();
tb.DataBinding+=newEventHandler(this.BindData);
container.Controls.Add(tb);
tb.ID=column;
RequiredFieldValidatorrfv=newRequiredFieldValidator();
rfv.Text="PleaseAnswer";
rfv.ControlToValidate=tb.ID;
rfv.Display=ValidatorDisplay.Dynamic;
rfv.ID="validate"+tb.ID;
container.Controls.Add(rfv);
}
publicvoidBindData(objectsender,EventArgse)
{
TextBoxtb=(TextBox)sender;
DataGridItemcontainer=(DataGridItem)tb.NamingContainer;
tb.Text=((DataRowView)container.DataItem)[column].ToString();
}
}
}
动态模版的实现
现在我们已经有两个实现了ITempalte接口的类了.一切准备好了!我们现在要做的就是把它们加入到我们的datagrid里面.
我们把BindData和DynamicColumns两个方法放在一起.BindData主要是创建SQL查询语句,往datagrid添加列(动态列),然后把数据表绑定到datagrid.
voidBindData()
{
stringsql="Select*frompublishersWhereStateIsnotnull";
DataGrid1.Columns.Add(DynamicColumns("pub_id",false));
DataGrid1.Columns.Add(DynamicColumns("pub_name",true));
DataGrid1.Columns.Add(DynamicColumns("city",true));
DataGrid1.Columns.Add(DynamicColumns("state",true));
DataGrid1.Columns.Add(DynamicColumns("country",true));
DataGrid1.DataKeyField="pub_id";
DataGrid1.DataSource=GetDataTable(sql);
DataGrid1.DataBind();
}
DynamicColumns有两个参数:column(字符类型)和isEditable(布尔类型).column变量当然就是我们要加入TemplateColumn的列名.isEditable变量是用作测试的,如果我们希望这个列是允许编辑的话.
protectedTemplateColumnDynamicColumns(stringcolumn,boolisEditable)
{
TemplateColumngenericcolumn=newTemplateColumn();
genericcolumn.HeaderText=column;
genericcolumn.ItemTemplate=newGenericItem(column);
if(isEditable)
{
genericcolumn.EditItemTemplate=newValidateEditItem(column);
}
returngenericcolumn;
}
正如你所看到的,首先我们实例化一个TemplateColumn(genericcolumn),根据我们要添加的列的名字设置HeaderText属性(当然,你可以设置为任何东西都可以).接着,我们通过添加新的GenericItem的参考(reference),把ItemTemplate添加到genericcolumn,并把名称传入.最后,我们必须检查isEditable,以便看看我们需不需要允许编辑这个列.如果为真,我们要往ValidateEditItem添加新的参考,而且把列名也传过去.
DataGrid事件
我们的编辑和取消事件是很标准的.你有可能已经看过它们100遍了.在我们的编辑事件里面,我们简单地取出被选中的行的编号,然后重新绑定数据.
protectedvoidEdit_Click(Objectsender,DataGridCommandEventArgse)
{
DataGrid1.EditItemIndex=e.Item.ItemIndex;
BindData();
}
我们的取消事件是把当前所选行号设为-1.这样就等于告诉datagrid,不在是编辑模式了.然后,我们重新绑定数据.
protectedvoidCancel_Click(Objectsender,DataGridCommandEventArgse)
{
DataGrid1.EditItemIndex=-1;
BindData();
}
更新事件会跟你以前看到的有一点点不同.然而,它却会让你想起你在ASP的日子.
protectedvoidUpdate_Click(Objectsender,DataGridCommandEventArgse)
{
//GetstheUniqueIDthatisattachedtothefrontofeachtextbox
//dyamicallyaddedtoourdatagrid'sEditItemTempate
stringuid=e.Item.UniqueID+":";
stringpub_id=(string)DataGrid1.DataKeys[e.Item.ItemIndex];
stringpub_name=(Request.Form[uid+"pub_name"].ToString());
stringcity=(Request.Form[uid+"city"].ToString());
stringstate=(Request.Form[uid+"state"].ToString());
stringcountry=(Request.Form[uid+"country"].ToString());
//SimplemethodtoupdateDB
UpdateRecord(pub_id,pub_name,city,state,country);
DataGrid1.EditItemIndex=-1;
BindData();
}
这样的话,EditItemTemplate就硬编码到页面中去了.你可能已经看过一些取表单提交数据的例子,其中的方法,或者是通过控件位置取值,或者是控件名称取值.但是,如果你是在运行时创建控件,那么,在PostBack的时候,ASP.NET是无法取得这些值的.为此,我们只能通过Request.Form的方法来得到这些值.
在你开始在ValidateEditItem类里面仔细寻找被小心命名的textbox的时候,你必须记住,ASP.NET已经为控件的名字冲突做了预防措施.一般来说,这包括增加每个datagrid父控件的名称,datagrid本身的名称,和一个代表每个textbox的序号的字符串放在textbox的ID前面.我们可以大量的使用这样的方法.但是这并不保证我们的代码绝对的模块化和可复用.相反,我们检查DataGridCommandEventArgs.Item.UniqueID并在尾部加上":".有了这个UniqueID,我们就可以安全地取得textbox里面的编辑数据,并更新到数据库.
结论
动态添加模版到你的模版控件会在开始的时候增加一点点的工作量.但是,一旦你建立了一系列的优秀的模版类,你会发现,实现ITemplate会非常的快速和容易.它运行你建立强大的控件来满足你数据操作的需要.如果你需要更好的例子,请看我即将发布在TripleASP的TableEditor控件.