ASP.net Server Controls and using AjaxcontrolToolkit - asp.net

I have a question about server controls VS. user controls (.ascx). Currently, when I write a user control, I want to be able to take advantage of using the AjaxControlToolKit DLL. In particular, I want my user control to be able to do partial post backs and use the various extenders (modalPopUp for example). My boss, though, prefers us to use server controls so that we can compile them into a DLL and use them in various applications. My question is: If I rewrite my user controls to be server controls, will I still be able to use the AjaxControlToolKit and all its features (asyn post backs and extenders)?
FYI: I am using Visual Studio 2010, AjaxControlToolkit 4.1.60919 and .Net 4.0

Yes, you'll be able to do this. Just inherit your control from the CompositeControl class interface and add any extender or control from the ACT project to the Controls collection the same way as you should do this with ordinal ASP.NET server controls. Also you can inherit existing control and implement INamingContainer interface but in this case you must call RenderChildren method manually from control's Render method:
[DefaultProperty("Text")]
[ToolboxData("<{0}:WatermarkedTextBox runat=server></{0}:WatermarkedTextBox>")]
public class WatermarkedTextBox : TextBox, INamingContainer
{
private AjaxControlToolkit.TextBoxWatermarkExtender _watermarkExtender;
public string WatermarkText
{
get
{
return ViewState["WatermarkText"] as string;
}
set
{
ViewState["WatermarkText"] = value;
}
}
protected override void CreateChildControls()
{
Controls.Clear();
this._watermarkExtender = new AjaxControlToolkit.TextBoxWatermarkExtender
{
ID = "wte",
TargetControlID = this.ID,
WatermarkText = this.WatermarkText
};
this.Controls.Add(_watermarkExtender);
}
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
RenderChildren(writer);
}
}
}

Related

How to access Page classes and Usercontrols in code files for website projects

I am trying to convert an ASP.net web application into a website. One issue i am coming across is trying to access usercontrols and masterpages in my code files.
The code below shows a class i am using for a base class, it inherits from System.Web.UI.Page.
public class AdminBase : BasePage
{
DeniedAccess deniedAccessControl;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
//All pages that inherit from this will have a denied access control which will display when an admin of one instance tries to access admin pages of a different instance
//which they don't have access to. This enables us to still use the built in web.config authroisation API
deniedAccessControl = (DeniedAccess)Page.LoadControl("~/App_Controls/DeniedAccess.ascx");
((MasterPage1)Master).MainContent.Controls.Add(deniedAccessControl);
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
if (deniedAccessControl.ContentVisible)
{
foreach (Control control in ((MasterPage1)Master).MainContent.Controls)
{
//only sets to false if the control is set to true as it will error if you try to set a non visiual controls "Visible" property. E.g an sqldatasource control
if (control.GetType() != deniedAccessControl.GetType() && control.Visible)
control.Visible = false;
}
}
}
}
The "DeniedAccess" control is a user control i am dynamically adding if the user fails some custom authorization.
This worked fine when this was a web application as the DeniedAccess control was compiled into the same dll so i could access it from the code. Now that it is a website it cannot find the namespace/class as it is not in the App_Code folder.
Is what I'm trying to do possible in a website project? I can't seem to find a way and the only alternative i see is having to write this code for each individual page rather than use it on a base page.
I also get the same problem when trying to cast the master page to my "MasterPage1" class for the same reason, it can't find it so i cannot access it's properties.
I ended up creating two interfaces to solve this problem, as it wasn't a code behind file i couldn't just add a reference to the control/master page in the source.
I made my usercontrol inherit this interface
public interface IDeniedAccessControl
{
bool ContentVisible
{
get;
}
}
And my master page inherit this one
public interface IMasterPage
{
void AddControl(Control control);
ControlCollection MainContentControls
{
get;
}
}
I then made my usercontrol and master page implement there respective interface with the desired functionality.
Master page implementation:
public void AddControl(Control control)
{
ContentPlaceHolder1.Controls.Add(control);
}
public ControlCollection MainContentControls
{
get { return ContentPlaceHolder1.Controls; }
}
User control implementation:
bool IDeniedAccessControl.ContentVisible
{
get { return ContentASPxPanel.Visible; }
}
I could then use this on any code file. Below is how i edited my original code.
public class AdminBase : BasePage
{
IDeniedAccessControl deniedAccessControl;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
//All pages that inherit from this will have a denied access control which will display when an admin of one instance tries to access admin pages of a different instance
//which they don't have access to. This enables us to still use the built in web.config authroisation API
deniedAccessControl = (IDeniedAccessControl)Page.LoadControl("~/App_Controls/DeniedAccess.ascx");
((IMasterPage)Master).AddControl((Control)deniedAccessControl);
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
if (deniedAccessControl.ContentVisible)
{
foreach (Control control in ((IMasterPage)Master).MainContentControls)
{
//only sets to false if the control is set to true as it will error if you try to set a non visiual controls "Visible" property. E.g an sqldatasource control
if (control.GetType() != deniedAccessControl.GetType() && control.Visible)
control.Visible = false;
}
}
}
}

