Display message in repeater when no row found - c#

I am using ASP.NET repeater and I want to display No Row Found message when query return 0 rows from database. I know its there in GridView.
Regards

If you have a HeaderTemplate or a FooterTemplate defined, you could add any HtmlControl or ServerControl inside of either of them and then programatically show/hide it in the codebehind.
<asp:Repeater id="Repeater1" runat="server" OnItemDataBound="">
<HeaderTemplate>
<h1>My Repeater Data</h1>
<div id="NoRecords" runat="server" visible="false">
No records are available.
</div>
</HeaderTemplate>
<ItemTemplate>
...
</ItemTemplate>
</asp:Repeater>
Here's the code behind
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (Repeater1.Items.Count < 1)
{
if (e.Item.ItemType == ListItemType.Header)
{
HtmlGenericControl noRecordsDiv = (e.Item.FindControl("NoRecords") as HtmlGenericControl);
if (noRecordsDiv != null) {
noRecordsDiv.Visible = true;
}
}
}
}

Think about using the ListView control instead that has an EmptyDataTemplate for use when the data source has no data. If you opt to stick with the Repeater control, think about testing your query for records and optionally displaying a Label or Literal that has your "no row found" message instead of your repeater.
if (query.Any())
{
repeater.DataSource = query;
repeater.DataBind();
}
else
{
noRecordsLiteral.Visible = true;
}

Here is a simple example to demonstrate Displaying No Record Found in a Repeater control.
<div id="NoRecords" runat="server" visible="false">
No records are available.
</div>
<asp:Repeater id="Repeater1" runat="server" OnPreRender="Repeater1_PreRender">
<HeaderTemplate>
<h1>My Repeater Data</h1>
</HeaderTemplate>
<ItemTemplate>
...
</ItemTemplate>
</asp:Repeater>
Here is the Code-Behind
protected void Repeater1_PreRender(object sender, EventArgs e)
{
if (Repeater1.Items.Count < 1)
{
NoRecords.Visible = true;
Repeater1.Visible = false;
}
}

You could do this by altering the logic in the repeater or by providing data to the repeater that drives the behavior you want. I prefer that the repeater is left out of it. It's a keep your logic out of the view thing if you are familiar with MVC.
I am saving space here by using a List as the data source instead of a database result but the principle is the same. You would probably have a collection of IDataRecord as your source if you are returning from a DB.
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;
using System.Linq;
namespace StackOverflowRepeater
{
public partial class Default : System.Web.UI.Page
{
protected override void OnInit(EventArgs e)
{
repeater.ItemDataBound += HandleRepeaterItemDataBound;
var data = new List<string>();
if (!data.Any()) // could also be data.Count < 1
{
data.Add("No Row Found");
}
repeater.DataSource = data;
repeater.DataBind();
base.OnInit(e);
}
void HandleRepeaterItemDataBound (object sender, RepeaterItemEventArgs e)
{
if ((e.Item.ItemType == ListItemType.AlternatingItem) || (e.Item.ItemType == ListItemType.Item))
{
var span = (HtmlGenericControl) e.Item.FindControl("output");
span.InnerText = e.Item.DataItem.ToString();
}
}
}
}
This is assuming the following mark-up:
<%# Page Language="C#" Inherits="StackOverflowRepeater.Default" %>
<html>
<body>
<form runat="server">
<asp:Repeater id='repeater' runat="server">
<ItemTemplate>
<span id='output' runat="server" />
</ItemTemplate>
</asp:Repeater>
</form>
</body>
</html>

I done something following;
<asp:Repeater ID="Repeater" runat="server">
<HeaderTemplate>
<table>
<% if (Repeater.Items.Count < 1)
{ %>
<tr>
<td colspan="3">No data found.</td>
</tr>
<% }
else { %>
<tr>
<th>Case Id</th>
<th>Claim Id</th>
<th>Notes</th>
</tr>
<% } %>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td rowspan="2"><%# Eval("CaseId") %></td>
<td rowspan="2">
<%# Eval("ClaimId") %>
</td>
<td><%# Eval("Note") %></td>
</tr>
<tr>
<td><%# Eval("CreatedBy") %> : <%# Eval("CreatedDate") %></td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>

