How to properly instantiate a Custom User Control from Code Behind - c#

I created a custom user control that has the following table as part of it (and I've simplified slightly to make this easier to read):
<table id="tblWeekCalendar" runat="server">
<!-- .... -->
</table>
I'm trying to create the Custom Control from Code Behind on a different page:
// I'm passing values in on the constructor as an alternative to using attributes
MasterScheduleCalendar sch = new MasterScheduleCalendar(true, 123);
However, when I try to access tblWeekCalendar (the main table from my custom control) from the custom control's code-behind while constructing the control, I find that it is null:
HtmlTableCell tableCell = tblWeekCalendar.FindControl($"tdR{week}Day{day}") as HtmlTableCell;
This code runs in a method that's called from Page_Load.
I tried doing the following instead of directly calling the constructor, as in this question, but still the same problem:
MasterScheduleCalendar ctrl = (MasterScheduleCalendar)Page.LoadControl(typeof(MasterScheduleCalendar), new object[] { true, schedule.MasterScheduleID });
As mentioned previously, this occurs in a method that's being called from the Page_Load event handler, so I'm slightly baffled as to why tblWeekCalendar is null at this point. Does anyone have any suggestions as to how to fix this?

So you are doing it wrong.
Do not try to access "tblWeekCalendar" or any other element inside your control.
Implement methods that recive a values and update your control, or methods that get values when you need.
MyWeekOfTheDay week = tblWeekCalendar.GetWeekOfTheDay();
List<MyDays> days = tblWeekCalendar.GetViewDays();
List<MyMonth> months = tblWeekCalendar.GetCurrentYear().GetMonths();
Something like that.

Related

Unable To Render Control To Placeholder

The following code will not add a control to an aspx placeholder:
var control = new ASCXControl { ID="searchFilters", Filters = filters };
var placeholder = Utility.FindControlRecursive(Page, "rightColumnSearchFilters") as PlaceHolder;
if(placeholder != null){
placeholder.Controls.Add(control);
placeholder.Visible = true;
}
When debugging, the placeholder is found and the control shows as added to the placeholder controls collection after entering the block, but yet I see nothing render into the placeholder on the page.
I currently need to pass variables to the control in order to find the filters I need to display. Although I don't like passing variables between controls, I don't see any other way.
What am I missing would make the control not render? Is there a better way to do this?
EDIT:
I am trying to get the HTML inside of the ascx to render. I am able to get the Filters parameters inside the Page_Load on the control.
This may or may not be the whole problem, but usually trying to instantiate a UserControl the way you're doing it leads to problems. You should do it using the LoadControl(path) method of the Page class:
ASCXControl ctl = (ASCXControl) LoadControl("path");
I'm not 100% sure, but I think that if you just instantiate it like an ordinary class/control, you wind up not running all the event handlers (such as Load) that you usually would.

Adding dynamic generic html control to page destroys bound dropdownlist

I encountered some weird behaviour today and I was hoping someone could shed some light on it for me because I'm perplexed.
I have a couple of methods I use to interact with the ui for the sole purpose of displaying error/success/warning messages to the user.
Here is one of them
public static void Confirm(string text)
{
var page = (Page)HttpContext.Current.Handler;
var uiConfirm = new HtmlGenericControl("div")
{
ID = "uiNotify",
InnerHtml = text
};
uiConfirm.Attributes.Add("class", "ui-confirm");
page.Master.FindControl("form1").Controls.AddAt(2, uiConfirm);
}
This works perfectly fine except for one nuance I encountered this morning and I was hoping someone could shed some light on it for me.
I am working on your run of the mill profile editing page. In this page, I am binding a couple of dropdownlists (country, province/state) on page load. I have a submit at the bottom and a click event that fires to update the information, then call the method above to notify the user that their information was successfully updated. This works the first time you click the submit button; the page posts back, the information gets updated in the database, the dynamically added div gets popped in, confirm message is displayed and all is good. However, if you then click the submit button again, it fails stating SelectedItem on the dropdowns I'm binding in the page load is null (Object reference not set to an instance of an object). The dropdown is actually wiped for some reason on the second postback, but not the first.
In sheer desperation after trying everything else, I decided to take out the call to the confirm method... and strangely enough the error disappears and I can update the information on the page as many times as I like.
If I add a generic control statically to the page I'm working on, and change my method slightly so that instead of adding a generic control to the form dynamically it just finds the generic control on the page, that does no produce the same error.
The problem also goes away if I remove the two dropdowns from the page or just stop interacting with them.
Why on earth would adding a dynamic control to the form wipe my dropdowns on postback?
I think you should consider using the PlaceHolder class in your MasterPage, the AddAt(2, uiConfirm) is going to bite you and probably is:
Markup:
.......
<asp:PlaceHolder id="PlaceHolder1"
runat="server"/>
......
Code-behind:
public static void Confirm(string text)
{
var page = (Page)HttpContext.Current.Handler;
var uiConfirm = new HtmlGenericControl("div")
{
ID = "uiNotify",
InnerHtml = text
};
uiConfirm.Attributes.Add("class", "ui-confirm");
//may need to change depending on where you put your placeholder
Control placeHolder = page.Master.FindControl("PlaceHolder1");
placeHolder.Controls.Clear();
placeHolder.Controls.Add(uiConfirm);
}

Adding RequiredFieldValidator runtime

I have a ASP.NET usercontrol ( a sign-up form ), and I need to add a requiredFieldValidator runtime. However, it's not working.
Here is how I add the validator.
System.Web.UI.WebControls.RequiredFieldValidator nameValidator = new System.Web.UI.WebControls.RequiredFieldValidator();
nameValidator.ControlToValidate = "SignUpName";
nameValidator.ErrorMessage = "You must provide your name";
nameValidator.ID = "nameValidator";
nameValidator.Display = ValidatorDisplay.Dynamic;
this.Page.Validators.Add(nameValidator);
What am I missing?
-------- EDIT ---------
I never found a solution for this. I achieved my goal by using a CustomValidator and doing all the validation manually.
If you are adding controls including validators dynamically then you need to add them on Page_Init not Page_Load otherwise they don't get added to the control tree correctly i.e they don't participate in ViewState
You're probably better using a custom validator rather than dynamically adding a required validator though. Dynamic controls in asp.net have been nothing but trouble for me. I would avoid if there is another solution.
Would you please try with as below:
Just passing the control id to ControlToValidate,
nameValidator.ControlToValidate = SignUpName.ClientID; // or SignUpName.UniqueID
Consider these two things:
Make the control to validate is added to the page control's hierarchy before you add your validator.
Add your validator to Page.Controls not Page.Validators

Dynamically adding controls to a Templated User Control?

A few weeks back I created a Templated User Control, for the most part based on the example here:
http://msdn.microsoft.com/en-us/library/36574bf6(v=VS.90).aspx
The difference is that I did not implement the "MessageContainer" class as I wanted just an empty template that I could add whatever controls I wanted to at design time.
This TUC has been working great, but I ran into a scenario I hadn't anticipated when I created the thing. The need to dynamically add this TUC to a page, which means that I would need to dynamically add the controls within the template of the TUC as well.
I found another example here on how to dynamically create a Template and add it to the Templated Control:
http://msdn.microsoft.com/en-us/library/y0h809ak(v=vs.71).aspx
This second example article discusses only the "DataList, Repeater, and DataGrid controls" but I figure since I am using the ITemplate interface here, it should be the same thing.
However, I am unable to get this to work, I keep getting an "Object reference not set to an instance of an object." error when I attempt to populate the TUC.
Here's what I am doing....
Like the example above I created an ITemplate class:
public class XPCTemplate : ITemplate
{
private readonly Control _control;
public XPCTemplate(Control control)
{
_control = control;
}
public void InstantiateIn(Control container)
{
container.Controls.Add(_control);
}
}
Then, in the test page code-behind I attempt to load up and display the TUC dynamically:
ExpandCollapseRegion xpcRegion; // The Templated User Control
protected void Page_Load(object sender, EventArgs e)
{
PlaceHolder ph;
// .... code here to dynamically create some labels, textboxes, etc. ....
// Create an instance of the TUC
xpcRegion = new ExpandCollapseRegion();
// Pass into the TUC's template the controls dynamically created above
xpcRegion.LayoutTemplate = new XPCTemplate(ph);
// add the dynamic TUC to the page
phDynamicUC.Controls.Add(xpcRegion);
phDynamicUC.Controls.Add(new LiteralControl("<br />"));
}
Test page HTML Source:
<body>
<form id="form1" runat="server">
<div>
Dynamically Loading User Control
<br />
<asp:PlaceHolder ID="phDynamicUC" runat="server" />
</div>
</form>
</body>
When I run the page, I get the "Object reference not set to an instance of an object" error on the "container.Controls.Add(_control);" line of the XPCTemplate class. When I debug the test page and TUC control, the code of the TUC receives the XPCTemplate into its LayoutTemplate during the TUC's Page_Init() method, but when the InstantiateIn() event back in the XPCTemplate fires immediately afterwards, the "container" argument is NULL.
I'm not sure why this is happening, it's like the InstantiateIn() method of the XPCTemplate class is trying to actually set the PlaceHolder control within the TUC rather than just passing the contents. Maybe this is supposed to be the way and I am missing something on the TUC side to allow this behavior?
This is the first TUC I have created and likewise the first time trying to dynamically fill/load it, so I am sure I am missing something needed to accomplish this. Any help is greatly appreciated.
-- Andrew
Found the solution to the problem, which was how I was loading the TUC.
Incorrect:
xpcRegion = new ExpandCollapseRegion();
Correct:
xpcRegion = (ExpandCollapseRegion)LoadControl("ExpandCollapseRegion.ascx");
Making this simple change took care of the problem. Also, found that I could forego the need for a custom ITemplate class by using the CompiledTemplateBuilder() method. Really simplifies the code.
-- Andrew
I believe the problem is that you haven't instantiated your PlaceHolder ph that you are passing into your template. So, the line it is complaining on is because it can't add the control to the PlaceHolder because it is null/nothing.
Also, I believe you're going to need to put your placeholder somewhere. Otherwise, you're adding items to a container that is never added to the page.

ASP.NET dynamically insert code into head

I'm working inside of a Web User Control (.ascx) that is going to be included in a regular web form (.aspx), but I need to be able to dynamically insert code into the head of the document from the User Control. In my Coldfusion days <cfhtmlhead> would do the trick. Is there an equivalent of this in ASP.NET or a similar hack?
To add HTML markup you can do the following:
In your UserControl's code you can access Page.Header, which is itself a control. To that control you can then add new controls:
HtmlGenericControl newControl = new HtmlGenericControl("someTag");
newControl.Attributes["someAttr"] = "some value";
Page.Header.Controls.Add(newControl);
To add script markup you don't need access to the head tag at all since ASP.NET has helper methods on the ClientScriptManager that do the work for you:
Here are examples of some code you can also put in your user control's code:
// Register some inline script:
Page.ClientScript.RegisterClientScriptBlock(GetType(), "myAlertScript", "alert('hello!')", true);
// Register a script reference:
Page.ClientScript.RegisterClientScriptInclude(GetType(), "myLibraryScript", "~/Scripts/MyScriptLibrary.js");
I realize that this is an old question, but this is another example.
Try This:
Page.Header.Controls.Add(
new LiteralControl(
"<script>alert('Literal Added to <Head>.');</script>"
)
);
If you want to add the script at a particular index of the <head> you can use
AddAt(index, new LiteralControl(...)) where index 0 equals the top of the <head>
Also, you need to add runat="server" in your head tag e.g. <head id="head1" runat="server">
this.Page.Header.Controls.Add
By doing this, you are adding controls to the head section. You can add any type of control. If you feel you need to add simple text (or you want to write the tags manually), then look into the LiteralControl class.
There's some guidance on using C# code to modify the page header here. It should work just fine from any server-side code that executes before the page load completes.
A simple e.g.
HtmlHead head = Page.Header;
HtmlTitle title = new HtmlTitle();
title.Text = "Test Page";
head.Controls.Add(title);
HTMLHead reference is in namespace
System.Web.UI.HtmlControls
Override the custom control's Load() method to add the controls or references you need into the page header while the parent .aspx page is being loaded server-side.
I have a simple solution for this. Create a runtime memory cache based on the url of the page (as a key) that holds x information about y (be it a file reference, script text, or class that generates JavaScript) and serialize its data to JSON. Newtonsoft is helpful for instances of any class. In fact, you can use it's output to initialize any new instance of a class based upon given input. In a way, that means you may have your instances of any particular class automatically instantiated despite what user control the instance is on. In the end, you create a simple web form to serve as a script reference and as the final endpoint. It pulls the JavaScript (or what've it) and spits out the client side code you need as a script reference inside the head tag.

Categories

Resources