ASP.net Migration: ReadOnly TextBox persistance
If you ever migrated from ASP.net 1.0 to ASP.net 2.0, you might have noticed the following
issue.
Imagine you've got a page like the following one:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script type="text/javascript"> function populate() { var txtTest = document.getElementById("<%=txtTest.ClientID %>"); txtTest.value = "test 123"; } </script> </head> <body> <form runat="server"> <div> <asp:TextBox runat="server" ID="txtTest" ReadOnly="true" Text="abc"></asp:TextBox> <input type="button" onclick="populate()" value="Test" /> <br /> <asp:Button ID="btnSubmit" runat="server" Text="Submit" /> </div> </form> </body> </html>
Basically what we've got here is client-side code modifying the contents of a server-side based control.
The user clicks on the test button, which sets the value of the readonly textbox "txtTest" to test 123 (the test button being our input method instead of the keyboard).
If we click submit (in ASP.net 1.0) it sends the value to the server assigning/persisting our readonly textbox with the new value (client-side javascript assigned). In ASP.net 2.0 you'll find that ASP.net simply ignores the changes we made via the test button (ignoring our client-side action).
This isn't a bug though, this is purely by design - its all about security; preventing a malicious user to change readonly values. Quite a logical design since readonly values are supposed to be readonly, right?
What about legitimate reasons e.g. a datepicker?
One workaround would be to rather set the readonly value using the attribute collection of the control:
txtTest.Attributes.Add("readonly", "readonly");
What about if we've got this issue throughout our application e.g. we just migrated from ASP.net 1.0? We can always go through every page and make the necessary changes or alternatively change the default behaviour of the textbox control to act like it did in ASP.net 1.0.
Or what if we prefer the ASP.net 1.0 way of handling readonly values?
We can always simply change the behaviour of the textbox control by creating a WebControlAdapter:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI.WebControls.Adapters; using System.Web.UI.WebControls; namespace CSTruter { public class TextBoxAdapter : WebControlAdapter { protected override void OnInit(EventArgs e) { TextBox sender = this.Control as TextBox; if (sender.Attributes["secure"] == null) { if (sender.ReadOnly == true) { sender.ReadOnly = false; sender.Attributes.Add("readonly", "readonly"); } } base.OnInit(e); } } /* Since the value of a multiline textbox doesn't persist when we create an adapter, we need to do the following */ protected override void Render(System.Web.UI.HtmlTextWriter writer) { TextBox sender = this.Control as TextBox; base.RenderBeginTag(writer); if (sender.TextMode == TextBoxMode.MultiLine) { System.Web.HttpUtility.HtmlEncode(sender.Text, writer); } else { base.RenderContents(writer); } base.RenderEndTag(writer); } }
Adding the previous snippet to our solution, along with the appropriate browserfile will change the default behaviour of all the TextBoxes in our application to function like it did in ASP.net 1.0. If we want a secure readonly textbox, we can simply add an attribute named secure to the textbox, inforcing the ASP.net 2.0 model e.g.
<asp:TextBox runat="server" ID="txtTest" Text="abc" Secure="true"></asp:TextBox>
Posted by - Christoff Truter
Date - 2010-04-28 16:56:38
Comments - 0
Date - 2010-04-28 16:56:38
Comments - 0
C#: Custom Attributes
In C# we've got a mechanism called attributes, which we use to provide
extra information about certain elements within our code.
An example of attributes available within C# is the XmlAttribute/XmlRoot/XmlElement
attributes - which we use in conjunction with the XmlSerializer class - used to
serialize objects to XML.
using System; using System.IO; using System.Xml.Serialization; public class friend { [XmlAttribute()] public String firstname { get; set; } [XmlAttribute()] public String lastname { get; set; } } class Program { static void Main(string[] args) { friend f = new friend { firstname = "John", lastname = "Doe" }; using (StreamWriter sw = new StreamWriter(@"c:\abc.xml")) { XmlSerializer xs = new XmlSerializer(typeof(friend)); xs.Serialize(sw.BaseStream, f); } } }
Internally the XmlSerializer class uses reflection to process the attributes we assigned to our class - in this instance we're telling the serializer to format our properties as xml attributes within the XML.
But what if we want to create our own custom attributes? Lets imagine we want to create attributes that defines certain constraints on our classes (not ideal, but useful as an example of how to create/retrieve custom attributes)
Basically we need to inherit from the Attribute class and define on which elements we wish to make our attribute available to:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class Constraint : Attribute { public Boolean Unique { get; set; } public Boolean Required { get; set; } }
An example of how to use our custom attribute:
public class post { [Constraint(Unique = true, Required = true)] public String title { get; set; } [Constraint(Required = true)] public Int32 age { get; set; } }
To process/read our custom attribute we use the reflection classes like this:
public class posts : List<post> { // Hide inherited member "Add" public new void Add(post item) { // Get properties within our class PropertyInfo[] properties = item.GetType().GetProperties(); foreach (PropertyInfo property in properties) { object o = property.GetValue(item, null); // Get custom attributes of type Constraint object[] attributes = property.GetCustomAttributes(typeof(Constraint), true); foreach (Constraint attribute in attributes) { // Check Required Constraint if (attribute.Required) { if (o == null) throw new Exception(String.Concat(property.Name, " property required")); else if (o is int) { if (((int)o) == 0) throw new Exception(String.Concat(property.Name, " property required")); } } // Check Unique Constraint if (attribute.Unique) { // linq query testing for duplicate entries if ((from p in this where p.GetType().GetProperty(property.Name).GetValue(p, null) == o select p).Count() > 0) throw new Exception(String.Concat(property.Name, " property unique violation")); } } } base.Add(item); } }
In the example we're checking for the constraint custom attribute on the properties of our class, but alternatively we may also add/process custom attributes on classes/structs/enum etc (see the AttributeTargets enum)
Posted by - Christoff Truter
Date - 2010-04-25 22:09:04
Comments - 0
Date - 2010-04-25 22:09:04
Comments - 0
First 6 7 8 9 10 11 12 13 14 15 Last / 42 Pages (83 Entries)