I have the following scenario:
My page has a dropdown list that allows user to choose a category.
For each category, there is a set of attributes, whose values should be fetched from the user. The number of attributes are different for each category.
Depending on the category the user chooses, a set of dropdown lists should be created corresponding to the attributes and filled with corresponding attribute values.
Since it is required that the page should not reload, I plan to fetch the data (from SQL Server 2008) using AJAX (?). I'm new to ASP.NET and have not used AJAX though I'm comfortable with C#. Need advice on how to proceed.
EDIT: Is this useful if I need to dynamically generate combo boxes?
You can use UpdatePanel or PageMethods
in both cases and in any case, I would say, you do need to know JavaScript when you want to use AJAX and make dynamic web applications. It take some time but it pays off don't worry.
you can search here in SO about PageMethod, for example see this one:
Regarding PageMethod in asp.net
You could use the following approach (if you do not feel comfortable building a more complex UI with javascript).
It works by dynamically creating the attribute DropDownLists when the page loads (you would implement it based on the result of a DB query) and hiding each one, ready for display later on.
Upon the selection of a category the correct DropDownLists would then be made visible (again a query here could determine which attribute DropDownLists become visible).
Obviously it will require some modifications to probably generate a Panel which contains each DropDownList and a Label control, instead of just creating a number of DropDownLists.
You would then show/hide the Panel instead of the DropDownList, but hopefully it points you in the right direction.
Hope this helps.
Markup
<style type="text/css">
select
{
display:block;
margin-top:10px;
}
</style>
....
<asp:ScriptManager ID="scriptManager" runat="server"></asp:ScriptManager>
<asp:UpdatePanel ID="updatePanel" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<!-- Category selection -->
<asp:DropDownList ID="categoryDropDownList" runat="server" AutoPostBack="true" OnSelectedIndexChanged="categoryDropDownList_SelectedIndexChanged">
<asp:ListItem Text="Please select a category" Value="0"></asp:ListItem>
</asp:DropDownList>
<br />
<!-- Used to store the drop downs -->
<asp:Panel ID="dropDownContainer" runat="server"></asp:Panel>
</ContentTemplate>
</asp:UpdatePanel>
Code
protected void Page_Load(object sender, EventArgs e)
{
LoadDropDownLists();
}
private void LoadDropDownLists()
{
//Dummy data, you would pull your categories/attributes from whichever datasource
//you are using
var categories = new[]{
new { Name = "Category 1", Id = 1, Attributes = new string[]{"GA", "FA", "RA"} },
new { Name = "Category 2", Id = 2, Attributes = new string[]{"GA", "NA"} }
};
//Loop through the categories, load the dropdown
foreach (var category in categories)
{
if (!IsPostBack)
categoryDropDownList.Items.Add(new ListItem(category.Name, category.Id.ToString()));
//For each attribute create a drop down and populate with data as required
foreach (var attribute in category.Attributes)
{
string dropDownListId = FormatDropDownListId(attribute);
if (!DropDownListExists(dropDownListId))
{
DropDownList dropDownList = new DropDownList();
dropDownList.ID = dropDownListId;
dropDownList.Visible = false;
dropDownList.Items.Add(new ListItem(attribute));
dropDownContainer.Controls.Add(dropDownList);
}
}
}
}
private bool DropDownListExists(string id)
{
DropDownList dropDownList = (DropDownList)dropDownContainer.FindControl(id);
return dropDownList != null;
}
protected void categoryDropDownList_SelectedIndexChanged(object sender, EventArgs e)
{
//Reset all visible dropdowns
HideAllDropDownLists();
//Get the selected category
string selectedItem = categoryDropDownList.SelectedItem.Value;
switch (selectedItem)
{
case "1":
{
//Here you would connect to db and pull correct attributes
//then set the visible dropdowns as required
SetDropDownVisibility(FormatDropDownListId("GA"));
SetDropDownVisibility(FormatDropDownListId("FA"));
SetDropDownVisibility(FormatDropDownListId("RA"));
} break;
case "2":
{
SetDropDownVisibility(FormatDropDownListId("GA"));
SetDropDownVisibility(FormatDropDownListId("NA"));
} break;
}
}
private void SetDropDownVisibility(string id)
{
DropDownList dropDownList = (DropDownList)dropDownContainer.FindControl(id);
if(dropDownList != null)
dropDownList.Visible = true;
}
private void HideAllDropDownLists()
{
foreach (Control control in dropDownContainer.Controls)
{
control.Visible = false;
}
}
private string FormatDropDownListId(string id)
{
return string.Format("dropDown{0}", id);
}
If you're using ASP.NET webforms then I don't believe you need to use AJAX or JavaScript.
I would do the following
Set autopostback = true on your combobox
Add an event handler for the OnChanged event of the combobox
Inside the event handler, apply rules at to load / generate / populate child comboboxes
Add those combo boxes to the form
You can either hide the comboboxes (as I see in #jdavies answer), or start without any and dynamically create & add them to the form.
This question deals with the same issue:
DropDownList and Update Panel
Related
- Primary Info:
In my recent project, I need to have a page with a DropDownList with some items like 'firstName' , 'lastName' , 'Age' and etc. I want to add optional controls to the page when every item selected by user. For example when user select the 'Age' another dropdownlist created dynamically with these values : 'Less than 10'
'Between 10 and 30'
'more than 30'
Here is a button that add this user selection to listBox and let user to choice another options. (I made a query at last according to user choices and send it to db)
- What I do:
I create a dropDownList and set it's AutoPostBack property to true and adds some items in it and user must select one of those item. then I add user SelectedValue of dropDownList in a Cache variable before page post back happens:
protected void DropDownListColumnNameSelectedIndexChanged(object sender, EventArgs e)
{
Cache["SelectedKey"] = dropDownListColumnName.SelectedValue;
}
When user select an item from dropDownList *DropDownList_SelectedIndexChanged* fire, and I must create controls dynamically in a place holder:
var textBoxName = new TextBox
{
ID = "textBoxName",
CssClass = "str-search-textbox-highlight",
ViewStateMode = ViewStateMode.Disabled
};
placeHolderFirstItem.Controls.Add(textBoxName);
- What is the problem?
When I try add new control in current Button_Click event, control added successfully to page but I can't find it by placeHolderFirstItem.Controls.Find("textBoxName") actually placeHolderFirstItem.Controls.Count is always zero. So I can't get textBoxName.Text values.
I try to google that for any solution and I found some solution that I must add controls in Page.OnInit so I add controls in overridden OnInit(e):
protected override void OnInit(EventArgs e)
{
if (!Page.IsPostBack) return;
var textBoxName = new TextBox
{
ID = "textBoxName",
CssClass = "str-search-textbox-highlight",
ViewStateMode = ViewStateMode.Disabled
};
placeHolderFirstItem.Controls.Add(textBoxName);
}
after doing this I can find "textBoxName" in placeHolderFirstItem, but it fire before DropDownList_SelectedIndexChanged !
so how can I add new controls to place holder exactly when user change the dropDownList value and how can I read new controls value?
Thanks in advance,
Mohsen.
- Updated:
Here is the better solution
(http://forums.asp.net/p/1959726/5596531.aspx?p=True&t=635244790943067485&pagenum=1)
When you are dynamically adding controls, you have to reload the controls into the control tree everytime thereafter for it to appear. With the help of viewstate, you could change your code sample to have:
ViewState("ShowTextbox") = true
And then in your init routine:
protected override void OnInit(EventArgs e)
{
if (!Page.IsPostBack) return;
if (ViewState("ShowTextBox") == true) {
var textBoxName = new TextBox
{
ID = "textBoxName",
CssClass = "str-search-textbox-highlight",
ViewStateMode = ViewStateMode.Disabled
};
placeHolderFirstItem.Controls.Add(textBoxName);
}
}
Please note it's much easier to have a control on the control tree, and then show/hide by setting Visible to true/false, because of these ASP.NET control tree issues.
I have a radio button list whose items I need to add on Page_Load
aspx code
<asp:radioButtonList ID="radio1" runat="server" RepeatLayout="Flow" RepeatDirection="Horizontal">
</asp:radioButtonList>
code behind
protected void Page_Load(object sender, EventArgs e)
{
RadioButtonList radioList = (RadioButtonList)Page.FindControl("radio1");
radioList.Items.Add(new ListItem("Apple", "1"));
}
After the control reaches radioList.Items.Add
I keep getting the Object reference not set to instance of an object
error
What am I doing wrong?
You don't need to to do a FindCOntrol. As you used the runat="server" attributes, just get the reference of your RadioList via its name "radio1"
protected void Page_Load(object sender, EventArgs e)
{
radio1.Items.Add(new ListItem("Apple", "1"));
}
By using
RadioButtonList radioList = (RadioButtonList)Page.FindControl("radio1");
radioList.Items.Add(new ListItem("Apple", "1"));
you are not adding your list on the control on your page, but on an un-instanciated Radiobuttonlist called radioList.
If the page is reachable from the class, use
radio1.Items.Add(new ListItem("Apple", "1"));
you must add !ispostback
if (!IsPostBack)
{
radio1.Items.Add(new ListItem("Apple", "1"));
}
As an alternative to using the < asp: **> tools -
I needed to reuse a radio option which relies on a lot of jQuery integration in the site. (Also wanted to avoid just CSS hiding the content within the html code of the aspx page.)
The radio buttons needed only appear in an 'edit' page depending on security ACU level logic within the codebehind and rendered with currently stored item value data found in the db.
So I used the following:
string RadioOnChk1 = (db.fieldChecked == true) ? "checked='checked'" : "";
string RadioOnChk2 = (db.fieldChecked == false) ? "checked='checked'" : "";
if (ACU > 3)
{
// Create radio buttons with pre-checked
StringBuilder RadioButtns = new StringBuilder(); // Form input values
{
RadioButtns.Append("<p><label><input type=\"radio\" id=\"radiocomm1\" name=\"custmComm\" value=\"1\"");
RadioButtns.Append(RateIncChk1 + "/>Included or </label>");
RadioButtns.Append("<label><input type=\"radio\" id=\"radiocomm2\" name=\"custmComm\" value=\"2\"");
RadioButtns.Append(RateIncChk2 + "/>Excluded</label>");
RadioButtns.Append("</p>");
}
htmlVariable = (RadioButtns.ToString());
}
It works.. Is this a wrong way of going about it?
I am dynamically adding a custom user control to an update panel. My user control contains two dropdownlists and a textbox. When a control outside of the update panel triggers a postsback, I am re-adding the user control to the update panel.
The problem is...on postback when I re-add the user controls, it's firing the "SelectedIndexChanged" event of the dropdownlists inside the user control. Even if the selectedindex did not change since the last postback.
Any ideas?
I can post the code if necessary, but there's quite a bit in this particular scenario.
Thanks in advance!
EDIT...CODE ADDED BELOW
*.ASCX
<asp:DropDownList ID="ddlColumns" OnSelectedIndexChanged="ddlColumns_SelectedChanged" AppendDataBoundItems="true" AutoPostBack="true" runat="server">
*.ASCX.CS
List<dataColumnSpecs> dataColumns = new List<dataColumnSpecs>();
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
fillDDLColumns();
}
}
public void fillDataColumnsList()
{
dataColumns.Clear();
//COMMON GETDATATABLE RETURNS A DATA TABLE POPULATED WITH THE RESULTS FROM THE STORED PROC COMMAND
DataTable dt = common.getDataTable(storedProcs.SELECT_COLUMNS, new List<SqlParameter>());
foreach (DataRow dr in dt.Rows)
{
dataColumns.Add(new dataColumnSpecs(dr["columnName"].ToString(), dr["friendlyName"].ToString(), dr["dataType"].ToString(), (int)dr["dataSize"]));
}
}
public void fillDDLColumns()
{
fillDataColumnsList();
ddlColumns.Items.Clear();
foreach (dataColumnSpecs dcs in dataColumns)
{
ListItem li = new ListItem();
li.Text = dcs.friendlyName;
li.Value = dcs.columnName;
ddlColumns.Items.Add(li);
}
ddlColumns.Items.Insert(0, new ListItem(" -SELECT A COLUMN- ", ""));
ddlColumns.DataBind();
}
protected void ddlColumns_SelectedChanged(object sender, EventArgs e)
{
//THIS CODE IS BEING FIRED WHEN A BUTTON ON THE PARENT *.ASPX IS CLICKED
}
*.ASPX
<asp:UpdatePanel ID="upControls" runat="server">
<ContentTemplate>
<asp:Button ID="btnAddControl" runat="server" Text="+" OnClick="btnAddControl_Click" />
</ContentTemplate>
</asp:UpdatePanel>
<asp:Button ID="btnGo" runat="server" Text="Go" OnClick="btnGo_Click" ValidationGroup="vgGo" />
<asp:GridView...
*.ASPX.CS
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
uc_Counter = 0;
addControl();
gridview_DataBind();
}
else
{
reloadControls();
}
}
protected void btnGo_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
//THIS BUTTON CLICK IS WHAT'S TRIGGERING THE
//SELECTEDINDEXCHANGED EVENT TO FIRE ON MY *.ASCX
gridview_DataBind();
}
}
private void reloadControls()
{
int count = this.uc_Counter;
for (int i = 0; i < count; i++)
{
Control myUserControl = Page.LoadControl("~/Controls/myUserControl.ascx");
myUserControl.ID = "scID_" + i;
upControls.ContentTemplateContainer.Controls.AddAt(i, myUserControl);
((customUserControl)myUserControl).fillDDLColumns();
}
}
private void addControl()
{
Control myUserControl = Page.LoadControl("~/Controls/myUserControl.ascx");
myUserControl.ID = "scID_" + uc_Counter.ToString();
upControls.ContentTemplateContainer.Controls.AddAt(upControls.ContentTemplateContainer.Controls.IndexOf(btnAddControl), myUserControl);
//((customUserControl)myUserControl).fillDDLColumns();
this.uc_Counter++;
}
protected int uc_Counter
{
get { return (int)ViewState["uc_Counter"]; }
set { ViewState["uc_Counter"] = value; }
}
Even though this is already answered I want to put an answer here since I've recently tangled with this problem and I couldn't find an answer anywhere that helped me but I did find a solution after a lot of digging into the code.
For me, the reason why this was happening was due to someone overwriting PageStatePersister to change how the viewstate hidden field is rendered. Why do that? I found my answer here.
One of the greatest problems when trying to optimize an ASP.NET page to be more search engine friendly is the view state hidden field. Most search engines give more score to the content of the firsts[sic] thousands of bytes of the document so if your first 2 KB are view state junk your pages are penalized. So the goal here is to move the view state data as down as possible.
What the code I encountered did was blank out the __VIEWSTATE hidden fields and create a view_state hidden field towards the bottom of the page. The problem with this is that it totally mucked up the viewstate and I was getting dropdownlists reported as being changed when they weren't, as well as having all dropdownlists going through the same handler on submit. It was a mess. My solution was to turn off this custom persister on this page only so I wouldn't have to compensate for all this weirdness.
protected override PageStatePersister PageStatePersister
{
get
{
if (LoginRedirectUrl == "/the_page_in_question.aspx")
{
return new HiddenFieldPageStatePersister(Page);
}
return new CustomPageStatePersister(this);
}
}
This allowed me to have my proper viewstate for the page I needed it on but kept the SEO code for the rest of the site. Hope this helps someone.
I found my answer in this post .net DropDownList gets cleared after postback
I changed my counter that I was storing in the viewstate to a session variable.
Then I moved my reloadControls() function from the Page_Load of the *.ASPX to the Page_Init.
The key was dynamically adding my user control in the Page_Init so it would be a member of the page before the Viewstate was applied to controls on the page.
I've got a pretty strange problem with my usercontrols that I'm working with. I'm coding some user controls to be used in a DotNetNuke module.
Basically I have a UserControl on my page that holds some controls and then there is a Placeholder where I am loading in a UserControl that has a GridView placed in it.
Basically here is my page structure
<asp:Panel runat="server" ID="pnlServiceType" Visible="false">
<uc:ServiceType runat="server" ID="ServiceType" />
</asp:Panel>
Within that user control are some form elements and then the following:
<asp:PlaceHolder runat="server" ID="phServices" />
That placeholder then has a user control added to it like so:
<p class="header"><asp:Label runat="server" ID="lblServiceHeader" Font-Bold="true" /></p>
<asp:GridView runat="server" ID="gvServices" AlternatingRowStyle-BackColor="#eaeaea" BorderStyle="None" GridLines="None"
AutoGenerateColumns="false" CellPadding="6" Width="100%" EnableViewState="false" OnRowEditing="gvServices_RowEditing">
When the user picks something in the DropDownList, I'm clearing the controls out of the Placeholder, and re-adding just a single UserControl for the set of records for whatever type they've picked like so
_serviceID = e.Value;
//Clear the controls out of the placeholder
phServices.Controls.Clear();
//Reset the count of grids for the OnInit() method
Session["GridCount"] = 0;
if (e.Value != "NULL")
{
PopulateServicesGrid(_service.GetServicesByFormType(Convert.ToInt32(e.Value)));
}
else
{
PopulateServicesGrid(_service.GetServicesByClient(Convert.ToInt32(_client)));
}
And the PopulateServicesGrid method:
private void PopulateServicesGrid(List<NOAService> services)
{
//Creates a LINQ grouping based on the Billing Codes
//Allows a super easy creation of grids based on the grouped billing codes
var query = services.Select(service => service.BillingCode).Distinct();
foreach (string code in query)
{
var servicesByCode = services.Where(service => service.BillingCode == code).ToList();
ServicesGrid servicesGrid = LoadControl("~/DesktopModules/LEL Modules/NOA/ServicesGrid.ascx") as ServicesGrid;
Label lblServiceHeader = servicesGrid.FindControl("lblServiceHeader") as Label;
GridView gvServices = servicesGrid.FindControl("gvServices") as GridView;
phServices.Controls.Add(servicesGrid);
servicesGrid.ID = code;
lblServiceHeader.Text = servicesByCode[0].FormTypeName;
gvServices.DataSource = servicesByCode;
gvServices.DataBind();
Session["GridCount"] = phServices.Controls.Count;
}
}
And on my ServiceType UserControl, on the PageInit, I'm readding the ServiceGrid usercontrol so that my grids show up across postbacks and aren't lost from the Placeholder
void NOAServiceType_Init(object sender, EventArgs e)
{
for (int i = 0; i < Convert.ToInt32(Session["GridCount"]); i++)
{
ServicesGrid servicesGrid = LoadControl("~/DesktopModules/LEL Modules/NOA/ServicesGrid.ascx") as ServicesGrid;
phServices.Controls.Add(servicesGrid);
}
}
The grids populate successfully and everything seems to work just fine. But for some reason, on my GridView, I have a CommandField of
<asp:CommandField ShowEditButton="true" ItemStyle-HorizontalAlign="Right" EditText="Edit" UpdateText="Update"
EditImageUrl="~/images/LELModules/appbar.edit.rest.png" CancelImageUrl="~/images/LELModules/appbar.close.rest.png" UpdateImageUrl="~/images/LELModules/appbar.check.rest.png"
ButtonType="Image" CausesValidation="false" />
When I click my Edit command on the Grid row, nothing happens. My grid doesn't lose its rows, the control is still there, everything seems like it should be ok. The RowEditing event doesn't fire until I click it a second time.
Any idea why this might be occuring?
UPDATE: I've managed to figure out that my SelectedIndexChanged handlers are effectively resetting the DataSource on the Grid contained by the UserControl when they are readded to the PlaceHolder. When the CommandField (Edit) is clicked though, the Init fires for the UserControl that holds the placeholder
ServiceType UserControl < `Init` fires here
-Form elements
-Placeholder which holds
--UserControl with GridView
The Init method loads up new instances of the UserControl and adds them to the PlaceHolder, but the DataSource is null. With EnableViewState=true it looks like the data is still bound, but if I handle PreRender, I can see that the DataSource on my gvServices | null
Is it even possible to edit rows like this on a GridView that is being added dynamically to a PlaceHolder over and over?
FIXED I found out what the issue was after referring to this article
http://www.west-wind.com/weblog/posts/2006/Feb/24/Overriding-ClientID-and-UniqueID-on-ASPNET-controls
It got me thinking, what if the IDs were getting changed? The controls are way nested. So I went and put a watch on the GridView's ID, ClientID, and UniqueID just to see. When the Control is loaded on my Init handler, it's assigned a super generic ID when it's added.
private void PopulateServicesGrid(List<NOAService> services)
{
//Creates a LINQ grouping based on the Billing Codes
//Allows a super easy creation of grids based on the grouped billing codes
var query = services.Select(service => service.BillingCode).Distinct();
foreach (string code in query)
{
var servicesByCode = services.Where(service => service.BillingCode == code).ToList();
ServicesGrid servicesGrid = LoadControl("~/DesktopModules/LEL Modules/NOA/ServicesGrid.ascx") as ServicesGrid;
Label lblServiceHeader = servicesGrid.FindControl("lblServiceHeader") as Label;
GridView gvServices = servicesGrid.FindControl("gvServices") as GridView;
phServices.Controls.Add(servicesGrid);
**servicesGrid.ID = code;**
lblServiceHeader.Text = servicesByCode[0].FormTypeName;
gvServices.DataSource = servicesByCode;
gvServices.DataBind();
Session["GridCount"] = phServices.Controls.Count;
}
}
I was setting the ID as something else. So when I was hitting the Edit RowCommand on my grid, it was reloading the controls again on Init and the IDs were being changed back from my custom set code (T2020, pulled from my database) to the generic ID again, it didn't know how to fire the event.
I hope this helps someone as I've lost at least 12 hours fixing this problem.
I'm populating a dropdownlist in c# asp.net-MVC from a SQL table using Linq2Sql. I'd like for the user to be able to enter something that isn't in the list into the drop down and have it add to the table. Is this possible?
Sounds like you need to add a radio button labeled "Other". When the user clicks the radio button a text box would appear that allows the user to input a new value that you can save to your DB and display in the drop down.
EDIT:
Quick snippet to enable the control using JavaScript:
<script language="javascript" type="text/javascript">
function radioclicked() {
textObj = document.getElementById('<NAME OF TEXT BOX');
textObj.disabled = false;
}
</script>
You can use a check box instead of a radio button so that the enabled property can be toggled.
To completely hide the text box then you will have to look into jQuery/Ajax.
Why can't we use a lightweight Add-on like www.combodropdown.info for this purpose? You can even consider AutoComplete plugin from jQuery, if your app already references jQuery.
Also a combobox will allow a user to enter a value in addition to picking from a list.
My MVC is not so so, but I assume this still applies as MVC is just model view controller.
What if you throw a drop down on your form visible=true, and a textbox on your form visible =false.
<asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="True"
onselectedindexchanged="DropDownList1_SelectedIndexChanged">
</asp:DropDownList>
<asp:TextBox ID="TextBox1" runat="server" Visible="False"></asp:TextBox>
Fill your drop down:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
List<int> s = Enumerable.Range(1, 10).ToList();
DropDownList1.DataSource = s;
DropDownList1.DataBind();
DropDownList1.Items.Add("Other");
}
}
Add an event to handle if someone selects other. If they do make the textbox visible:
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
switch (this.DropDownList1.SelectedItem.Text)
{
case "Other":
this.TextBox1.Visible=true;
break;
default:
this.TextBox1.Visible=false;
break;
}
}
Now you can enter your value and re-store back to the db.