Making a radiobutton selection required - asp.net

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;
}

Related

How to detect repeater on OnCheckedChanged event of radio button

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();
}
}

how to check the checkbox inside the repeater at binding time accordingto value?

I have a repeater and inside it i have a checkbox. Now i want to check it according to columns value(0/1). I have tried it through the itemDataBound event of the repeater. what its doing if the rows has value 1 then its checked all checkbox and if first checkbox is unchecked then its unchecked all.
my code is:-
`
<td align="center">
<asp:CheckBox ID="chk" runat="server" />
</td>
</tr>
</ItemTemplate>
</asp:Repeater>`
The ItemDataBound events code is :-
protected void rp_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
DataTable dt = new DataTable();
dt = obj.abc(id);
if (dt.Rows.Count > 0)
{
CheckBox chk = (CheckBox)(e.Item.FindControl("chk"));
if (chk != null)
{
if (Convert.ToInt32(dt.Rows[0]["xyz"]) == Convert.ToInt32("0"))
{
chk.Checked = false;
}
else
{
chk.Checked = true;
}
}
}
}
There are many ways to do that.
You can write inline ASP.NET:
<asp:CheckBox id='isMarried' runat='server'
Checked='<%# Convert.ToBool(Eval("IsMarried")) ? true : false %>' />
As you have mentioned, you can use repeater_ItemDataBound to find the check-box of each row, and set its value accordingly:
protected void repeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
Person person = (Person)e.Item; // e.Item is the datasoruce of the row
if (person.IsMarried)
{
CheckBox isMarried = (CheckBox)(e.Item.FindControl("isMarried"));
isMarried.Checked = true;
}
}
Another way could be to inject the Boolean state (here, the marriage state) in a hidden field and send it to the client, then on client-side, using jQuery (or any other JavaScript framework), update check-boxes' checked state according to the value of those hidden fields:

Maintain selected item in DropDownList inside GridView after removing some ListItems

I have a GridView with a DropDownList in each row. (The items in the DropDownList are the same for each.) I have a DropDownList "ddlView" outside of the GridView that is used for filtering the available options in the other DropDownLists. The default selection for ddlView is no filter.
When a user selects a new value for ddlView any selected values in the other DropDownLists disappear if they are not one of the values present after the filter is applied. What I would like to happen in that case is the previously selected value still be present and selected.
What is the best way to accomplish this?
The previously selected values are available during postback but appear to be cleared once DataBind() is called on the GridView, so I am unable to determine their previous value in the method where they are populated (the RowDataBound event).
My best idea so far is to manually store that information into an object or collection during postback and reference it later during the databinding events.
Is there a better way?
I don't think there is a better way to accomplish this as when the GridView is bound all of the controls are recreated thus removing the selections.
The following works: (I store the selections on postback to retrieve again in the RowDataBound event)
Markup
<asp:Button ID="button1" runat="server" Text="Post Back" OnClick="button1_Click" />
<br />
<asp:GridView ID="gridView1" runat="server">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:DropDownList ID="gridViewDropDownList" runat="server">
<asp:ListItem Value="1">Item 1</asp:ListItem>
<asp:ListItem Value="2">Item 2</asp:ListItem>
<asp:ListItem Value="3">Item 3</asp:ListItem>
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Code
public class GridViewDropDownSelections
{
public int RowIndex { get; set; }
public int SelectedIndex { get; set; }
}
...
private List<GridViewDropDownSelections> selectedDropDownListItems = new List<GridViewDropDownSelections>();
protected override void OnLoad(EventArgs e)
{
var selections = gridView1.Rows.Cast<GridViewRow>().Where(r => r.RowType == DataControlRowType.DataRow)
.Select(r => new GridViewDropDownSelections() { RowIndex = r.RowIndex, SelectedIndex = ((DropDownList)r.FindControl("gridViewDropDownList")).SelectedIndex }).ToList();
selectedDropDownListItems.AddRange(selections);
gridView1.RowDataBound += new GridViewRowEventHandler(gridView1_RowDataBound);
if (!IsPostBack)
{
BindDataGrid();
}
base.OnLoad(e);
}
protected void button1_Click(object sender, EventArgs e)
{
BindDataGrid();
}
private void BindDataGrid()
{
//Dummy data
string[] data = new string[] { "Item 1", "Item 2", "Item 3" };
gridView1.DataSource = data;
gridView1.DataBind();
}
void gridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
var selection = selectedDropDownListItems.FirstOrDefault(i => i.RowIndex == e.Row.RowIndex);
if (selection != null)
{
try
{
DropDownList gridViewDropDownList = (DropDownList)e.Row.FindControl("gridViewDropDownList");
gridViewDropDownList.SelectedIndex = selection.SelectedIndex;
}
catch (Exception)
{
}
}
}
}
Hope it helps.
Instead of using DataBinding to apply the filter, you could add/remove the DropdownList items through the Items property, when another filter is selected. This way the selected value in the dropdowns should not be resetted.
You might be able to accomplish this with a simple condition. I don't know how you're filtering the items, but it would go something like this:
for (int itemIndex = 0; itemIndex < DropDownList1.Items.Count; itemIndex++)
{
ListItem item = DropDownList1.Items[itemIndex];
if (DropDownList1.Items.IndexOf(item) > ddlView.SelectedIndex)
{
if (!item.Selected)
{
DropDownList1.Items.Remove(item);
}
}
}
Not sure if this is what you're looking for, but hope it helps.
Use client side javascript
Search for all the appropriate select inputs and for each one found, go through the options and remove the one selected in the master ddl
function UpdateDropDowns (tbl) {
var controls = tbl.getElementsByTagName('select');
for (var i = 0; i < controls.length; i++) {
RemoveOption(controls[i], 'the value to remove');
}
}
function RemoveOption(ddl, val)
{
for (var i = ddl.options.length; i>=0; i--) {
if (ddl.options[i].value == val) {
ddl.remove(i);
}
}
}

