For your information (this was my original problem webforms : add dynamically in javascript option to a dropdownlist, solved thanks to ConnorsFan).
My goal is to having a infragistics dropdownlist enabling multi selection and at each selection I want an event fired server side without refreshing the whole page.
This is my aspx page :
<%# Register assembly="Infragistics45.Web.v16.1, Version=16.1.20161.1000, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb" namespace="Infragistics.Web.UI.ListControls" tagprefix="ig" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<ig:WebDropDown ID="WebDropDown1" runat="server" Width="200px" OnSelectionChanged="WebDropDown1_SelectionChanged" EnableMultipleSelection="true" EnableClosingDropDownOnSelect="false" AutoPostBack="true">
</ig:WebDropDown>
<asp:UpdatePanel runat="server" UpdateMode="Conditional">
<Triggers>
<asp:AsyncPostBackTrigger ControlId="WebDropDown1" EventName="SelectionChanged"/>
</Triggers>
</asp:UpdatePanel>
This is my code-behind page :
private List<string> allPossiblechoices = new List<string>() { "a", "b", "c","d","e" };
private List<string> defaultChoices = new List<string>() { "a", "b", "c" };
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
foreach(var choice in allPossiblechoices)
{
WebDropDown1.Items.Add(
new DropDownItem()
{
Text = choice,
Value = choice,
Selected = defaultChoices.Contains(choice)
}
);
}
}
}
protected void WebDropDown1_SelectionChanged(object sender, DropDownSelectionChangedEventArgs e)
{
// I put a breakpoint here to see what e.NewSelection and e.OldSelection are
}
By default, when the page is requested for the first time, the dropdown is composed of a,b,c,d,e and only a,b,c are selected.
When I select d, a request is indeed send to the server (I put a breakpoint in my event handler) and the results are correct :
EventArgs e.OldSelection contains a,b,c.
EventArgs e.NewSelection contains a,b,c,d.
Then, I deselect d and the results are the following :
EventArgs e.OldSelection contains a,b,c.d.
EventArgs e.NewSelection contains a,b,c,d.
I don't understand why EventArgs e.NewSelection contains d even if I deselected it.
The fact that it's even more strange, is that I have done the same thing without the updatePanel and everything works fine, the selection (new and old) are correct.
Thanks in advance for your help.
You can call the RegisterStartupScript static method of the ScriptManager class to add some Javascript code to be executed after the event handler has returned. In the code below, I assume that the ID of the UpdatePanel is UpdatePanel1.
protected void WebDropDown1_SelectionChanged(object sender, DropDownSelectionChangedEventArgs e)
{
WebDropDown wdd = sender as WebDropDown;
string scriptCode = string.Format("document.getElementById('{0}').openDropDown();", wdd.ClientID);
ScriptManager.RegisterStartupScript(UpdatePanel1, UpdatePanel1.GetType(), "WDDScript1", scriptCode, true);
}
If it works, you will probably see the WebDropDown closing/opening when the panel is updated (unfortunately).
Related
I have a Web Form dependent on a Master Page, that contains a simple Form like this:
<asp:TextBox runat="server" ID="txtValue"></asp:TextBox>
<asp:DropDownList runat="server" ID="ddlProduct" />
<asp:Button runat="server" ID="btnConfirm" Text="Confirm" OnClick="btnConfirm_OnClick" />
on Page_Load I dynamically populate the DropDownList with values:
foreach(var product in products)
{
ListItem item = new ListItem(product.Nazev, product.ProductId.ToString());
ddlProduct.Items.Add(item);
}
Now .. normally if the DropDownList was populated statically, I would go with this, to get my selected value out of it:
protected void btnConfirm_OnClick(object sender, EventArgs e)
{
string selectedProduct = ddlProduct.SelectedValue;
}
But this is a no-go in this situation. Hence I try to directly get the selected value from POSTed parameters with one of those approaches (which are in fact practically the same):
protected void btnConfirm_OnClick(object sender, EventArgs e)
{
string selectedProduct = Request.Form.Get(ddlProdukt.ClientID);
string selectedProduct2 = Request.Params[ddlProdukt.ClientID];
}
But it doesn't work. If I debug it, the actual "id/name" of the DropDownList in the POSTed Form (Request.Params) is:
"ctl00$MainContent$ddlProdukt"
whereas the ClientId my approach gives me is:
"ctl00_MainContent_ddlProdukt"
I can't figure out, why does it replace '_' for '$' in the ID of my DDL control.. It seems to be such a trivial thing. There should be a better way to find out the right ID, than replacing the character, right?
Is there some other way I should "look/ask" for the selected value?
*Take into account, that I'am not looking for a solution with an UpdatePanel. I know, that using an UpdatePanel would result in the DDL value being sucessfully selected after the Postback, but that is NOT the answer I'm looking for here.
Thank you for any input.
If you still need to use the Request.Form.Get or Request.Params you should call it on ddlProdukt.UniqueID as it's separator is '$' versus '_' on the .ClientID
As requested an example. This code and the <asp:DropDownList ID="ddlProduct" runat="server"></asp:DropDownList> are on the aspx page, not the Master.
protected void Page_Load(object sender, EventArgs e)
{
//bind data not here
if (IsPostBack == false)
{
//but inside the ispostback check
ddlProduct.Items.Add(new ListItem("Item 1", "1"));
ddlProduct.Items.Add(new ListItem("Item 2", "2"));
ddlProduct.Items.Add(new ListItem("Item 3", "3"));
ddlProduct.DataBind();
}
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = ddlProduct.SelectedValue;
}
This is probably really simple, but I cant seem to get my values picked up at all from the parent page?
Aspx page is within a master page and ContentPwith the code for example
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<asp:TextBox ID="tbFromDate" runat="server" ></asp:TextBox>
<asp:Button ID="butDisplayInfo" runat="server" Text="Display info" OnClick="butDisplayInfo_Click"
CssClass="button-gray" />
<asp:PlaceHolder ID="placeHolderContent" runat="server">
</asp:PlaceHolder>
What I want is when the button is pressed it loads the UserControl and loads data based on the date put in the text box which I can then use to load data to the control?
.aspx.cs
protected void butDisplayInfo_Click(object sender, EventArgs e)
{
var ctrl = LoadControl("~/Controls/DailyShiftStats.ascx");
ctrl.ID = "ucUserCtrl1";
placeHolderContent.Controls.Add(ctrl);
}
Usercontrol ascx.cs
protected void Page_Load(object sender, EventArgs e)
{
TextBox tb = (TextBox)this.Parent.Page.FindControl("ContentPlaceHolder1").FindControl("tbFromDate");
Response.Write(tb.Text);
}
public void getShiftInfo(DateTime shiftDate)
{
//load my data
}
As per my comment - you would be better defining a property in your control and then have the page pass this in, I have not done this in a while but the basic idea is -
In your control
public DateTime? ShiftDate
{
set { this.shiftDate = value; }
}
private DateTime? shiftDate;
Then you can use shiftDate anywhere in your control where it is needed, if you make it Nullable as above then you can check to see if it has been set and throw an error (or whatever is appropriate) if not.
In your page when creating your control you would then have (Note: you need to cast your control to correct type)
var ctrl = (DailyShiftStats)LoadControl("~/Controls/DailyShiftStats.ascx");
ctrl.ID = "ucUserCtrl1";
//TODO: Handle an invalid date
DateTime shiftDate;
if (DateTime.TryParse(tbFromDate.Text, out shiftDate))
{
ctrl.ShiftDate = shiftDate;
}
placeHolderContent.Controls.Add(ctrl);
I'm having a hard time figuring this out and I hope you guys would help me.
I have a page called Index.aspx with a DropDownList that is a separate UserControl class (because it will be used in other pages). Here's the code for that:
UcSelecionarLocal.ascx:
<%# Control Language="C#" AutoEventWireup="true"
CodeBehind="UcSelecionarLocal.ascx.cs"
Inherits="QuickMassage.uc.UcSelecionarLocal" %>
<asp:DropDownList ID="ddlLocais" runat="server"
CssClass="span4 dropdown-toggle" AutoPostBack="true">
</asp:DropDownList>
UcSelecionarLocal.ascx.cs:
public partial class UcSelecionarLocal : UserControl {
protected void Page_Load(object sender, EventArgs e) {
if (!this.IsPostBack) {
PreencherLocais();
}
}
private void PreencherLocais() {
ddlLocais.Items.Clear();
ddlLocais.Items.Add(new ListItem("Selecione", "0"));
ControleLocal controle = new ControleLocal();
DataTable tab = controle.ListarLocais();
foreach (DataRow row in tab.Rows) {
ddlLocais.Items.Add(new ListItem(row["Descricao"].ToString(),
row["ID"].ToString()));
}
}
}
This control is placed in Index.aspx and loads its values correctly. The form that it's contained in, has the action set to agendamentos.aspx. When I change the ddlist, the page is submitted to the forms action page, as it should be.
On the other page the problems begin: I get the parameters posted to this page and one of them is the ddlist value. In the immediate window, I check the value and it's there, let's say that it is 1.
To make long story short, I have this code:
agendamentos.aspx.cs:
protected void Page_Load(object sender, EventArgs e) {
DropDownList locais = ObterComponenteListaLocais();
try {
locais.SelectedIndex =
int.Parse(HttpContext.Current.Request["ucSelLocal$ddlLocais"]);
}
While debugging, I see that locais.SelectedIndex is -1. After the assignment it remains -1. The page loads and then I change the ddlist value again to 2. When debugging the same code above, I see that the locais.SelectedIndex is now 1. Again, setting it to 2, as it would normally be, produces no effect. If I change the ddlist again to 3, the SelectedIndex becomes 2 and does not take the value 3.
In other words: the value of the index in a newly loaded page is the value of the page that was loaded before.
Could you guys help me?
This is because the Page_Load event is firing in your page before the user control is loading. Do this:
public partial class UcSelecionarLocal : UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
public void PreencherLocais()
{
ddlLocais.Items.Clear();
ddlLocais.Items.Add(new ListItem("Selecione", "0"));
ControleLocal controle = new ControleLocal();
DataTable tab = controle.ListarLocais();
foreach (DataRow row in tab.Rows)
{
ddlLocais.Items.Add(new ListItem(row["Descricao"].ToString(), row["ID"].ToString()));
}
}
}
Then in your aspx page:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
this.idOfYourUserControl.PreencherLocais();
DropDownList locais = ObterComponenteListaLocais();
try {
locais.SelectedIndex =
int.Parse(HttpContext.Current.Request["ucSelLocal$ddlLocais"]);
}
}
Also because your question is a little confusing, an important note is that Page_Load fires before data is captured from controls that post back data. So that's a bad place to get their information because it will be what it was previously. That's why you need to create a function that fires on something like a button click that will execute after the controls data have been loaded.
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 been searching how to acomplish this but I have'nt been able to find a solution. I simplified the problem from my original project where I have to achieve this to the following:
In the .aspx I have:
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:UpdatePanel ID="udpDynamicControls" UpdateMode="Conditional" runat="server">
<ContentTemplate>
<asp:PlaceHolder ID="PlaceHolder" runat="server">
</asp:PlaceHolder>
</ContentTemplate>
</asp:UpdatePanel>
And in code behind:
public partial class _Default : System.Web.UI.Page
{
protected string TextToShow
{
get
{
return Session["TextToShow"] == null ? "Original Text" :
Session["TextToShow"].ToString();
}
set { Session["TextToShow"] = value; }
}
protected void Page_Init(object sender, EventArgs e)
{
var lblToChange = new Label
{
ID = "lblToChange",
Text = TextToShow
};
var chkChangeText = new CheckBox
{
ID = "btnChangeText",
Text = "Change Text",
AutoPostBack = true
};
chkChangeText.CheckedChanged += ChkChangeTextClick;
PlaceHolder.Controls.Add(lblToChange);
PlaceHolder.Controls.Add(chkChangeText);
}
private void ChkChangeTextClick(object sender, EventArgs e)
{
var check = (CheckBox) sender;
TextToShow = check.Checked ? "Text Changed" : "Original Text";
udpDynamicControls.Update();
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
As you can see what i'm trying to do si to update the label when the check box is changed threw the update panel. But the udpDynamicControls.Update(); Doesnt fire the page_init event, where it would take its new value. I already checked out similar questions but I believe this is a diferent scenario.
I will be very thankfull to anybody that could help. I apologize for any bad english.
So I resolved my problem on my original project some other way, but I thought I might as well post the answer to what I did in case somebody has the same problem, the they might use the same solution.
Since I was depending on the page_init to run again after the update just to update the new value for my control and this wasn't happing, then a possible solution would be to update the value of the control in the check box changed event, where as the label is already created, and in the next page life cycle run, it would persist its value.
So the only thing I changed was the ChkChangeTextClick Event and the end result was:
protected void ChkChangeTextClick(object sender, EventArgs e)
{
var check = (CheckBox) sender;
TextToShow = check.Checked ? "Text Changed" : "Original Text";
((Label) PlaceHolder.FindControl("lblToChange")).Text = TextToShow;
}
Replacing:
udpDynamicControls.Update();
For:
((Label) PlaceHolder.FindControl("lblToChange")).Text = TextToShow;
Come to think about it, its a more simple approach, in my original project I have several dynamic controls and I just set a pattern in my controls ID to find the ones I need and change the values. The events of the controls don't get lost and I can show the updated information without the browser postback.
I hope this is of any help to anyone in the future, because I almost ripped all my hair out with this.