自定义ASP.NET控件的样式属性设置时会抛出异常

3

我编写了一个继承自System.Web.UI.Control的自定义控件。我添加了如下的Style属性:

public class MyControl : Control
{
    public CssStyleCollection Style
    {
        get { return ViewState["Style"] as CssStyleCollection; }
        set { ViewState["Style"] = value; }
    }
}

我是如下使用它的

<custom:MyControl runat="server" Style="float: left; padding: 2px 4px;" ... />

当我尝试加载页面时,出现以下异常:
无法从其字符串表示形式“float: left; padding: 2px 4px;”创建类型为“System.Web.UI.CssStyleCollection”的对象,用于属性“Style”。
我想我应该在属性中添加TypeConverter属性或类似的东西,但是我在System.Web.UI命名空间中找不到适当的属性。我也在Google上搜索了一下,关于这个问题的信息并不多。 我知道如果扩展WebControl,这将起作用,但我更喜欢处理Control的解决方案。
2个回答

3

当您为ASP.NET控件添加样式时,应该能够使用标准的style属性。该属性可能会被intellisense隐藏,但它是一个被接受的HTML属性,因此它仍然可用:

<asp:TextBox ID="TextBox1" runat="server" style="font-weight:bold;" />

幕后,style属性将被解析,并将属性加载到Styles集合中:

string weight = TextBox1.Styles["font-weight"]; //== "bold"

我能否通过Style属性以编程方式访问样式,而不是Attributes ["style"]?我正在寻找在代码中清晰操作该属性的方法。也许我最好使用WebControl作为基类,尽管这并非我的首选。 - Ivaylo Slavov
是的,内联样式将被解析并填充到幕后的“Styles”集合中。 - James Johnson
好的,我会等一段时间,可能会接受你的答案。不过我还是很想知道是否可以使用类型转换器或其他方法避免异常。 - Ivaylo Slavov
1
你可能需要创建一个包装器来包含一些逻辑来解析标记并输出CssStylesCollection集合。 - James Johnson
谢谢,我想那就是我要做的了。 - Ivaylo Slavov

1

这是我解决问题的方法,但并不完美:

  1. 将自定义控件的Style属性设置为字符串而非CssStyleCollection。
  2. 创建一个私有实例变量,类型为CssStyleCollection。
  3. 使用反射来创建CssStyleCollection的实例,使用其私有构造函数。
  4. 设置和获取私有CssStyleCollection的“Value”属性。这是一个字符串,在设置时将被解析。
  5. 重写Render方法,并使用CssStyleCollection实例的“Value”属性。
    private CssStyleCollection iStyle;
    public string Style 
    {
        get
        {
            if (iStyle == null)
            {
                iStyle = CreateStyle(ViewState);                    
            }
            return iStyle.Value;
        }
        set
        {
            if (iStyle == null)
            {
                iStyle = CreateStyle(ViewState);                    
            }
            //Could add differnet logic here
            iStyle.Value += value == null ? string.Empty : value;
        }
    }

    private static CssStyleCollection CreateStyle(StateBag aViewState)
    {
        var tType = typeof(CssStyleCollection);
        var tConstructors = tType.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
        foreach (var tConstructor in tConstructors)
        {
            var tArgs = tConstructor.GetParameters();
            if (tArgs.Length == 1 && tArgs[0].ParameterType == typeof(StateBag))
            {
                return (CssStyleCollection)tConstructor.Invoke(new object[] { aViewState });
            }
        }
        return null;
    }

    protected override void Render(HtmlTextWriter aOutput)
    {
        aOutput.AddAttribute("style", Style);

        aOutput.RenderBeginTag("span");
        foreach (Control tControl in this.Controls)
        {
            tControl.RenderControl(aOutput);
        }
        aOutput.RenderEndTag();            
    }

如果我从未使用Style属性,那么在Render方法中我可能会得到一个NullReferenceException,因为iStyle将不会被初始化。顺便问一下,参数和变量前缀(例如Render方法中的aOutput)的含义是什么?我曾在旧版Visual C++书籍中看到过类似的命名约定,但这似乎有所不同。 - Ivaylo Slavov
1
感谢Ivaylo Slavov发现这个问题。现在,我们已经通过使用Style而不是iStyle.Value来修复了空指针错误。关于前缀,我只知道它们是我曾经工作过的公司编码标准的一部分。例如,“i”表示实例变量,“a”表示参数,“t”表示临时/作用域变量。 - Bucket
谢谢,现在这些奇怪(在我看来)的名称对我来说有点意义了。如果您看到我已经接受的答案,那么没有必要手动实例化CssStyleCollection。如果我公开一个字符串属性,.NET将正确填充样式,并且在Web应用程序中进行反射调用可能会成为瓶颈 - 想象一下所讨论的控件位于重复器的ItemTemplate内,该重复器绑定到大型数据源。在最坏的情况下,将有与项目数量相同的反射调用。我的意思是,您的解决方案将在没有它的情况下正常工作。 - Ivaylo Slavov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接