I\'m looking for ideas and opinions here, not a \"real answer\", I guess...
Back in the old VB6 days, there was this property called \"Tag\" in all controls, that wa
You can also use the composite pattern instead of using inheritence:
public class TaggedControl<TControl, TTag> : Control
where TControl : Control, new()
{ public TaggedControl() {this.Control= new TControl();}
public TControl Control {get; private set;}
public TTag Tag {get; set;}
protected override void CreateChildControls(){Controls.Add(Control);}
}
var textBox = new TaggedControl<TextBox, string>();
textBox.Tag = "Test";
label.Text = textBox.Tag;
You can use asp:Hidden controls to store data between posts. Like will says, there's no sense to have a tag property if you lose it's value.
You can add attributes to some controls, but that seems to add some nasty HTML around the rendered control, something like <div attrName="attrValue">
... (it might be a span though)
I took mdb's brilliant solution, and ported it over to C# for my own needs. I also changed the Tags from a Dictionary of String, String, to a Dictionary of String, Object... since theoretically any type of object can be stored in the Session, not just strings:
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
public static class Extensions
{
public static void SetTag(this Control ctl, object tagValue)
{
if (ctl.SessionTagDictionary().ContainsKey(TagName(ctl)))
ctl.SessionTagDictionary()[TagName(ctl)] = tagValue;
else
ctl.SessionTagDictionary().Add(TagName(ctl), tagValue);
}
public static object GetTag(this Control ctl)
{
if (ctl.SessionTagDictionary().ContainsKey(TagName(ctl)))
return ctl.SessionTagDictionary()[TagName(ctl)];
else
return string.Empty;
}
private static string TagName(Control ctl)
{
return ctl.Page.ClientID + "." + ctl.ClientID;
}
private static Dictionary<string, object> SessionTagDictionary(this Control ctl)
{
Dictionary<string, object> SessionTagDictionary;
if (HttpContext.Current.Session["TagDictionary"] == null)
{
SessionTagDictionary = new Dictionary<string, object>();
HttpContext.Current.Session["TagDictionary"] = SessionTagDictionary;
}
else
SessionTagDictionary = (Dictionary<string, object>)HttpContext.Current.Session["TagDictionary"];
return SessionTagDictionary;
}
}
I'm not sure what the tag property did in VB6, but maybe you're looking for the Attributes
property of Web controls:
MyImgCtrl.Attributes["myCustomTagAttribute"] = "myVal";
No, there's no direct equivalent, but if you're using v3.5 of the Framework, you can add this functionality quite easily using an extension method. For example:
Imports System.Runtime.CompilerServices
Public Module Extensions
<Extension()> _
Public Sub SetTag(ByVal ctl As Control, ByVal tagValue As String)
If SessionTagDictionary.ContainsKey(TagName(ctl)) Then
SessionTagDictionary(TagName(ctl)) = tagValue
Else
SessionTagDictionary.Add(TagName(ctl), tagValue)
End If
End Sub
<Extension()> _
Public Function GetTag(ByVal ctl As Control) As String
If SessionTagDictionary.ContainsKey(TagName(ctl)) Then
Return SessionTagDictionary(TagName(ctl))
Else
Return String.Empty
End If
End Function
Private Function TagName(ByVal ctl As Control) As String
Return ctl.Page.ClientID & "." & ctl.ClientID
End Function
Private Function SessionTagDictionary() As Dictionary(Of String, String)
If HttpContext.Current.Session("TagDictionary") Is Nothing Then
SessionTagDictionary = New Dictionary(Of String, String)
HttpContext.Current.Session("TagDictionary") = SessionTagDictionary
Else
SessionTagDictionary = DirectCast(HttpContext.Current.Session("TagDictionary"), _
Dictionary(Of String, String))
End If
End Function
End Module
Then, in your ASP.NET pages, first bring your extensions into scope, e.g:
Imports WebApplication1.Extensions
...and then use it your controls as desired:
TextBox1.SetTag("Test")
Label1.Text = TextBox1.GetTag
LATER EDIT: and if you really, really don't want to store your tags in the Session object, it's possible to stuff them into your Viewstate instead. This will of course mean that your tags will be exposed in the page markup sent to the user (albeit in obfuscated form), and, unfortunately, that some reflection-fu is required, since the ViewState property of a Page is marked as 'protected' for some reason.
So, this code should pretty much be considered for entertainment purposes only, unless you actually like to raise eyebrows during code reviews:
<Extension()> _
Public Sub SetTag(ByVal ctl As Control, ByVal tagValue As String)
ViewState.Add(ctl.ID & "_Tag", tagValue)
End Sub
<Extension()> _
Public Function GetTag(ByVal ctl As Control) As String
Return ViewState(ctl.ID & "_Tag")
End Function
Private Function ViewState() As Web.UI.StateBag
Return HttpContext.Current.Handler.GetType.InvokeMember("ViewState", _
Reflection.BindingFlags.GetProperty + _
Reflection.BindingFlags.Instance + _
Reflection.BindingFlags.NonPublic, _
Nothing, HttpContext.Current.CurrentHandler, Nothing)
End Function
FINAL EDIT (I promise...). And here's a way to get rid of the reflection: first, create a new class to expose the ViewState property with a usable protection level, then change your Code-Behind (.aspx.vb) classes to inherit that instead of Web.UI.Page, e.g.:
Public Class PageEx
Inherits System.Web.UI.Page
Friend ReadOnly Property ViewStateEx() As Web.UI.StateBag
Get
Return MyBase.ViewState
End Get
End Property
End Class
Now, in your extensions module, you can access this newly defined property as:
Private Function ViewState() As Web.UI.StateBag
Return DirectCast(HttpContext.Current.Handler, PageEx).ViewStateEx
End Function
Still a bit of a hack, but much more acceptable than using reflection...