From b08d6964c503086ee68ac7932f782716a0f90276 Mon Sep 17 00:00:00 2001 From: "Jeffrey T. Fritz" Date: Thu, 29 Jan 2026 15:10:22 -0500 Subject: [PATCH 01/35] Add documentation for HyperLink and Menu components; update navigation structure --- README.md | 4 +- .../HyperLink.md | 0 docs/{ => NavigationControls}/Menu.md | 51 +++++++++++++++++++ mkdocs.yml | 3 +- .../Menu/DynamicMenuStyleSample.razor | 3 +- .../wwwroot/js/Basepage.module.js | 13 ++++- 6 files changed, 69 insertions(+), 5 deletions(-) rename docs/{EditorControls => NavigationControls}/HyperLink.md (100%) rename docs/{ => NavigationControls}/Menu.md (87%) diff --git a/README.md b/README.md index 3e26a5e2e..617de49a9 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ There are a significant number of controls in ASP.NET Web Forms, and we will foc - [DropDownList](docs/EditorControls/DropDownList.md) - FileUpload - [HiddenField](docs/EditorControls/HiddenField.md) - - [HyperLink](docs/EditorControls/HyperLink.md) - [Image](docs/EditorControls/Image.md) - [ImageButton](docs/EditorControls/ImageButton.md) - ImageMap @@ -69,7 +68,8 @@ There are a significant number of controls in ASP.NET Web Forms, and we will foc - [RequiredFieldValidator](docs/ValidationControls/RequiredFieldValidator.md) - [ValidationSummary](docs/ValidationControls/ValidationSummary.md) - Navigation Controls - - [Menu](docs/Menu.md) + - [HyperLink](docs/NavigationControls/HyperLink.md) + - [Menu](docs/NavigationControls/Menu.md) - SiteMapPath - [TreeView](docs/NavigationControls/TreeView.md) - Login Controls diff --git a/docs/EditorControls/HyperLink.md b/docs/NavigationControls/HyperLink.md similarity index 100% rename from docs/EditorControls/HyperLink.md rename to docs/NavigationControls/HyperLink.md diff --git a/docs/Menu.md b/docs/NavigationControls/Menu.md similarity index 87% rename from docs/Menu.md rename to docs/NavigationControls/Menu.md index d88ad0ddc..65759410c 100644 --- a/docs/Menu.md +++ b/docs/NavigationControls/Menu.md @@ -341,5 +341,56 @@ The Menu component is meant to emulate the asp:Menu control in markup and is def ## Blazor Syntax +```html + + + + + + + + + + + + + + + + + + + + + + + + +``` + ##### [Back to top](#menu) diff --git a/mkdocs.yml b/mkdocs.yml index b34b898fe..9787c9f59 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -69,7 +69,6 @@ nav: - CheckBoxList: EditorControls/CheckBoxList.md - DropDownList: EditorControls/DropDownList.md - HiddenField: EditorControls/HiddenField.md - - HyperLink: EditorControls/HyperLink.md - Image: EditorControls/Image.md - ImageButton: EditorControls/ImageButton.md - Label: EditorControls/Label.md @@ -95,6 +94,8 @@ nav: - RequiredFieldValidator: ValidationControls/RequiredFieldValidator.md - ValidationSummary: ValidationControls/ValidationSummary.md - Navigation Controls: + - HyperLink: NavigationControls/HyperLink.md + - Menu: NavigationControls/Menu.md - TreeView: NavigationControls/TreeView.md - Login Controls: - Login: LoginControls/Login.md diff --git a/samples/AfterBlazorServerSide/Pages/ControlSamples/Menu/DynamicMenuStyleSample.razor b/samples/AfterBlazorServerSide/Pages/ControlSamples/Menu/DynamicMenuStyleSample.razor index fb4f619a7..53086ccb6 100644 --- a/samples/AfterBlazorServerSide/Pages/ControlSamples/Menu/DynamicMenuStyleSample.razor +++ b/samples/AfterBlazorServerSide/Pages/ControlSamples/Menu/DynamicMenuStyleSample.razor @@ -1,4 +1,5 @@ -@using static BlazorWebFormsComponents.WebColor +@page "/ControlSamples/Menu/DynamicMenuStyleSample" +@using static BlazorWebFormsComponents.WebColor @using static BlazorWebFormsComponents.Enums.BorderStyle

