I did the same modification on 3 kind of BaseValidator. I search a way to remove the duplicate code.
I did the same code for RequiredFieldValidator , RegularExpressionValidator and CustomValidator
Public Class CustomValidator
Inherits System.Web.UI.WebControls.CustomValidator
Protected Overrides Sub Render(writer As HtmlTextWriter)
Try
If Not String.IsNullOrWhiteSpace(ControlToValidate) Then
Dim ctv As Control = Me.FindControl(ControlToValidate)
writer.AddAttribute("for", ctv.ClientID)
End If
Catch
End Try
MyBase.Render(writer)
End Sub
Protected Overrides ReadOnly Property TagKey() As System.Web.UI.HtmlTextWriterTag
Get
Return HtmlTextWriterTag.Label
End Get
End Property
End Class
I ran into the same issue you have with these exact classes; I wanted to add some additional features to the validation controls. The way I ended up sharing some common code was implementing my own classes that inherited from the validator classes and then implementing the shared logic in a utility class. I'm not familiar with VB, so please bear with the C# example.
public static class ValidationUtilities
{
public static void AddFor(Control validationControl, string controlToValidate, HtmlTextWriter writer)
{
if (!string.IsNullOrWhiteSpace(controlToValidate))
{
var ctv = validationControl.FindControl(controlToValidate);
writer.AddAddtribute("for", ctv.ClientID);
}
}
}
class MyRegularExpressionValidator : RegularExpressionValidator
{
protected override void Render(HtmlTextWriter writer)
{
ValidationUtilities.AddFor(this, ControlToValidate, writer);
}
}
class MyRequiredFieldValidator : RequiredFieldValidator
{
protected override void Render(HtmlTextWriter writer)
{
ValidationUtilities.AddFor(this, ControlToValidate, writer);
}
}
You could make the argument that's more work than it's worth for not duplicating a small amount of code, but if you have a lot more, or your duplicated code is complex in some way, this is a way you can share it. Also, I made my utility class static, but there's no reason it couldn't be otherwise.
Related
Newbie here, I need help with a website I'm creating.
I have a class that does some analysis on some text that is input by the user, the class then finds an appropriate answer and sends it back to the textbox. (in theory)
Problem is I don't know how I can control and access the textbox on the default.aspx page from a class, all I get is "object reference is required non static field".
I made the textbox public in the designer file yet still no joy. :(
I've also read this: How can I access the controls on my ASP.NET page from a class within the solution? , which I think is along the lines of what I'm trying to achieve but I need clarification/step by step on how to achieve this.
Hope someone can point me in the right direction.
Many thanks,
Kal
This is the code I have added to the designer.cs file:
public global::System.Web.UI.WebControls.TextBox TextBox3;
public string MyTextBoxText
{
get
{
return TextBox3.Text;
}
set
{
TextBox3.Text = value;
}
}
This is the class method i have created:
public static cleanseMe(string input)
{
string utterance = input;
string cleansedUtt = Regex.Replace(utterance, #"[!]|[.]|[?]|[,]|[']", "");
WebApplication1._Default.TextBox3.text = cleansedUtt;
}
I could just return the cleansedUtt string i know, but is it possible for me to just append this string to the said textbox from this method, within this class?
I also tried it this way, i wrote a class that takes in the name of the textbox and string to append to that textbox. it works BUT only on the default.aspx page and does not recognise the textbox names within the difference classes. The code is as follows:
public class formControl
{
public static void ModifyText(TextBox textBox, string appendthis)
{
textBox.Text += appendthis + "\r\n";
}
I would suggest you that do not access the Page Controls like TextBox in your class. It will be more useful and a good practice that whatever functionality your class does, convert them into function which accept the parameters and returns some value and then on the basis of that value you can set the controls value.
So now you have reusable function that you can use from any of the page you want. You do not need to write it for every textbox.
Here I am giving you a simple example
public class Test
{
public bool IsValid(string value)
{
// Your logic
return true;
}
}
Now you can use it simple on your page like this
Test objTest = new Test();
bool result=objTest.IsValid(TextBox1.Text);
if(result)
{
TextBox1.Text="Everything is correct";
}
else
{
TextBox1.Text="Something went wrong";
}
If you have your class in the same project (Web Project) the following will work:
public class Test
{
public Test()
{
//
// TODO: Add constructor logic here
//
}
public static void ValidateTextBox(System.Web.UI.WebControls.TextBox txt)
{
//validation logic here
if (txt != null)
txt.Text = "Modified from class";
}
}
You can use this from your webform like this:
protected void Page_Load(object sender, EventArgs e)
{
Test.ValidateTextBox(this.txt);
}
If your class is in a different (class project), you would need to add a reference to System.Web to your project.
I just would like to know what will happen if we don't put base.createchildcontrols() in the code. Will composite control be created without calling base.createchildcontrols()?
[ToolboxData("<{0}:Login runat=server></{0}:Login>")]
public class Login : CompositeControl
{
private TextBox txtUsername = new TextBox();
private TextBox txtPassword = new TextBox();
private Button btnLogin = new Button();
protected override void CreateChildControls()
{
txtUsername.ID = "txtUsername";
txtPassword.ID = "txtPassword";
txtPassword.TextMode = TextBoxMode.Password;
btnLogin.ID = "btnLogin";
btnLogin.Text = "Login";
Controls.Add(txtUsername);
Controls.Add(txtPassword);
Controls.Add(btnLogin);
base.CreateChildControls();
}
}
The short answer is... Nothing! You don't need to call the base implementation (although you can always try removing it to see what happens ;-)
Using ILSpy, we can see that CompositeControl inherits from WebControl which inherits from Control.
CreateChildControl() is defined on Control as:
protected internal virtual void CreateChildControls()
{
}
i.e. It is only there to be overriden.
Compare this with some other controls that inherit from Control, like BaseDataList and you can see that that method has a lot of functionality for checking and rendering the output.
This makes sense. Reading the MSDN documentation, here, we can see that it is for you to implement the rendering of any child controls. Only if the class you are inhereting from requires this method to be called, then you'll have to call it.
The following asp.net side code of control:
<asp:TextBox runat="server" ID="LimitTextBox" Text="20" ClientIDMode="Static" />
Generates such HTML-code:
<input name="ctl11$ctl00$ctl02$TeamPlayerSelector$LimitTextBox"
type="text" value="20" id="LimitTextBox">
ID attribute - as is required, but how can I remove 'name' attribute? It is not required for me and is also too long to transfer it to user browser.
How can I prevent 'name' attribute generation? Thanks
P.S. I work under ASP.NET 4.0
create a Filter (class that inherits from Stream), assign it to your HttpContext.Response.Filter attribute, and in it you would overwrite the Write method, to remove all the name-tags from the generated html :)
See this page for more information http://msdn.microsoft.com/en-us/library/system.web.httpresponse.filter.aspx
Update
Looking at the sourcecode for TextBox it reveals that Name is actually added to the Attributes-list during render, so it should be possible to interfere with the rendering of the TextBox class and prevent this attribute from being added. This should do
public class NoNamesTextBox : TextBox
{
private class NoNamesHtmlTextWriter : HtmlTextWriter
{
public NoNamesHtmlTextWriter(TextWriter writer) : base(writer) {}
public override void WriteAttribute(string name, string value, bool fEncode)
{
if (name.Equals("name", StringComparison.OrdinalIgnoreCase)) return;
base.WriteAttribute(name, value, fEncode);
}
}
protected override void Render(HtmlTextWriter writer)
{
var noNamesWriter = new NoNamesHtmlTextWriter(writer);
base.Render(noNamesWriter);
}
}
Update once more
How could i forget! You don't even need to subclass your textbox. In asp.net you can define which HtmlTextWriter type you want to use per control, so you can just configure that every TextBox control should use an instance of your own NoNamesHtmlTextWriter like this
<browsers>
<browser refID="Default">
<controlAdapters>
<adapter
controlType="System.Web.UI.WebControls.TextBox"
adapterType="NoNamesTextBoxAdapter"
/>
</controlAdapters>
</browser>
</browsers>
public class NoNamesTextBoxAdapter : ControlAdapter
{
private class NoNamesHtmlTextWriter : HtmlTextWriter
{
public NoNamesHtmlTextWriter(TextWriter writer) : base(writer) { }
public override void WriteAttribute(string name, string value, bool fEncode)
{
if (name.Equals("name", StringComparison.OrdinalIgnoreCase)) return;
base.WriteAttribute(name, value, fEncode);
}
}
protected override void Render(HtmlTextWriter writer)
{
var noNamesRender = new HtmlTextWriter(writer);
base.Render(noNamesRender);
}
}
For some unknown reason the WriteAttribute override didn't work. I replaced it with:
public override void AddAttribute(HtmlTextWriterAttribute key, string value)
{
if (key == HtmlTextWriterAttribute.Name) return;
base.AddAttribute(key, value);
}
And it worked like a charm.
Also if you just need a custom Name you can just override the UniqueID property:
public class MyCustomControl : TextBox
{
public override string UniqueID
{
get
{
//return base.UniqueID;
return "test123";
}
}
}
Thanks for your help!
Setting EnableViewState="False" will slim down the name. You can also make a class that inherits the Textbox Control and override the Render procedure to not include the name.
Public Class CustomTextBox
Inherits TextBox
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
MyBase.Render(writer)
'Simplified rendering of control...
writer.WriteLine("<input type='text' id='" & MyBase.ClientID & "'>")
End Sub
End Class
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim MyCustomTextBox As New CustomTextBox
form1.Controls.Add(MyCustomTextBox)
End Sub
Alternatively, if you don't want to have to add the control at run-time, you can make your CustomTextBox as a ServerControl so that you can add it at design time.
what ASP.Net controls render in name attributes, comes from UniqueID property. It's not necessarily good idea to override it, since ASP.NET uses that to locate control on postback to route postback data & events. However, if you are sure it is ok in your scenario, you certainly can override UniqueID property following #Pauli Østerø's answer. Have fun!
I think better is to change name property to same like ID is..
Just try bellow by using Jquery on document.ready(function(){})
document.ready(function(){
$.each($('div').children(), function() {
$(this).attr("name",$(this).attr("id"));
});
});
You can override the name property and return whatever you want (http://referencesource.microsoft.com/#System.Web/xsp/system/Web/UI/HtmlControls/HtmlInputControl.cs).
The following test:
[TestClass]
public class MyTestClass
{
private TestContext _testContext;
protected TestContext TestContext
{
get { return _testContext; }
set { _testContext = value; }
}
[TestMethod]
[HostType("ASP.NET")]
[UrlToTest("http://localhost/MyPage.aspx")]
public void TestMyPage()
{
TextBox tb = TestContext.RequestedPage.FindControl("ControlId") as TextBox;
Assert.IsNotNull(tb);
}
}
fails, and using the string "ctl00$ContentPlaceHolder1$ControlId" as control Id provide a proper control... I know, ASP.NET contains "ClientID" property for web-controls, but is there any possibility to know in advance the control's client Id in the TEST (Under VS 2008)?
Thanks.
I don't think the ClientID is what you're after here. I think your problem is that FindControl is not doing what you think it is.
FindControl is not recursive. If your textbox is inside of a ContentPlaceHolder, then you need to call FindControl on the placeholder, not the Page.
Otherwise, I suggest writing a recursive FindControl function that will search the entire control heirarchy. You can see an example here.
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?