views:

493

answers:

2

Hi, I have a gridview to which I'm adding template fields programmatically. Each of the template fields have a textbox. I would like to make this text box have 2-way binding to a database column. Please see below code.

public class CustomEditItemTemplate : ITemplate
{
private DataControlRowType templateType;
private string columnName;

public CustomEditItemTemplate(DataControlRowType type, string colname)
{
    this.templateType = type;
    this.columnName = colname;
}

public void InstantiateIn(System.Web.UI.Control container)
{
TextBox tb = new TextBox();
tb.ID = columnName;
tb.DataBinding += new EventHandler(this.tb_DataBinding);
container.Controls.Add(tb);
}

private void tb_DataBinding(Object sender, EventArgs e)
{
    TextBox t = (TextBox)sender;
    DetailsView dv = (DetailsView)t.NamingContainer;

    //This line does only one way binding. It takes the rows from the database and displays
    //them in the textboxes. The other way binding is not done. This is why my code fails
    t.Text = DataBinder.Eval(dv.DataItem, columnName).ToString();        
}

}

I'm calling the above class as follows

tf = new TemplateField();
tf.HeaderText = "My First Names";
tf.EditItemTemplate = new CustomEditItemTemplate(DataControlRowType.DataRow, "firstName");
dvModify.Fields.Add(tf);

How can I make the text box such that when I edit the text, this change is reflected in the database as well?

Thanks for your time everyone

A: 

Perhaps you could changed the line t.Text = DataBinder.Eval(dv.DataItem, columnName).ToString(); to something like t.Text= string.Format("<%# Bind(\"{0}\") %>", columnName); ?

This is just a guess...

If that doesn't work, I found some articles that actually write new classes for handling two way databinding:

Article at CodeProject circa 2005

Article at Programmer's Heaven

Hopefully one of these options will be useful.

Amanda Myer
A: 

It's actually not too bad to do template fields programatically, after you've seen it once, of course. Here's how we do it, abridged:

TemplateField tf = new TemplateField();
//Do some config like headertext, style, etc;

tf.ItemTemplate = new CompiledTemplateBuilder(delegate(Control container)
{
   //Add the regular row here, probably use a Label or Literal to show content
   Label label = new Label();
   lable.DataBinding += delegate(object sender, EventArgs e)
   {
        label.Text = ((MyDataType)DataBinder.GetDataItem(label.BindingContainer)).MyLabelDataField;
   };
});

tf.EditItemTemplate = new CompiledBindableTemplateBuilder(delegate(Control container)
{
   //Here we do the edit row.  A CompiledBindableTemplateBuilder lets us bind in both directions
   TextBox text = new TextBox();
   text.DataBound += delegate(object sender, EventArgs e)
   {
       text.Text = ((MyDataType)DataBinder.GetDataItem(text.BindingContainer)).MyLabelDataField;
   }
},
delegate(Control container)
{
   OrderedDictionary dict = new OrderedDictionary();
   dict["myLabelDataColumn"] = ((TextBox)container.FindControl(text.ID)).Text;
   return dict;
});

So here's what's going on. We use CompiledBindableTemplateBuilder and CompiledTemplateBuilder to actually build our template field's contents. Using a delegate is just an easy way to set this up.

The key to answering your question is in the second argument to the CompiledBindableTemplateBuilder, which is a delegate establishing the binding. After an edit, during submit, the template field will call ExtractValuesFromCell and recover an IBindableTemplate, from which it will extract a Dictionary and then pull the value(s) out of it, adding them to it's own dictionary which eventually gets turned into the uploaded data. So that's why you return an OrderedDictionary from the Binding delegate. Hope that helps!

CodexArcanum