Gridview inside a repeater ondataitembound grid view rebinding - asp.net

I've a repeater which has an asp gridview as following :
<asp:Repeater ID="rep" runat="server" OnItemDataBound="rep_ItemDataBound">
<ItemTemplate>
<asp:GridView ID="grdVw" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="Link" HeaderText="Link" />
</Columns>
</asp:GridView>
</ItemTemplate>
</asp:Repeater>
then on page load I bind a datatable that contains 20 rows to this repeater:
this is my page load code
protected void Page_Load(object sender, EventArgs e){
//dummy code for testing
table.Columns.Add("Name");
table.Columns.Add("Link");
DataRow row ;
for (int i = 0; i <20; i++)
{
row = table.NewRow();
row[0] = "dsadsd";
row[1] = "text";
table.Rows.Add(row);
}
for (int i = 0; i < 10; i++)
{
ds.Tables.Add(table.Copy());
}
rep.DataSource = ds;
rep.DataBind();
}
Then I handle the repeater's ItemDataBoundevent like this:
protected void rep_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item)
{
GridView grdVw = e.Item.FindControl("grdVw") as GridView;
grdVw.DataSource = table;
grdVw.DataBind();
}
}
Suprisingly the result is 20 gridviews!! I am thinking this is because ItemDataBound fires on each row in the table. So in this handler event I bind the table 20 times, but is this logically sound or am I doing something wrong?!
I could easily have a flag to make sure that the data has been bound to the table only once, but what if I've bound a dataset to the repeater? How can I handle this binding to the grid views ?

Change this line of code:
rep.DataSource = ds;
to this:
rep.DataSource = ds.Tables;

You can bind a repeater with a dataset but you need to be careful how you handle the tables inside that set.
this:
rep.DataSource = table;
rep.DataBind();
Should be:
rep.DataSource = {Some Dataset with more than one table};
rep.DataBind();
Then in your handler:
protected void rep_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item)
{
GridView grdVw = e.Item.FindControl("grdVw") as GridView;
//EDIT: my vb was showing.
grdVw.DataSource = ((DataTable)e.Item.DataItem).Rows;
grdVw.DataBind();
}
}
This is not tested but it should work with a little fiddling. What you need to know is that within the repeater you need to be referencing the dataItem of the listItem in order to get a reference to the table. Also Tables are not iEnumerable so make sure you are binding on the rows object.

Related

Gridview - Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control

the problem comes from:
double dItemPremium = (Eval("Premium")!= DBNull.Value) ? (double)(Eval("Premium")) : 0;
and I need help changing it.
I am working with a Gridview.
protected void dgCustomer_DataBound(object sender, System.EventArgs e)
{
Label pLabel = new Label();
double dItemPremium = (Eval("Premium")!= DBNull.Value) ? (double)(Eval("Premium")) : 0;
pLabel.Text = dItemPremium.ToString("0.00");
dTotal_m += dItemPremium;
dgCustomer.HeaderRow.TableSection = TableRowSection.TableHeader;
dgCustomer.FooterRow.TableSection = TableRowSection.TableFooter;
dgCustomer.FooterRow.Controls.Add(pLabel);
}
protected void dgCustomer_RowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
dTotal_m += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem,"Premium"));
}
else if (e.Row.RowType == DataControlRowType.Footer)
{
e.Row.Cells[0].Text = "Totals:";
e.Row.Cells[1].Text = dTotal_m.ToString("0.00");
e.Row.Cells[1].HorizontalAlign = HorizontalAlign.Right;
e.Row.Font.Bold = true;
}
}
In HTML I used
<asp:TemplateField>
<ItemTemplate>
<%#DataBinder.Eval(DataItem,"Premium")%></ItemTemplate>
<HeaderTemplate>
Premium Amount Paid</HeaderTemplate>
</asp:TemplateField>
I added DataBinder and DataItem but still nothing.
Thank you!
You can access bound values in code behind using the controls. Eval, Bind, etc methods are to be used to bind values to controls. There are many ways to access bound values in code bebind, one of them is
e.Row.Cells[0].Text
another is putting bound value in Label and accessing it like,
string tempstr=((Label)dgCustomer.Rows[e.RowIndex].Cells[<cell_number>].FindControl("Label_ID")).Text;
In case anyone have this problem, here is another solution: on the aspx file change from <%# to <%=
<ItemTemplate>
<%= DataBinder.Eval(DataItem,"Premium")%></ItemTemplate>
<HeaderTemplate>

Gridview AutoGeneratingColumn Event?

