Delete Button within Repeater does not exist within current context - c#

===================
UPDATE: 29/06/2017
I am trying to get the delete button within my repeater control to function as intended. The aim is to get the button to "fire" the stored procedure within my MSSQL database.
I would like to thank Win for his in-depth response although I am still struggling to resolve the issue. I accept that I was perhaps unable to articulate my question correctly in the first instance. I have therefore edited my post to show the code I have now. I am confident that I am close to cracking the issue and would sincerely appreciate any assistance.
Code within my *.*aspx page:
<asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$
ConnectionStrings:ConnectionString %>" SelectCommand="SELECT * FROM
[Comments] WHERE ([Ad_ID] = #Ad_ID) ORDER BY [CommentCreationDateTime] ASC">
And further down the *.*aspx page:
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="SqlDataSource2"
Visible="True" OnItemCommand="Repeater1_ItemCommand">
<HeaderTemplate></HeaderTemplate>
<ItemTemplate>
<table id="displayCommentsTable" class="displayCommentsTable">
<tr class="displayCommentsTable"><td class="displayCommentsTable">
<asp:ImageButton ID="deleteCommentImageButtonReal" runat="server"
class="rightCross" ImageUrl="images/Red-Cross-Mark-PNG.png"
OnClientClick="return confirm('Are you sure you wish to delete this
comment?');" Height="11" Width="11" CommandName="Delete"
CommandArgument='<%# Eval("Comment_ID") %>' /><%# Eval("CommenterName") %>
commented on <%# Eval("CommentCreationDateTime", "{0:d/M/yyyy <i> hh:mm:ss
tt}") %>
</td></tr>
<tr class="displayCommentsTable"><td class="displayCommentsTable"><%#
Eval("CommentText") %><br /></td></tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
And finally, my code behind where the magic should be happening but isn't:
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName == "Delete")
{
DeleteCommentById(Convert.ToInt32(e.CommandArgument))
}
}
private void DeleteCommentById(int Comment_ID)
{
SqlConnection conn;
SqlCommand deleteCommentById;
string connectionString =
ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
conn = new SqlConnection(connectionString);
deleteCommentById = new SqlCommand("usp_deleteCommentById", conn);
deleteCommentById.CommandType = System.Data.CommandType.StoredProcedure;
deleteCommentById.Parameters.Add("#Comment_ID", System.Data.SqlDbType.Int);
deleteCommentById.Parameters["#Comment_ID"].Value = Comment_ID;
conn.Open();
deleteCommentById.ExecuteNonQuery();
conn.Close();
}
It is perhaps worth mentioning that if I "hard code" the line I am attempting to delete then it works. For example, if I used the following within my delete button:
CommandArgument='44'
then the stored procedure would fire and affect line 44 as intended.

Easiest way is to use ItemCommand event.
<%# Page Language="C#" AutoEventWireup="true"
CodeBehind="RepeaterDemo.aspx.cs" Inherits="DemoWebForm.RepeaterDemo" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:Repeater ID="Repeater1" OnItemCommand="Repeater1_ItemCommand" runat="server">
<ItemTemplate>
<p>
<%#Eval("Name") %>
<asp:ImageButton CommandArgument='<%# Eval("Id") %>' runat="server"
ImageUrl="~/Images/Delete.png" CommandName="Delete" />
</p>
</ItemTemplate>
</asp:Repeater>
</form>
</body>
</html>
Code Behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DemoWebForm
{
public partial class RepeaterDemo : System.Web.UI.Page
{
public class Comment
{
public int Id { get; set; }
public string Name { get; set; }
}
public static IList<Comment> Comments = new List<Comment>
{
new Comment {Id = 1, Name = "One"},
new Comment {Id = 2, Name = "Two"},
new Comment {Id = 3, Name = "Three"}
};
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Repeater1.DataSource = Comments;
Repeater1.DataBind();
}
}
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName == "Delete")
{
int id = Convert.ToInt32(e.CommandArgument);
var comment = Comments.FirstOrDefault(c => c.Id == id);
Comments.Remove(comment);
Repeater1.DataSource = Comments;
Repeater1.DataBind();
}
}
}
}

In what event are you trying to access the repeater button?
You will need to try to find the control inside the repeater item.
For eg:
Button btn1 = (Button)rptItem.FindControl("btn1");

