Child Control Initialization in Custom Composite in ASP.NET - asp.net

Part of the series of controls I am working on obviously involves me lumping some of them together in to composites. I am rapidly starting to learn that this takes consideration (this is all new to me!) :)
I basically have a StyledWindow control, which is essentially a glorified Panel with ability to do other bits (like add borders etc).
Here is the code that instantiates the child controls within it. Up till this point it seems to have been working correctly with mundane static controls:
protected override void CreateChildControls()
{
_panel = new Panel();
if (_editable != null)
_editable.InstantiateIn(_panel);
_regions = new List<IAttributeAccessor>();
_regions.Add(_panel);
}
The problems came today when I tried nesting a more complex control within it. This control uses a reference to the page since it injects JavaScript in to make it a bit more snappy and responsive (the RegisterClientScriptBlock is the only reason I need the page ref).
Now, this was causing "object null" errors, but I localized this down to the render method, which was of course trying to call the method against the [null] Page object.
What's confusing me is that the control works fine as a standalone, but when placed in the StyledWindow it all goes horribly wrong!
So, it looks like I am missing something in either my StyledWindow or ChildControl. Any ideas?
Update
As Brad Wilson quite rightly pointed out, you do not see the controls being added to the Controls collection. This is what the _panel is for, this was there to handle that for me, basically then override Controls (I got this from a guide somewhere):
Panel _panel; // Sub-Control to store the "Content".
public override ControlCollection Controls
{
get
{
EnsureChildControls();
return _panel.Controls;
}
}
I hope that helps clarify things. Apologies.
Update Following Longhorn213's Answer
Right, I have been doing some playing with the control, placing one within the composite, and one outside. I then got the status of Page at event major event in the control Lifecycle and rendered it to the page.
The standalone is working fine and the page is inited as expected. However, the one nested in the Composite is different. It's OnLoad event is not being fired at all! So I am guessing Brad is probably right in that I am not setting up the control hierarchy correctly, can anyone offer some advice as to what I am missing? Is the Panel method not enough? (well, it obviously isn't is it?!) :D
Thanks for your help guys, appreciated :)

I don't see you adding your controls to the Controls collection anywhere, which would explain why they can't access the Page (since they've never been officially placed on the page).

I have always put the JavaScript calls on the OnLoad Function. Such as below.
protected override void OnLoad(EventArgs e)
{
// Do something to get the script
string script = GetScript();
this.Page.ClientScript.RegisterClientScriptBlock(this.Page.GetType(), "SomeJavaScriptName", script);
// Could also use this function to determine if the script has been register. i.e. more than 1 of the controls exists
this.Page.ClientScript.IsClientScriptBlockRegistered("SomeJavaScriptName");
base.OnLoad(e);
}
If you still want to do the render, then you can just write the script in the response. Which is what the RegisterScriptBlock does, it just puts the script inline on the page.

Solved!
Right, I was determined to get this cracked today! Here were my thoughts:
I thought the use of Panel was a bit of a hack, so I should remove it and find out how it is really done.
I didn't want to have to do something like MyCtl.Controls[0].Controls to access the controls added to the composite.
I wanted the damn thing to work!
So, I got searching and hit MSDN, this artcle was REALLY helpful (i.e. like almost copy 'n' paste, and explained well - something MSDN is traditionally bad at). Nice!
So, I ripped out the use of Panel and pretty much followed the artcle and took it as gospel, making notes as I went.
Here's what I have now:
I learned I was using the wrong term. I should have been calling it a Templated Control. While templated controls are technically composites, there is a distinct difference. Templated controls can define the interface for items that are added to them.
Templated controls are very powerful and actually pretty quick and easy to set up once you get your head round them!
I will play some more with the designer support to ensure I fully understand it all, then get a blog post up :)
A "Template" control is used to specify the interface for templated data.
For example, here is the ASPX markup for a templated control:
<cc1:TemplatedControl ID="MyCtl" runat="server">
<Template>
<!-- Templated Content Goes Here -->
</Template>
</cc1:TemplatedControl>
Heres the Code I Have Now
public class DummyWebControl : WebControl
{
// Acts as the surrogate for the templated controls.
// This is essentially the "interface" for the templated data.
}
In TemplateControl.cs...
ITemplate _template;
// Surrogate to hold the controls instantiated from
// within the template.
DummyWebControl _owner;
protected override void CreateChildControls()
{
// Note we are calling base.Controls here
// (you will see why in a min).
base.Controls.Clear();
_owner = new DummyWebControl();
// Load the Template Content
ITemplate template = _template;
if (template == null)
template = new StyledWindowDefaultTemplate();
template.InstantiateIn(_owner);
base.Controls.Add(_owner);
ChildControlsCreated = true;
}
Then, to provide easy access to the Controls of the [Surrogate] Object:
(this is why we needed to clear/add to the base.Controls)
public override ControlCollection Controls
{
get
{
EnsureChildControls();
return _owner.Controls;
}
}
And that is pretty much it, easy when you know how! :)
Next: Design Time Region Support!

