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>
Related
I'm working on a asp.net project. I've been asked to create a page so site admins could add articles and everyone else could add comments under those articles.
I have 2 separate tables in my db, Articles/Comments. On page load I'm populating all the articles with their own related comments within a panel.
I've also been asked to use an accordion so all comments would display in a collapsible manner!
My problem is that only the first article shows up with collapsible comments under it, and the rest do not!
Here is my aspx page:
<%# Page Title="Ref Notes" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="RefNotes.aspx.cs" Inherits="Root.RefNotes" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<asp:Panel ID="refNotes" CssClass="col-xs-12 data-container" runat="server">
</asp:Panel>
</asp:Content>
aspx.cs code:
public partial class RefNotes : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
populateNotes();
Page.Title = "Latest Ref Notes";
}
protected void populateNotes()
{
string tag = "";
if (Request.QueryString["tag"] != null) tag = Request.QueryString["tag"];
int post = 0;
if (Request.QueryString["post"] != null) post = Int32.Parse(Request.QueryString["post"]);
MediaService.Article[] articles = Global.mediaService.GetArticles(1, 1); //gets all articles
for (int i = 0; i < articles.Length; i++)
{
if (post > 0 && articles[i].ID != post) continue;
Root.Controls.Blog.RefNotesPost p = (Root.Controls.Blog.RefNotesPost)LoadControl("~/Controls/Blog/RefNotesPost.ascx");
p.SetData(articles[i]);
refNotes.Controls.Add(p);
Root.Controls.Blog.CommentControl c = (Root.Controls.Blog.CommentControl)LoadControl("~/Controls/Blog/CommentControl.ascx");
c.SetData(articles[i].ID);
refNotes.Controls.Add(c);
}
if (articles.Length == 0)
{
Literal l = new Literal();
l.Text = "<h1>No content currently available.</h1>";
refNotes.Controls.Add(l);
}
}
}
CommentControl.ascx code:
# Control Language="C#" AutoEventWireup="true" CodeBehind="CommentControl.ascx.cs" Inherits="Root.Controls.Blog.CommentControl" %>
<div class="row">
Comments:
<asp:Panel ID="errorPanelID" runat="server" CssClass="loginbox-textbox" Visible="false" Style="margin-top: 20px;">
<div class="alert alert-danger fade in">
<button class="close" data-dismiss="alert">
×
</button>
<i class="fa-fw fa fa-times"></i>
<asp:Label ID="errorMsgID" runat="server"></asp:Label>
</div>
</asp:Panel>
</div>
<div id="dvAccordion" style="width: auto">
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<h3>
<asp:Label ID="Label1" runat="server" Text='<%#Eval("user") + " at " + Eval("dateTime") + " says:"%>'></asp:Label></h3>
<div style="background-color: #CFDEE3">
<asp:Literal ID="lit" runat="server" Text='<%#Eval("comment")%>' Mode="Transform" />
</div>
</ItemTemplate>
</asp:Repeater>
</div>
<div>
Add a Comment:<br />
<asp:TextBox ID="txtComment" runat="server" Rows="5" TextMode="MultiLine"></asp:TextBox>
<br />
<asp:Button ID="Button1" runat="server" OnClick="saveBtn_Click" Text="Submit " />
<asp:HiddenField runat="server" ID="articleID" Value="0" />
</div>
<script type="text/javascript">
$(function () {
$("#dvAccordion").accordion();
});
</script>
CommentControl.ascx.cs code:
public partial class CommentControl : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
public void SetData(int artID)
{
Services.Comment[] comments = Global.Tools.GetComments(artID);
articleID.Value = artID.ToString();
if (comments.Length > 0)
{
Repeater1.Visible = true;
Repeater1.DataSource = comments;
Repeater1.DataBind();
}
else
{
Repeater1.Visible = false;
}
}
protected void saveBtn_Click(object src, EventArgs e)
{
AdminService.Employee emp = Global.Tools.GetEmployee(Int32.Parse(Session["eid"].ToString()));
Blog.Comment a = new Blog.Comment();
a.CommentOn = txtComment.Text;
a.CreatedBy = emp.username;
a.DatePosted = DateTime.Now;
a.isVisible = 1;
a.ArticleID = int.Parse(articleID.Value);
if (Request.QueryString["post"] != null)
{
a.ID = Int32.Parse(Request.QueryString["post"]);
}
int result = Global.Tools.CreateComment(a);
if (result <= 0)
{
errorMsgID.Text = "Failed to create comment.";
errorPanelID.Visible = true;
}
else
{
Response.Redirect("/News.aspx");
}
}
txtComment.Text = string.Empty;
}
In the CommentControl.ascx file I had set the accordion based on id (#dvAccordion). I changed it to be based on class (.dvAccordion) and that solved the issue.
So for future references: when you're on a page an "id" can only be called once but a "class" can be called multiple times!
I am trying to generate a dynamic radio button list, but the issue is that the dynamic list does not show on page load. Only the Submit button shows. Here is the code I am using:
<asp:PlaceHolder runat="server" ID="PlaceHolder1"/>
<asp:Button runat="server" ID="Button1" OnClick="Button1_Click" Text="Submit" />
<asp:Label runat="server" ID="Label1"/>
protected void Page_Load(object sender, EventArgs e)
{
LoadControls();
}
protected void Button1_Click(object sender, EventArgs e)
{
var radioButtonList = PlaceHolder1.FindControl("1") as RadioButtonList;
Label1.Text = radioButtonList.SelectedValue;
}
private void LoadControls()
{
var tmpRBL = new RadioButtonList();
tmpRBL.ID = "1";
for (int i = 1; i <= 5; i++)
{
var tmpItem = new ListItem(i.ToString(), i.ToString());
tmpRBL.Items.Add(tmpItem);
}
PlaceHolder1.Controls.Add(tmpRBL);
}
WebForm1.aspx
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:PlaceHolder runat="server" ID="PlaceHolder1"/>
<asp:Button runat="server" ID="Button1" OnClick="Button1_Click" Text="Submit" />
<asp:Label runat="server" ID="Label1"/>
</div>
</form>
</body>
</html>
WebForm1.aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebApplication1
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
LoadControls();
}
protected void Button1_Click(object sender, EventArgs e)
{
var radioButtonList = PlaceHolder1.FindControl("1") as RadioButtonList;
Label1.Text = radioButtonList.SelectedValue;
}
private void LoadControls()
{
var tmpRBL = new RadioButtonList();
tmpRBL.ID = "1";
for (int i = 1; i <= 5; i++)
{
var tmpItem = new ListItem(i.ToString(), i.ToString());
tmpRBL.Items.Add(tmpItem);
}
PlaceHolder1.Controls.Add(tmpRBL);
}
}
}
It works correct. I hope it will be helpful.
You could define an empty RadioButtonList control which you then populate with items on Page_Load. Would have the advantage that you don't have to create it manually. Also using the DataBind mechanism the control offers you together with an ObjectDataSource is a better solution (the Select method of the ObjectDataSource should return the items).
But aside this, maybe AutoEventWireup of the #Page directive (first line in the markup of the ASPX page) is set to false. If so the Page_Load event handler is not called. The directive usually looks like this:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApplication1.WebForm1" %>
I copied the rest of your code and everything runs just fine.
I am stuck at a problem where i really didn't expect to get held up so many hours and it's driving me nuts
As mentioned in the title i need two DropDownLists where the ToYears List has values starting with the selection of FromYear:
FromYear
2000
2001
2002
2003
2004
ToYear
2002
2003
2004
So i tried to use 2 <asp:dropdownlist> and change the selected ToYear during the SelectedIndexEvent of FromYear but this was triggering the selected FromYear event and somehow it wouldn't fire again.
Now i found the CascadingDropDown from the Ajax Control Toolkit and thought this might be a good thing. But i don't want to call a web service, instead i would like to use a Method in the code behind the actual page.
Also the selection should be remembered after a postback - and the range of years changes depending on properties in the Code behind.
I read somewhere that autopostback does not work with the CascadingDropDown.
What would you think would be the most elegant and easy solution?
Thank you very much in advance.
EDIT: i am going to post a few parts of my post - hope that helps
Markup:
<asp:DropDownList AutoPostBack="True" ID="DropDownFromYear" runat="server" OnSelectedIndexChanged="FromYearChanged" />
<asp:Label ID="UntilLabel" runat="server" Text=" until " />
<asp:UpdatePanel ID="ToYearUpdatePanel" runat="server" style="display: inline-block;">
<ContentTemplate>
<asp:DropDownList AutoPostBack="true" ID="DropDownToYear" runat="server" OnSelectedIndexChanged="ToYearChanged" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="DropDownFromYear" EventName="SelectedIndexChanged" />
</Triggers>
</asp:UpdatePanel>
CodeBehind (called from OnInit):
private void InitializeDropDownYears()
{
//Calculate the YearMin YearMax Properties
CalculateYearMinMax();
int adaptedFromYear = 0, adaptedToYear = 0;
//get the previously selected Years
if (DropDownToYear.SelectedItem != null) adaptedToYear = int.Parse(DropDownToYear.SelectedValue);
if (DropDownFromYear.SelectedItem != null) adaptedFromYear = int.Parse(DropDownFromYear.SelectedValue);
//check the minimum year constraints 2005 was selected but minYear is 2010 -> adpated is set to 2010
if (YearMin > adaptedFromYear || adaptedFromYear == 0) adaptedFromYear = YearMin;
if (YearMax < adaptedToYear || adaptedToYear == 0) adaptedToYear = YearMax;
//check the 5 year range constraint
if ((YearMax - YearMin) > 5)
{
adaptedFromYear = DateTime.Now.Year - 2;
adaptedToYear = DateTime.Now.Year + 2;
}
Dictionary<string, string> toYears = new Dictionary<string, string>();
Dictionary<string, string> fromYears = new Dictionary<string, string>();
for (int tempYear = YearMin; tempYear <= YearMax; tempYear++)
{
fromYears.Add(tempYear.ToString(), tempYear.ToString());
if (tempYear >= adaptedFromYear)
{
toYears.Add(tempYear.ToString(), tempYear.ToString());
}
}
DropDownFromYear.DataSource = fromYears;
DropDownFromYear.DataValueField = "Key";
DropDownFromYear.DataTextField = "Value";
DropDownFromYear.SelectedValue = adaptedFromYear.ToString();
DropDownFromYear.DataBind();
DropDownToYear.DataSource = toYears;
DropDownToYear.DataValueField = "Key";
DropDownToYear.DataTextField = "Value";
DropDownToYear.SelectedValue = adaptedToYear.ToString();
DropDownToYear.DataBind();
if(!IsPostBack)
{
SelectedFromYear = adaptedFromYear;
SelectedToYear = adaptedToYear;
}
}
private void CalculateYearMinMax()
{
IList<Task> taskList = CurrentLicense.TaskList;
List<DateTime> startDates = taskList.Select(task => task.StartDate).ToList();
YearMin = startDates.Min(date => date).Year;
List<DateTime> endDates = taskList.Select(task => task.EndDate).ToList();
YearMax = endDates.Max(date => date).Year;
}
EventHandler:
protected void FromYearChanged(object sender, EventArgs e)
{
SelectedFromYear = int.Parse(DropDownToYear.SelectedValue);
SelectedToYear = int.Parse(DropDownFromYear.SelectedValue);
if (SelectedFromYear > SelectedToYear)
{
SelectedToYear = SelectedFromYear;
}
UpdateGanttTables();
}
protected void ToYearChanged(object sender, EventArgs e)
{
SelectedFromYear = int.Parse(DropDownToYear.SelectedValue);
SelectedToYear = int.Parse(DropDownFromYear.SelectedValue);
UpdateGanttTables();
}
Filling DropDownList controls might sound easy right? and it is, when you use the default behavior of ASP.NET WebForms. However you could face several problems when you want to get specific functionality, for example, in WebForms, trying to fill DropDownLists using AJAX (this is a real pain, and the only solution I have found is to disable the security check on the page <%# Page EnableEventValidation="false")
For reference:
How to fill an asp:DropDown client side?
Since you are using WebForms probably the best way is to use the gross UpdatePanel
Example:
Result
ASPX markup
<asp:ScriptManager runat="server" ID="sm" />
<asp:UpdateProgress runat="server" AssociatedUpdatePanelID="updatePanel" DisplayAfter="0" DynamicLayout="true">
<ProgressTemplate>
Working...
</ProgressTemplate>
</asp:UpdateProgress>
<asp:UpdatePanel runat="server" ID="updatePanel">
<ContentTemplate>
<div>
<asp:Label ID="Label1" Text="From" runat="server" AssociatedControlID="from" />
</div>
<div>
<asp:DropDownList runat="server" ID="from" AutoPostBack="true" CausesValidation="false" OnSelectedIndexChanged="from_SelectedIndexChanged">
</asp:DropDownList>
</div>
<div>
<asp:Label ID="Label2" Text="To" runat="server" AssociatedControlID="to" />
</div>
<div>
<asp:DropDownList runat="server" ID="to" />
</div>
</ContentTemplate>
</asp:UpdatePanel>
Page code behind
private const int MaxYear = 2030;
private const int MinYear = 1959;
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
var fromRange = Enumerable.Range(MinYear, MaxYear - MinYear);
this.from.DataSource = fromRange;
this.from.DataBind();
}
}
protected void from_SelectedIndexChanged(object sender, EventArgs e)
{
var selectedYear = Convert.ToInt32(this.from.SelectedValue);
var toRange = Enumerable.Range(selectedYear, MaxYear - selectedYear);
this.to.DataSource = toRange;
this.to.DataBind();
}
I just uploaded this sample to my GitHub for reference
In the end I figured it out and got - as I think a nice solution because it enables me to:
initially fill both DDLs with data
preselect items in both DDLs
still use EnableEventValidation="true" on the Page
i can update the DDLs by simply calling DataBind(); on them
The main difference to my previous approaches is, that I fill the DDLs with data during their OnDataBinding event. So it will happen automatically when i execute DataBind() during PageLoad. Only thing i have to make sure first is to fill the dictionaries that contain my information.
If I want to update ToYearsDDL from the selectionChangeEvent of FromYearsDDL I simply update the Data for ToYearsDDL and call ToYearsDDL.DataBind();
Hope this helps someone else that runs into the same wall!
Here comes the markup:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="LinkedDropDownsBound.aspx.cs"
Inherits="ASP.Net_Spielwiese.LinkedDropDownsBound" EnableEventValidation="true" %>
<!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">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div>
<asp:UpdatePanel runat="server" ID="updatePanel">
<ContentTemplate>
<div>
<asp:Label ID="FromLabel" Text="From" Enabled="false" runat="server" />
</div>
<div>
<asp:DropDownList runat="server" ID="FromYearsDDL" AutoPostBack="true" CausesValidation="false" OnDataBinding="DDLFromDataBind"
OnSelectedIndexChanged="DDLFromSelectedIndexChanged">
</asp:DropDownList>
</div>
<div>
<asp:Label ID="ToLabel" Text="To" Enabled="false" runat="server" />
</div>
<div>
<asp:DropDownList runat="server" ID="ToYearsDDL" AutoPostBack="true" CausesValidation="false" OnDataBinding="DDLToDataBind"
OnSelectedIndexChanged="DDLToSelectedIndexChanged"/>
</div>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
And here comes the code behind:
public partial class LinkedDropDownsBound : System.Web.UI.Page
{
public Dictionary<String, Boolean> FromYears
{
get
{
if (ViewState["FromYears"] == null)
{
ViewState["FromYears"] = new Dictionary<String, Boolean>();
}
return ViewState["FromYears"] as Dictionary<String, Boolean>;
}
set
{
ViewState["FromYears"] = value;
}
}
public Dictionary<String, Boolean> ToYears
{
get
{
if (ViewState["ToYears"] == null)
{
ViewState["ToYears"] = new Dictionary<String, Boolean>();
}
return ViewState["ToYears"] as Dictionary<String, Boolean>;
}
set
{
ViewState["ToYears"] = value;
}
}
public int MinYear = 1975;
public int MaxYear = 2015;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
InitFromYears();
InitToYears();
DataBind();
}
}
private void InitFromYears()
{
FromYears = new Dictionary<string, bool>();
IEnumerable<int> fromRange = Enumerable.Range(MinYear, MaxYear - MinYear);
foreach (var fromYear in fromRange)
{
FromYears.Add(fromYear.ToString(), fromYear == (DateTime.Now.Year - 2));
}
}
private void InitToYears()
{
ToYears = new Dictionary<string, bool>();
//get the selected FromYear Value
int minToYear = Convert.ToInt32(FromYears.FirstOrDefault(dict => dict.Value).Key);
//make sure ToYears is at least FromYears
if (minToYear < Convert.ToInt32(FromYears.Min(k => k.Key)))
{
minToYear = Convert.ToInt32(FromYears.Min(k => k.Key));
}
IEnumerable<int> toRange = Enumerable.Range(minToYear, MaxYear - minToYear);
foreach (var toYear in toRange)
{
ToYears.Add(toYear.ToString(), toYear == (DateTime.Now.Year + 2));
}
}
protected void DDLFromDataBind(object sender, EventArgs e)
{
FromYearsDDL.DataSource = FromYears;
FromYearsDDL.DataValueField = "Key";
FromYearsDDL.DataTextField = "Key";
FromYearsDDL.SelectedValue = FromYears.FirstOrDefault(y => y.Value).Key;
}
protected void DDLFromSelectedIndexChanged(object sender, EventArgs e)
{
//update the FromYear Dictionary
var tempDictionary = FromYears.ToDictionary(fromYear => fromYear.Key, fromYear => fromYear.Key.Equals(FromYearsDDL.SelectedValue));
FromYears = tempDictionary;
//Call Bind on the ToYear DDL
ToYearsDDL.DataBind();
//do my other update stuff here
FromLabel.Text = FromYearsDDL.SelectedValue;
ToLabel.Text = ToYearsDDL.SelectedValue;
}
protected void DDLToSelectedIndexChanged(object sender, EventArgs e)
{
//do my other update stuff here
FromLabel.Text = FromYearsDDL.SelectedValue;
ToLabel.Text = ToYearsDDL.SelectedValue;
}
protected void DDLToDataBind(object sender, EventArgs e)
{
InitToYears();
ToYearsDDL.DataSource = ToYears;
ToYearsDDL.DataValueField = "Key";
ToYearsDDL.DataTextField = "Key";
ToYearsDDL.SelectedValue = ToYears.FirstOrDefault(y => y.Value).Key;
}
}
This is The HTML page
In the code behind c# page
private void BindYearDropdown()
{
int year;
for (year = DateTime.Now.Year; year >= 2010 ; year--)
{
DDLYear.Items.Add(year.ToString());
}
}
The above code is for Years Back ward
I have been searching around and I am lost on how to do what I am attempting to do.
I am trying to create a form where the first column is a list of names, and the next column is all dropdown lists. The idea is that for each name the user will pic a value. Each name may require two, or three or more values. I want to create a dynamic form where a user can click add in the row and another dropdown list appears.
Ex.
"Name" | Add Button | DropDown
then when I click add...
"Name" | Add Button | DropDown | DropDown
and have it keep going.
I am able to create the form and I have it working creating the dropdown lists. The problem is that I am adding the controls on the ItemCommand of a repeater, so they must be recreated every time. Because of this I cannot find a way to keep the values selected in each dropdown, when I have to recreate them.
Typically no more than two dropdowns are required but there are a few cases where three is needed, and it could arise for more. I would like to keep this dynamic as possible.
I know that if I added the dropdown's in the page Init they would be persisted on the postback, but at least in my design the user has to click add to get another drop down.
Is there a way to capture the data from these dropdowns then reload them every time? Or a better way to achieve this functionality?
Thank You for your help.
Here is some of the asp and code behind that I am using. This is functioning as I wish, but I don't know how to keep the data on a postback, as all of the dropdown lists I add are lost.
ASP:
<table>
<asp:Repeater ID="repChemicals" runat="server" OnItemCommand="repChemicals_OnItemCommand">
<ItemTemplate>
<tr>
<td>
<asp:HiddenField ID="hfNumber" runat="server" />
<%# Eval("ChemicalName") %>
</td>
<td>
<asp:Button runat="server" ID="btnAdd" Text="Add" CommandArgument="ADD" />
</td>
<td>
<div id="divContainer" runat="server">
<asp:DropDownList runat="server" Width="60px" ID="ddlTest"></asp:DropDownList>
</div>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
C#:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
List<Chem> Chemicals = new List<Chem>();
Random rnd = new Random();
for (int z = 0; z <= 10; z++)
{
List<string> t = new List<string>();
Chem a = new Chem()
{
ChemicalName = "Chemical" + z.ToString()
};
Chemicals.Add(a);
}
repChemicals.DataSource = Chemicals;
repChemicals.DataBind();
}
}
public void repChemicals_OnItemCommand(object sender, RepeaterCommandEventArgs e)
{
int number = 0;
foreach (RepeaterItem i in repChemicals.Items)
{
HiddenField hf = (HiddenField)repChemicals.Items[i.ItemIndex].FindControl("hfNumber");
if (i.ItemIndex == e.Item.ItemIndex)
{
if (!string.IsNullOrWhiteSpace(hf.Value))
{
number = Convert.ToInt16(hf.Value) + 1;
}
else
{
number = 1;
}
hf.Value = number.ToString();
}
else
{
if (!string.IsNullOrWhiteSpace(hf.Value))
{
number = Convert.ToInt16(hf.Value);
}
else
{
number = 0;
}
}
for (int x = 0; x < number; x++)
{
DropDownList ddl = new DropDownList();
ddl.Style.Add("width", "60px");
ddl.ID = "ddl" + i.ToString() + x.ToString();
ddl.Style.Add("Margin-right", "3px");
ddl.Attributes.Add("runat", "server");
ddl.DataSource = DataSource();
ddl.DataBind();
Control c = repChemicals.Items[i.ItemIndex].FindControl("divContainer");
c.Controls.Add(ddl);
}
}
Some of the loops are for creating test data. Basically I am storing a number of dynamic controls on each row in a hiddenfield. Then on the item command I loop through all of the rows and recreate all of the previously existing ddl's, and add one to the row that teh command came from.
This isn't exactly answering your question, but it accomplishes your ultimate goal in a less complex way and one that takes advantage of built in ASP.NET controls so maintaining state between postbacks is taken care of for you.
It utilizes jQuery, jQuery UI and a DropDownChecklist plugin.
ASPX
<!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>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.21/jquery-ui.min.js"></script>
<script type="text/javascript" src="js/ui.dropdownchecklist.js"></script>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.21/themes/base/jquery-ui.css"/>
</head>
<body>
<form id="form1" runat="server">
<div>
<table>
<asp:Repeater ID="repChemicals" runat="server">
<ItemTemplate>
<tr>
<td>
<%# Container.DataItem %>
</td>
<td>
<div id="divContainer" runat="server">
<asp:ListBox ID="lstAttributes" SelectionMode="Multiple" runat="server"></asp:ListBox>
</div>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
<asp:Button ID="btnPostback" Text="Postback" runat="server"/>
</div>
</form>
</body>
</html>
C#
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace PersonAttributes
{
public partial class People : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
repChemicals.ItemCreated += RepChemicalsOnItemCreated;
var chemicals = new[] {"Hydrogen", "Helium", "Lithium", "Beryllium", "Boron"};
if(!IsPostBack)
{
repChemicals.DataSource = chemicals;
repChemicals.DataBind();
}
var dropDownChecklist = "$(document).ready(function () { $('select').dropdownchecklist(); });";
ScriptManager.RegisterStartupScript(this,GetType(),"initDropDownChecklist",dropDownChecklist,true);
}
private void RepChemicalsOnItemCreated(object sender, RepeaterItemEventArgs repeaterItemEventArgs)
{
var lst = repeaterItemEventArgs.Item.FindControl("lstAttributes") as ListBox;
if (lst == null)
return;
lst.DataSource = new[] {"Option 1", "Option 2", "Option 3"};
}
}
}
See it in action at CodeRun.
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.