Everything was working fine but as I had not specified to only return results that had not been soft deleted, everything was getting returned. Noob mistake, learnt something for the future!

Related

Print ArrayList data to ASP.NET page using GridView

Trying to print the stored data of arraylist to the gridview.
.aspx File, Working perfectly fine.
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApplication4.WebForm1" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Stock Management System</title>
<style>
body{
background-color:#63AA9C;
text-align:center
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<table align="center" border="0" style="background-color:lightcoral">
<tr><td>Item Name:</td><td><asp:TextBox ID="Name" placeholder="Item Name" runat="server"></asp:TextBox></td></tr>
<tr><td>Quantity:</td><td><asp:TextBox ID="Quan" placeholder="Quantity" runat="server"></asp:TextBox></td></tr>
<tr><td>Price:</td><td><asp:TextBox ID="Pri" placeholder="Price" runat="server"></asp:TextBox></td></tr>
<tr><td>Description:</td><td><asp:TextBox ID="Desc" placeholder="Descrition" runat="server"></asp:TextBox></td></tr>
<tr><td><asp:Button ID="Save" runat="server" Text="Store Data" OnClientClick="Store()" width="200px"/></td></tr>
<tr><td><asp:Button ID="Show" runat="server" Text="Show Data" OnClientClick="Print()" width="200px" /></td></tr>
<tr><td><asp:Button ID="Delete" runat="server" Text="Remove Last Added Data" OnClientClick="Remove()" width="200px" /></td></tr>
</table>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="true" AlternatingRowStyle-BackColor="#C2D69B" HeaderStyle-BackColor="LightCoral" ></asp:GridView>
<asp:GridView ID="GridView2" runat="server" AutoGenerateColumns="true" AlternatingRowStyle-BackColor="#C2D69B" HeaderStyle-BackColor="LightCoral" ></asp:GridView>
<asp:GridView ID="GridView3" runat="server" AutoGenerateColumns="true" AlternatingRowStyle-BackColor="#C2D69B" HeaderStyle-BackColor="LightCoral"></asp:GridView>
<asp:GridView ID="GridView4" runat="server" AutoGenerateColumns="true" AlternatingRowStyle-BackColor="#C2D69B" HeaderStyle-BackColor="LightCoral"></asp:GridView>
</div>
</form>
</body>
</html>
.aspx.cs File
No error, but not working while pressing the show data button.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
namespace WebApplication4
{
public partial class WebForm1 : System.Web.UI.Page
{
ArrayList items = new ArrayList();
ArrayList price = new ArrayList();
ArrayList quantity = new ArrayList();
ArrayList description = new ArrayList();
static int a = 0;
protected GridView GridView1;
protected GridView GridView2;
protected GridView GridView3;
protected GridView GridView4;
protected void Page_Load(object sender, EventArgs e)
{
}
public void Store()
{
items.Add(Name.Text);
price.Add(Pri.Text);
quantity.Add(Quan.Text);
description.Add(Desc.Text);
a++;
}
public void Print()
{
GridView1.DataSource = items;
GridView1.DataBind();
GridView2.DataSource = quantity;
GridView2.DataBind();
GridView3.DataSource = price;
GridView3.DataBind();
GridView4.DataSource = description;
GridView4.DataBind();
}
public void Remove()
{
if (a > 0)
{
items.RemoveAt(a);
a--;
}
}
}
}
My page, Trying to show stored data
About the attribute OnClientClick you are using, The documentation says
Gets or sets the client-side script that executes when a Button control's Click event is raised.
which means you are trying to execute a JavaScript function called "Print" in your code (same goes with other buttons).
You should check Button.Click event instead.
https://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.button.click(v=vs.110).aspx

Asp repeater control

I have a problem with the repeater asp control.
I have a table on my database called course with columns CourseID, CourseName, CourseLink and another table called module with columns ModuleID, ModuleName. And another table called timetable with columns CourseID and ModuleID.
The way this works is when I click on HyperLink1,the paragraph bellow with the the same course id, it would changes the paragraph's style display from hidden to show (I used JavaScript to complete this function so it's not the problem here).
What I would like to know is, how can I list the ModuleNames That relate to that specif course ID.
I was thinking of using another repeater inside a repeater. However that just complicate things and to be hones really confuses me.
How can I get this to work, don't work about the sql because I am comfortable in creating a select command.
Here is the the ASP.NET forum.
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="DataSource1">
<ItemTemplate>
<p>
<a id="HyperLink1" runat="server" href='<%# Eval("courselink") %>'>
<%# Eval("CourseName") %>
</a> <br />
</p>
<p id='<%# Eval("CourseID") %>' style="display:none">
<%# Eval("ModulName") %>
</p>
<br />
</ItemTemplate>
</asp:Repeater>
Thank you for your time and I look forward to finding out what I can do to solve this problem.
You pretty much will need to use another repeater, the trick (if I remember correctly has I haven't used the legacy ASP.NET Web Forms since 2011) is that you hook into the ItemDataBound event in your code behind for the parent repeater and then use the Event Arguments to get the id that you associate to your child table, get that data, lookup the repeater by id from Repeater (I think it's the sender but there is some way to get at it) and bind the data.
I want to note this is much easier and cleaner with ASP.NET MVC and the use of ASP.NET Web Forms is generally discouraged.
It could be the worst this way
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="DataSource1">
<ItemTemplate>
<p>
<a id="HyperLink1" runat="server" href='<%# Eval("courselink") %>'>
<%# Eval("CourseName") %>
</a> <br />
</p>
<p id='<%# Eval("CourseID") %>' style="display:none">
<%# moduleName(Eval("id").ToString()) %>
</p>
<br />
</ItemTemplate>
public string moduleName(string id)
{
string returnValue = "<ul>";
using (SqlConnection con = new SqlConnection(DB))
{
using (SqlCommand com = new SqlCommand("SELECT * FROM module WHERE id = #id", con))
{
com.Parameters.AddWithValue("#id", id);
if (con.State == System.Data.ConnectionState.Closed)
{
con.Open();
using (SqlDataReader dr = com.ExecuteReader())
{
while (dr.Read())
{
returnValue += "<li>"+ dr["modulename"].ToString()+"</li>";
}
}
con.Close();
}
}
}
returnValue += "</ul>";
return returnValue;
}
The best solution would be to create a strongly typed model for your presentation and don't use Eval, you would get something like this in your webform:
<asp:Repeater runat="server" DataSourceID="<%# Model %>">
<ItemTemplate>
<p>
<%# ((Course)Container.DataItem).Name %>
</p>
<p id='<%# ((Course)Container.DataItem).ID %>' style="display:none">
<asp:Repeater runat="server" DataSource="<%# ((Course)Container.DataItem).Modules %>">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li><%# ((Module)Container.DataItem).Name %></li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
</p>
</ItemTemplate>
Then in your code behind create the following property:
public List<Course> Model
{
get
{
List<Course> courses = new List<Course>();
// Some example data, in your situation you should instantiate the classes based on the data from the database.
Course exampleCourse = new Course();
exampleCourse.ID = 1;
exampleCourse.Name = "Example course";
// Create example module 1
Module exampleModule1 = new Module();
exampleModule1.ID = 10;
exampleModule1.Name = "Example Module 1";
// Create example module 2
Module exampleModule2 = new Module();
exampleModule2.ID = 11;
exampleModule2.Name = "Example Module 2";
// add modules to the course
exampleCourse.Modules.Add(exampleModule1);
exampleCourse.Modules.Add(exampleModule2);
// add course to the courses
courses.Add(exampleCourse);
return courses;
}
}
Classes needed:
public class Course
{
public int ID { get; set; }
public string Link { get; set; }
public string Name { get; set; }
public List<Module> Modules { get; set; }
public Course()
{
this.Modules = new List<Module>();
}
}
public class Module
{
public int ID { get; set; }
public string Name { get; set; }
}
Last, in your page load, don't forget to databind:
protected void Page_Load(object sender, EventArgs e)
{
this.DataBind();
}

