I have created a usercontrol (.ascx) for use in my asp.net website (not mvc). The control has a few text boxes and allows the user to enter a new customer. It has two buttons, one to add the customer and one to cancel the control. I call display the control by using a “ModalPopupExtender “ with the code shown below:
This is in an updatePanel:
<asp:ModalPopupExtender ID="ModalPopupExtender2" runat="server" TargetControlID="hiddenButton"
PopupControlID="Panel3" BackgroundCssClass="modalBackground" DropShadow="true"/>
Here is the Panel3 markup:
<asp:Panel ID="Panel3" runat="server" Style="display: none" CssClass="modalPopup" Width="100%" Height="100%">
<uc3:CustomerForm runat="server" ID="CustomerForm1" ParentUpdatePanel="UpdatePanel1" ErrorDisplayControl="lblErrorMessage"/>
</asp:Panel>
When I click a button on the main page the code behind calls:
ModalPopupExtender2.Show();
This shows my control and when I add a new customer the database is updated so this side of things works fine. What I need to do is enhance the code so that when a customer is added and the usercontrol closes, the customer dropdownlist on the main page is loaded with the updated customer list and the customer that the user enter is selected. The dropdownlist is databound so my question is, how do I know when my usercontrol has been closed so I can work out if a new customer has been added and then re databind my list and select the correct item? Working with asp.net and javascript is not an area I have a lot of skill in so please forgive if I have used the wrong terminology.
I have found a solution to my problem. In my usercontrol, after a new customer has been added I call:
RaiseBubbleEvent(this, new CustomerArgs {OrganisationId = organisationId});
In the calling page’s code behind I listen for the bubble event:
protected override bool OnBubbleEvent(object sender, EventArgs e)
{
if (sender is CustomerForm && e is CustomerArgs)
{
_organisationID = (e as CustomerArgs).OrganisationId.ToString();
DoOrganisationListDataBind();
return true;
}
return false;
}
This works fine for what I’m after. Not sure if this is a good way to do it but it works.
Related
I'm using nested controls for a series of forms and having problem with the onclick event.
I'm just using textboxes and a manual code-behind insert rather than Gridview/Details view structure.
The scenario is that I have a car insurance form where users can add additional named drivers. At the top is a section where you add your own details (which is a seperate user control) and there is a section underneath where you can add up to 4 named drivers (which is also a seperate usercontrol). What I wanted to do was use my 'Add Driver' linkbutton onclick event to add the details into the database, then clear the form fields so the user could then add another driver. This all works fine until you go to click the Add Driver button again and discover it doesn't work.
This is the Parent control that houses the Add Driver user control:
<h2>Add Permanent Driver</h2>
<asp:ValidationSummary ID="uxValidationSummary" runat="server" ShowSummary="true" ValidationGroup="ChangeDetails" />
<div class="clear"></div>
<h3>
Please enter your details below as they are on your insurance documents</h3>
<uc1:UserDetails ID="uxUserDetails" runat="server" />
<uc2:AddDriver ID="uxAddDriver" EntryType="Permanent" runat="server" />
This is a cutdown version of the onclick event for the Add Driver submit button on the AddDriver control
protected void uxSubmit_Click(object sender, EventArgs e)
{
Page.Validate("AddDriver");
if (Page.IsValid)
{
DriverDAL.DriverTableAdapters.DriverTableAdapter taDriver = new DriverDAL.DriverTableAdapters.DriverTableAdapter();
taDriver.DriverInsert(StartDate, EndDate, Title,
FirstName, Surname, DateOfBirth, Gender);
uxDriverList.DataBind();
uxDate.Text = string.Empty;
uxEndDate.Text = string.Empty;
uxTitle.SelectedIndex = 0;
uxFirstName.Text = string.Empty;
uxSurname.Text = string.Empty;
uxDateOfBirth.Text = string.Empty;
uxGender.SelectedIndex = 0;
}
}
Is there anything I can do to reactivate this linkbutton after the first OnClick (or remove it and recreate it) so I can add multiple drivers? I tried to databind the button after the submit, but as expected, that had no effect, but that is the kind of thing I'm looking for. I can't redirect the page, because as mentioned earlier in the post, it is using nested controls and I arrive at this form by using the SelectedIndexChanged event of a DropDownList.
Any ideas appreciated - thanks. Let me know if more info is required.
It sounds like you might have some validation that's preventing the button from doing a postback. Try setting CausesValidation="false" and see if that makes a difference.
If validation is causing the problem, I would either separate the validation into groups and assign the button to a specific group, or toggle the enabled state of the validators based on some condition, for example:
RequiredFieldValidator1.Enabled = Panel1.Visible;
I have a repeater that lists customer items, one of which is a button for more information. When the user clicks that button, a user control appears immediately below it. I'd like for the user control to then close when the button is clicked again. I initially toggled visibility server-side, but I now can't use this because the user control needs to be loaded on the button click due to parameters that need to bepassed through. I also can't used show and hide by jquery because that would involve downloading all the data first for the whole page which could make the page too cumbersome.
So currently in each repeater I have a linkbutton with command arguments and an OnCommand event which fires this event:
protected void uxPolicyDocsButton_Command(object sender, CommandEventArgs e)
{
//Find Policy Summary control within repeater item in order to toggle visibility
LinkButton button = sender as LinkButton;
if (button != null)
{
RepeaterItem ri = button.Parent as RepeaterItem;
if (ri != null)
{
PlaceHolder policySummaryPlaceHolder = (PlaceHolder)ri.FindControl("uxPolicySummaryPlaceHolder");
Control policyDocuments = (Control)PolicySummaryPlaceHolder.FindControl("uxPolicyDocumentsControl");
foreach (Control c in policySummaryPlaceHolder.Controls)
{
//FindControl action goes in here. Have stepped through though and it doesn't appear
}
if (policyDocuments != null)
{
policySummaryPlaceHolder.Controls.Remove(policyDocuments);
}
else
{
policyDocuments uxPolicyDocumentsControl = (PolicyDocuments)LoadControl("~/Controls/Home/PolicyDocuments.ascx");
uxPolicyDocumentsControl.PolicyNumber = button.CommandArgument;
policySummaryPlaceHolder.Controls.Add(uxPolicyDocumentsControl);
}
}
}
}
My plan for the toggle was that if the PolicyDocuments control was null, then load the control, and if not then remove the control, but it always came back null. It did load the control correctly though.
Here is the section of the repeater:
<ItemTemplate>
<tr>
<td>
<%#Eval("StartDate","{0:d}")%>
</td>
<td class="center-cell-ctrl">
Postcode:<br />
<%#Eval("Postcode")%>
</td>
<td id='<%#Eval("PolicyNumber")%>' class="button-cell">
<asp:LinkButton ID="uxPolicyDocsButton" CommandName="PolicyNumber" CommandArgument='<%#Eval("PolicyNumber")%>' OnCommand="uxPolicyDocsButton_Command" runat="server" Visible="false">Policy<br />Documents</asp:LinkButton>
</td>
</tr>
<asp:PlaceHolder ID="uxPolicySummaryPlaceHolder" runat="server"></asp:PlaceHolder>
<asp:PlaceHolder ID="uxPolicyDocumentsPlaceHolder" runat="server"></asp:PlaceHolder>
</ItemTemplate>
I've had a look around for people who have had similar problems, and a common answer is that you need to load controls in the Page_Init event. Does that mean that this way won't work? Any ideas for an alternative if this is the case?
Many thanks
Every time your page is loaded (either initially, or via PostBack) the Control Tree needs to be re-created.
The controls declared in the ASPX part of your page are added by the framework (kind of), but any controls that you add "dynamically" need to be placed back in the control tree too. If they aren't then, as you've disovered, they simply can't be found again.
Complex controls, like the datagridview, rebuild their control tree by storing (a huge amount of) data in the ViewState.
It might be simpler, rather than have your page add/remove items from a PlaceHolder, to create a user control that you can and hide, and load it's data the first time its shown. This also helps your controls and pages adhere to the Single Repsonsibility Principal
I dont' understand, why you can't add the PolicyDocuments with visible = false and no datasource instead of Placeholder, like that:
<uc:PolicyDocuments ID="uxPolicyDocuments" runat="server" Visible="false"></asp:PolicyDocuments>
and during the command use such code:
if (ri != null)
{
var policyDocuments = ri.Controls.OfType<PolicyDocuments>().FirstOrDefault();
if (policyDocuments == null)
return;
if (policyDocuments.Visible)
{
policyDocuments.Visible = false;
}
else
{
policyDocuments.PolicyNumber = button.CommandArgument;
policyDocuments.Visible = true;
}
}
In this case you don't need to use some hacks in Init event of the page.
Anyway, you always can use the .Controls.OfType<PolicyDocuments>() extension method to get all the user controls you need.
I'm building an asp.net cutom control inside which I have two dropdownlists: companyIdSelection and productFamilySelection.I populate the companyIdSelection at Page_Load and in order to populate the productFamilySelection depending on the selected item in companyIdSelection.I'm using UpdatePanels to achieve this, but for some reason every time I update companyIdSelection Page_Load is being called ( which as far as I know should happen only when the entire page is reloaded ), the list is being reloaded again and the item the user selected is lost( the selected item is always the top one ).Here's the code
<asp:UpdatePanel ID="updateFamilies"
runat="server"
UpdateMode="Always">
<ContentTemplate>
Company ID:<br>
<br></br>
<asp:DropDownList ID="companyIdSelection"
runat="server"
AutoPostBack="True"
OnSelectedIndexChanged="companyIdSelection_SelectedIndexChanged">
</asp:DropDownList>
<br></br>
Product Family:
<br></br>
<asp:DropDownList ID="productFamilySelection" runat="server"
AutoPostBack="True"
onselectedindexchanged="productFamilySelection_SelectedIndexChanged">
</asp:DropDownList>
<br>
</ContentTemplate>
</asp:UpdatePanel>
protected void Page_Load(object sender, EventArgs e)
{
this.companyIdSelection.DataSource = companyIds(); //companyIds returns the object containing the initial data items
this.companyIdSelection.DataBind();
}
protected void companyIdSelection_SelectedIndexChanged(object sender, EventArgs e)
{
// Page_Load is called again for some reason before this method is called, so it
// resets the companyIdSelection
EngDbService s = new EngDbService();
productFamilySelection.DataSource = s.getProductFamilies(companyIdSelection.Text);
productFamilySelection.DataBind();
}
Also, I tried setting the UpdateMode of the UpdatePanel to "Conditional" and adding an asyncpostback trigger
but the result was the same.
What am I doing wrong?
PS:
I fixed the updating problem, by using Page.IsPostBack in the Page_Load method, but I would still want to avoid a full postback if possible
I think you misunderstand how UpdatePanels work. They DO actually do a full page postback, it's just that during the rendering stage they capture only a portion of the output and send it back in the AJAX response so the page can be updated. More info here.
So you will still need to check whether it is a postback in your page_load event and only perform your data load if it isn't one.
The update panel call back will go through the page load on every call back. In face, the pull page lifecycle (minus render and prerender) will occur. Update panels give the appearance of ajax, but your client side code still posts back to the same page - which is the problem you are describing. If you can avoid using Update Panels, I suggest you do so. Use something like jQuery instead. If not, then use this in your Page_Load
if (Page.IsCallback) { } //Do callback specific code here
else { } //Do Postback specific code here
Hope this helps. Good Luck.
I don't know whether this is really possible, but I'm trying my best.
If I have a (complex) custom server control which (beside other controls) renders a TextBox on the UI. When placing the server control on a page, would it be possible to attach a RequiredField validator to that server control, such that the validator validates the Text property of that control which points to the Text property of the rendered TextBox?
Of course I could incorporate the RequiredField validator directly into the server control, but this is for other reasons not possible (we are rendering RequiredField validators automatically on the UI).
Thanks for your help.
I think one solution is to put your TextBox control inside a Panel then you add the RequiredValidator control dynamically on the Page_Load event handler.
<asp:Panel ID="Panel1" runat="server">
<MyCustomTextBox ID="TextBox1" runat="server"></MyCustomTextBox>
</asp:Panel>
<asp:Button ID="Button1" runat="server" Text="Button" />
then
protected void Page_Load(object sender, EventArgs e)
{
var validator = new RequiredFieldValidator();
validator.ControlToValidate = "TextBox1";
validator.ErrorMessage = "This field is required!";
Panel1.Controls.Add(validator);
}
I put the CustomTextBox inside the panel to assure that the validation controle place is correct when added
I got it, the 2nd time that I'm answering to my own post :) Next time I'll do a deeper research before.
For those of you that may encounter the same problem. You have to specify the ValidationProperty attribute on your server control's class. For instance if your server control exposes a property "Text" which is displayed to the user and which should also be validated, you add the following:
[ValidationProperty("Text")]
Then it should work.
I have a listbox control. When the user clicks it, it tells a custom control to use a certain ID to use.
The custom control draws the same thing everytime(dynamically), just loads different content depending on this ID(it's loaded from a database into a dynamic form like control).
Ok, Now I'm having trouble with viewstate spillage. When you click the listbox to load say ID #1, it'll all look good. Then, you click on ID #2 and all the textbox controls created inside the custom control has the same thing that was put in ID #1. So when the listbox index changes I need to clear the view state, but I can't get this to work.
All of the controls are created at Page_Load also.
I tried ViewState.Clear() at Page_Load but that didn't do anything.
I had the custom control derive from INamingInterface, but I guess the IDs still match for viewstate.
I've tried changing the custom controls ID to something unique(like "CONROL_"+id.ToString()) I've also tried doing the same thing with the panel containing the custom control.
I can not seem to get rid of this view state!
EDIT
Ok here is code that demonstrates the problem
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (ddl.SelectedValue == "1")
{
Create("ID #1");
}
else if (ddl.SelectedValue == "2")
{
Create("ID #2");
}
}
void Create(string text)
{
TextBox t = new TextBox();
t.Text = text;
pnl.Controls.Add(t);
}
}
the markup:
<div>
<asp:Panel ID="pnl" runat="server">
<asp:DropDownList ID="ddl" runat="server" AutoPostBack="True">
<asp:ListItem Text="id 1" Value="1">
</asp:ListItem>
<asp:ListItem Text="id 2" Value="2"></asp:ListItem>
</asp:DropDownList>
</asp:Panel>
</div>
If you run this code you'll notice that if you change what is in the textbox and then you change the dropdown list, then what you typed earlier will be kept in there instead of it being overwritten..
My basic goal with this is to get it so that when you change to ID #2, it puts "ID #2" in the textbox no matter what(preferably without disabling viewstate altogether)
If I set the ID of the Text control then it doesn't retain the old value. Are you giving all controls a unique id?
void Create(string text)
{
TextBox t = new TextBox();
t.ID = text;
t.Text = text;
pnl.Controls.Add(t);
}
You can't do it that way.
For viewstate to work properly all controls must be created before it is loaded and with the same id's. So you must store the control definitions in session and recreate then with the same ids to ASP.NET load their properties from the view state. Page_load is too late, do it at PreLoad.
But it is easier to have all controls created at design time with visible set to false, and alternate their visibility so viewstate will work properly.
Actually this is no longer relevant. We fixed it by just disabling viewstate for our dynamically created controls. This would not work in all instances, but in our case there are two buttons the user can push(that are to do with the dynamic controls) or a list box to switch forms. The two buttons both save the state of the controls to database, so viewstate is not actually needed.(I always get confused when thinking about viewstate and how it interacts with controls.. )
So basic advice: If your having trouble controlling the viewstate, be sure you actually need it.