You can also do it like this:
CSS
.div-no-display {
display: none;
}
Code Behind - Usually don't bind it if its empty in that case you have to put the div outside the repeater, but if you bind 0 items you can put it in the header template
this.Repeater.DataSource = ChildLinksList;
this.Repeater.DataBind();
public List<Link> ChildLinksList { get; set; }
Page
<HeaderTemplate>
<div id="NoRecords" class='<%= ChildLinksList.Count > 0 ? "div-no-display" : "" %>'>
No child links active.
</div>
</HeaderTemplate>

Related

How to send data from Datalist using button to different pages in asp.net?

This is the design page that contains my datalist. Also a button within datalist which will pass data to another page on clicking.
Product.aspx
<%# Page Title="" Language="C#" MasterPageFile="~/Home.Master" AutoEventWireup="true" CodeBehind="Products.aspx.cs" Inherits="GasBookingPortal.Products" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
<style type="text/css">
</style>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<asp:DataList ID="DataListProduct" ItemStyle-Width="500px" ItemStyle-BorderStyle="Solid" runat="server" RepeatColumns="2" RepeatDirection="Horizontal" RepeatLayout="Table" EnableViewState="false">
<ItemStyle BorderStyle="Solid" Width="500px"></ItemStyle>
<ItemTemplate>
<table>
<tr>
<td style="border-width: 1px">
<img width="100" src='Product_Images/<%# Eval("Product_Image") %>' />
</td>
</tr>
<tr>
<td style="border-width: 1px">
Product Name
</td>
<td>
<%# Eval("Product_Name") %>
</td>
<tr>
<td style="border-width: 1px">
Product ID
</td>
<td>
<%# Eval("P_ID") %>
</td>
</tr>
</tr>
<tr>
<td style="border-width: 1px">
Product Price
</td>
<td>
<%# Eval("Product_Price") %>
</td>
</tr>
<tr>
<td style="border-width: 1px">
Product Category
</td>
<td>
<%# Eval("Product_Category") %>
</td>
</tr>
<tr>
<td style="border-width: 1px">
Product Description
</td>
<td>
<%# Eval("Product_Description") %>
</td>
</tr>
<tr>
<td style="border-width: 1px">
<asp:Button ID="btnBook" runat="server" Text="BOOK" OnClick="btnBook_Click"/>
</td>
</tr>
</table>
</ItemTemplate>
</asp:DataList>
</asp:Content>
This is the code behind. Here I want to write button click event for redirection. Also want to pass few data. I don't know what exactly I have to use for example datalist_itemcommand event or button_click event to redirect into another page and to pass data from datalist.
Product.aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;
namespace GasBookingPortal
{
public partial class Products : System.Web.UI.Page
{
SqlConnection con = new SqlConnection(WebConfigurationManager.ConnectionStrings["gbpcon"].ToString());
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
GetDataFromTable();
BindDataToDataList();
}
}
public void GetDataFromTable()
{
SqlDataAdapter da = new SqlDataAdapter("select Product_Image,Product_Name,Product_Price,P_ID,Product_Category,Product_Description from Product", con);
DataSet ds = new DataSet();
da.Fill(ds, "table_product");
DataTable dt = ds.Tables["table_product"];
//Session["Product_Category"] = dt.Rows[0]["Product_Category"].ToString();
Session["da"] = da;
Session["dt"] = dt;
}
public void BindDataToDataList()
{
DataListProduct.DataSource = (DataTable)Session["dt"];
DataListProduct.DataBind();
}
protected void btnBook_Click(object sender, EventArgs e)
{
Response.Redirect("PaymentGateway.aspx");
}
}
}
Question: I want to send Product_ID, Product_Name, Product_Price to another page Payment.aspx whenever someone will click on BOOK button within datalist. On clicking BOOK button the Product page should redirect into Payment page alongwith few data. The button within datalist can be any button like normal button, or image button or link button. Please guide me how to do this. Trying to solve this problem since last 4 days.
Please tell me how to do this. Will be very grateful.
I have tried with all possible ways from google and youtube but none of them worked.
tried...?
Session["Product_ID"] = Product_ID;
Session["Product_Name"] = Product_Name;
Session["Product_Price"] = Product_Price; Response.Redirect("PaymentGateway.aspx");
In your *.aspx code bind your data like <%# DataBinder.Eval(Container.DataItem,"P_ID") %> instead of <%# Eval("P_ID") %> to identify your each column uniquely in code-behind.
Code-Behind:
protected void btnBook_Click(object sender, EventArgs e)
{
// get clicked item of datalist
DataListItem dli = (sender as Button).NamingContainer as DataListItem;
// access clecked data from datalist
string id = DataBinder.Eval(dli, "P_ID").ToString();
string name = DataBinder.Eval(dli, "P_name").ToString();
// ... other fields
string sendingData = id + "," + name;
// send data using query string
Response.Redirect("PaymentGateway.aspx?data="+sendingData);
}
Payment.aspx page: Access data from query string like:
string data = Request.QueryString["data"];
string[] dArr = data.Split(',');
string id = dArr[0]+"";
string name = dArr[1] + "";
// ... other fields
Edit: For image use asp control:
<asp:Image ID="Image1" runat="server" ImageUrl='Product_Images/<%# Eval("Product_Image") %>' />
Edit 2: Another solution is:
Add product id and other values as CommandArgument to your button:
<asp:Button ID="btnBook" runat="server" Text="BOOK" OnClick="btnBook_Click"
CommandArgument='<%# Eval("P_ID") %>'/>
In button's OnClick method get value(s) from CommandArgument and store in Session DATA:
Button btn = sender as Button;
Session["DATA"] = btn.CommandArgument.ToString();
Response.Redirect("PaymentGateway.aspx");
And, finally get data from session anywhere an your project files/pages:
string sessionData = (string)Session["DATA"];

