7

我在这里寻找想法和意见,而不是“真正的答案”,我猜......

回到旧的 VB6 时代,所有控件中都有一个名为“Tag”的属性,这是存储与控件相关的自定义信息的有用方法。每一个控制都有它,一切都很幸福......

现在,在 .Net 中(至少对于 WebForms),它不再存在了......

有没有人有一个很好的替代品?

我经常发现这个问题,我有不同的函数在生命周期的不同时间运行,它们用我的控件做一些事情,我想让它们保持独立,但一个应该将信息传递给另一个关于具体控制。

我可以想到一百万种替代方法(显然,从模块级字典开始),但没有一个像好的 ol' Tag 那样干净。

(注意:我知道我可以对所有控件进行子类化并改用我的版本。我宁愿不这样做)

有什么建议么?你通常如何解决这个问题?关于他们为什么首先删除这个的任何想法?

编辑:我正在寻找请求内的东西,而不是请求间的东西。我不需要这些信息仍然存在于 PostBack 上。例如,它位于 _Load 和 _PreRender 方法之间。

EDIT2:我知道我的 ASp.Net,我知道桌面和网络之间的区别,伙计们!我只是想最大限度地使用 .Net 给我的抽象。我理解权衡,相信我,假设我这样做,请回答。

4

8 回答 8

5

不,没有直接的等价物,但如果您使用的是框架的 v3.5,您可以使用扩展方法轻松添加此功能。例如:

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

然后,在您的 ASP.NET 页面中,首先将您的扩展纳入范围,例如:

Imports WebApplication1.Extensions

...然后根据需要使用您的控件:

TextBox1.SetTag("Test")

Label1.Text = TextBox1.GetTag

稍后编辑:如果您真的,真的不想将标签存储在 Session 对象中,则可以将它们填充到您的 Viewstate 中。这当然意味着您的标签在发送给用户的页面标记中公开(尽管是以模糊的形式),不幸的是,需要一些反射功能,因为页面的 ViewState 属性被标记为“受保护” ' 由于某些原因。

因此,这段代码应该仅用于娱乐目的,除非您真的在代码审查期间引起人们的注意:

<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

最终编辑(我保证......)。这里有一种摆脱反射的方法:首先,创建一个新类以公开具有可用保护级别的 ViewState 属性,然后更改您的 Code-Behind (.aspx.vb) 类以继承它而不是 Web.UI。页面,例如:

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

现在,在您的扩展模块中,您可以访问这个新定义的属性:

Private Function ViewState() As Web.UI.StateBag
  Return DirectCast(HttpContext.Current.Handler, PageEx).ViewStateEx
End Function

仍然有点hack,但比使用反射更容易接受......

于 2008-10-08T15:03:00.047 回答
3

您还可以使用复合模式而不是使用继承:

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;
于 2008-10-08T16:41:59.193 回答
1

我采用了 mdb 的出色解决方案,并将其移植到 C# 以满足我自己的需要。我还将标签从字符串字典更改为字符串字典,对象......因为理论上任何类型的对象都可以存储在会话中,而不仅仅是字符串:

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;
    }
}
于 2020-04-02T21:52:59.123 回答
0

您可以向某些控件添加属性,但这似乎会在呈现的控件周围添加一些讨厌的 HTML,例如<div attrName="attrValue">...(尽管它可能是一个跨度)

于 2008-10-08T14:41:50.200 回答
0

我不确定标签属性在 VB6 中做了什么,但也许您正在寻找AttributesWeb 控件的属性:

MyImgCtrl.Attributes["myCustomTagAttribute"] = "myVal";
于 2008-10-08T14:43:03.160 回答
0

您询问了 ASP.Net,但 vb6 是一种桌面语言,它的“标记”控件也是如此。用于经典 asp 的 VBScript 并没有真正的控件概念。

现在在 .Net 中,对于桌面控件,您可以使用继承,这比旧的 Tag 属性更强大。继承也适用于 Web 控件,尽管您必须小心使用它来保持状态。

于 2008-10-08T14:43:47.760 回答
-1

您可以使用 asp:Hidden 控件在帖子之间存储数据。就像意志所说,如果你失去了它的价值,那么拥有一个标签属性是没有意义的。

于 2008-10-08T14:45:27.447 回答
-1

Tag属性始终是一种奇怪的疣状东西,很有用,您可以从中挂出您想要的任何数据。但它不是强类型的,也没有真正实现连贯的设计。该属性本身就像一个奇怪的旋钮一样挂在控件上。他们将其保存在 WinForms 中以方便从 VB6 移植代码。新的 WPFControl类没有Tag.

使用 .NET,您拥有完整的面向对象和足够的类型多态性,因此您可以强类型化您想要与代码关联的任何额外信息,无论它是在子类中还是在Dictionary<Control,TValue>.

如果它在单个Page请求中,并且您想要一个通用解决方案,那么页面本身中每个值类型(例如,Dictionary<Control,string>Dictionary<Control,BusinessObject>)的字典应该正是您所需要的。

于 2008-10-08T15:06:04.907 回答