|
| 1 | +# NamingContainer |
| 2 | + |
| 3 | +The `NamingContainer` component establishes a naming scope for child components, equivalent to [INamingContainer](https://learn.microsoft.com/dotnet/api/system.web.ui.inamingcontainer) in ASP.NET Web Forms. Child controls get IDs prefixed with this container's ID, separated by underscores — exactly as they did in Web Forms. |
| 4 | + |
| 5 | +`NamingContainer` renders **no HTML of its own**. It is a purely structural component that exists solely to define the naming hierarchy for ID generation. |
| 6 | + |
| 7 | +## Why It Exists |
| 8 | + |
| 9 | +In Web Forms, any control implementing `INamingContainer` created a scope so that child controls received fully-qualified `ClientID` values. This ensured unique IDs across the page and enabled JavaScript and CSS selectors that relied on predictable ID patterns like `MainContent_txtUsername`. |
| 10 | + |
| 11 | +Blazor has no built-in equivalent. `NamingContainer` fills that gap, letting your migrated JavaScript and CSS continue to target elements by their original Web Forms IDs. |
| 12 | + |
| 13 | +## Features Supported in Blazor |
| 14 | + |
| 15 | +- **Naming scope** — Prefixes child component IDs with the container's own ID using underscore separators |
| 16 | +- **UseCtl00Prefix** — Optionally prepends `ctl00` to the naming hierarchy for full Web Forms ID compatibility |
| 17 | +- **Nesting** — Multiple `NamingContainer` components can be nested; IDs accumulate through the hierarchy |
| 18 | +- **Visible** — Controls whether child content renders (inherited from `BaseWebFormsComponent`) |
| 19 | + |
| 20 | +### Features NOT Supported |
| 21 | + |
| 22 | +- **ClientIDMode** — Web Forms offered `AutoID`, `Static`, `Predictable`, and `Inherit` modes. This library uses a simplified model equivalent to `AutoID`. |
| 23 | +- **UniqueID** — The postback-oriented `UniqueID` (colon-separated) is not replicated; only `ClientID` (underscore-separated) is supported. |
| 24 | + |
| 25 | +## Web Forms Syntax |
| 26 | + |
| 27 | +```asp |
| 28 | +<%-- INamingContainer was typically an interface on custom controls or built-in containers --%> |
| 29 | +<asp:Panel ID="MainContent" runat="server"> |
| 30 | + <asp:TextBox ID="txtSearch" runat="server" /> |
| 31 | + <asp:Button ID="btnGo" runat="server" Text="Search" /> |
| 32 | +</asp:Panel> |
| 33 | +``` |
| 34 | + |
| 35 | +Rendered IDs: `MainContent_txtSearch`, `MainContent_btnGo` |
| 36 | + |
| 37 | +With `Page` as the root naming container and `ClientIDMode="AutoID"`: |
| 38 | + |
| 39 | +```html |
| 40 | +<input id="ctl00_MainContent_txtSearch" type="text" /> |
| 41 | +<input id="ctl00_MainContent_btnGo" type="submit" value="Search" /> |
| 42 | +``` |
| 43 | + |
| 44 | +## Blazor Syntax |
| 45 | + |
| 46 | +```razor |
| 47 | +<NamingContainer ID="MainContent"> |
| 48 | + <TextBox ID="txtSearch" /> |
| 49 | + <Button ID="btnGo" Text="Search" /> |
| 50 | +</NamingContainer> |
| 51 | +``` |
| 52 | + |
| 53 | +With `UseCtl00Prefix`: |
| 54 | + |
| 55 | +```razor |
| 56 | +<NamingContainer ID="MainContent" UseCtl00Prefix="true"> |
| 57 | + <TextBox ID="txtSearch" /> |
| 58 | + <Button ID="btnGo" Text="Search" /> |
| 59 | +</NamingContainer> |
| 60 | +``` |
| 61 | + |
| 62 | +## Parameters |
| 63 | + |
| 64 | +| Parameter | Type | Default | Description | |
| 65 | +|---|---|---|---| |
| 66 | +| `ID` | `string` | `null` | Sets the naming scope prefix for child component IDs | |
| 67 | +| `UseCtl00Prefix` | `bool` | `false` | When true, prepends `ctl00` to the naming hierarchy | |
| 68 | +| `Visible` | `bool` | `true` | Controls whether child content renders | |
| 69 | +| `ChildContent` | `RenderFragment` | — | The child components to wrap | |
| 70 | + |
| 71 | +## Examples |
| 72 | + |
| 73 | +### Basic Usage |
| 74 | + |
| 75 | +Wrap a group of controls to establish a naming scope: |
| 76 | + |
| 77 | +```razor |
| 78 | +<NamingContainer ID="SearchPanel"> |
| 79 | + <Label ID="lblQuery" Text="Search:" /> |
| 80 | + <TextBox ID="txtQuery" /> |
| 81 | + <Button ID="btnSearch" Text="Go" /> |
| 82 | +</NamingContainer> |
| 83 | +``` |
| 84 | + |
| 85 | +Rendered IDs: |
| 86 | + |
| 87 | +| Component | Rendered `id` attribute | |
| 88 | +|---|---| |
| 89 | +| Label | `SearchPanel_lblQuery` | |
| 90 | +| TextBox | `SearchPanel_txtQuery` | |
| 91 | +| Button | `SearchPanel_btnSearch` | |
| 92 | + |
| 93 | +Your existing JavaScript continues to work: |
| 94 | + |
| 95 | +```javascript |
| 96 | +var query = document.getElementById('SearchPanel_txtQuery').value; |
| 97 | +``` |
| 98 | + |
| 99 | +### Nested Containers |
| 100 | + |
| 101 | +`NamingContainer` components can be nested. Each level adds its ID as a prefix: |
| 102 | + |
| 103 | +```razor |
| 104 | +<NamingContainer ID="Page"> |
| 105 | + <NamingContainer ID="Sidebar"> |
| 106 | + <TextBox ID="txtFilter" /> |
| 107 | + </NamingContainer> |
| 108 | + <NamingContainer ID="Content"> |
| 109 | + <Button ID="btnSave" Text="Save" /> |
| 110 | + </NamingContainer> |
| 111 | +</NamingContainer> |
| 112 | +``` |
| 113 | + |
| 114 | +Rendered IDs: |
| 115 | + |
| 116 | +| Component | Rendered `id` attribute | |
| 117 | +|---|---| |
| 118 | +| TextBox | `Page_Sidebar_txtFilter` | |
| 119 | +| Button | `Page_Content_btnSave` | |
| 120 | + |
| 121 | +### UseCtl00Prefix for Full Web Forms Compatibility |
| 122 | + |
| 123 | +In Web Forms, the page-level naming container prepended `ctl00` to all client IDs. Enable this behavior with `UseCtl00Prefix`: |
| 124 | + |
| 125 | +```razor |
| 126 | +<NamingContainer ID="MainContent" UseCtl00Prefix="true"> |
| 127 | + <TextBox ID="txtName" /> |
| 128 | + <Button ID="btnSubmit" Text="Submit" /> |
| 129 | +</NamingContainer> |
| 130 | +``` |
| 131 | + |
| 132 | +Rendered IDs: |
| 133 | + |
| 134 | +| Component | Rendered `id` attribute | |
| 135 | +|---|---| |
| 136 | +| TextBox | `ctl00_MainContent_txtName` | |
| 137 | +| Button | `ctl00_MainContent_btnSubmit` | |
| 138 | + |
| 139 | +This is essential when your JavaScript or CSS targets the full `ctl00_`-prefixed IDs that Web Forms generated. |
| 140 | + |
| 141 | +### Migration Example |
| 142 | + |
| 143 | +**Before (Web Forms):** |
| 144 | +```asp |
| 145 | +<form id="form1" runat="server"> |
| 146 | + <asp:Panel ID="MainContent" runat="server"> |
| 147 | + <asp:TextBox ID="txtEmail" runat="server" /> |
| 148 | + <asp:Button ID="btnRegister" runat="server" Text="Register" /> |
| 149 | + </asp:Panel> |
| 150 | +</form> |
| 151 | +
|
| 152 | +<script> |
| 153 | + // JavaScript targeting Web Forms IDs |
| 154 | + var email = document.getElementById('ctl00_MainContent_txtEmail'); |
| 155 | + var btn = document.getElementById('ctl00_MainContent_btnRegister'); |
| 156 | +</script> |
| 157 | +``` |
| 158 | + |
| 159 | +**After (Blazor):** |
| 160 | +```razor |
| 161 | +<NamingContainer ID="MainContent" UseCtl00Prefix="true"> |
| 162 | + <TextBox ID="txtEmail" /> |
| 163 | + <Button ID="btnRegister" Text="Register" /> |
| 164 | +</NamingContainer> |
| 165 | +
|
| 166 | +<script> |
| 167 | + // JavaScript unchanged — same IDs as before |
| 168 | + var email = document.getElementById('ctl00_MainContent_txtEmail'); |
| 169 | + var btn = document.getElementById('ctl00_MainContent_btnRegister'); |
| 170 | +</script> |
| 171 | +``` |
| 172 | + |
| 173 | +## Relationship to WebFormsPage |
| 174 | + |
| 175 | +[WebFormsPage](WebFormsPage.md) inherits from `NamingContainer` and adds theme/skin cascading. Use the table below to choose the right component: |
| 176 | + |
| 177 | +| Component | Naming Scope | Theming | When to Use | |
| 178 | +|---|---|---|---| |
| 179 | +| `NamingContainer` | ✅ | ❌ | Nested naming scopes within a page, or when theming is not needed | |
| 180 | +| `WebFormsPage` | ✅ | ✅ | Layout-level wrapper providing both naming scope and theme support | |
| 181 | + |
| 182 | +If you only need ID prefixing — for example, to scope a subsection of a page — use `NamingContainer`. If you need the full Web Forms `Page`-level experience (naming + themes), use `WebFormsPage` in your layout. |
| 183 | + |
| 184 | +## Moving On |
| 185 | + |
| 186 | +As you complete your migration away from Web Forms patterns: |
| 187 | + |
| 188 | +1. **Replace string-based IDs with `@ref`** — Blazor's component references are type-safe and don't depend on naming conventions |
| 189 | +2. **Remove `ctl00` prefixes** — Update JavaScript to use simpler IDs, then set `UseCtl00Prefix="false"` or remove `NamingContainer` entirely |
| 190 | +3. **Use CSS classes instead of ID selectors** — CSS class selectors are more resilient and don't depend on component hierarchy |
| 191 | + |
| 192 | +## See Also |
| 193 | + |
| 194 | +- [ID Rendering](IDRendering.md) — How component IDs work across the library |
| 195 | +- [WebFormsPage](WebFormsPage.md) — Combined naming container and theme wrapper |
| 196 | +- [ViewState](ViewState.md) — How ViewState is emulated in Blazor |
0 commit comments