How to access a legend tag from code

I have the following data-bound repeater code :
<%--categories--%>
<asp:Repeater ID="CategoryRepeater" runat="server" OnItemDataBound="ItemBound">
<ItemTemplate>
<div class="groupbox">
<fieldset>
<legend><%# Container.DataItem %></legend>
<table>
<asp:Repeater ID="ItemRepeater" runat="server">
<ItemTemplate>
<tr>
<td>
<asp:CheckBox id="chkItem" runat="server" Text='<%# Eval("Text")%>' />
<asp:HiddenField id="pgNos" runat="server" Value='<%# Eval("PGNos")%>' />
<asp:Button ID="btnXRefs" Text="x-refs" runat="server" CssClass="xRefButton" OnClick="btnSelectXRefs_Click" />
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
</fieldset>
</div>
</ItemTemplate>
</asp:Repeater>
There is a repeater inside a repeater. How do I access the text inside the legend (<legend><%# Container.DataItem %></legend>) from code?
I tried :
foreach (RepeaterItem cr in CategoryRepeater.Items)
{
string heading = (string) cr.DataItem; // returns null
}
Container.DataItem is a runtime alias for the DataItem for this specific item in the bound list. For a Repeater which displays 10 rows of data, this is one row from the datasource...Basically, it's a specific row and at runtime you can get the Property Values from this row
I saw your above Mark Up...Seems like you are missing to mention the property of Data-Bound type Class like below.
<%# ((Your Class Name)Container.DataItem).Class Property Name %>
There is a repeater inside a repeater. How do I access the text inside
the legend (<%# Container.DataItem %>) from code?
As told by phemt.latd, you can change the Legend tag into server side control like below.
<legend id="lg" runat="server">
<%# ((Your Class Name)Container.DataItem).Class Property Name %>
</legend>
Now, in the Item-Bound Data Event, Find the Legend Control.
protected void rpt_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item ||
e.Item.ItemType == ListItemType.AlternatingItem)
{
HtmlGenericControl ctl = (HtmlGenericControl)e.Item.FindControl("lg");
ctl.InnerText //This is what will give you the result.
}
}
The legend tag that you use is not visible on server side. Is a client control and not a server one.
Try with this:
<legend id="myLegend" runat="server"><%# Container.DataItem %></legend>
Then in codebehind:
protected void ItemBound(Object sender, RepeaterItemEventArgs e)
{
if (e.Item.DataItem == null) return;
HtmlGenerics body = (HtmlGenerics)e.Item.FindControl("myLegend");
body.InnerText = "Foo";
}