DynamicMenuStyle Sample

diff --git a/src/BlazorWebFormsComponents/wwwroot/js/Basepage.module.js b/src/BlazorWebFormsComponents/wwwroot/js/Basepage.module.js index cc63f68a6..f7eefa0d5 100644 --- a/src/BlazorWebFormsComponents/wwwroot/js/Basepage.module.js +++ b/src/BlazorWebFormsComponents/wwwroot/js/Basepage.module.js @@ -14,6 +14,16 @@ export function onAfterRender() { formatClientClick(); } +export function addScriptElement(location, callback) { + var el = document.createElement("script"); + el.setAttribute("src", location); + document.head.appendChild(el); + + if (callback != null) { + el.addEventListener("load", function () { eval(callback); }); + } +} + function formatClientClick() { var elementsToReplace = document.querySelectorAll("*[onclientclick]"); for (var el of elementsToReplace) { @@ -31,6 +41,7 @@ if (typeof window !== 'undefined') { window.bwfc.Page = { setTitle, getTitle, - OnAfterRender: onAfterRender + OnAfterRender: onAfterRender, + AddScriptElement: addScriptElement }; } From 287fbcdb9426959432de802dd89fa1374d897d94 Mon Sep 17 00:00:00 2001 From: "Jeffrey T. Fritz" Date: Thu, 29 Jan 2026 15:20:28 -0500 Subject: [PATCH 02/35] Enhance documentation for Button, HiddenField, and Image components; add usage examples and clarify features Fixes #228 Fixes #219 Fixes #217 --- docs/EditorControls/Button.md | 237 +++++++++++++++++- docs/EditorControls/HiddenField.md | 94 ++++++- docs/EditorControls/Image.md | 190 +++++++++++++- .../Components/Layout/NavMenu.razor | 2 + .../Components/Pages/ComponentList.razor | 2 + .../ControlSamples/HiddenField/Index.razor | 86 +++++++ .../Pages/ControlSamples/Image/Index.razor | 112 +++++++++ 7 files changed, 700 insertions(+), 23 deletions(-) create mode 100644 samples/AfterBlazorServerSide/Components/Pages/ControlSamples/HiddenField/Index.razor create mode 100644 samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Image/Index.razor diff --git a/docs/EditorControls/Button.md b/docs/EditorControls/Button.md index acf38d834..9fa39b22c 100644 --- a/docs/EditorControls/Button.md +++ b/docs/EditorControls/Button.md @@ -1,19 +1,37 @@ -It may seem strange that we have a Button component when there already is an HTML button and Blazor has features that enable C# interactions with that button, but we need to activate other features that were once present in Web Forms. Original Web Forms documentation is at: https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.webcontrols.button?view=netframework-4.8 +# Button -## Blazor Features Supported +The **Button** component displays a push button control that allows users to trigger actions. It may seem strange that we have a Button component when there already is an HTML button and Blazor has features that enable C# interactions with that button, but we need to activate other features that were once present in Web Forms, such as `OnCommand` event bubbling, `OnClientClick` JavaScript execution, and `CausesValidation` support. -- OnClick event handler -- OnClientClick JavaScript pointer -- OnCommand event handler with event bubbling -- Button Style attributes and CssClass formatting -- CausesValidation will control whether Form validation is triggered on click +Original Microsoft documentation: https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.webcontrols.button?view=netframework-4.8 -## WebForms Features Not Supported +## Features Supported in Blazor -- PostBackUrl is not supported as you will be triggering Button click events on the same page -- UseSubmitBehavior is not supported as Blazor buttons trigger click events and you can inspect the Form regardless +- `Text` - the text displayed on the button +- `OnClick` - event handler triggered when button is clicked +- `OnClientClick` - JavaScript code to execute on client-side click +- `OnCommand` - event handler with event bubbling, receives `CommandEventArgs` with `CommandName` and `CommandArgument` +- `CommandName` - the command name passed to `OnCommand` event +- `CommandArgument` - the command argument passed to `OnCommand` event +- `CausesValidation` - controls whether form validation is triggered on click (default: `true`) +- `Enabled` - enables or disables the button +- `Visible` - controls button visibility +- `ToolTip` - tooltip text displayed on hover +- All style properties (`BackColor`, `ForeColor`, `BorderColor`, `BorderStyle`, `BorderWidth`, `CssClass`, `Width`, `Height`, `Font`) -## WebForms Syntax +### Blazor Notes + +- The `OnCommand` event uses a `CommandEventArgs` class that contains `CommandName` and `CommandArgument` properties +- When `CommandName` is set, clicking the button triggers `OnCommand` instead of `OnClick` +- Event bubbling is supported for container components that need to handle commands from child buttons + +## Web Forms Features NOT Supported + +- **PostBackUrl** - Not supported; Blazor uses component events instead of postbacks to different pages +- **UseSubmitBehavior** - Not supported; Blazor buttons trigger click events and you can inspect the form regardless +- **ValidationGroup** - Not yet implemented; use EditForm validation instead +- **AccessKey** - Use HTML `accesskey` attribute directly if needed + +## Web Forms Declarative Syntax ```html ``` + +## Blazor Razor Syntax + +### Basic Button with Click Event + +```razor + +``` + +**Styled Button Input:** +```razor + +``` + +## Migration Notes + +When migrating from Web Forms to Blazor: + +1. **Remove `asp:` prefix** - Change `` to ` + +@code { + private bool showBanner = true; + + void ToggleBanner() + { + showBanner = !showBanner; + } +} +``` + +### Text Wrapping with Aligned Image + +```razor +@using BlazorWebFormsComponents.Enums + + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit. + This text wraps around the left-aligned image. The ImageAlign + property allows you to control how content flows around the image.

+ +
+``` + +## See Also + +- [ImageButton](ImageButton.md) - A clickable image that acts as a button +- [HyperLink](../NavigationControls/HyperLink.md) - Wrap an Image in a HyperLink for clickable images +- [Live Demo](https://blazorwebformscomponents.azurewebsites.net/ControlSamples/Image) - Interactive Image samples diff --git a/samples/AfterBlazorServerSide/Components/Layout/NavMenu.razor b/samples/AfterBlazorServerSide/Components/Layout/NavMenu.razor index 7dbd1ab3e..02baa5f73 100644 --- a/samples/AfterBlazorServerSide/Components/Layout/NavMenu.razor +++ b/samples/AfterBlazorServerSide/Components/Layout/NavMenu.razor @@ -24,6 +24,8 @@ + + diff --git a/samples/AfterBlazorServerSide/Components/Pages/ComponentList.razor b/samples/AfterBlazorServerSide/Components/Pages/ComponentList.razor index 5a212a27c..922c4b28f 100644 --- a/samples/AfterBlazorServerSide/Components/Pages/ComponentList.razor +++ b/samples/AfterBlazorServerSide/Components/Pages/ComponentList.razor @@ -6,6 +6,8 @@
  • Button
  • CheckBox
  • DropDownList
  • +
  • HiddenField
  • +
  • Image
  • HyperLink
  • LinkButton
  • Literal
  • diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/HiddenField/Index.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/HiddenField/Index.razor new file mode 100644 index 000000000..66211f39e --- /dev/null +++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/HiddenField/Index.razor @@ -0,0 +1,86 @@ +@page "/ControlSamples/HiddenField" + +HiddenField Sample + +

    HiddenField - Basic Usage

    + +

    The HiddenField component stores a non-displayed value. While the field is not visible to users, the value is accessible to both server-side code and JavaScript.

    + +
    +

    Demo

    + + + +

    Current hidden value: @HiddenValue

    + +
    + + +
    + +
    + +
    + +

    Source Code

    + +
    @@page "/ControlSamples/HiddenField"
    +
    +<HiddenField ID="myHiddenField" Value="@@HiddenValue" OnValueChanged="HandleValueChanged" />
    +
    +<p><strong>Current hidden value:</strong> @@HiddenValue</p>
    +
    +<div class="mb-3">
    +    <label class="form-label">Update hidden field value:</label>
    +    <input type="text" class="form-control" @@bind="NewValue" />
    +</div>
    +
    +<Button Text="Update Hidden Field" OnClick="UpdateHiddenValue" />
    +
    +@@code {
    +    private string HiddenValue = "initial-secret-value";
    +    private string NewValue = "";
    +    private string Message = "";
    +
    +    private void UpdateHiddenValue()
    +    {
    +        if (!string.IsNullOrEmpty(NewValue))
    +        {
    +            HiddenValue = NewValue;
    +            Message = $"Hidden field updated to: {HiddenValue}";
    +            NewValue = "";
    +        }
    +    }
    +
    +    private void HandleValueChanged(EventArgs e)
    +    {
    +        Message = "Value changed event triggered!";
    +    }
    +}
    + +@code { + private string HiddenValue = "initial-secret-value"; + private string NewValue = ""; + private string Message = ""; + + private void UpdateHiddenValue() + { + if (!string.IsNullOrEmpty(NewValue)) + { + HiddenValue = NewValue; + Message = $"Hidden field updated to: {HiddenValue}"; + NewValue = ""; + } + } + + private void HandleValueChanged(EventArgs e) + { + Message = "Value changed event triggered!"; + } +} diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Image/Index.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Image/Index.razor new file mode 100644 index 000000000..41f97b15c --- /dev/null +++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Image/Index.razor @@ -0,0 +1,112 @@ +@page "/ControlSamples/Image" +@using BlazorWebFormsComponents +@using BlazorWebFormsComponents.Enums + +Image Sample + +

    Image - Basic Usage

    + +

    The Image component displays an image on a web page, emulating the ASP.NET Web Forms Image control.

    + +
    + +

    Basic Image

    +

    A simple image with alternate text:

    + +
    + +
    + +

    Code:

    +
    <Image ImageUrl="https://via.placeholder.com/150x100" 
    +       AlternateText="Sample placeholder image" />
    + +
    + +

    Image with Tooltip

    +

    Hover over the image to see the tooltip:

    + +
    + +
    + +

    Code:

    +
    <Image ImageUrl="https://via.placeholder.com/150x100/0066cc/ffffff?text=Hover+Me" 
    +       AlternateText="Image with tooltip"
    +       ToolTip="This is a tooltip displayed on hover" />
    + +
    + +

    Image Alignment

    +

    Images can be aligned using the ImageAlign property:

    + +
    + +

    This text flows around the left-aligned image. The ImageAlign property + controls how content flows around the image, similar to the CSS float property. + This is useful when migrating Web Forms applications that rely on this behavior.

    +
    + +

    Code:

    +
    <Image ImageUrl="https://via.placeholder.com/80x80" 
    +       AlternateText="Left aligned image"
    +       ImageAlign="ImageAlign.Left" />
    +<p>This text flows around the left-aligned image...</p>
    + +
    + +

    Dynamic Image with Visibility Toggle

    +

    Click the button to toggle image visibility:

    + +
    + + +
    + +
    +
    + +

    Code:

    +
    <button @@onclick="ToggleVisibility">Toggle</button>
    +<Image ImageUrl="..." AlternateText="Toggleable image" Visible="@@isVisible" />
    +
    +@@code {
    +    private bool isVisible = true;
    +    
    +    void ToggleVisibility()
    +    {
    +        isVisible = !isVisible;
    +    }
    +}
    + +
    + +

    Decorative Image (Empty Alt Text)

    +

    For decorative images that don't convey content, use GenerateEmptyAlternateText:

    + +
    + +

    The decorative separator above has an empty alt attribute for accessibility compliance.

    +
    + +

    Code:

    +
    <Image ImageUrl="..." GenerateEmptyAlternateText="true" />
    + +@code { + private bool isVisible = true; + + void ToggleVisibility() + { + isVisible = !isVisible; + } +} From 7f484dcdbd24fd632cc06498996cf6a7d3a4d090 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:29:28 +0000 Subject: [PATCH 03/35] Initial plan From f23da2ec776839af3d47a02fa8885b2baf476dc7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:40:13 +0000 Subject: [PATCH 04/35] Add Sender property to EventArgs classes and update components - Added Sender property to all custom EventArgs classes (CommandEventArgs, DataListItemEventArgs, AdCreatedEventArgs, ListViewItemEventArgs, FormView*EventArgs, Login*EventArgs) - Updated all components to populate Sender property when raising events - Added tests to verify Sender property is correctly populated - All 434 tests passing (430 original + 4 new Sender property tests) Co-authored-by: csharpfritz <78577+csharpfritz@users.noreply.github.com> --- .../Button/SenderProperty.razor | 56 +++++++++++++++++ .../DataList/SenderProperty.razor | 61 +++++++++++++++++++ .../AdCreatedEventArgs.cs | 5 ++ .../AdRotator.razor.cs | 3 +- .../ButtonBaseComponent.cs | 2 +- .../CommandEventArgs.cs | 5 ++ src/BlazorWebFormsComponents/DataList.razor | 4 +- .../DataListItemEventArgs.cs | 5 ++ .../FormView.razor.cs | 20 +++--- .../FormViewDeleteEventArgs.cs | 5 ++ .../FormViewDeletedEventArgs.cs | 5 ++ .../FormViewInsertEventArgs.cs | 5 ++ .../FormViewModeEventArgs.cs | 5 ++ .../FormViewUpdateEventArgs.cs | 5 ++ .../FormViewUpdatedEventArgs.cs | 5 ++ src/BlazorWebFormsComponents/ListView.razor | 2 +- .../ListViewItemEventArgs.cs | 5 ++ .../LoginControls/AuthenticateEventArgs.cs | 5 ++ .../LoginControls/Login.razor.cs | 4 +- .../LoginControls/LoginCancelEventArgs.cs | 5 ++ .../LoginControls/LoginStatus.razor.cs | 2 +- 21 files changed, 196 insertions(+), 18 deletions(-) create mode 100644 src/BlazorWebFormsComponents.Test/Button/SenderProperty.razor create mode 100644 src/BlazorWebFormsComponents.Test/DataList/SenderProperty.razor diff --git a/src/BlazorWebFormsComponents.Test/Button/SenderProperty.razor b/src/BlazorWebFormsComponents.Test/Button/SenderProperty.razor new file mode 100644 index 000000000..4aca4a85c --- /dev/null +++ b/src/BlazorWebFormsComponents.Test/Button/SenderProperty.razor @@ -0,0 +1,56 @@ +@using Moq +@using Microsoft.AspNetCore.Http +@using Microsoft.AspNetCore.Routing +@inherits BunitContext + +@code { + + private object capturedSender = null; + private string commandName = string.Empty; + private object commandArg = null; + + void OnCommand(CommandEventArgs args) + { + capturedSender = args.Sender; + commandName = args.CommandName; + commandArg = args.CommandArgument; + } + + [Fact] + public void Button_Command_PopulatesSenderProperty() + { + // Arrange + Services.AddSingleton(new Mock().Object); + Services.AddSingleton(new Mock().Object); + capturedSender = null; + var cut = Render(@); + + // Act + cut.Find("button").Click(); + + // Assert + capturedSender.ShouldNotBeNull(); + capturedSender.ShouldBeOfType + + + + + + + + + +@if (SelectedCustomer != null) +{ +
    +

    Selected Customer Details

    +

    Customer ID: @SelectedCustomer.CustomerID

    +

    Company Name: @SelectedCustomer.CompanyName

    +

    First Name: @SelectedCustomer.FirstName

    +

    Last Name: @SelectedCustomer.LastName

    +
    +} + +
    + +

    Code Example

    +

    This sample demonstrates the master-detail pattern using GridView row selection:

    + +
    <GridView ItemType="Customer"
    +          DataKeyNames="CustomerID"
    +          Items="@@_customers">
    +    <Columns>
    +        <TemplateField ItemType="Customer" HeaderText="">
    +            <ItemTemplate Context="customer">
    +                <button type="button" class="btn btn-sm btn-primary" @@onclick="@@(() => SelectCustomer(customer))">Select</button>
    +            </ItemTemplate>
    +        </TemplateField>
    +        <BoundField DataField="CustomerID" HeaderText="ID" />
    +        <BoundField DataField="CompanyName" HeaderText="Company" />
    +        <BoundField DataField="FirstName" HeaderText="First Name"/>
    +        <BoundField DataField="LastName" HeaderText="Last Name"/>
    +    </Columns>
    +</GridView>
    +
    +@@code {
    +    private Customer? SelectedCustomer;
    +    private List<Customer> _customers = new List<Customer>
    +    {
    +        new Customer { CustomerID = 1, FirstName = "John", LastName = "Smith", CompanyName = "Acme Corporation" },
    +        new Customer { CustomerID = 2, FirstName = "Jane", LastName = "Doe", CompanyName = "TechStart Inc." },
    +        new Customer { CustomerID = 3, FirstName = "Bob", LastName = "Johnson", CompanyName = "Global Solutions" },
    +        new Customer { CustomerID = 4, FirstName = "Alice", LastName = "Williams", CompanyName = "Innovation Labs" }
    +    };
    +    
    +    private void SelectCustomer(Customer customer)
    +    {
    +        SelectedCustomer = customer;
    +        // CustomerID (the DataKey) is available via customer.CustomerID
    +    }
    +}
    + +@code { + private Customer? SelectedCustomer; + private List _customers = new List + { + new Customer + { + CustomerID = 1, + FirstName = "John", + LastName = "Smith", + CompanyName = "Acme Corporation" + }, + new Customer + { + CustomerID = 2, + FirstName = "Jane", + LastName = "Doe", + CompanyName = "TechStart Inc." + }, + new Customer + { + CustomerID = 3, + FirstName = "Bob", + LastName = "Johnson", + CompanyName = "Global Solutions" + }, + new Customer + { + CustomerID = 4, + FirstName = "Alice", + LastName = "Williams", + CompanyName = "Innovation Labs" + } + }; + + private void SelectCustomer(Customer customer) + { + SelectedCustomer = customer; + } +} diff --git a/src/BlazorWebFormsComponents.Test/GridView/RowSelection.razor b/src/BlazorWebFormsComponents.Test/GridView/RowSelection.razor new file mode 100644 index 000000000..36a9936c4 --- /dev/null +++ b/src/BlazorWebFormsComponents.Test/GridView/RowSelection.razor @@ -0,0 +1,59 @@ +@inherits BunitContext +@using SharedSampleObjects.Models +@using Microsoft.AspNetCore.Routing +@using Microsoft.AspNetCore.Http +@using Moq + +@code { + + [Fact] + public void GridView_RowSelection_SelectsCorrectCustomer() + { + // Register required services + var mockLinkGenerator = new Mock(); + Services.AddSingleton(mockLinkGenerator.Object); + + var mockHttpContextAccessor = new Mock(); + Services.AddSingleton(mockHttpContextAccessor.Object); + + var customers = new List + { + new Customer { CustomerID = 1, FirstName = "John", LastName = "Smith", CompanyName = "Acme" }, + new Customer { CustomerID = 2, FirstName = "Jane", LastName = "Doe", CompanyName = "TechStart" }, + new Customer { CustomerID = 3, FirstName = "Bob", LastName = "Johnson", CompanyName = "Global" } + }; + + Customer? selectedCustomer = null; + + var cut = Render(@ + + + + + + + + + + ); + + // Verify data is displayed + var rows = cut.FindAll("tr"); + rows.Count.ShouldBeGreaterThan(1, "Should have data rows"); + + // Click the second row's select button + var buttons = cut.FindAll("button"); + buttons.Count.ShouldBe(3, "Should have 3 select buttons"); + + buttons[1].Click(); + + // Verify correct customer was selected + selectedCustomer.ShouldNotBeNull(); + selectedCustomer.CustomerID.ShouldBe(2); + selectedCustomer.FirstName.ShouldBe("Jane"); + } + +} From 522742583ebbcf870d52c660d62ca66362142e55 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 23:02:07 -0500 Subject: [PATCH 10/35] Add DataGrid component (#314) Co-authored-by: csharpfritz <78577+csharpfritz@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- README.md | 2 +- docs/DataControls/DataGrid.md | 298 ++++++++++++++++++ mkdocs.yml | 1 + .../ControlSampleTests.cs | 3 + .../Components/Layout/NavMenu.razor | 6 + .../Components/Pages/ComponentList.razor | 1 + .../DataGrid/AutoGeneratedColumns.razor | 11 + .../ControlSamples/DataGrid/Default.razor | 40 +++ .../Pages/ControlSamples/DataGrid/Nav.razor | 9 + .../DataGrid/ShowHideHeader.razor | 31 ++ .../DataGrid/AutoGenerateColumns.razor | 26 ++ .../DataGrid/DataBoundFields.razor | 50 +++ .../DataGrid/EmptyDataText.razor | 18 ++ .../DataGrid/ShowHeader.razor | 30 ++ src/BlazorWebFormsComponents/DataGrid.razor | 46 +++ .../DataGrid.razor.cs | 141 +++++++++ .../DataGridColumnGenerator.cs | 73 +++++ .../DataGridCommandEventArgs.cs | 17 + .../DataGridRow.razor | 15 + .../DataGridRow.razor.cs | 27 ++ status.md | 12 +- 21 files changed, 850 insertions(+), 7 deletions(-) create mode 100644 docs/DataControls/DataGrid.md create mode 100644 samples/AfterBlazorServerSide/Components/Pages/ControlSamples/DataGrid/AutoGeneratedColumns.razor create mode 100644 samples/AfterBlazorServerSide/Components/Pages/ControlSamples/DataGrid/Default.razor create mode 100644 samples/AfterBlazorServerSide/Components/Pages/ControlSamples/DataGrid/Nav.razor create mode 100644 samples/AfterBlazorServerSide/Components/Pages/ControlSamples/DataGrid/ShowHideHeader.razor create mode 100644 src/BlazorWebFormsComponents.Test/DataGrid/AutoGenerateColumns.razor create mode 100644 src/BlazorWebFormsComponents.Test/DataGrid/DataBoundFields.razor create mode 100644 src/BlazorWebFormsComponents.Test/DataGrid/EmptyDataText.razor create mode 100644 src/BlazorWebFormsComponents.Test/DataGrid/ShowHeader.razor create mode 100644 src/BlazorWebFormsComponents/DataGrid.razor create mode 100644 src/BlazorWebFormsComponents/DataGrid.razor.cs create mode 100644 src/BlazorWebFormsComponents/DataGridColumnGenerator.cs create mode 100644 src/BlazorWebFormsComponents/DataGridCommandEventArgs.cs create mode 100644 src/BlazorWebFormsComponents/DataGridRow.razor create mode 100644 src/BlazorWebFormsComponents/DataGridRow.razor.cs diff --git a/README.md b/README.md index 617de49a9..579c81979 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ There are a significant number of controls in ASP.NET Web Forms, and we will foc - Xml - Data Controls - Chart(?) - - DataGrid + - [DataGrid](docs/DataControls/DataGrid.md) - [DataList](docs/DataControls/DataList.md) - DataPager - DetailsView diff --git a/docs/DataControls/DataGrid.md b/docs/DataControls/DataGrid.md new file mode 100644 index 000000000..099db307f --- /dev/null +++ b/docs/DataControls/DataGrid.md @@ -0,0 +1,298 @@ +# DataGrid + +The DataGrid component is meant to emulate the asp:DataGrid control in markup and is defined in the [System.Web.UI.WebControls.DataGrid class](https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.webcontrols.datagrid?view=netframework-4.8) + +## Features supported in Blazor + +- Readonly grid +- Bound, Button, Hyperlink, and Template columns +- Auto-generate columns +- Show/Hide header row +- Empty data text + +### Blazor Notes + +- The `ItemCommand.CommandSource` object will be populated with the `ButtonField` object +- DataGrid uses the same column types as GridView (BoundField, ButtonField, etc.) +- DataGrid is a legacy control superseded by GridView in ASP.NET 2.0, but is provided for compatibility + +## Web Forms Features NOT Supported + +The following DataGrid features from Web Forms are not currently supported: + +- Paging (AllowPaging, PageSize, CurrentPageIndex) +- Sorting (AllowSorting) +- Editing (EditItemIndex, OnEditCommand, OnUpdateCommand, OnCancelCommand) +- Selection +- Custom paging +- Footer templates + +## Web Forms Declarative Syntax + +```html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +## Blazor Syntax + +Currently, not every syntax element of Web Forms DataGrid is supported. In the meantime, the following DataGrid in Blazor syntax will only include the implemented ones. **Non-implemented elements will be included later**. + +```html + + OnEditCommand=EventCallBack + OnCancelCommand=EventCallBack + OnUpdateCommand=EventCallBack + OnDeleteCommand=EventCallBack + OnInit=EventCallBack + OnLoad=EventCallBack + OnPreRender=EventCallBack + OnUnload=EventCallBack + OnDisposed=EventCallBack + SelectMethod=SelectHandler + ShowHeader=bool + TabIndex=int + Visible=bool +> + + + + + + + + + + + +``` + +## Examples + +### Basic DataGrid with Manual Columns + +```razor + + + + + + + + +``` + +### DataGrid with Auto-Generated Columns + +```razor + + +``` + +### DataGrid without Header + +```razor + + + + + + +``` + +## Comparison with GridView + +DataGrid and GridView are very similar in Blazor. The main differences are: + +- DataGrid uses `DataKeyField` (singular) while GridView uses `DataKeyNames` +- DataGrid event names use "Item" (OnItemCommand) while GridView uses "Row" (OnRowCommand) +- DataGrid is a legacy control from ASP.NET 1.x, while GridView was introduced in ASP.NET 2.0 +- For new projects, GridView is recommended as it has more features and better design-time support + +Both controls use the same column types (BoundField, ButtonField, HyperLinkField, TemplateField) and render similar HTML. + +## Migration from Web Forms + +When migrating DataGrid from Web Forms to Blazor: + +1. Replace `` with `` +2. Replace `runat="server"` (not needed in Blazor) +3. Change `DataKeyNames` to `DataKeyField` if using +4. Update event handler names (e.g., `OnItemCommand` instead of event code-behind) +5. Use `SelectMethod` instead of setting DataSource in code-behind +6. Consider migrating to GridView for better feature support + +## See Also + +- [GridView](GridView.md) - The recommended data grid control for new projects +- [DataList](DataList.md) - For custom layout of repeating data +- [Repeater](Repeater.md) - For lightweight data repetition diff --git a/mkdocs.yml b/mkdocs.yml index 9787c9f59..b36bef08f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -81,6 +81,7 @@ nav: - RadioButtonList: EditorControls/RadioButtonList.md - TextBox: EditorControls/TextBox.md - Data Controls: + - DataGrid: DataControls/DataGrid.md - DataList: DataControls/DataList.md - FormView: DataControls/FormView.md - GridView: DataControls/GridView.md diff --git a/samples/AfterBlazorServerSide.Tests/ControlSampleTests.cs b/samples/AfterBlazorServerSide.Tests/ControlSampleTests.cs index b6b9be0fa..0038b6448 100644 --- a/samples/AfterBlazorServerSide.Tests/ControlSampleTests.cs +++ b/samples/AfterBlazorServerSide.Tests/ControlSampleTests.cs @@ -34,6 +34,9 @@ public async Task EditorControl_Loads_WithoutErrors(string path) // Data Controls [Theory] + [InlineData("/ControlSamples/DataGrid")] + [InlineData("/ControlSamples/DataGrid/AutoGeneratedColumns")] + [InlineData("/ControlSamples/DataGrid/ShowHideHeader")] [InlineData("/ControlSamples/DataList")] [InlineData("/ControlSamples/Repeater")] [InlineData("/ControlSamples/ListView")] diff --git a/samples/AfterBlazorServerSide/Components/Layout/NavMenu.razor b/samples/AfterBlazorServerSide/Components/Layout/NavMenu.razor index 02baa5f73..2c279159a 100644 --- a/samples/AfterBlazorServerSide/Components/Layout/NavMenu.razor +++ b/samples/AfterBlazorServerSide/Components/Layout/NavMenu.razor @@ -48,6 +48,12 @@ + + + + + + diff --git a/samples/AfterBlazorServerSide/Components/Pages/ComponentList.razor b/samples/AfterBlazorServerSide/Components/Pages/ComponentList.razor index 922c4b28f..793eb6684 100644 --- a/samples/AfterBlazorServerSide/Components/Pages/ComponentList.razor +++ b/samples/AfterBlazorServerSide/Components/Pages/ComponentList.razor @@ -23,6 +23,7 @@

    Data Controls