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. :\
Related
I am creating a web user control for a simple poll. I am currently registering it on the page and then referencing it via tagprefix.
The form for the poll is in basic html (no server controls) and is in the front-end of the web control. How can I change the look of the user control depending on the settings passed into it? Is this possible without using server controls?
Update
Can I change the html layout of a user control? If so could someone post some examples. Please note I do not use asp.net form controls, so none of that please :)
You might be able to also use jQuery to replace existing css setting in your code. Create properties on for your user control, and then pass settings in the classes. Then use jQuery to replace them. This however requires jQuery to be linked to your page (or within your control) and you'd have to write the CSS classes out to the jQuery code (using server controls, but you could use the literal control so there's no excess code).
Personally I'd go with the option of using server controls instead of straight up HTML, you'd get alot more flexibility, and then passing through the settings would be pretty straightforward, put something like this in your controls backend code:
Private _TextBoxCssClass As String
Public Property TextBoxCssClass() As String
Get
Return _TextBoxCssClass
End Get
Set(ByVal value As String)
_TextBoxCssClass = value
txtBox1.CssClass = value
txtBox2.CssClass = value
End Set
End Property
You most likely want to have a property or event in the control that changes the css. It may end up best to add some server controls or javascript / jquery to make it easier.
If its only the styles you want to change, then you can expose a property to set the style attribuites of the respective control inside your User Control. If you want to control the whole HTML layout of the control then Custom Control is the viable option.
Could any give me a best Custom control application with code snippet and explain me the basic difference between Custom control and user control?
Thanks in advance ,
Laxmi
User Control is collection or grouping of WebControls. It is similar to aspx page and is used to place common markup code in a common file.
Refer Web Forms User Controls to create user control, convert page to user control, consume user control, e.t.c
Custom Control Inherits from WebControl to achieve some extra functionality. These are classes.
Sample Custom Control
public class DropdownListExtended : DropdownList
{
// My custom logic
// ususaly an event is overriden by developers to achieve extra functionality
}
Webcontrols:- are TextBox, Label, DropdownList, e.t.c
Hopefully your understanding are builds.
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.
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?
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);