The .NET DataGrid control has an AutoGeneratingColumn Event that fires once for every bound data item right after the data source is changed. This is useful because you could define certain columns in the template, like so:
<Columns>
<asp:HyperLinkField DataNavigateUrlFields="ID" DataNavigateUrlFormatString="ww{0}" DataTextField="ID" DataTextFormatString="{0}" HeaderText="ID" />
</Columns>
and then prevent the same column from being replicated when columns are autogenerated from your data source. In this example, you could prevent the ID column from being autogenerated like this:
Private Sub DG_AutoGeneratingColumn(ByVal sender As Object, ByVal e As DataGridAutoGeneratingColumnEventArgs)
Dim headername As String = e.Column.Header.ToString()
If headername = "ID" Then
e.Cancel = True
End If
End Sub
My question is whether a similar functionality can be achieved with a GridView control.
DETAILS
The data source for the gridview is a DataTable object, which I am binding like so:
GridView1.DataSource = results.Tables("Vitals")
GridView1.DataBind()
The number of columns in my DataTable will vary, which is why it is extremely convenient for me to use AutoGenerateColumns.
To do that you should handle the RowCreated event and write something like the following:
private List<int> hideColumnsIndexes = new List<int>();
protected void Page_Load(object sender, EventArgs e)
{
hideColumnsIndexes.Clear();
}
protected void GridView1_OnRowCreated(object sender, GridViewRowEventArgs e)
{
//Find indexes
if (e.Row.RowType == DataControlRowType.Header)
{
for (int i = 0; i < e.Row.Cells.Count; i++ )
{
if (e.Row.Cells[i].Text == "Id")
{
hideColumnsIndexes.Add(i);
}
//Add more columns to hide
}
}
//Hide cells
foreach (var index in hideColumnsIndexes)
{
e.Row.Cells[index].Visible = false;
}
}

How to add dynamic n checkbox columns to gridview ASP.NET

Ex: mypage.aspx?num=xxx
How can I add a num checkbox column to Gridview in mypages by code-behind?
I had added num columns to Datatable with parameter typeof(bool) but when I loaded mypage, the checkboxes were disabled so I can't check them.
This is my grid aspx code
<asp:GridView ID="gv" runat="server" AutoGenerateColumns="false">
<Columns>
</Columns>
</asp:GridView>
First of all add num number of template fields to your gridview
protected void Page_PreInit(object sender, EventArgs e)
{
int num = Request.QueryString["num"];
for (int i = 0; i < num; i++)
{
TemplateField tf = new TemplateField();
tf.HeaderText = "Status";
gv.Columns.Add(tf);
}
}
After adding template feilds, now we will add checkboxes to the gridview. We write a function to add checkboxes. Below is the code
private void AddCheckBox()
{
int num = Request.QueryString["num"];
for (int i = 0; i < num; i++)
{
foreach (GridViewRow row in gv.Rows)
{
if (row.RowType == DataControlRowType.DataRow)
{
CheckBox cb = new CheckBox();
cb.Checked = true;
row.Cells[i].Controls.Add(cb);
}
}
}
}
Now place this function in your grid databound event.
protected void gv_DataBound(object sender, EventArgs e)
{
AddCheckBox();
}
At the end also call the function in the page load event so first time when grid loads it shows checkboxes are checked
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
AddCheckBox();
}
}
To check my code:
I add a bound feild to grid view and bind the grid view with a datatable:
<asp:GridView ID="gv" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:BoundField DataField="Data" HeaderText="Data" />
</Columns>
</asp:GridView>
and in code behind in page_load event i add the following code
DataTable dt = new DataTable();
dt.Columns.Add("Data");
DataRow dr = dt.NewRow();
dr[0] = "Test";
dt.Rows.Add(dr);
gv.DataSource = dt;
gv.DataBind();
and also made 1 change in the function AddCheckBox as
for (int i = 1; i < num + 1; i++)
The above change is made because I have a databound feild at index 0 of the grid view columns so I changed it to start from 1.
and here is the result ( page output )
Data Status Status Status Status Status Status Status Status Status Status
Test Checked Checked Checked Checked Checked Checked Checked Checked Checked Checked
Checked is used for checkbox is checked
Use:
Request.QueryString["num"]
to get the value of num in the URL. Then iterate num times, adding a CheckBoxField to your GridView each time.

Cannot find selected value of dynamically added controls in repeater