Right, I got playing and I figured that there was something wrong with my control instantiation, since Longhorn was right, I should be able to create script references at OnLoad (and I couldn't), and Brad was right in that I need to ensure my Controls hierarchy was maintained by adding to the Controls collection of the composite.
So, I had two things here:
I had overriden the Controls property accessor for the composite to return this Panel's Controls collection since I dont want to have to go ctl.Controls[0].Controls[0] to get to the actual control I want. I have removed this, but I need to get this sorted.
I had not added the Panel to the Controls collection, I have now done this.
So, it now works, however, how do I get the Controls property for the composite to return the items in the Panel, rather than the Panel itself?

Related

How to validate controls inside editform template of ASPxGridView?

I have an ASPxGridView with edit form template and some bound controls inside. After update I want to validate, check the values in controls on server side. As far as I could find this is not possible. DevExpress recommends subscribing to the RowUpdating event, but this is plain wrong. Useless as is very much of their so called support.
The problem is, that if controls contains some invalid text and it raises an exception somewhere long before RowUpdating and it gets eaten by devexpress. All it comes back to client is some message like "Input string was not in a correct format".
I want to validate input controls on the server side.
And yes, I do row validating also, but this is useful only for validating business logic.
So, how to validate controls that are bound inside EditForm template on server side?
Could you please clarify? You want to validate the values after you update or actually before you write the values to the database or each control individually as it loses focus before you can initiate the update? If it is necessary to do server side validation then I would recommend doing it in the RowUpdating and RowInserting server side event handlers as was recommended by DevExpress. Why do you think this is wrong? You can validate each of the bound controls' values in the e.NewValues collection of grid's Updating and Inserting events. If any of the values do not pass validation you can cancel the update/insert action. Could you outline your desired workflow in a little more detail?
A previous poster said a hack was necessary, putting a container inside the edit form template, which is not true. You can use the edit form template itself via the .NamingContainer of any control in the edit form template. Put your validation routine in the server side _Validation event handler of the specific controls.
You can evaluate the template controls as a group:
EditFormValid = ASPxEdit
.AreEditorsValid(myGrid.FindEditFormTemplateControl("myControl")
.NamingContainer);
Or you can update a class variable during each control's validation routine
public class foo
{
bool EditFormValid = true;
.
.
.
void myControl_Validation(object sender, ValidationEventArgs e)
{
EditFormValid = EditFormValid && myControl.IsValid;
}
void myGrid_RowUpdating(object sender, ASPxDataUpdatingEventArgs e)
{
If(EditFormValid)
{
.
.
.
}
else e.Cancel = true;
}
}
I have found DevExpress extremely effective and flexible. However the flexibility can be a double edge sword as there are many ways to almost do everything you need most of the time but usually one way to do everything you need all of the time. A developer can easily build on top of what works in one instance but isn't necessarily right/best practice and get to a point where they have coded into a corner as they continue to build upon a project.
If you can provide more specifics, so can I.
As far as I know this is not possible to do. Devexpress controls leave a lot to wish for. There is no way to check if validation was successful. Clearly a big issue.
What you can do is to run validation again with ASPxEdit.AreEditorsValid(). But for this you would have to do a little hack (as always with devexpress).
Put a container inside your edit form, a simple div with runat="server" and ID would do. This is your container.
Than get his div with FindEditFormTemplate() and use it in ASPxEdit.AreDitorsValid().
This workaround has drawbacks:
clutters your aspx code with unnecessary elements
page execution on server side is slower
page rendering on browser side is slower
ValidateEditorsIncontainer() runs validation again so there is a big
performance hit
All of the above are trademarks of DevExpress controls. But look at it from the bright side. Their grid sometimes takes up to five unnnecesary server and database roundtrips just to start editing.

ASP.NET 4.0: Is it possible to make some user control's child controls public without using FindControl or public properties?

I have a custom user control that contains text boxes, dropdowns, etc. I need these controls to be public so that I can go like ucEmployeeAddress.txtAddr1.Text from outside the control.
I know that I can use public properties in the control that return an instance of the control inside or use FindControl to locate my control from outside the user control, but I don't want to do that due to excess code.
If there is no way to do what I want then I will just go the public property route.
Edit: Would the person who thumbed my question down be so kind as to explain how my question shows lack of research effort, is unclear, or not useful?
You just need to expose a property in the user control:
public string Address
{
get
{
return txtAddr1.Text;
}
set
{
txtAddr1.Text = value;
}
}
Do you really need to expose the entire control ?
If its just the text property you could just expose that.
public string TitleText
{
get { return this.txtTitle.Text;}
}
If you really need the control i would suggest exposing it via a property, consumers may not even know the existance or name of the control, and nor should they care about your internal workings - using FindControl is a poor solution from outside of the control.
public TextBox TitleTextBox
{
get { return this.txtTitle;}
}
As an alternative you may be able to modify the visual studio templates to expose all your controls as public, however im not sure if this is such a great idea or how you would do it..
Well, about three hours later, I finally came upon a solution. I don't know if this is new in VS2010, but you can actually edit the user control's designer and turn all members from Protected to Public. I swear I've tried this with earlier versions of VS in the past without success, but it's apparently working for me now.
What's interesting is that the IDE has a keen sense of what parts of the designer it should and should not regenerate. For example, if you comment out the entire contents of the designer class, it will not regenerate the commented-out members. To get it to regenerate them, you have to completely delete the members that you want regenerated. What's also cool is that you can comment out the entire designer class's contents, switch back to the markup and add a server control like a textbox, and flip back to the designer to discover that it generated the member definition for only that control while the rest of the member references remain commented-out. Edit: And if you delete a control from the markup whose designer member you had modified from protected to public, it will still delete the reference from the designer.
I will note that I am also using VB.NET. I would have to assume this works with C#, as well, but cannot say for sure.
The proper way to do this is through event bubbling. This way you can keep the implementation of your controls hidden while being able to modify the properties that you chose.
This link does a good job explaining how to accomplish this.
As a side note, you should be more concerned with the elegance of your code than the amount of it.
If you take the time to implement event bubbling, for example, as opposed to exposing the control's children as public, any manipulation of the control's children is handled by that control. This makes it easy to maintain if ever the logic of manipulation were to change, and easy to implement across your entire application.
However, if you expose your control's children as public instead, you must repeat that manipulation everywhere it is used.
Therefore, the "excess code" will both improve the quality of your code and actually decrease this "excess code" you are concerned about.

Best way to start building a custom-rendered control?

I am needing to create a custom control.
Basically, I'm wishing to create a light weight(and better using jquery) Accordion control.
What are some good references for getting started with doing such a thing. I will be deriving it from a Panel because it's very similar(just needs a bit of JS tacked onto the end) but I want it to only be able to add controls of a certain type. I'm having trouble finding any information about custom rendered controls.
Can anyone point me to some references? Also, for the ID tag in HTML, would you use UniqueID or ClientID?
Any good ASP.NET book should have a chapter devoted to custom controls. If all you wanted to do was add some JS to Panel I would think you could just do this:
public class AccordianPanel : System.Web.UI.WebControls.Panel
{
protected override void OnPreRender(EventArgs e)
{
base.Render(writer);
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "accordianscript", ScriptText);
}
}
Or create a User Control with nothing but a Panel and the javascript in the ascx. Limiting the allowed controls is problematic at best...
As for the ID, always use ClientID to reference controls in client-side script.
I would consider using the AjaxControlToolkit, and look at the documentation there (on asp.net) for how to use that to create a custom control or extender. That makes it easy to create custom controls, and you can embed JQuery in those components. The ACT provides all the plumbing; there is less work for you to use the MS ajax framework and link everything together.
I've used it and it works well, though I must admit I've recently scrapped it for my set of AJAX components within my custom framework (http://www.codeplex.com/nucleo). That was for other reasons.
I missed part of your question; to validate for allowing only certain controls, you can override the AddedControl method, which is called when the control gets added. You can validate control types in this method.
Or, most controls implement a custom control collection, and I believe there is a CreateControlCollection that you can use to create a custom control. This custom control collection validates the control within the Add/AddAt method.

How To: Use AJAX in an ASP.NET Custom Server Control

Does anyone know of a good tutorial that demonstrates using an existing AJAX control extender in a Custom ASP.NET Server Control?
I do not want to build a "Custom AJAX Server Control". I want to build a Custom Server Control that uses an existing AJAX control extender.
I would like to combine an asp:TextBox, asp:ImageButton, asp:CustomValidator (with client side javascript from an embedded resource), and an ajax:CalendarExtender into one custom server control. Or has this already been created?
Any help would be greatly appreciated. Thanks.
UPDATE: Basically, I would like to create a CompositeControl that has an ajax:CalendarExtender as a child control.
Sounds like what you're after is a composite control. They are pretty much exactly like a user control only instead of using the ascx file to create the controls you create them all programmatically. The big advantage of doing this over using a user control is you end up with something you can put in an assembly and use in different projects.
A composite control can inherit from either Control or WebControl. I personally usually find Control more useful to inherit from because I usually don't need a lot of the extra stuff you get from WebControl such as the styling properties since I usually just style through a single CssClass property.
You'll also need to make sure your class implements the INamingContainer interface. This will make sure that each child control will automatically get a unique name if the control is used multiple times in the same parent container.
The most important thing to do when creating a composite control is to override Control's CreateChildControls method. All the logic for actually creating the controls should go in here. The framework will automatically make sure that this gets called at the right time in the page lifecycle.
Here's a little example:
public class MyCompositeControl : Control, INamingContainer
{
protected override void CreateChildControls()
{
Controls.Clear();
var textbox = new TextBox();
textbox.ID = "TextBox1";
Controls.Add(textbox);
if (!Page.IsPostBack || !IsTrackingViewState)
{
// make sure you set the properties after
// you add it to the parent or the viewstate
// won't save properly
textbox.MaxLength = 30;
}
var button = new Button();
button.ID = "Button1";
Controls.Add(button);
if (!Page.IsPostBack || !IsTrackingViewState)
{
button.Text = "Save";
}
}
}
I don't think ASP.NET AJAX should complicate this much. The only thing I can think of ist you'll need to make sure that you create a ScriptManager on whatever page the composite control will be added to.
There's a full example of this on the MSDN site. There's another nice example on this blog.
What you want is to build a user control and not a custom control most probably. A user control is a composite control whereas a custom control is a control built either from the ground up either derived from a basic control.
I would suggest you search on MSDN. I have seen several good articles about that topic in their magazines over the last year or two, that have been fairly thorough. But I don't have links to them and I'm too lazy to Google for you. :\

Creating a UserControl Programmatically within a repeater?

I have a repeater that is bound to some data.
I bind to the ItemDataBound event, and I am attempting to programmatically create a UserControl:
In a nutshell:
void rptrTaskList_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
CCTask task = (CCTask)e.Item.DataItem;
if (task is ExecTask)
{
ExecTaskControl foo = new ExecTaskControl();
e.Item.Controls.Add(foo);
}
}
The problem is that while the binding works, the user control is not rendered to the main page.
Eh, figured out one way to do it:
ExecTaskControl foo = (ExecTaskControl)LoadControl("tasks\\ExecTaskControl.ascx");
It seems silly to have a file depedancy like that, but maybe thats how UserControls must be done.
You could consider inverting the problem. That is add the control to the repeaters definition and the remove it if it is not needed. Not knowing the details of your app this might be a tremendous waste of time but it might just work out in the end.
If you are going to do it from a place where you don't have an instance of a page then you need to go one step further (e.g. from a webservice to return html or from a task rendering emails)
var myPage = new System.Web.UI.Page();
var myControl = (Controls.MemberRating)myPage.LoadControl("~/Controls/MemberRating.ascx");
I found this technique on Scott Guithrie's site so I assume it's the legit way to do it in .NET
I think that #Craig is on the right track depending on the details of the problem you are solving. Add it to the repeater and remove it or set Visible="false" to hide it where needed. Viewstate gets tricky with dynamically created controls/user controls, so google that or check here if you must add dynamically. The article referenced also shows an alternative way to load dynamically:
Control ctrl=this.LoadControl(Request.ApplicationPath +"/Controls/" +ControlName);

Resources