为 ASP.Net Control 的属性设置动态值

发布于 2025-01-12 19:46:17 字数 5202 浏览 5 评论 0 原文

我试图将动态变量传递到控件中:

<% foreach(var product in CartModel.Products) { %>
    <kc:Warning runat="server" id="Warning" ProductId="<%= product.ProductId %>" />
%>

但是当我访问控件中的 ProductId(在 /ProductAvailabilityWarning.ascx.cs 中)时,它会按字面意思传递 "<%= Product.ProductId %>; ",而不是 product.ProductId 的值。

我不想将所有代码重写为 Repeater,因为 foreach 块内有大量我必须修改的动态代码。

编辑:由于这段代码看起来很混乱,因为它是遗留代码的弗兰肯斯坦怪物,这里有一个更完整的代码片段,以及为什么我试图避免将整个代码重写为中继器

<% foreach(var product in CartModel.Products) { %>
<kc:Warning runat="server" id="Warning" ProductId="<%= product.ProductId %>" />

<div class="cart-product js-cart-product">
    <div class="cart-product__product-details">
        <div class="cart-product__name"><%= product.ProductName %></div>
        <div class="cart-product__details">
            <div class="cart-product__detail-label"><%= product.MaturityLabel %></div>
            <div class="cart-product__detail-description"><%= product.RelativeMaturity %> <%= Dictionary.GetValue("eBusiness", "days" ) %></div>
            <ul class="cart-product__detail-icons">
                <% foreach(var maturityIcon in product.MaturityIcons) { %>
                <li>
                    <img
                        src="<%= maturityIcon.Src %>"
                        alt="" />
                    <span class="badge-label"><%= maturityIcon.Alt %></span>
                </li>
                <% } %>
            </ul>
        </div>
        <div class="cart-product__treatments-label">
            <%= Dictionary.GetValue("eBusiness", "seed treatments selected" ) %>
        </div>
        <ul class="cart-product__treament-grid">
                <% foreach(var seedTreatmentImage in product.SeedTreatmentImages) { %>
                <li class="cart-product__selected-treatment"> 
                    <img
                    src="<%= seedTreatmentImage.Src %>"
                    alt="<%= seedTreatmentImage.Alt %>" />
                </li>
                <% } %>
        </ul>
    </div>
    <div class="cart-product__order-totals">
        <% if (product != null && product.ProductBagImage != null) { %>
        <div class="cart-product__logo">
            <img width="325" height="500"
                src="<%= product.ProductBagImage.Src %>"
                alt="<%= product.ProductBagImage.Alt %>" />
        </div>
        <% } %>
        <div class="cart-product__quantity">
            <span class="cart-product__value"><%= product.Quantity %></span>
            <% if (product.Size == eBusiness.Sizes.Bag) { %>
            <span class="cart-product__label"><%= Dictionary.GetValue("eBusiness", "size_bags" ) %></span>
            <% } else if (product.Size == eBusiness.Sizes.Tote) { %>
            <span class="cart-product__label"><%= Dictionary.GetValue("eBusiness", "size_totes" ) %></span>
            <% } else if (product.Size == eBusiness.Sizes.SeedPak) { %>
            <span class="cart-product__label"><%= Dictionary.GetValue("eBusiness", "size_seedpaks" ) %></span>
            <% } %>
        </div>
        <div class="cart-product__acres">
            <span class="cart-product__value"><%= product.Acres %></span>
            <span class="cart-product__label"><%= Dictionary.GetValue("eBusiness", "acres") %></span>
        </div>
        <div class="cart-product__divider"></div>
        <div class="cart-product__total">
            <sup class="cart-product__currency">$</sup>
            <span class="cart-product__value"><%= product.Msrp.ToString("0,0.00") %></span>
            <span class="cart-product__label"><%= Dictionary.GetValue("eBusiness", "MSRP") %></span>
        </div>
    </div>
    <% if(ShowRemoveButton){ %>
    <div class="cart-product__remove">
        <button
            class="cart-product__cta-button js-cart-product__remove"
            type="button"
            data-bundle-id="<%= product.BundleId %>">
            <img src="/build/img/svg-sprite/icon-garbage.svg" alt="" />
            <span><%= Dictionary.GetValue("eBusiness", "remove") %></span>
        </button>
    </div>
    <% } %>
    <% if(ShowEditButton){ %>
    <div class="cart-product__edit">
        <a href='<%= ProductCustomizationBaseUrl + product.ProductId + "&c=" + product.BundleId %>' class="cart-product__cta-button">
            <img src="/build/img/icon-edit.svg" alt="" />
            <span><%= Dictionary.GetValue("eBusiness", "edit") %></span>
        </a>
    </div>
    <% } %>
</div>
<% } %>

I'm trying to pass a dynamic variable into a control:

<% foreach(var product in CartModel.Products) { %>
    <kc:Warning runat="server" id="Warning" ProductId="<%= product.ProductId %>" />
%>

but when I access ProductId in the control (in /ProductAvailabilityWarning.ascx.cs), it's being passed as literally "<%= product.ProductId %>", not as the value of product.ProductId.

I'd rather not rewrite all of my code as a Repeater, since there's a ton of dynamic code inside the foreach block that I'd have to modify.

EDIT: Since this code appears to be confusing, due to being a Frankenstein monstrosity of legacy code, here's a more complete snippet of the code, and why I'm trying to avoid rewriting the whole thing as a Repeater

<% foreach(var product in CartModel.Products) { %>
<kc:Warning runat="server" id="Warning" ProductId="<%= product.ProductId %>" />

<div class="cart-product js-cart-product">
    <div class="cart-product__product-details">
        <div class="cart-product__name"><%= product.ProductName %></div>
        <div class="cart-product__details">
            <div class="cart-product__detail-label"><%= product.MaturityLabel %></div>
            <div class="cart-product__detail-description"><%= product.RelativeMaturity %> <%= Dictionary.GetValue("eBusiness", "days" ) %></div>
            <ul class="cart-product__detail-icons">
                <% foreach(var maturityIcon in product.MaturityIcons) { %>
                <li>
                    <img
                        src="<%= maturityIcon.Src %>"
                        alt="" />
                    <span class="badge-label"><%= maturityIcon.Alt %></span>
                </li>
                <% } %>
            </ul>
        </div>
        <div class="cart-product__treatments-label">
            <%= Dictionary.GetValue("eBusiness", "seed treatments selected" ) %>
        </div>
        <ul class="cart-product__treament-grid">
                <% foreach(var seedTreatmentImage in product.SeedTreatmentImages) { %>
                <li class="cart-product__selected-treatment"> 
                    <img
                    src="<%= seedTreatmentImage.Src %>"
                    alt="<%= seedTreatmentImage.Alt %>" />
                </li>
                <% } %>
        </ul>
    </div>
    <div class="cart-product__order-totals">
        <% if (product != null && product.ProductBagImage != null) { %>
        <div class="cart-product__logo">
            <img width="325" height="500"
                src="<%= product.ProductBagImage.Src %>"
                alt="<%= product.ProductBagImage.Alt %>" />
        </div>
        <% } %>
        <div class="cart-product__quantity">
            <span class="cart-product__value"><%= product.Quantity %></span>
            <% if (product.Size == eBusiness.Sizes.Bag) { %>
            <span class="cart-product__label"><%= Dictionary.GetValue("eBusiness", "size_bags" ) %></span>
            <% } else if (product.Size == eBusiness.Sizes.Tote) { %>
            <span class="cart-product__label"><%= Dictionary.GetValue("eBusiness", "size_totes" ) %></span>
            <% } else if (product.Size == eBusiness.Sizes.SeedPak) { %>
            <span class="cart-product__label"><%= Dictionary.GetValue("eBusiness", "size_seedpaks" ) %></span>
            <% } %>
        </div>
        <div class="cart-product__acres">
            <span class="cart-product__value"><%= product.Acres %></span>
            <span class="cart-product__label"><%= Dictionary.GetValue("eBusiness", "acres") %></span>
        </div>
        <div class="cart-product__divider"></div>
        <div class="cart-product__total">
            <sup class="cart-product__currency">
lt;/sup>
            <span class="cart-product__value"><%= product.Msrp.ToString("0,0.00") %></span>
            <span class="cart-product__label"><%= Dictionary.GetValue("eBusiness", "MSRP") %></span>
        </div>
    </div>
    <% if(ShowRemoveButton){ %>
    <div class="cart-product__remove">
        <button
            class="cart-product__cta-button js-cart-product__remove"
            type="button"
            data-bundle-id="<%= product.BundleId %>">
            <img src="/build/img/svg-sprite/icon-garbage.svg" alt="" />
            <span><%= Dictionary.GetValue("eBusiness", "remove") %></span>
        </button>
    </div>
    <% } %>
    <% if(ShowEditButton){ %>
    <div class="cart-product__edit">
        <a href='<%= ProductCustomizationBaseUrl + product.ProductId + "&c=" + product.BundleId %>' class="cart-product__cta-button">
            <img src="/build/img/icon-edit.svg" alt="" />
            <span><%= Dictionary.GetValue("eBusiness", "edit") %></span>
        </a>
    </div>
    <% } %>
</div>
<% } %>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

硪扪都還晓 2025-01-19 19:46:17
  • 删除 控件语法,控件不会像那样工作
    • 在 WebForms 下,控件在 Init (IIRC) 中实例化,然后在 __VIEWDATA 的反序列化事件中设置其属性,并在数据绑定事件中再次设置(但仅适用于 <%# %> 样式语法)。
    • <%= %> 语法表示调用 HtmlTextWriter.Write(),它只是将内部字符串值写入输出流,而且它只是在页面的渲染函数期间调用:因此 <%= %> 语法不能用于设置控件的属性(仅 <%# %>可以用于此目的,但前提是您使用 WebForms 的(非常复杂的)数据绑定系统。


  • 如果您想加载 UserControl (又名 .ascx)并使用。它渲染 HTML,而不会影响 WebForm 的控件生命周期,并且
    古老而可怕的数据绑定系统好消息是:你可以!

    • 涉及到一些跑腿工作,但很容易理解,这就是这个答案的全部内容。

像这样:

  1. 使用 Page.LoadControl(String virtualPath) 获取代表用户控件的 Control 对象实例。
    • virtualPath 参数将是一个 const String,如 ~/MyControls/Warning.ascx 或类似的。
  2. 将结果转换为特定的 UserControl 子类(在 .ascx 的代码隐藏文件中定义的类型。
  3. 然后在循环体内设置属性,然后调用 RenderControl,从 .aspx 的渲染函数传递(隐藏的)__w 对象,

如下所示:

MyPage.aspx

<%
    MyWarningControl warningCtrl = (MyWarningControl)this.LoadControl("~/MyControls/Warning.ascx");
%>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. </p>

<%    foreach(var product in CartModel.Products) { %>
<%
    warningCtrl.ProductId = product.ProductId;
    warningCtrl.AnythingElse = product;

    warningCtrl.RenderControl( __w );
%>

<div class="cart-product js-cart-product">

<!-- etc --> 
</div>

<% } /*foreach product*/ %>

如果添加自定义,您可以稍微简化事情Render 函数到您的 UserControl 的代码隐藏类,如下所示:

此外,如果您喜欢冒险(并且在 .NET Framework 上运行),您还可以使用 ASP.NET Core 风格的构造函数依赖注入到您的 UserControl 中4.7.2 或更高版本)

public static class LoadingExtensions
{
    public static MyWarningControl LoadMyWarningControl( this Page p )
    {
        MyWarningControl warningCtrl = (MyWarningControl)p.LoadControl( "~/MyControls/Warning.ascx" );
        return warningCtrl;
    }
}

public partial class MyWarningControl : UserControl
{
    public void RenderProduct( HtmlTextWriter w, Product p )
    {
        if( w is null ) throw new ArgumentNullException( nameof(w) );

        try
        {
            this.Product = p ?? throw new ArgumentNullException( nameof(p) );

            this.RenderControl( w );
        }
        finally
        {
            this.Product = null;
        }
    }

    public Product Product { get; private set; }

    public Int32 ProductId => this.Product.ProductId;
}

然后您的 .aspx 逻辑简化为:

<%
    MyWarningControl warningCtrl = this.LoadMyWarningControl();
%>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. </p>

<%    foreach(var product in CartModel.Products) { %>
<%
   warningCtrl.RenderProduct( __w, product );
%>

<div class="cart-product js-cart-product">

<!-- etc --> 
</div>

<% } /*foreach product*/ %>

更简单!


如果您想知道 __w 来自哪里:它就是 HTML 被写入的 HtmlTextWriter 它作为参数传递到从 .aspx 文件生成的渲染函数中。

您可以通过将 aspnet_compiler.exe 生成的 .dll 文件加载到 ILSpy 中并在 中找到生成的 __Render_controlN 方法来亲自查看它Page 子类,例如:

在此处输入图像描述

因此每次出现 <%= x %><%: y %> 转换为 __w.Write(x);__w.Write(HttpUtility.HtmlEncode(y)); 分别。

...这也解释了为什么 :因为是一个具有自己的逻辑和渲染函数的对象,而不仅仅是通过HtmlTextWriter。

  • Remove the <kc:Warning/> control syntax, controls don't work like that.
    • Under WebForms, controls are instantiated in Init (IIRC), then have their properties set during __VIEWDATA's deserialization events and again in the data-binding events (but only for <%# %>-style syntax).
    • The <%= %> syntax denotes a call to HtmlTextWriter.Write() which simply writes the inner string value to the output stream, and it's only invoked during the page's render-function: so the <%= %> syntax cannot be used to set properties of controls (only <%# %> can be used for that, but only if you're using WebForms' (horribly complicated) data-binding system.
  • If you want to load a UserControl (aka .ascx) and just use it render HTML without faffing around with WebForm's control lifecycle and
    old-and-horrible data-binding system the good news is: you can!

    • There is a tiny bit of legwork involved, but it's straightforward to understand, and that's is what this answer is all about.

Like so:

  1. Use Page.LoadControl(String virtualPath) to get a Control object instance representing your user-control.
    • The virtualPath argument will be a const String like ~/MyControls/Warning.ascx or similar.
  2. Cast the result to your specific UserControl subclass (the type defined in your .ascx's code-behind file.
  3. Then inside the loop body set the properties then call RenderControl, passing the (hidden) __w object from your .aspx's render function.

Like so:

MyPage.aspx:

<%
    MyWarningControl warningCtrl = (MyWarningControl)this.LoadControl("~/MyControls/Warning.ascx");
%>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. </p>

<%    foreach(var product in CartModel.Products) { %>
<%
    warningCtrl.ProductId = product.ProductId;
    warningCtrl.AnythingElse = product;

    warningCtrl.RenderControl( __w );
%>

<div class="cart-product js-cart-product">

<!-- etc --> 
</div>

<% } /*foreach product*/ %>

You can simplify things somewhat if you add a custom Render function to your UserControl's code-behind class, like so:

Also, you can also use ASP.NET Core-style constructor-dependency injection into your UserControl's if you're feeling adventurous (and are running on .NET Framework 4.7.2 or later).

public static class LoadingExtensions
{
    public static MyWarningControl LoadMyWarningControl( this Page p )
    {
        MyWarningControl warningCtrl = (MyWarningControl)p.LoadControl( "~/MyControls/Warning.ascx" );
        return warningCtrl;
    }
}

public partial class MyWarningControl : UserControl
{
    public void RenderProduct( HtmlTextWriter w, Product p )
    {
        if( w is null ) throw new ArgumentNullException( nameof(w) );

        try
        {
            this.Product = p ?? throw new ArgumentNullException( nameof(p) );

            this.RenderControl( w );
        }
        finally
        {
            this.Product = null;
        }
    }

    public Product Product { get; private set; }

    public Int32 ProductId => this.Product.ProductId;
}

Then your .aspx logic simplifies down to just this:

<%
    MyWarningControl warningCtrl = this.LoadMyWarningControl();
%>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. </p>

<%    foreach(var product in CartModel.Products) { %>
<%
   warningCtrl.RenderProduct( __w, product );
%>

<div class="cart-product js-cart-product">

<!-- etc --> 
</div>

<% } /*foreach product*/ %>

Much simpler!


If you're wondering where __w comes from: it's the HtmlTextWriter where your HTML is being written to. It's passed as a parameter into the render function generated from your .aspx file.

You can see it yourself by loading the .dll file generated by aspnet_compiler.exe into ILSpy and locating the generated __Render_controlN method in your Page subclass, for example:

enter image description here

So every occurrence of <%= x %> and <%: y %> is converted to __w.Write(x); and __w.Write(HttpUtility.HtmlEncode(y)); respectively.

...which also explains why <asp:SomeControl runat="server" PropertyName="<%= x %>" /> isn't allowed: because <asp:SomeControl is an object with its own logic and render function, and not just plaintext that's passed through HtmlTextWriter.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文