I am creating a survey page that has a list of questions and answers that can be radiobuttonlists, checkboxlists or textboxes. These controls are added dynamically to a Repeater in its ItemDataBound event using Controls.Add.
I've managed to render the page ok but when I submit the form and iterate over the controls in the repeater to get the selectedvalues of the radiobuttons and textbox values, FindControl returns null. What do I need to do to get get the selected values? I've tried iterating over the RepeaterItems but that returned null too. I've tried different types of FindControl but it never resolves the control types.
It works if I add a declarative DataBinder in the Repeater like this
<asp:Repeater ID="rptSurvey" runat="server" Visible="true" EnableViewState="true" >
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem, "Question") %>
</ItemTemplate>
</asp:Repeater>
However, I want want to dynamically add the controls but in doing this i cant get the selectedvalues when submitting. This is tha main structure of my code...
<html>
<asp:Repeater ID="rptSurvey" runat="server" Visible="true">
</asp:Repeater>
<asp:Button ID="btnSubmit" runat="server" Text="Submit" OnClick="btnSubmit_Click" />
</html>
protected void Page_Load(object sender, EventArgs e)
{
...
if (!IsPostBack)
{
rptSurvey.DataSource = GetQuestions();
rptSurvey.DataBind();
}
...
}
protected void rptSurvey_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
string question = (DataBinder.Eval(e.Item.DataItem, "Question")).ToString();
litQuestion = new Literal();
litQuestion.Text = question;
RadioButtonList rblAnswer = (RadioButtonList)item;
rptSurvey.Controls.Add(rblAnswer);
}
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
...
Dictionary<int, string> answers = new Dictionary<int, string>();
try
{
var list = FindControls(rptSurvey, c => c is RadioButtonList || c is CheckBoxList || c is TextBox);
foreach (Control item in list)
{
QuestionId = int.Parse(Questions.Rows[list.IndexOf(item)][0].ToString());
if (item is TextBox)
{
TextBox txtAnswer = (TextBox)item;
answers.Add(QuestionId, txtAnswer.Text);
}
else if (item is RadioButtonList)
{
RadioButtonList rblAnswer = (RadioButtonList)item;
answers.Add(QuestionId, rblAnswer.SelectedItem.Text);
}
else if (item is CheckBoxList)
{
// Iterate through the Items collection of the CheckBoxList
string cblMultiAnswer = "";
for (int i = 0; i < cblAnswer.Items.Count; i++)
{
if (cblAnswer.Items[i].Selected)
{
cblMultiAnswer += cblAnswer.Items[i].Value + ",";
}
}
answers.Add(QuestionId, cblMultiAnswer);
}
}
bSurvey.BLInsertSurveyAnswers(answers, dateCreated, _userEmail);
}
}
public static List<Control> FindControls(Control parent, Predicate<Control> match)
{
var list = new List<Control>();
foreach (Control ctl in parent.Controls)
{
if (match(ctl))
list.Add(ctl);
list.AddRange(FindControls(ctl, match));
}
return list;
}
you have to create the control tree first (always - not only on non-postbacks). do it in the oninit or onpreload event.
look here: https://web.archive.org/web/20211020131055/https://www.4guysfromrolla.com/articles/081402-1.aspx

Why is the footer-item not included in Repeater.Items?

I need to get a value from a textbox inside a FooterTemplate in the OnClick event of a button. My first thought was to loop through the items-property on my repeater, but as you can see in this sample, it only includes the actual databound items, not the footer-item.
ASPX:
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
Item<br />
</ItemTemplate>
<FooterTemplate>
Footer<br />
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
</FooterTemplate>
</asp:Repeater>
<asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
Code-behind.cs:
protected void Page_Load(object sender, EventArgs e)
{
ListItemCollection items = new ListItemCollection();
items.Add("value1");
items.Add("value2");
Repeater1.DataSource = items;
Repeater1.DataBind();
}
protected void Button1_Click(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine(Repeater1.Items.Count);
}
This code will only output "2" as the count, so how do I get to reference my textbox inside the footertemplate?
From the MSDN documentation, the Items is simply a set of RepeaterItems based off the DataSource that you are binding to and does not include items in the Header or FooterTemplates.
If you want to reference the textbox, you can get a reference on ItemDataBound event from the repeater where you can test for the footer.
E.g.
private void Repeater_ItemDataBound(Object Sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Footer)
{
TextBox textBox = e.Item.FindControl("TextBox1") as TextBox;
}
}
You can find controls in the repeater. That will give you all the controls in the repeater (RepeaterItems collection). Now you can do something like this:
RepeaterItem footerItem=null;
foreach(Control cnt in Repeater1.Controls)
{
if(cnt.GetType() == typeof(RepeaterItem) && ((RepeaterItem)cnt).ItemType == ListItemType.Footer)
{
footerItem = cnt;
break;
}
}
The footer should be the last child control of the repeater so you can do something like..
RepeaterItem riFooter = Repeater1.Controls[Repeater1.Controls.Count - 1] as RepeaterItem;
if (riFooter != null && riFooter.ItemType == ListItemType.Footer) {
TextBox TextBox1 = riFooter.FindControl("TextBox1") as TextBox;
if (TextBox1 != null) {
TextBox1.Text = "Test";
}
}

Resources