【问题标题】:NullReferenceException using RenderControl with ASP.NET Menu Server ControlNullReferenceException 将 RenderControl 与 ASP.NET 菜单服务器控件一起使用
【发布时间】:2012-05-23 17:27:13
【问题描述】:

我在调用 ASP.NET 菜单服务器控件的 RenderControl 方法时遇到 NullReferenceException 异常。

我使用以下代码动态创建了控件:

private string RenderMenuHTML()
{
    StringBuilder sb = new StringBuilder();
    StringWriter sw = new StringWriter(sb);
    HtmlTextWriter hw = new HtmlTextWriter(sw);

    string menuXML = MenuManager.GetMenuXML();

    if (!string.IsNullOrEmpty(menuXML))
    {
        Menu menuControl = new UI.Menu();
        menuControl.ID = "menu";

        // Required properties to use RenderControl
        menuControl.SkipLinkText = string.Empty;
        menuControl.StaticPopOutImageUrl = "fake.gif";
        menuControl.ScrollUpImageUrl = "fake.gif";
        menuControl.ScrollDownImageUrl = "fake.gif";

        // Data binding
        XmlDataSource xmlDS = new XmlDataSource();
        xmlDS.ID = "xdsMenu";
        xmlDS.Data = menuXML;
        xmlDS.XPath = "MenuItems/MenuItem";

        // Menu data binding
        MenuItemBinding menuItemBinding = new MenuItemBinding();
        menuItemBinding.DataMember = "MenuItem";
        menuItemBinding.ValueField = "Value";
        menuItemBinding.TextField = "Text";
        menuItemBinding.ToolTipField = "Text";
        menuItemBinding.NavigateUrlField = "NavigateUrl";
        menuControl.DataBindings.Add(menuItemBinding);

        menuControl.DataSource = xmlDS;
        menuControl.DataBind();

        menuControl.RenderControl(hw); // <-- Problem here
    }

    return sb.ToString();
}

我需要做的是获取控件生成的 HTML 代码,这样我就不必手动生成它了。我正在绑定一个从递归菜单层次结构构建的 XML 数据源。

我查看了异常的详细信息,调试器似乎没有提供更多信息。

当我将控件放入 PlaceHolder 时一切正常,但我需要的是 HTML 代码。

我似乎缺少 Menu 或 MenuItem 对象中的一些必需属性,但我在网络上找不到任何依赖于此的东西。

有什么想法吗?

谢谢

【问题讨论】:

    标签: c# .net asp.net


    【解决方案1】:

    我假设控件使用它的上下文(即它所在的页面之类的东西)来做某事。虽然它不在页面上,但它无法访问此信息,这就是它在占位符中而不是在占位符之外工作的原因。我建议在调用 rendercontrol 方法之前将其添加到页面中的占位符中。如果您想在之后将其从页面中删除,那么没有什么可以阻止您这样做。我不确定这是否会产生副作用(例如,您可能需要注意一些 ID 的命名等,因为它们可能取决于您将其放在页面中的位置)。

    我还会继续质疑您使用此 HTML 做什么?如果您在页面上使用它,为什么不将控件放在页面中并让它做它的事情呢?如果您在其他地方使用它,那么为什么不使用静态 HTML?并不是说你在做什么是错的,我只是在努力思考你在做什么,所以想确保你已经明确考虑过为什么要这样做。 :)

    【讨论】:

    • 我有一个字符串,其中包含来自预定义模板的 HTML 代码。我想用实际菜单替换 {Template.Menu} 之类的东西,所以我坚持我会使用 RenderControl 获取代码。
    • @Jason:啊,好的。那讲得通。可能有比这更好的方法,但它们也可能因您正在做的事情而过度设计。 ;-) 我认为我的建议应该可以满足您的需求。
    • 您可能是对的,我想我将使用 HtmlTextWriter 而不是 Menu 控件手动生成代码。谢谢你的建议!
    • 当我说我的建议会起作用时,我的意思是,如果您首先将菜单控件添加到页面上,它可能会很好地呈现给您的 htmltextwriter。您可能想确保那里没有上下文敏感的东西,或者是否有措施对其进行调整,但我认为应该可以继续广泛地使用您开始使用的方法。只需在渲染控件和 Page.Controls.Remove(menu) 之前执行 Page.Controls.Add(menu) 或在渲染之后执行任何等效操作。
    【解决方案2】:

    你可以使用 init Event 来初始化你的控件

    这是一个在 ASP 页面上初始化 Menu 控件的示例

        protected void Menu1_Init(object sender, EventArgs e)
        {
            List<Category> categories = db.GetCategory();
            for (int i = 0; i < categories.Count; i++)
            {
                ((Menu)sender).Items.Add(new MenuItem(categories.ElementAt(i).Name));
    
            }
        } 
    

    【讨论】:

      【解决方案3】:

      看起来控件依赖于页面生命周期来初始化在 RenderControl 中使用的一些变量。

      您必须Debug the .NET Framework Source Code 才能看到那是什么。

      【讨论】:

        【解决方案4】:

        我认为您需要覆盖 Render 或 RenderChildren 事件。

        这是微软的一个例子

        // Override default implementation to Render children according to needs. 
        protected override void RenderChildren(HtmlTextWriter output)
        {
           if (HasControls())
           {
              // Render Children in reverse order.
              for(int i = Controls.Count - 1; i >= 0; --i)
              {
                 Controls[i].RenderControl(output);
              }
           }         
        }
        
        protected override void Render(HtmlTextWriter output)
        {       
           output.Write("<br>Message from Control : " + Message);       
           output.Write("Showing Custom controls created in reverse" +
                                                            "order");         
           // Render Controls.
           RenderChildren(output);
        }
        

        这适用于标准 ASPX 页面:

        protected override void Render(HtmlTextWriter writer) 
        {
            StringBuilder sb = new StringBuilder();
            HtmlTextWriter htw = new HtmlTextWriter(new StringWriter(sb));
            base.Render(htw);
            writer.Write(sb.ToString());
        
            Logger.Write("TransferToColdFusion HTML: " + sb.ToString(), "Debug");
        }
        

        试试这个方法并传入你的菜单控件。

        public string RenderControl(Control ctrl) 
        {
            StringBuilder sb = new StringBuilder();
            StringWriter tw = new StringWriter(sb);
            HtmlTextWriter hw = new HtmlTextWriter(tw);
        
            ctrl.RenderControl(hw);
            return sb.ToString();
        }
        

        【讨论】:

        • 重写这些方法后没有错误,但 sb.ToString() 什么也不返回。
        【解决方案5】:

        我测试了你的代码,它运行良好。硬件在 RenderControl 方法之后有这个。

        > <div id="menu">   <ul class="level1">
        >       <li><a title="test" class="level1"
        > href="www.google.com">test</a></li>
        > 
        
        </ul> </div>
        
        The only difference was that I used a sample xml = @"<MenuItems><MenuItem Value='Test' Text='test' NavigateUrl='www.google.com'>Test</MenuItem></MenuItems>";
        

        所以可能与您的 Menu Xml 源有关。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-06-09
          • 1970-01-01
          • 1970-01-01
          • 2012-03-15
          • 1970-01-01
          • 2011-10-14
          • 1970-01-01
          • 2012-03-30
          相关资源
          最近更新 更多