Bind 5 items in each row of repeater

I have a set of items coming from the database. Their number may vary. I have bound them in a repeater. Now my following example will explain what I want:
I have 11 items coming from database, I want them to be grouped in terms of 5 items per row.
1st row: 5 items.
2nd row: 5 items.
3rd row: 1 item.
Currently, I am just binding them in a repeater. How do I do this?
Yes. It is possible:
<asp:Repeater ID="rptItems" runat="server">
<ItemTemplate>
<asp:Literal runat="server" Text='<%# Eval("Value") %>'></asp:Literal>
<div style="clear: both" runat="server" Visible="<%# (Container.ItemIndex+1) % 5 == 0 %>"></div>
</ItemTemplate>
</asp:Repeater>
It produces following results for the sequence of numbers:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
if you can use ListView, then you can use GroupItemCount . some thing like this MSDN Example
<asp:ListView ID="ContactsListView"
DataSourceID="yourDatasource"
GroupItemCount="5"
runat="server">
<LayoutTemplate>
<table id="tblContacts" runat="server" cellspacing="0" cellpadding="2">
<tr runat="server" id="groupPlaceholder" />
</table>
</LayoutTemplate>
<ItemTemplate>
<div> your Items here </div>
</ItemTemplate>
<GroupTemplate>
<tr runat="server" id="ContactsRow" style="background-color: #FFFFFF">
<td runat="server" id="itemPlaceholder" />
</tr>
</GroupTemplate>
<ItemSeparatorTemplate>
<td runat="server" style="border-right: 1px solid #00C0C0"> </td>
</ItemSeparatorTemplate>
</asp:ListView>
If you want to stick with a Repeater, I can think of two approaches.
Firstly, you could stick with a flat list of items and make the repeater insert a "new line" after each 5th item. You should be able to do this in the <ItemTemplate> with a block like
<% if ((Container.DataItemIndex % 5) == 4) { %>
</div>
<div>
<% } %>
which honestly isn't very nice.
Alternatively, you could use MoreLINQ's Batch method to batch your items up into IEnumerables of 5, and then use two nested repeaters to render them. Set the outer repeater to wrap the inner repeater in <div> tags, and set the inner repeater's DataSource='<%# Container.DataItem %>'. This should result in much cleaner markup.
You can try below, I mistakenly said ListView, actually I meant DataList
<asp:DataList ID="DataList1" runat="server" RepeatColumns="5"
RepeatDirection="Horizontal" RepeatLayout="Flow">
<ItemTemplate >
<%--Your Item Data goes here--%>
</ItemTemplate>
</asp:DataList>
You may use nested Data controls (i.e Repeater) and also handle the OnItemDataBound event to bind the inner Repeater.
Sample Data Source component:
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public static List<List<Item>> getItems()
{
List<Item> list = new List<Item>()
{
new Item(){ ID=11, Name="A"},
new Item(){ ID=12, Name="B"},
new Item(){ ID=13, Name="C"},
new Item(){ ID=14, Name="D"},
new Item(){ ID=15, Name="E"},
};
/* Split the list as per specified size */
int size = 2;
var lists = Enumerable.Range(0, (list.Count + size - 1) / size)
.Select(index => list.GetRange(index * size,
Math.Min(size, list.Count - index * size)))
.ToList();
return lists;
}
}
Markup (.aspx)
<asp:Repeater ID="outerRepeater"
runat="server" onitemdatabound="outerRepeater_ItemDataBound"
>
<ItemTemplate>
<p>
Row
</p>
<asp:Repeater ID="innerRepeater"
runat="server">
<ItemTemplate>
<asp:Literal ID="literal1" runat="server" Text='<%# Eval("ID") %>' />
<asp:Literal ID="literal2" runat="server" Text='<%# Eval("Name") %>' />
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
Code-behind
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
outerRepeater.DataSource = Item.getItems();
outerRepeater.DataBind();
}
}
protected void outerRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
Repeater repeater = e.Item.FindControl("innerRepeater") as Repeater;
repeater.DataSource = Item.getItems()[e.Item.ItemIndex];
repeater.DataBind();
}
<asp:Repeater ID="Repeater1" runat="server"
OnItemDataBound="Repeater1_databinding">
<HeaderTemplate>
<table id="masterDataTable" class="reportTable list issues" width="100%">
<thead>
<tr>
<asp:Literal ID="literalHeader" runat="server"></asp:Literal>
</tr>
</thead>
<tbody>
</HeaderTemplate>
<ItemTemplate>
<tr>
<asp:Literal ID="literals" runat="server"></asp:Literal>
</tr>
</ItemTemplate>
<FooterTemplate>
</tbody> </table>
</FooterTemplate>
</asp:Repeater>
<input id="hdnColumnName" runat="server" clientidmode="Static" type="hidden" />
<input id="hdnColumnOrder" runat="server" clientidmode="Static" type="hidden" />
// javascript Function
<script type="text/javascript">
$(document).ready(function () {
$('#ddlReport').removeClass('required');
$('.sort').click(function () {
$('#hdnColumnName').val($(this).text());
$('#hdnColumnOrder').val($(this).attr('class'));
$(this).toggleClass("desc asc");
$("#lnkSort").click();
});
});
</script>
// Bind repeater
DataTable dt = objReport.GetCustomRecord();
fn = new List<string>();
for (int i = 0; i < dt.Columns.Count; i++)
{
if (dt.Columns[i].ColumnName != "Maxcount" )
{
fn.Add(dt.Columns[i].ColumnName);
}
}
Repeater1.DataSource = dt;
Repeater1.DataBind();
protected void Repeater1_databinding(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Header)
{
if (e.Item.FindControl("literalHeader") != null)
{
StringBuilder sb = new StringBuilder();
Literal li = e.Item.FindControl("literalHeader") as Literal;
fieldName().ForEach(delegate(string fn)
{
if (hdnColumnName.Value != fn.ToString())
{
sb.Append("<th width=\"10%\"> <a id=\"btnCustomerName\" class=\"sort desc\" onclick=\"btnSorts_onclick()\" style=\"cursor:pointer;text-decoration: none !important;\" >"
+ fn.ToString() + "</a></th>");
}
else
{
if (hdnColumnOrder.Value == "sort asc")
sb.Append("<th width=\"10%\"> <a id=\"btnCustomerName\" class=\"sort desc\" onclick=\"btnSorts_onclick()\" style=\"cursor:pointer;text-decoration: none !important;\" >"
+ fn.ToString() + "</a></th>");
else
sb.Append("<th width=\"10%\"> <a id=\"btnCustomerName\" class=\"sort asc\" onclick=\"btnSorts_onclick()\" style=\"cursor:pointer;text-decoration: none !important;\">"
+ fn.ToString() + "</a></th>");
}
});
li.Text = sb.ToString();
}
}
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
if (e.Item.FindControl("literals") != null)
{
DataRowView drv = (DataRowView)e.Item.DataItem;
Literal li = e.Item.FindControl("literals") as Literal;
StringBuilder sb = new StringBuilder();
fieldName().ForEach(delegate(string fn)
{
sb.Append("<td>" + drv[fn.ToString()] + "</td>");
});
li.Text = sb.ToString();
}
}
}