Preserving CheckBox's Value in Dynamic GridView TemplateField

I am trying to create a GridView which will contain a list of users and permission levels that they have access to. Each row will have the following fields: User_ID, Username, Permission1, Permission2, ..., PermissionN, where the values of permissions field are "0" if the user does not have that permission and "1" if they do (note that columns returned by the DAL are not named Permission1, but rather are the actual name of the permission). I would like to represent the data by using CheckBoxes, to allow an admin to quickly grant or revoke permissions to a large number of users at once.
Since I will not know the number of permissions before-hand, I dynamically create TemplateFields and CheckBoxes, and this works fine; the GridView shows the current permission levels of all the users. I run into a problem when I try to update permissions based on the user checking and unchecking boxes.
Once the user is done changing permissions, I have an "Update" button, which of course causes a postback. Since the postback occurs before the OnClick event, by the time I reach OnClick all of the CheckBoxes have been reset to their initial values. Is there a way I can somehow grab the value of the CheckBoxes' Checked property before the postback occurs? Is there another (better) way of doing this? Thanks.
ASPX:
<asp:GridView ID="gvUsers" runat="server" AutoGenerateColumns="False"
EnableModelValidation="True" DataKeyNames="ID">
</asp:GridView>
<asp:ObjectDataSource ID="odsUsers" runat="server" SelectMethod="GET_USERS"
TypeName="oDAL">
</asp:ObjectDataSource>
<asp:Button ID="btnUpdate" runat="server" Text="Update Permissions" OnClick="btnUpdate_OnClick"/>
Code Behind:
private static string[] excludeCols = { "ID", "Username" };
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) // Removing the IsPostBack check refreshes the GridView when the Update button is clicked
bindGridViewWithHiddenID(gvUsers, ((DataView)odsUsers.Select()).Table, excludeCols);
}
public static bool bindGridViewWithHiddenID(GridView gv, DataTable dt, string[] excludeCols)
{
gv.Columns.Clear();
gv.DataBind();
if (gv != null && dt != null && dt.Rows.Count > 0)
{
DataControlField newField;
foreach (DataColumn column in dt.Columns)
{
if (excludeCols.Contains(column.ColumnName))
{
newField = new BoundField();
((BoundField)newField).DataField = column.ColumnName;
}
else
{
newField = new TemplateField();
((TemplateField)newField).ItemTemplate = new CustomTemplate(column.ColumnName);
}
newField.HeaderText = column.ColumnName;
if (column.ColumnName == "ID" || column.ColumnName.EndsWith("_ID"))
newField.Visible = false;
gv.Columns.Add(newField);
}
gv.DataSource = dt;
gv.DataBind();
return true;
}
return false;
}
// By this time execution reaches here the CheckBoxes have already been reset
protected void btnUpdate_Click(object sender, EventArgs e)
{
...
}
CustomTemplate class:
public class CustomTemplate : ITemplate
{
private string binding;
private static int count = 0;
public CustomTemplate(string colNameBinding)
{
binding = colNameBinding;
}
public void InstantiateIn(Control container)
{
CheckBox chk = new CheckBox();
chk.ID = "chk" + count++;
chk.DataBinding += new EventHandler(this.chk_OnDataBinding);
container.Controls.Add(chk);
}
public void chk_OnDataBinding(object sender, EventArgs e)
{
CheckBox chk = (CheckBox)sender;
GridViewRow namingContainer = (GridViewRow)chk.NamingContainer;
DataRowView dataRow = (DataRowView)namingContainer.DataItem;
if (dataRow[binding].ToString() == "1")
chk.Checked = true;
else
chk.Checked = false;
}
This behavior is expected and is normal. It has to do with the Page Life cycle and tracking of ViewState on dynamically added controls. I recommend you read this article from Scott Mitchell, specially the section titled: View State and Dynamically Added Controls.
I tried adding the GridView binding to OnInit() but since the CheckBoxes aren't actually instantiated until the GridView is DataBound, and since DataBinding will revert the GridView back to its original state, I seem to be stuck again.

Cannot find selected value of dynamically added controls in repeater

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

Resources