Using Control Adapter for User Controls in ASP.NET

We can change the output rendering of an ASP.NET control with Control Adapters. Now I want to know that, is it possible to change a user control rendering by control adapters in ASP.NET? By the way I have several user controls in my page and I don’t want to do same actions for them.
Yes you can but you must define your controls childs as public in your User control
(Associate public on child controls in order to access from your adapter)
public class UserControlSample : userControl
{
public TextBox TextBoxWrapper
{
get
{
return MyTextBox;
}
set
{
MyTextBox = value;
}
}
}
//Access from adapter
UserControlSample.TextBoxWrapper.Text
UserControlSample.TextBoxWrapper.Id
.....

integrate custom control controls validation with Page.IsValid

I have created a custom server control. So far this control renders some html in the webpage. On submit of the page i need to take the values entered in the textbox of the server control and call some webservice to validate the input of the user. i don't want to write this code in code behind of the page that this control is used in. I want all the validations to be written in the server control itself and if validation fails, Page.IsValid should be set to false. If the user input value in server control is valid Page.IsValid will be true.
I am trying to achieve is the same functionality as google recaptcha. All user needs to do to use this control is to user the control in the page. user entered value is correct or incorrect is handled in the control itself and in the code behind of the page, there is only Page.IsValid. Here is the page on google that explains this
http://code.google.com/apis/recaptcha/docs/aspnet.html
and i have also used the google recaptcha and it works as expected. I also want to build same kind of functionality for my server control, Please help, if it is possible.
Thank's for answering the questions. I found the solution. Here is the entire code of the server control. The trick was to implement IValidator. It gives us two property and one metod. ErrorMessage and IsValid properties and Validate method. I wrote all the validation code in Validate method and set this.IsValid. This solved the problem.
[ToolboxData("<{0}:MyControl runat=server></{0}:MyControl>")]
public class MyControl : WebControl, IValidator
{
protected override void RenderContents(HtmlTextWriter output)
{
//Render the required html
}
protected override void Render(HtmlTextWriter writer)
{
this.RenderContents(writer);
}
protected override void OnInit(EventArgs e)
{
Page.Validators.Add(this);
base.OnInit(e);
}
public string ErrorMessage
{
get;
set;
}
public bool IsValid
{
get;
set;
}
public void Validate()
{
string code = Context.Request["txtCode"];
this.IsValid = Validate(code);//this method calls the webservice and returns true or false
if (!this.IsValid)
{
ErrorMessage = "Invalid Code";
}
}
}
You could incorporate the validator in the server control. It would need a server validate method to call the web service.
The net result would be a server control that you drop on the page, no other validators needed. if your control can't validate it's contents, then page.isvalid will be false.
Simon

WebPart RenderControl doesn't render contents

