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">$</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>
<% } %>
<kc:Warning/>control syntax, controls don't work like that.Init(IIRC), then have their properties set during__VIEWDATA's deserialization events and again in the data-binding events (but only for<%# %>-style syntax).<%= %>syntax denotes a call toHtmlTextWriter.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.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!Like so:
Page.LoadControl(String virtualPath)to get aControlobject instance representing your user-control.virtualPathargument will be aconst Stringlike~/MyControls/Warning.ascxor similar.UserControlsubclass (the type defined in your.ascx's code-behind file.RenderControl, passing the (hidden)__wobject from your.aspx's render function.Like so:
MyPage.aspx:
You can simplify things somewhat if you add a custom
Renderfunction to yourUserControl'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).Then your
.aspxlogic simplifies down to just this:Much simpler!
If you're wondering where
__wcomes from: it's theHtmlTextWriterwhere your HTML is being written to. It's passed as a parameter into the render function generated from your.aspxfile.You can see it yourself by loading the
.dllfile generated byaspnet_compiler.exeinto ILSpy and locating the generated__Render_controlNmethod in yourPagesubclass, for example: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:SomeControlis an object with its own logic and render function, and not just plaintext that's passed throughHtmlTextWriter.