Set checkbox as checked in Repeater/CheckboxList

I'm using a Repeater to show some data coming from a web service.
My Repeater structure is:
<asp:Repeater ID="rptgrp" runat="server">
<ItemTemplate>
<asp:CheckBoxList ID="chkBoxListGoup" runat="server"
DataSource='<%# DataBinder.Eval(Container.DataItem, "Titles")%>'
DataTextField="Title"
DataValueField="IDTitle">
</asp:CheckBoxList>
</ItemTemplate>
</asp:Repeater>
Now, my web service returns these fields in "Titles":
1) Title
2) IDTitle
3) isUserTitle
Now, I would like to set checked a checkbox when isUserTitle is = 1.
How can I do that?
You can find checkboxlist as follows
Find checkboxlist in itemdatabound,
check item text of every checkboxlist using loop,
select the item whose text is 1
Protected void Repeater_ItemDataBound(Object Sender, RepeaterItemEventArgs e) {
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
CheckBoxList chklst = (CheckBoxList)e.Item.FindControl("chkBoxListGoup");
for (int i = 0; i < chk.Items.Count; i++)
{
if (chk.Items[i].Text == "1")
{
chk.Items[i].Selected = true;
}
}
}
}
Try changing <asp:CheckBoxList ID="chkBoxListGoup" runat="server"
to
<asp:CheckBoxList ID="chkBoxListGoup" Checked='<%#Eval("Flag")%>' runat="server"
Flag being your Column..
Then in your method or event handler you want to run some code to say if this value = 1 checked, elseif value = 0 unchecked...
Here is sample code that demonstrates the idea:
Code-behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI.WebControls;
using WebApp.RepeaterCheckboxList.TODODatasetTableAdapters;
namespace WebApp.RepeaterCheckboxList
{
public partial class WebForm1 : System.Web.UI.Page
{
IEnumerable<TODODataset.TasksViewRow> view;
IEnumerable<TODODataset.TasksViewRow> subview;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
TasksViewTableAdapter adp = new TasksViewTableAdapter();
var dt = adp.GetData();
view = dt.AsEnumerable();
var names = (from x in view
select new
{
Person = x.Name,
ID = x.PersonID
}).Distinct();
DataList1.DataSource = names;
DataList1.DataBind();
}
}
protected void CheckBoxList1_DataBound(object sender, EventArgs e)
{
CheckBoxList theList = (CheckBoxList)sender;
var person = ((DataListItem)theList.Parent).DataItem as dynamic;
var name = person.Person;
var id = person.ID;
var vw = subview;
for (int i = 0, j = vw.Count(); i < j; i++)
{
var task = vw.ElementAt(i);
theList.Items[i].Selected = task.Completed;
}
}
protected IEnumerable<TODODataset.TasksViewRow> GetTasks(object data)
{
var vw = data as dynamic;
return subview = (from x in view
where x.PersonID == vw.ID
select x);
}
}
}
Aspx:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApp.RepeaterCheckboxList.WebForm1" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:DataList ID="DataList1" runat="server">
<ItemTemplate>
<div style="padding:5px">
<h3><%# Eval("Person") %></h3>
<div>
<asp:CheckBoxList OnDataBound="CheckBoxList1_DataBound" ID="CheckBoxList1"
runat="server"
DataTextField="TaskDesc" DataValueField="TaskID"
DataSource="<%# GetTasks(Container.DataItem) %>"></asp:CheckBoxList>
</div>
</div>
</ItemTemplate>
</asp:DataList>
</div>
</form>
</body>
</html>
If you are interested in the data, click here
Try just setting the Checked value to the object being Evaled.
<asp:Repeater ID="rptgrp" runat="server">
<ItemTemplate>
<asp:CheckBoxList ID="chkBoxListGoup" runat="server"
Checked=<%# Eval("isUserTitle") %>>
</asp:CheckBoxList>
</ItemTemplate>
</asp:Repeater>