I have a custom web part that I am trying to call the RenderContents method on, but the results only contains the surrounding div for the web part, and not any child controls.
Take for example this simple web part:
namespace MyWebParts
{
public class MyTestWebPart : WebPart
{
public MyTestWebPart()
{
this.CssClass = "myTestWebPart";
}
protected override void CreateChildControls()
{
base.CreateChildControls();
this.Controls.Add(new LiteralControl("Nothing here yet."));
}
}
}
Then, in an http handler, I'm trying to instantiate this web part and call its RenderControl method. The result is <div class="myTestWebPart"></div>.
Does anyone know why I am not getting my controls from CreateChildControls also added to the output?
It's because when you're only instantiating a control and calling RenderControl on it, without it being added to a Controls collection, then it's not part of the Page lifecycle which causes all the events to fire.
In particular the PreRendering which calls EnsureChildControl isn't called.
The easy solution is to override Render like this:
protected override void Render(HtmlTextWriter writer)
{
EnsureChildControls();
base.Render(writer);
}
i would suggest to write your code in render method rather than writing in createchild control

ASP.NET: How to process postback events for my own control?

I have my own Control1 which is dynamically added as child control to Control2 which implements INamingContainer in CreateChildControls() of control2.
Control1 itself implements IPostBackEventHandler. But RaisePostBackEvent() method is never called on Control1, despite I do call postback method from JavaScript.
And yes, there are other controls which implement IPostBackEventHandler interface on the page.
What did I miss?
What could cause the issue?
UPDATE: Control1 is always created exactly the same way and assigned exactly the same ID in Control2
it looks like this in Control2:
protected override void CreateChildControls()
{
if(!this.DesignMode)
{
Control1 c = new Control1();
c.ID = "FIXED_ID";
}
base.CreateChildControls();
}
UPDATE2:
Control1:
public class Control1: Control, IPostBackEventHandler
{
...
protected virtual void RaisePostBackEvent(string eventArgument)
{
if(!String.IsNullOrEmpty(eventArgument))
{
// Some other code
}
}
}
if I add line
Page.RegisterRequiresRaiseEvent(c);
In CreateChildControls() in Control2 then this method is being called but always with null eventArgument.
UPDATE3:
In JavaScript on some onClick event I do the following:
__doPostBack(Control1.UniqueID,'commandId=MyCommand');
where Control1.UniqueID is of course substituted with real uniqueID during rendering. I checked, this script is being called.
Can you show us the source code of first control? Anyway there is a simple example.
public class TestControl2 : CompositeControl
{
protected override void CreateChildControls()
{
base.CreateChildControls();
if (!DesignMode)
this.Controls.Add(new TestControl());
}
}
public class TestControl : WebControl, IPostBackEventHandler
{
public TestControl() : base(HtmlTextWriterTag.Input) { }
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Type, "button");
writer.AddAttribute(HtmlTextWriterAttribute.Name, base.UniqueID);
writer.AddAttribute(HtmlTextWriterAttribute.Onclick, Page.ClientScript.GetPostBackEventReference(this, null));
writer.AddAttribute(HtmlTextWriterAttribute.Value, "Submit Query");
}
void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
{
// Raise post back event
}
}
Edit
Why you are generating the post back script out of the control and manually? You have to use Page.ClientScript.GetPostBackEventReference method. It generates and includes some necessary inline and embedded scripts to the page.
Why you are deriving your class from Control? It's good for those controls which don't have any user interface.
From MSDN
This is the primary class that you
derive from when you develop custom
ASP.NET server controls. Control does
not have any user interface (UI)
specific features. If you are
authoring a control that does not have
a UI, or combines other controls that
render their own UI, derive from
Control. If you are authoring a
control that does have a UI, derive
from WebControl or any control in the
System.Web.UI.WebControls namespace
that provides an appropriate starting
point for your custom control.
You have to derive your control from WebControl class as follows.
public class TestCtl : WebControl, IPostBackEventHandler
{
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
// Add onclick event.
writer.AddAttribute(HtmlTextWriterAttribute.Onclick, Page.ClientScript.GetPostBackEventReference(this, "Arguments"));
}
void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
{
throw new NotImplementedException();
}
}
I'm going to guess that it's the "dynamically added as child control to Control2" that is the issue, but without any code it's pretty hard to diagnose.
When during the page lifecycle are you dynamically adding it? Are you recreating the dynamic control in the exact same way, with the same ID, after the postback?

Resources