Skip to content

Commit 5844fc6

Browse files
committed
feat: add DetailsView and PasswordRecovery components (Sprint 3)
DetailsView: Single-record data display with auto-generated rows, edit/insert modes, paging, and full CRUD events. Inherits DataBoundComponent<T>. PasswordRecovery: 3-step password reset wizard (UserName Question Success) following ChangePassword/CreateUserWizard table-based patterns. Also includes: - 71 new bUnit tests (42 DetailsView + 29 PasswordRecovery), 797 total passing - Documentation for both components - Deferred controls migration guide (Chart, Substitution, Xml) - Sample pages for both components - Integration tests (smoke + interaction) for both sample pages - NavMenu and ComponentList updated Components: 50/53 complete (94%)
1 parent e5399ae commit 5844fc6

28 files changed

Lines changed: 3953 additions & 4 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ There are a significant number of controls in ASP.NET Web Forms, and we will foc
5454
- [DataGrid](docs/DataControls/DataGrid.md)
5555
- [DataList](docs/DataControls/DataList.md)
5656
- [DataPager](docs/DataControls/DataPager.md)
57-
- DetailsView
57+
- [DetailsView](docs/DataControls/DetailsView.md)
5858
- [FormView](docs/DataControls/FormView.md)
5959
- [GridView](docs/DataControls/GridView.md)
6060
- [ListView](docs/DataControls/ListView.md)
@@ -78,7 +78,7 @@ There are a significant number of controls in ASP.NET Web Forms, and we will foc
7878
- [LoginName](docs/LoginControls/LoginName.md)
7979
- [LoginStatus](docs/LoginControls/LoginStatus.md)
8080
- [LoginView](docs/LoginControls/LoginView.md)
81-
- PasswordRecovery
81+
- [PasswordRecovery](docs/LoginControls/PasswordRecovery.md)
8282

8383
We will NOT be converting any DataSource objects (SqlDataSource, ObjectDataSource, EntityDataSource, LinqDataSource, XmlDataSource, SiteMapDataSource, AccessDataSource), Wizard components, skins or themes. Once this first collection of controls is written, we can consider additional features like modern tag formatting.
8484