Page cycle question asp.net

I am using nested repeaters. I render several groups and every group has members that I need to render out different properties to.
I am using ItemDataBound on the nested repeater to find out which member the nested repeater is rendering out at the moment. I have <% %> tags in the .aspx which checks if the current user has a certain property, and if it has, I render the divs and other HTML elements that need to go there.
All the data is correct. When I debug I collect the correct data and I get the right info about who the user is and what properties the user has. I save the current evaluated user in a protected string currentRenderedUser that I use to find out who the user is in the aspx file.
The problem is that by the time the <% %> on the aspx gets evaluated the repeater has already gone through all the sets or something, because the currentRenderedUser is the same during every evalutaion in the aspx file.
I don't know the correct terminology for "<% %>" code in the aspx file, sorry.
This is the relevant parts of the aspx (I think):
<ItemTemplate>
<!-- nested repeater data -->
<tr>
<td width="50%"><div class="memberName"><%# DataBinder.Eval(Container.DataItem, "userEmail" )%></div></td>
<div style="display: none;"></div>
<% if (CheckIfUserIsAdmin(email, currentRenderedGroup))
{ %>
<%if (CheckIfUserIsAdmin(currentRenderedUser, currentRenderedGroup))
{ //Checks so that the user being rendered isn't already admin%>
<td><div style="margin-right:30px;" class="makeAdminButton"></div></td>
<% }
else
{%>
<td><div style="margin-right:30px;" class="makeAdminButton"><asp:ImageButton ID="ImageButtonMakeUserAdmin" CommandName="MakeAdmin" BorderWidth="0" ImageUrl="~/gfx/doAdmin.png" CommandArgument='<%#Convert.ToString(DataBinder.Eval(Container.DataItem, "userEmail" )) + " " + Convert.ToString(DataBinder.Eval(((RepeaterItem)Container.Parent.Parent).DataItem, "groupId"))%>' runat="server" AlternateText="Make admin" /></div></td>
<td><div class="removeUserButton"><asp:ImageButton ID="ImageButtonRemoveUserFromGroup" CommandName="RemoveUserFromGroup" BorderWidth="0" ImageUrl="~/gfx/can.png" CommandArgument='<%# Convert.ToString(DataBinder.Eval(Container.DataItem, "userEmail" )) + " " + Convert.ToString(DataBinder.Eval(((RepeaterItem)Container.Parent.Parent).DataItem, "groupId"))%>' runat="server" AlternateText="Delete" /></div></td>
<% } %>
<%}%>
</tr>
</ItemTemplate>
And this is the part of the back-end that evaluates the current user:
protected void NesterRepeater_ItemDataBound(object sender,
System.Web.UI.WebControls.RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.AlternatingItem
|| e.Item.ItemType == ListItemType.Item)
{
DataRowView dataRowView = (DataRowView)e.Item.DataItem;
string memberInGroup = Convert.ToString(dataRowView["userEmail"]);
currentRenderedUser = memberInGroup;
}
}
How can I solve this in a better (or atleast, working) way?
Thanks
The problem is that the event ItemDataBound is executed first so you probably always get the value of the last record from the Repeater's DataSource if I'm not mistaken.
It is better to do it this way:
<ItemTemplate>
<tr>
<td width="50%"><div class="memberName"><%# DataBinder.Eval(Container.DataItem, "userEmail" )%></div></td>
<div style="display: none;"></div>
<asp:PlaceHolder ID="phNoAdmin" runat="server" Visible="false">
<td><div style="margin-right:30px;" class="makeAdminButton"></div></td>
</asp:PlaceHolder>
<asp:PlaceHolder ID="phAdmin" runat="server" Visible="false">
<td><div style="margin-right:30px;" class="makeAdminButton"><asp:ImageButton ID="ImageButtonMakeUserAdmin" CommandName="MakeAdmin" BorderWidth="0" ImageUrl="~/gfx/doAdmin.png" CommandArgument='<%#Convert.ToString(DataBinder.Eval(Container.DataItem, "userEmail" )) + " " + Convert.ToString(DataBinder.Eval(((RepeaterItem)Container.Parent.Parent).DataItem, "groupId"))%>' runat="server" AlternateText="Make admin" /></div></td>
<td><div class="removeUserButton"><asp:ImageButton ID="ImageButtonRemoveUserFromGroup" CommandName="RemoveUserFromGroup" BorderWidth="0" ImageUrl="~/gfx/can.png" CommandArgument='<%# Convert.ToString(DataBinder.Eval(Container.DataItem, "userEmail" )) + " " + Convert.ToString(DataBinder.Eval(((RepeaterItem)Container.Parent.Parent).DataItem, "groupId"))%>' runat="server" AlternateText="Delete" /></div></td>
</asp:PlaceHolder>
</tr>
</ItemTemplate>
And the code behind:
protected void NesterRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item)
{
DataRowView dataRowView = (DataRowView)e.Item.DataItem;
PlaceHolder phNoAdmin = e.Item.FindControl("phNoAdmin") as PlaceHolder;
PlaceHolder phAdmin = e.Item.FindControl("phAdmin") as PlaceHolder;
string memberInGroup = Convert.ToString(dataRowView["userEmail"]);
currentRenderedUser = memberInGroup;
if (CheckIfUserIsAdmin(email, currentRenderedGroup))
{
if (CheckIfUserIsAdmin(currentRenderedUser, currentRenderedGroup))
{
phNoAdmin.Visible = true;
phAdmin.Visible = false;
}
else
{
phNoAdmin.Visible = false;
phAdmin.Visible = true;
}
}
}
}
It would be better if you bind the data inside the 2 PlaceHolder control I wrote also in code behind for maintainability reason.
HTH

