Custom Web Control - ParseChildren & Resource Objects - asp.net

I hope someone can help me. I have the following custom server control:
[ParseChildren(true)]
public class MyControl : WebControl, INamingContainer
{
[PersistenceMode(PersistenceMode.InnerProperty)]
public string MyProperty
{
get;set;
}
}
It works perfectly with the following mark-up:
<acme:MyControl runat="sever">
<MyProperty>Test String</MyProperty>
</acme:MyControl>
But if I try to localise the property string, I get a parse error:
<acme:MyControl runat="sever">
<MyProperty><%=(string)GetLocalResourceObject("MyResourceKey") %></MyProperty>
</acme:MyControl>
Even if I cast the type, ASP.NET indicates that the property cannot accept controls as children. How should the expression look like if I want to localise it? I can make the property accessible as an attribute of the control's tag, but I prefer the mark-up above, it looks more elegant and clean.
Thanks

Add a second property called MyPropertyResourceKey, so your final code would be:
[ParseChildren(true)]
public class MyControl : WebControl, INamingContainer
{
[PersistenceMode(PersistenceMode.InnerProperty)]
public string MyProperty
{
get;set;
}
string _propertyResourceKey;
[PersistenceMode(PersistenceMode.InnerProperty)]
public string MyPropertyResourceKey
{
get {
return _propertyResourceKey;
}
set {
_propertyResourceKey = value;
MyProperty = (string)GetLocalResourceObject(_propertyResourceKey);
}
}
}
EDIT:
Based on comments, it seems that there may also be a demand for a more flexible method. Generally, if you are looking assign a dynamic value to a control, you will want to either create a Data Bound control (if applicable) http://msdn.microsoft.com/en-us/library/ms366540.aspx.
If, on the other hand, the control does't know how its will usually be assigned, the accepted method is to assign the property value in the code-behind of the page.

Related

How to access attributes of inner tags in custom controls in asp.net

I have a custom control for building forms to automatically generate a label-input field pair. But I need to be able to modify attributes of the generated label-input field pair in my vb code behind file. The problem is that the code behind file seems to be applied before the custom control. How do I work around this issue?
You could expose the inner controls as public properties of your custom control.
For example:
public class MyCustomControl : CustomControl
{
Label _label;
// initialize _label in OnInit() ...
public string LabelText { get { return _label.Text; } set { _label.Text = value; } }
}

is there a kind of property only set in a web control constructor?

I have a WebControl and it has a property. However, value of this property should not be changed once the control has been constructed... in other words, the property can be set only in some code like:
<ct:Acontrol ID="xxx" Aproperty="xxx" runat="server"></ct:Acontrol>
but not:
xxx.Aproperty=...
so what is the normal way to do that? Thanks!
The properties that you are using in markup must be public properties with a public getter and setter. There is no special syntax for "only set this once".
What you can do is check in the setter whether it was already set and if so, not set to the new value.
private string _aProperty;
public string Aproperty
{
get { return _aProperty;}
set
{
if(_aProperty == null)
{
_aProperty = value;
}
}
}
You should be able to use
xxx.Attributes("Aproperty")
All ASP.NET markup attributes are set as properties after the constructor is executed. You can select specific read-only properties to be set only in the constructor by using sub-classes of the control.
<!-- Aproperty=xxx -->
<ct:Acontrolxxx ID="xxx" runat="server"></ct:Acontrolyyy>
<!-- Aproperty=yyy -->
<ct:Acontrolyyy ID="yyy" runat="server"></ct:Acontrolxxx>
public class Acontrolxxx : Acontrolbase
{
public Acontrolxxx () { base.Aproperty = xxx; }
}
The property is probably using a combination of the EditorBrowsable and DesignerSerializationVisibility attributes:
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string SomeProperty { get; set; }
The DesignerSerializationVisibility attribute shows the property in markup, and the EditorBrowsable attribute hides the property in code-behind.

Designer problem with CompositeDataBoundControl

I have a custom class:
SimpleTemplatedControl : CompositeDataBoundControl
private ITemplate _itemTemplate;
[PersistenceMode(PersistenceMode.InnerProperty),
TemplateContainer(typeof(SimpleItem)),
]
public ITemplate ItemTemplate
{
get { return _itemTemplate; }// get
set { _itemTemplate = value; }// set
}
protected override int CreateChildControls(
System.Collections.IEnumerable dataSource,
bool dataBinding)
{
//
}
When I drop this on a webform I get such a smart tag in which I can choose a DataSource control. Pretty convinient. However if I add this attribute to this class:
[Designer(typeof(SimpleDesigner))]
I don't get to see that anymore but instead a smart tag to fill in my Template (also handy).
I would like to have both option available from within the same smart tag just like with a GridView control. How to accomplish this?
Which is the Designer type you're using? Normally it would be ControlDesigner but for the CompositeDataBoundControl you should use the DataBoundControlDesigner class to inherit your designer from.
Grz, Kris.

Can't find control (ASP.NET, MS TEST)

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.

Can an ASP.Net control be configued to accept ANY attributes, even ones not defined as properties in the class definition?

Is it possible to define a control to have non-specified set of attributes? For instance:
<MyPrefix:MyControl SomeAttribute="SomeValue" runat="server"/>
I don't want to define a property on the control class beforehand for "SomeAttribute." I'd really just like a HashTable or some other construct like this:
"SomeAttribute" => "SomeValue"
So this control can be used in many places with attributes that are essentially made up at runtime.
I'm wonder if there's some parsing method I can override which iterates through the attributes at parse time. I can:
Look for a property with the name and set it
If I don't find such a property, put the attribute name and value into a HashTable
Possible?
You want to use the IAttributeAccessor interface.
Defines methods used by ASP.NET server controls to provide programmatic access to any attribute declared in the opening tag of a server control.
Example control:
using System;
using System.Collections.Generic;
using System.Web.UI;
namespace App_Code.Controls {
public class OutputAttributesControl : Control, IAttributeAccessor {
private readonly IDictionary<String, String> _attributes = new Dictionary<String, String>();
protected override void Render(HtmlTextWriter writer) {
writer.Write("Attributes:<br/>");
if (_attributes.Count > 0) {
foreach (var pair in _attributes) {
writer.Write("{0} = {1} <br/>", pair.Key, pair.Value);
}
} else {
writer.Write("(None)");
}
}
public String GetAttribute(String key) {
return _attributes[key];
}
public void SetAttribute(String key, String value) {
_attributes[key] = value;
}
}
}
Invocation:
<AppCode:OutputAttributesControl runat="server" attr="value" />
Output:
Attributes:
attr = value
Caveats:
It seems that SetAttribute is only called on attributes that can not be resolved normally. This means you'll not see the id- or the runat-attribute in your code. Assigned properties (attr="<%= DateTime.Now %>") show up as an empty string. Databound properties does not show up at all in design mode, but works in normal mode (assuming that someone called DataBind, as usual).

Resources