docs/DataControls/DetailsView.md

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
# DetailsView
2+
3+
The **DetailsView** component emulates the ASP.NET Web Forms `asp:DetailsView` control. It displays a single record from a data source in a vertical table layout, with one row per field. It supports read-only, edit, and insert modes, paging between records, and auto-generated or explicitly defined fields.
4+
5+
Original Microsoft documentation: https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.webcontrols.detailsview?view=netframework-4.8
6+
7+
## Blazor Features Supported
8+
9+
- Single-record display in a two-column table (label + value per row)
10+
- `AutoGenerateRows` — automatically generates rows from the data item's public properties
11+
- Three display modes via `DetailsViewMode` enum: `ReadOnly`, `Edit`, `Insert`
12+
- `DefaultMode` — sets the initial mode
13+
- `ModeChanging` / `ModeChanged` events
14+
- `AutoGenerateEditButton`, `AutoGenerateDeleteButton`, `AutoGenerateInsertButton` — command row buttons
15+
- CRUD events: `ItemCommand`, `ItemDeleting` / `ItemDeleted`, `ItemInserting` / `ItemInserted`, `ItemUpdating` / `ItemUpdated`
16+
- Cancellable pre-operation events (`ItemDeleting`, `ItemInserting`, `ItemUpdating`)
17+
- `AllowPaging` with numeric pager — navigate between items in the data source
18+
- `PageIndex` / `PageIndexChanging` / `PageIndexChanged` events
19+
- `DataKeyNames` — primary key field identification
20+
- `HeaderText` / `HeaderTemplate`, `FooterText` / `FooterTemplate`
21+
- `EmptyDataText` / `EmptyDataTemplate` — displayed when data source is empty
22+
- `PagerTemplate` — custom pager UI
23+
- `Fields` — explicit field definitions (BoundField, TemplateField)
24+
- `GridLines`, `CellPadding`, `CellSpacing` — table layout control
25+
- `CssClass` — CSS class on the outer table
26+
- `Visible` — show/hide the entire control
27+
- Generic `ItemType` for strongly typed data binding
28+
29+
### Blazor Notes
30+
31+
- The component is generic: `DetailsView<ItemType>`. You must specify `ItemType` when using it.
32+
- Field definitions are added as child content inside a `<Fields>` block. They register themselves with the parent via a `CascadingValue`.
33+
- When `AutoGenerateRows="true"` (the default) and no explicit fields are defined, the component reflects over `ItemType` to generate rows for each public readable property.
34+
- The pager displays one page number per item in the data source (each "page" is one record).
35+
36+
## Web Forms Features NOT Supported
37+
38+
- **DataSourceID** — Blazor does not use server-side data source controls; bind data directly via `Items`
39+
- **Sorting**`AllowSorting` is not implemented
40+
- **PagerSettings** — Only numeric paging or custom `PagerTemplate`; `PagerSettings` sub-properties (NextPrevious, FirstLast modes) are not supported
41+
- **Row styles** (HeaderStyle, RowStyle, AlternatingRowStyle, EditRowStyle, etc.) — Use CSS classes instead
42+
- **CommandField / ButtonField** columns — Use `AutoGenerateEditButton` / `AutoGenerateDeleteButton` / `AutoGenerateInsertButton` or custom templates
43+
- **ViewState** — Not needed; Blazor preserves component state natively
44+
- **Theming / SkinID** — Not applicable to Blazor
45+
46+
## Web Forms Declarative Syntax
47+
48+
```html
49+
<asp:DetailsView
50+
AllowPaging="True|False"
51+
AutoGenerateDeleteButton="True|False"
52+
AutoGenerateEditButton="True|False"
53+
AutoGenerateInsertButton="True|False"
54+
AutoGenerateRows="True|False"
55+
CellPadding="integer"
56+
CellSpacing="integer"
57+
CssClass="string"
58+
DataKeyNames="string"
59+
DataSourceID="string"
60+
DefaultMode="ReadOnly|Edit|Insert"
61+
EmptyDataText="string"
62+
GridLines="None|Horizontal|Vertical|Both"
63+
HeaderText="string"
64+
FooterText="string"
65+
ID="string"
66+
OnItemCommand="ItemCommand event handler"
67+
OnItemDeleted="ItemDeleted event handler"
68+
OnItemDeleting="ItemDeleting event handler"
69+
OnItemInserted="ItemInserted event handler"
70+
OnItemInserting="ItemInserting event handler"
71+
OnItemUpdated="ItemUpdated event handler"
72+
OnItemUpdating="ItemUpdating event handler"
73+
OnModeChanged="ModeChanged event handler"
74+
OnModeChanging="ModeChanging event handler"
75+
OnPageIndexChanged="PageIndexChanged event handler"
76+
OnPageIndexChanging="PageIndexChanging event handler"
77+
PageIndex="integer"
78+
Visible="True|False"
79+
runat="server"
80+
>
81+
<Fields>
82+
<asp:BoundField DataField="string" HeaderText="string" ReadOnly="True|False" />
83+
<asp:TemplateField HeaderText="string">
84+
<ItemTemplate><!-- child controls --></ItemTemplate>
85+
<EditItemTemplate><!-- child controls --></EditItemTemplate>
86+
</asp:TemplateField>
87+
</Fields>
88+
<HeaderTemplate><!-- child controls --></HeaderTemplate>
89+
<FooterTemplate><!-- child controls --></FooterTemplate>
90+
<EmptyDataTemplate><!-- child controls --></EmptyDataTemplate>
91+
<PagerTemplate><!-- child controls --></PagerTemplate>
92+
</asp:DetailsView>
93+
```
94+
95+
## Blazor Syntax
96+
97+
```razor
98+
<DetailsView ItemType="Product"
99+
Items="@Products"
100+
AutoGenerateRows="true"
101+
AllowPaging="true"
102+
AutoGenerateEditButton="true"
103+
AutoGenerateDeleteButton="true"
104+
DefaultMode="DetailsViewMode.ReadOnly"
105+
HeaderText="Product Details"
106+
EmptyDataText="No products found."
107+
CssClass="details-grid"
108+
GridLines="GridLines.Both"
109+
ItemDeleting="HandleDeleting"
110+
ItemUpdating="HandleUpdating"
111+
ModeChanging="HandleModeChanging"
112+
PageIndexChanging="HandlePageChanging" />
113+
114+
@code {
115+
private List<Product> Products = new();
116+
117+
private void HandleDeleting(DetailsViewDeleteEventArgs e)
118+
{
119+
// e.RowIndex gives you the current page index
120+
// Perform delete logic; set e.Cancel = true to abort
121+
}
122+
123+
private void HandleUpdating(DetailsViewUpdateEventArgs e)
124+
{
125+
// Perform update logic; set e.Cancel = true to abort
126+
}
127+
128+
private void HandleModeChanging(DetailsViewModeEventArgs e)
129+
{
130+
// e.NewMode tells you the target mode
131+
// e.CancelingEdit tells you if this is a cancel operation
132+
}
133+
134+
private void HandlePageChanging(PageChangedEventArgs e)
135+
{
136+
// e.NewPageIndex gives you the target page
137+
}
138+
}
139+
```
140+
141+
### With Explicit Fields
142+
143+
```razor
144+
<DetailsView ItemType="Product"
145+
Items="@Products"
146+
AutoGenerateRows="false"
147+
AllowPaging="true">
148+
<Fields>
149+
<BoundField DataField="Name" HeaderText="Product Name" />
150+
<BoundField DataField="Price" HeaderText="Unit Price" />
151+
<TemplateField HeaderText="Actions">
152+
<ItemTemplate>
153+
<a href="/products/@context.Id">View</a>
154+
</ItemTemplate>
155+
</TemplateField>
156+
</Fields>
157+
</DetailsView>
158+
```
159+
160+
### With Custom Templates
161+
162+
```razor
163+
<DetailsView ItemType="Product"
164+
Items="@Products"
165+
AutoGenerateRows="true">
166+
<HeaderTemplate>
167+
<strong>Product Information</strong>
168+
</HeaderTemplate>
169+
<EmptyDataTemplate>
170+
<p>No products available. <a href="/products/new">Add one</a>.</p>
171+
</EmptyDataTemplate>
172+
<FooterTemplate>
173+
<em>Last updated: @DateTime.Now.ToShortDateString()</em>
174+
</FooterTemplate>
175+
</DetailsView>
176+
```
177+
178+
## HTML Output
179+
180+
The component renders a table with one row per field, matching the Web Forms DetailsView output:
181+
182+
```html
183+
<table cellspacing="0" rules="all" border="1"
184+
style="border-collapse:collapse" class="details-grid">
185+
<!-- Header Row -->
186+
<tr>
187+
<td colspan="2">Product Details</td>
188+
</tr>
189+
<!-- Field Rows (one per property or defined field) -->
190+
<tr>
191+
<td>Id</td>
192+
<td>1</td>
193+
</tr>
194+
<tr>
195+
<td>Name</td>
196+
<td>Widget</td>
197+
</tr>
198+
<tr>
199+
<td>Price</td>
200+
<td>9.99</td>
201+
</tr>
202+
<!-- Command Row (when edit/delete/insert buttons enabled) -->
203+
<tr>
204+
<td colspan="2">
205+
<a href="javascript:void(0);">Edit</a>&nbsp;
206+
<a href="javascript:void(0);">Delete</a>
207+
</td>
208+
</tr>
209+
<!-- Pager Row (when AllowPaging and multiple items) -->
210+
<tr>
211+
<td colspan="2">
212+
<table>
213+
<tr>
214+
<td><span>1</span></td>
215+
<td><a href="javascript:void(0);">2</a></td>
216+
<td><a href="javascript:void(0);">3</a></td>
217+
</tr>
218+
</table>
219+
</td>
220+
</tr>
221+
<!-- Footer Row -->
222+
<tr>
223+
<td colspan="2">Footer text here</td>
224+
</tr>
225+
</table>
226+
```
227+
228+
In **Edit** or **Insert** mode, the command row changes to show Update/Cancel or Insert/Cancel links:
229+
230+
```html
231+
<!-- Edit mode command row -->
232+
<tr>
233+
<td colspan="2">
234+
<a href="javascript:void(0);">Update</a>&nbsp;
235+
<a href="javascript:void(0);">Cancel</a>
236+
</td>
237+
</tr>
238+
```
239+
240+
## Migration Notes
241+
242+
1. **Remove `asp:` prefix** — Change `<asp:DetailsView>` to `<DetailsView>`
243+
2. **Remove `runat="server"`** — Not needed in Blazor
244+
3. **Add `ItemType`** — The Blazor component is generic; specify `ItemType="YourClass"`
245+
4. **Replace `DataSourceID`** — Use `Items="@yourCollection"` instead of binding to a server-side DataSource control
246+
5. **Field declarations** — Remove `asp:` prefix from `<asp:BoundField>` and `<asp:TemplateField>`; place them inside `<Fields>` block
247+
6. **Event signatures** — Events use typed `EventArgs` classes (`DetailsViewDeleteEventArgs`, `DetailsViewUpdateEventArgs`, etc.)
248+
7. **Styles** — Replace `<HeaderStyle>`, `<RowStyle>`, etc. with CSS classes via `CssClass`
249+
250+
### Before (Web Forms)
251+
252+
```html
253+
<asp:DetailsView ID="dvProduct"
254+
DataSourceID="SqlDataSource1"
255+
AutoGenerateRows="True"
256+
AllowPaging="True"
257+
AutoGenerateEditButton="True"
258+
OnItemUpdating="dvProduct_ItemUpdating"
259+
runat="server">
260+
<HeaderStyle BackColor="#336699" ForeColor="White" />
261+
</asp:DetailsView>
262+
```
263+
264+
### After (Blazor)
265+
266+
```razor
267+
<DetailsView ItemType="Product"
268+
Items="@Products"
269+
AutoGenerateRows="true"
270+
AllowPaging="true"
271+
AutoGenerateEditButton="true"
272+
CssClass="product-details"
273+
ItemUpdating="HandleUpdating" />
274+
275+
@code {
276+
private List<Product> Products = new();
277+
278+
protected override async Task OnInitializedAsync()
279+
{
280+
Products = await ProductService.GetAllAsync();
281+
}
282+
283+
private void HandleUpdating(DetailsViewUpdateEventArgs e)
284+
{
285+
// Perform update
286+
}
287+
}
288+
```
289+
290+
## See Also
291+
292+
- [FormView](FormView.md) — Similar single-record view with full template control
293+
- [GridView](GridView.md) — Multi-record tabular display with similar field types
294+
- [DataList](DataList.md) — Repeating data display

0 commit comments

Comments
 (0)