advancing to button code-behind when validation fails... how to solve it without Page.IsValid?

I have an ASP .NET page with both asp .net validators and some javascript checking too.
I am advancing into the button code behind:
protected void Button2_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
/// ...
Even when the validation fails!
The Page.IsValid check solves it, but it also resets all the JavaScript variables… May there be a way not to advance into the button code?
I have an ajax update panel and some image buttons on the page too… Anything I may look out for?
Thanks in advance!
Here is my aspx:
<%# Page Language="C#" AutoEventWireup="true"
CodeBehind="WebForm2.aspx.cs"
Inherits="WebForm2" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div style="width: 500px;">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<script type="text/javascript">
var nrOptions = 0;
alert(nrOptions + " - variable initialized");
function IncrementQuantity() {
nrOptions++;
alert(nrOptions);
}
function DecrementQuantity() {
nrOptions--;
alert(nrOptions);
}
function MoreThanTwo() {
alert(nrOptions);
if (nrOptions > 1) {
alert('okay - you have: ' + nrOptions);
return true;
}
else {
alert('error - must have at least two options, you only have: ' + nrOptions);
return false;
}
}
</script>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Repeater ID="Repeater1" runat="server" OnItemCommand="Repeater1_ItemCommand">
<HeaderTemplate>
Fill in with two or more options:<br />
<table border="1" width="100%">
</HeaderTemplate>
<ItemTemplate>
<tr>
<td valign="middle">
</td>
<td valign="middle">
Description:
<asp:TextBox ID="TBox1" runat="server" Width="120px" Text='<%# Eval("Desc")%>'></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="TBox1"
ValidationGroup="InsertVal" ErrorMessage="*"></asp:RequiredFieldValidator>
Points:
<asp:TextBox ID="TBox2" runat="server" Width="20px" Text='<%# Eval("Pont")%>'></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ControlToValidate="TBox2"
ValidationGroup="InsertVal" ErrorMessage="*"></asp:RequiredFieldValidator>
<asp:Button ID="ImageButton1" runat="server" Text="x" CommandName="DeleteRow" OnClientClick="DecrementQuantity();" />
<asp:RegularExpressionValidator ID="RegularExpressionValidator1" ControlToValidate="TBox2"
ValidationExpression="\d+" ValidationGroup="InsertVal" runat="server"
ErrorMessage="Number >= 0."></asp:RegularExpressionValidator>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
<tr>
<td colspan="2" align="right">
<asp:Button ID="lnkAddRow" runat="server" Text="Add option" OnClientClick="IncrementQuantity();"
CommandName="AddRow" OnClick="lnkAddRow_Click" />
</td>
</tr>
</table>
</FooterTemplate>
</asp:Repeater>
<br />
<p style="text-align: right;">
<asp:Button ID="Button2" runat="server" Text="Save" OnClick="Button2_Click" OnClientClick="return MoreThanTwo();"
ValidationGroup="InsertVal" />
</p>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
And my code-behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
public partial class WebForm2 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
DTable = EmptyDTOptions();
Repeater1.DataSource = DTable;
Repeater1.DataBind();
}
}
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName == "AddRow")
{
DTable.Rows.Add(0, "", "");
Repeater1.DataSource = DTable;
Repeater1.DataBind();
return;
}
if (e.CommandName == "DeleteRow")
{
int idx = e.Item.ItemIndex;
DTable.Rows.RemoveAt(idx);
Repeater1.DataSource = DTable;
Repeater1.DataBind();
return;
}
}
protected void lnkAddRow_Click(object sender, EventArgs e)
{
foreach (RepeaterItem item in Repeater1.Items)
{
int idx = item.ItemIndex;
TextBox tb1 = (TextBox)item.FindControl("TBox1");
TextBox tb2 = (TextBox)item.FindControl("TBox2");
DTable.Rows[idx]["Desc"] = tb1.Text;
DTable.Rows[idx]["Pont"] = tb2.Text;
}
}
protected void Button2_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
// save!
}
}
private DataTable DTable
{
get
{
DataTable _dt = (DataTable)Session["DTable"];
if (Object.Equals(_dt, null))
{
_dt = EmptyDTOptions();
Session.Add("DTable", _dt);
}
return _dt;
}
set
{
Session["DTable"] = value;
}
}
DataTable EmptyDTOptions()
{
DataTable dt = new DataTable();
DataColumn dc;
dc = new DataColumn("OptionNr", System.Type.GetType("System.Int32"));
dt.Columns.Add(dc);
dc = new DataColumn("Desc");
dt.Columns.Add(dc);
dc = new DataColumn("Pont");
dt.Columns.Add(dc);
return dt;
}
}
I think it shows what I'm trying to avoid... Advancing to the button2_click with the failed validation (and resetting the javascript variable)... In order to get a list of two or more pairs of items, one of them being a number.
Rather than calling your function from the OnClientClick on the button, you can add a CustomValidator that calls your JavaScript function.
<asp:CustomValidator ID="CheckMoreThanTwo" runat="server"
ValidationGroup="InsertVal"
ClientValidationFunction="MoreThanTwo" />
Then, modify your JavaScript function as follows:
function MoreThanTwo(source, arguments) {
alert(nrOptions);
if (nrOptions > 1) {
alert('okay - you have: ' + nrOptions);
arguments.IsValid=true;
} else {
alert('error - must have at least two options, you only have: '
+ nrOptions);
arguments.IsValid=false;
}
}
Doing this allows your custom JavaScript validation to work with all the validation code that ASP.NET uses. For instance, if you change to a validation summary, or change validation groups, this custom validator will continue to work the way the rest of the validators work.

Categories