asp.net C# - DataList2 Refresh based on the value of DataList1

I am new to ASP.NET and C#. I want to refresh the 2nd DataList (DataList2) when a user clicks on the (+) sign.
Datalist1 will list all Company records with a column that has a (+) sign. If a user clicks the (+), it will expand (DataList2) below the company and list all the contact names.
Please help. Thanks!
<%# Page Language="C#" AutoEventWireup="true" CodeFile="CompanyList.aspx.cs" Inherits="_CompanyList" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<link id="Link1" rel="stylesheet" runat="server" media="screen" href="/Apps/ERP/ERP_Styles.css" />
</head>
<body>
<form id="form1" runat="server">
<table style="width:100%" border="0">
<tr>
<td style="width:20%"></td>
<td style="width:60%">
<p class="PageTitle">Company List</p>
</td>
<td style="width:20%">
<asp:Button ID="myBtn" runat="server" Text="Click me"
OnClientClick="window.parent.location.href='/Apps/ERP/ASPX/UploadContact/UploadContact.aspx'; return false;" />
</td>
</tr>
</table>
<%-- ////////////////////////////////////// Start Data Row ///////////////////////////////////////// --%>
<table width="595px">
<asp:DataList BackColor="#ffffff" id="DataList1" DataSourceID="dsCompanyList" runat="server" Width="100%">
<ItemTemplate>
<tr>
<td>
<asp:LinkButton ID="LinkButton1" runat="server" Text="+" CommandArgument='<%#Container.ItemIndex%>'
OnCommand="LinkButton1_Command"
></asp:LinkButton>
</td>
<td><%#Eval("Row")%></td>
<td><%#Eval("Company")%></td>
</tr>
<asp:Panel ID="pnlChildView" runat="server">
<asp:DataList ID="DataList2" runat="server" Width="100%">
<ItemTemplate>
<tr>
<td><%#Eval("FirstName")%></td>
<td><%#Eval("LastName")%></td>
</tr>
</ItemTemplate>
</asp:DataList>
</asp:Panel>
</ItemTemplate>
</asp:DataList>
</table>
<asp:SqlDataSource ID="dsCompanyList" runat="server"
ConnectionString="<%$ ConnectionStrings:ConnApps %>"
SelectCommand="SELECT ROW_NUMBER() OVER (ORDER By MatchFlag ASC) AS ROW
, Company FROM (
SELECT DISTINCT(Company) AS Company, MatchFlag
--,InsertFlag, MatchFlag
FROM dbo.Import_CompanyContact icc
WHERE RefNum = #RefNum
AND MatchFlag = 2
) a "
>
<SelectParameters>
<asp:QueryStringParameter Name="RefNum" QueryStringField="RefNum" DefaultValue="0" Type="Int16" />
</SelectParameters>
</asp:SqlDataSource>
</form>
</body>
</html>
The code behind .cs file
using System;
using System.Collections.Generic;
//using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class _CompanyList : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void LinkButton1_Command(object sender, CommandEventArgs e)
{
//pass index of item in command argument
int itemIndex = Convert.ToInt32(e.CommandArgument);
//depending on your needs bind the details on demand
//or preload during ItemDataBound
Panel childViewPanel = (Panel)DataList1.Items[itemIndex].FindControl("pnlChildView");
if (childViewPanel != null)
{
//toggle visibility of childViewPanel and bind child list if panel is visible
if (childViewPanel.Visible)
{
DataList childList = (DataList)childViewPanel.FindControl("DataList2");
if (childList != null)
{
int keyValue = (int)DataList1.DataKeys[itemIndex];
//bind the list using DataList1 data key value
childList.DataSource = "SELECT FirstName, LastName FROM dbo.Import_CompanyContact WHERE Company = 'Applied Micro'"; //get data using keyValue
childList.DataBind();
}
}
}
}
}
There are a few things needed to make this work for you, but first - please consider using stored procedures or at least parameterized queries.
You are attempting to access the .DataKeys of the "parent" DataList but you never included that on your definition. In order to accurately do this, you're going to need to change your source query to include some key value (instead of just ROW_NUMBER() (not a key) and Company):
<asp:DataList BackColor="#ffffff"
id="DataList1"
DataSourceID="dsCompanyList"
runat="server"
Width="100%"
DataKeyField="YourKeyField">
Then, you'll need to alter your code-behind to set the .DataSource of your "child" DataList...and while I'm using your query, please consider using something other than dynamic SQL:
protected void LinkButton1_Command(object sender, CommandEventArgs e)
{
int itemIndex = Convert.ToInt32(e.CommandArgument);
Panel childViewPanel = (Panel)DataList1.Items[itemIndex].FindControl("pnlChildView");
if (childViewPanel != null)
{
if (childViewPanel.Visible)
{
DataList childList = (DataList)childViewPanel.FindControl("DataList2");
if (childList != null)
{
int keyValue = (int)DataList1.DataKeys[itemIndex];
using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["YourConnectionString"].ConnectionString))
{
con.Open();
using (SqlCommand cmd = new SqlCommand("SELECT FirstName, LastName FROM dbo.Import_CompanyContact WHERE Company = " + keyValue, con))
{
cmd.CommandType = CommandType.Text;
using (SqlDataReader dr = cmd.ExecuteReader())
{
childList.DataSource = dr;
childList.DataBind();
}
}
}
}
}
}
}

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