I have a nested repeater and there is a radiobutton in the inner one ( rptrSubscriptions ). What I want to do is to highlight the radio button that is selected and unhighlight the previous one .
There must be only one radio button selected between all of the radio buttons in the outer repeater .I also need to save the itemID of the selected one in a hidden field.
I have developed a method with the name of HighlightSubscription that will be called when user clicks on the radio button (OnCheckedChanged="HighlightSubscription") .
The problem is this, the system doesn't recognize rptrSubscriptions in HighlightSubscription method, I tried to use findcontrol but unfortunately there is no access to e.items in this method.
I also defined rptrSubscriptions as a public variable but it will be null too in this method.
I have copied the source of HighlightSubscription from HighlightBox that is a radio button inside a single repeater and it works fine as its definition is inside the designer,
but for the nested repeater the definition cant be in the designer as the system automatically removes it in compile time.
Any suggestion is highly appreciated.
protected void rptrSubscriptionGroups_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
Repeater rptrSubscriptions = (Repeater)e.Item.FindControl("rptrSubscriptions");
if (rptrSubscriptions != null)
{
RepeaterItem item = e.Item;
if (e.Item.ItemType == (ListItemType.Item) || e.Item.ItemType == (ListItemType.AlternatingItem))
{
DataTable SubscriptionTbl = SubscriptionsBind(SelectedLanguageID, SelectedPlatformID, SelectedCurrencyID, SelectedGroupID, 1);
rptrSubscriptions.DataSource = SubscriptionTbl;
rptrSubscriptions.DataBind();
}
}
}
protected void HighlightSubscription(object source, EventArgs e)
{
//find previous selected row
if (hiddenSelectedSubscriptionRowIndex.Value.Length > 0)
{
int rowIndex = int.Parse(hiddenSelectedSubscriptionRowIndex.Value);
//Repeater rptrSubscriptions = (Repeater)e.Item.FindControl("rptrSubscriptions");
RepeaterItem item = rptrSubscriptions.Items[rowIndex];
if (rptrSubscriptions != null)
{
// Uncheck Old Radio Button
RadioButton rbOld = (RadioButton)item.FindControl("rbBox");
rbOld.Attributes.CssStyle.Add("border-color", "lightgray");
rbOld.Attributes.CssStyle.Add("border-width", "1px");
rbOld.Checked = false;
}
}
RadioButton rb = source as RadioButton;
int CurrentrepeaterItemIndex = ((RepeaterItem)rb.NamingContainer).ItemIndex;
// save current row index in a hiddden field
hiddenSelectedSubscriptionRowIndex.Value = CurrentrepeaterItemIndex.ToString();
}
<asp:Repeater ID="rptrSubscriptionGroups" runat="server" OnItemDataBound="rptrSubscriptionGroups_ItemDataBound" OnItemCommand="rptrSubscriptionGroups_ItemCommand">
<ItemTemplate>
<asp:Repeater ID="rptrSubscriptions" runat="server" OnItemDataBound="rptrSubscriptions_ItemDataBound" OnItemCommand="rptrSubscriptions_ItemCommand">
<ItemTemplate>
<asp:RadioButton ID="rbSubscription" runat="server" OnCheckedChanged="HighlightSubscription" AutoPostBack="true"/>
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
A little edit from me you can try this one :
protected void HighlightSubscription(object source, EventArgs e)
{
Repeater rptrSubscriptions = new Repeater();
foreach (RepeaterItem a in rptrSubscriptionGroups.Items)
{
rptrSubscriptions = (Repeater)a.FindControl("rptrSubscriptions");
}
//find previous selected row
if (hiddenSelectedSubscriptionRowIndex.Value.Length > 0)
{
int rowIndex = int.Parse(hiddenSelectedSubscriptionRowIndex.Value);
//Repeater rptrSubscriptions = (Repeater)e.Item.FindControl("rptrSubscriptions");
RepeaterItem item = rptrSubscriptions.Items[rowIndex];
if (rptrSubscriptions != null)
{
// Uncheck Old Radio Button
RadioButton rbOld = (RadioButton)item.FindControl("rbBox");
rbOld.Attributes.CssStyle.Add("border-color", "lightgray");
rbOld.Attributes.CssStyle.Add("border-width", "1px");
rbOld.Checked = false;
}
}
RadioButton rb = source as RadioButton;
int CurrentrepeaterItemIndex = ((RepeaterItem)rb.NamingContainer).ItemIndex;
// save current row index in a hiddden field
hiddenSelectedSubscriptionRowIndex.Value = CurrentrepeaterItemIndex.ToString();
}
Something like this:
protected void rptrSubscriptionGroups_ItemDataCreated(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
RepeaterItem item = e.Item;
RadioButton rb = (RadioButton)item.FindControl("rbSubscription");
rb.AutoPostBack = true;
rb.CheckedChanged += new EventHandler(rb_CheckChanged);
}
}
private void rb_CheckChanged(object sender, EventArgs e)
{
RadioButton rb = (RadioButton)sender;
if(rb.Checked == true){
HighlightSubscription();
}
}
Related
I am using OnRowDataBound to automatically add a link button to my grid view that looks like this. The problem I am having is setting the command argument.
<asp:LinkButton ID = "lnkDelete" Text = "Delete" CommandArgument = '<%# Eval("Value") %>' runat = "server" OnClick = "DeleteFile" />
Below is the code that adds the links. I set the command argument to Eval("Value") but that doesn't work. Here is a link to the original code that I'm trying to change so it is dynamic.
protected void OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
LinkButton lnkView = new LinkButton();
lnkView.ID = "lnkDelete";
lnkView.Text = "Delete";
lnkView.Click += DeleteFile;
lnkView.CommandArgument = Eval("Value");
e.Row.Cells[1].Controls.Add(lnkView);
}
RowDataBound is not the right event to add controls dynamically because they need to be re-created on every consecutive postback. RowDataBound is triggered only if you call GridView.DataBind().
So use RowCreated instead, but assign the CommandArgument value in RowDataBound and don't use Eval("Value") but the actual datasource which you get from e.Row.DataItem.
Something like this should work:
protected void OnRowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
LinkButton lnkView = new LinkButton();
lnkView.ID = "lnkDelete";
lnkView.Text = "Delete";
lnkView.Click += DeleteFile;
e.Row.Cells[1].Controls.Add(lnkView);
}
}
protected void OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
LinkButton lnkView = (LinkButton)e.Row.FindControl("lnkDelete");
var rowView = (DataRowView)e.Row.DataItem;
lnkView.CommandArgument = rowView.Row.Field<string>("Value");
}
}
If this throws an exception at runtime you need to change (DataRowView)e.Row.DataItem to the actual datasource which you can get from the debugger.
Edit: as commented it is a ListItem and you want to use it's Value property:
var item = (ListItem) e.Row.DataItem;
lnkView.CommandArgument = item.Value;
I have radio buttons within a datagrid which is within a repeater. There are 4 radiobuttons that are created for each "repeat" within the repeater and I want to make the selection of 1 of these 4 mandatory. I have already put these radiobuttons within the same GroupName, so only 1 of them can be selected.
They are not in a radiobuttonlist and I do not want to place it in a radiobutton list.
Since you have them in the same group name you can make a custom validator.
<asp:CustomValidator ID="myValidator" runat="server" ErrorMessage="you must select one" onservervalidate="myValidator_serverValidate" />
Then in the code behind,
protected void myValidator_serverValidate(object sender, ServerValidateEventArgs e)
{
bool isSelected = false;
foreach (Control control in yourForm.Controls)
{
if (control == RadioButton)
{
RadioButton rb = (RadioButton)control;
if (rb.GroupName == "myGroup" && rb.Checked)
{
isSelected = true;
}
}
}
e.IsValid = isSelected;
}
I have a ListView in an ASP.NET web application. When a user clicks the edit button, I want textfields to pop up that are dependent on certain values of the item. However, I can't seem to find any controls inside of my ListView1_ItemEditing() function.
I have read the Microsoft documentation and various help threads on the internet, but their suggestions do not appear to work for me. This is generally what I see:
ListViewItem item = ProductsListView.Items[e.NewEditIndex];
Label dateLabel = (Label)item.FindControl("DiscontinuedDateLabel");
For the sake of simplicity I just want to be able to select a label in ListView1_ItemEditing(). This is the code in ListView1_ItemEditing():
protected void ListView1_ItemEditing(Object sender, ListViewEditEventArgs e)
{
DataBind(); //not sure if this does anything
ListViewItem item = ListView1.Items[e.NewEditIndex];
Label debugLabel = (Label)item.FindControl("label_editing");
debugLabel.Text = "Works";
}
Here is the ASP
<EditItemTemplate>
<asp:Label ID="label_editing" runat="server" Text="hello world"></asp:Label>
</EditItemTemplate>
When debugging, item and debugLabel are both NULL.
UPDATE: I resolved this issue by moving my logic to ItemDataBound and then checking if my tr (containing textboxes) was in that particular data item. Code below:
protected void ListView1_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
Control tr_verizon = e.Item.FindControl("tr_verizonEdit");
Control tr_att = e.Item.FindControl("tr_attEdit");
if (tr_verizon != null)
{
//Control tb_meid = e.Item.FindControl("TextBox_Meid");
Label lbl_carrierId = (Label)e.Item.FindControl("lbl_carrierId");
if (lbl_carrierId == null)
{
Message.Text = "lbl_carrierId is null!";
}
else if (lbl_carrierId.Text.Equals(""))
{
Message.Text = "lbl_carrierId is empty!";
}
else
{
string recordId = lbl_carrierId.Text;
if (tr_verizon != null && tr_att != null)
{
if (lbl_carrierId.Text.Equals("1"))
{
tr_verizon.Visible = false;
tr_att.Visible = true;
}
else
{
tr_verizon.Visible = true;
tr_att.Visible = false;
}
}
}
}
}
}
The ItemEditing event is raised when an item's Edit button is clicked, but before the ListView item is put in edit mode. Therefore controls in EditItemTemplate are not available at this time.
More Info and example
You should do the DataBind() first, like this:
ListView1.EditIndex = e.NewEditIndex;
ListView1_BindData(); // a function that get the DataSource and then ListView1.DataBind()
// Now find the control as you did before
Have you tried casting the sender object instead of trying to access your ListViewItem by index?
protected void ListView1_ItemEditing(Object sender, ListViewEditEventArgs e)
{
var item = sender as ListViewItem;
var debugLabel = item.FindControl("label_editing") as Label;
debugLabel.Text = "Works";
}
This must be something that a lot of people have done. Basically, it's a custom GridView (i.e. inherited control) with the ability to update all rows at once. I've tried putting the "update all" button in various places (footer, pager, outside the grid), but it looks neatest (to me) when the button is in an extra row as the last row of the GridView.
NB: The pager row is not a suitable place for this button because this custom control could be used in a situation where paging is false. Similarly, the normal footer may be required for some other purpose (e.g. totals).
Here's my code for putting the button in the correct place (with apologies for the terse variables etc.):
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
//Add an extra row to the table...
if (_updateAllEnabled)
{
GridViewRow r = base.CreateRow(-1, -1, DataControlRowType.Footer, DataControlRowState.Normal);
Button btn = new Button();
TableCell c = new TableCell();
btn.ID = "UpdateAllButton"; // tried with and without this line
btn.Text = "Update All";
btn.Click += new EventHandler(UpdateAll);
r.Cells.Add(c);
c.Controls.Add(btn);
Table t = this.Controls[0] as Table;
c.ColumnSpan = this.Columns.Count;
t.Rows.Add(r);
}
}
This gives the appearance that I want, but the click event (UpdateAll) does not fire.
I assume that the stuff is being added too late in the life cycle (PreRender), but where else can I do this to ensure that the row is at the end of the GridView? I also thought that there might be trouble identifying the button, so I tried setting the ID. In any case, the ID in the generated HTML looks OK (consistent with "working" buttons in the pager row.
Is there a way for me to achieve this or am I attempting the impossible?
The best place to create your footer-controls is RowCreated since that's early enough in the lifecycle and also ensures that their recreated on every postback:
Footer approach:
protected void Grid_RowCreated(Object sender, GridViewRowEventArgs e) {
if(e.Row.RowType == DataControlRowType.Footer) {
Button btn = new Button();
TableCell c = new TableCell();
btn.ID = "UpdateAllButton";
btn.Text = "Update All";
btn.Click += new EventHandler(UpdateAll);
var firstCell=e.Row.Cells[0];
firstCell.ColumnSpan =e.Row.Cells.Count;
firstCell.Controls.Add(btn);
while(e.Row.Cells.Count > 1)e.Row.Cells.RemoveAt(e.Row.Cells.Count-1);
}
}
Of course you have to set ShowFooter to true:
<asp:GridView ID="GridView1"
ShowFooter="true"
OnRowCreated="Grid_RowCreated"
runat="server"
</asp:GridView>
Pager approach:
In my opinion this is the purpose of the FooterRow. But if you really want to ensure that your Button is in the very last row of a GridView(even below Pager as commented), i would try my next approach.
Here I'm using the pager for your costom control(s) by adding another TableRow to the PagerTable which inherits from Table.
protected void Grid_RowCreated(Object sender, GridViewRowEventArgs e) {
switch(e.Row.RowType){
case DataControlRowType.Pager:
Button btnUpdate = new Button();
btnUpdate.ID = "UpdateButton";
btnUpdate.Text = "Update";
btnUpdate.Click += new EventHandler(UpdateAll);
var tblPager = (Table)e.Row.Cells[ 0 ].Controls[ 0 ];
var row = new TableRow();
var cell = new TableCell();
cell.ColumnSpan = tblPager.Rows[ 0 ].Cells.Count;
cell.Controls.Add(btnUpdate);
row.Cells.Add(cell);
tblPager.Rows.Add(row);
break;
}
}
To ensure that the pager is visible even if only one page is shown(note that the real pager is invisible if PageSize==1):
protected void Grid_PreRender(object sender, EventArgs e){
GridView gv = (GridView)sender;
GridViewRow gvr = (GridViewRow)gv.BottomPagerRow;
if(gvr != null) {
gvr.Visible = true;
var tblPager = (Table)gvr.Cells[ 0 ].Controls[ 0 ];
//hide real pager if unnecessary
tblPager.Rows[ 0 ].Visible = GridView1.PageCount > 1;
}
}
Of course now you have to set AllowPaging=true:
<asp:GridView ID="GridView1"
AllowPaging="true"
PagerSettings-Mode="NumericFirstLast"
OnRowCreated="Grid_RowCreated"
OnPreRender="Grid_PreRender"
OnPageIndexChanging="Grid_PageChanging"
runat="server">
</asp:GridView>
Final approach(working for a custom GridView and all PagerPositions):
public PagerPosition OriginalPagerPosition{
get { return (PagerPosition)ViewState[ "OriginalPagerPosition" ]; }
set { ViewState[ "OriginalPagerPosition" ] = value; }
}
protected void Page_Load(object sender, EventArgs e){
if(!IsPostBack) OriginalPagerPosition = GridView1.PagerSettings.Position;
GridView1.PagerSettings.Position = PagerPosition.TopAndBottom;
GridView1.AllowPaging = true;
// databinding stuff ...
}
Keep the RowCreated the same as above in Pager approach.
Visibility of top/bottom pagers will be controlled in PreRender according to the OriginalPagerPosition property. Both pagers are created even with PagerPosition=TOP, the bottom pager is required for your additional control(s):
protected void Grid_PreRender(object sender, EventArgs e)
{
GridView gv = (GridView)sender;
GridViewRow tpr = (GridViewRow)gv.TopPagerRow;
GridViewRow bpr = (GridViewRow)gv.BottomPagerRow;
tpr.Visible = gv.PageCount > 1 && (OriginalPagerPosition == PagerPosition.Top || OriginalPagerPosition == PagerPosition.TopAndBottom);
bpr.Visible = true;
var tblBottomPager = (Table)bpr.Cells[ 0 ].Controls[ 0 ];
tblBottomPager.Rows[ 0 ].Visible = gv.PageCount > 1 && (OriginalPagerPosition == PagerPosition.Bottom || OriginalPagerPosition == PagerPosition.TopAndBottom);
var tblTopPager = (Table)tpr.Cells[ 0 ].Controls[ 0 ];
tblTopPager.Rows[1].Visible = false;
}
Note: if you are extending the GridView control, you have to replace all occurences of GridView1(my test-grid) with this.
It would be easy to add an extra row into the grid. But the difficulty in your requirement is that the GridView's RowCollection should not contain this row since that would be error-prone. It should also be the very last row even if paging is enabled. This is (afaik) not possible.
Hence i've chosen to extend the pager with this functionality.
I'll add this as separate answer since my other is already too detailed and describes two different ways(footer,pager) to add controls to a GridView without extending it.
This approach extends a GridView as in your own requirement and is similar to my other pager-approach. But it's cleaner and only adds the additional row to the BottomPager. It woks also for every setting(AllowPaging=false,Pager-Position: Top,Bottom,BottomTop):
[DefaultProperty("EnableUpdateAll")]
[ToolboxData("<{0}:UpdateGridView runat=server></{0}:UpdateGridView>")]
public class UpdateGridView : GridView
{
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("true")]
[Localizable(true)]
public bool EnableUpdateAll
{
get
{
Object val = ViewState["EnableUpdateAll"];
return ((val == null) ? true : (bool)val);
}
set
{
ViewState["EnableUpdateAll"] = value;
}
}
private bool OriginalAllowPaging
{
get
{
Object val = ViewState["OriginalAllowPaging"];
return (bool)val;
}
set
{
ViewState["OriginalAllowPaging"] = value;
}
}
private PagerPosition OriginalPagerPosition
{
get
{
Object val = ViewState["OriginalPagerPosition"];
return (PagerPosition)val;
}
set
{
ViewState["OriginalPagerPosition"] = value;
}
}
protected override void OnInit(System.EventArgs e)
{
if (ViewState["OriginalPagerPosition"] == null)
OriginalPagerPosition = base.PagerSettings.Position;
if(OriginalPagerPosition != PagerPosition.Bottom)
PagerSettings.Position=PagerPosition.TopAndBottom;
if (ViewState["OriginalAllowPaging"] == null)
OriginalAllowPaging = base.AllowPaging;
base.AllowPaging = true;
}
protected override void OnRowCreated(GridViewRowEventArgs e)
{
switch (e.Row.RowType)
{
case DataControlRowType.Pager:
//check if we are in BottomPager
if (this.Rows.Count != 0 && this.EnableUpdateAll)
{
Button btnUpdate = new Button();
btnUpdate.ID = "BtnUpdate";
btnUpdate.Text = "Update";
btnUpdate.Click += new EventHandler(UpdateAll);
var tblPager = (Table)e.Row.Cells[0].Controls[0];
var row = new TableRow();
var cell = new TableCell();
cell.ColumnSpan = tblPager.Rows[0].Cells.Count;
cell.Controls.Add(btnUpdate);
row.Cells.Add(cell);
tblPager.Rows.Add(row);
}
break;
}
}
protected override void OnPreRender(EventArgs e)
{
bool bottomPagerVisible =
OriginalAllowPaging &&
PageCount > 1 &&
(OriginalPagerPosition == PagerPosition.Bottom || OriginalPagerPosition == PagerPosition.TopAndBottom);
BottomPagerRow.Visible = bottomPagerVisible || EnableUpdateAll;
var tblBottomPager = (Table)BottomPagerRow.Cells[0].Controls[0];
tblBottomPager.Rows[0].Visible = bottomPagerVisible;
}
private void UpdateAll(Object sender, EventArgs e)
{
// do something...
}
}
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