diff --git a/.gitignore b/.gitignore
index 86ed346..2495d53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,343 +1,344 @@
-## Ignore Visual Studio temporary files, build results, and
-## files generated by popular Visual Studio add-ons.
-##
-## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
-
-# User-specific files
-*.suo
-*.user
-*.userosscache
-*.sln.docstates
-*.ncrunchproject
-*.ncrunchsolution
-*.swp
-
-# Project XML documentation
-BlazorWebFormsComponents.xml
-
-# User-specific files (MonoDevelop/Xamarin Studio)
-*.userprefs
-.ionide/
-.vscode/
-
-# Build results
-[Dd]ebug/
-[Dd]ebugPublic/
-[Rr]elease/
-[Rr]eleases/
-x64/
-x86/
-bld/
-[Bb]in/
-[Oo]bj/
-[Ll]og/
-
-# Visual Studio 2015/2017 cache/options directory
-.vs/
-# Uncomment if you have tasks that create the project's static files in wwwroot
-#wwwroot/
-
-# Visual Studio 2017 auto generated files
-Generated\ Files/
-
-# MSTest test Results
-[Tt]est[Rr]esult*/
-[Bb]uild[Ll]og.*
-
-# NUNIT
-*.VisualState.xml
-TestResult.xml
-
-# Build Results of an ATL Project
-[Dd]ebugPS/
-[Rr]eleasePS/
-dlldata.c
-
-# Benchmark Results
-BenchmarkDotNet.Artifacts/
-
-# .NET Core
-project.lock.json
-project.fragment.lock.json
-artifacts/
-**/Properties/launchSettings.json
-
-# StyleCop
-StyleCopReport.xml
-
-# Files built by Visual Studio
-*_i.c
-*_p.c
-*_i.h
-*.ilk
-*.meta
-*.obj
-*.iobj
-*.pch
-*.pdb
-*.ipdb
-*.pgc
-*.pgd
-*.rsp
-*.sbr
-*.tlb
-*.tli
-*.tlh
-*.tmp
-*.tmp_proj
-*.log
-*.vspscc
-*.vssscc
-.builds
-*.pidb
-*.svclog
-*.scc
-
-# Chutzpah Test files
-_Chutzpah*
-
-# Visual C++ cache files
-ipch/
-*.aps
-*.ncb
-*.opendb
-*.opensdf
-*.sdf
-*.cachefile
-*.VC.db
-*.VC.VC.opendb
-
-# Visual Studio profiler
-*.psess
-*.vsp
-*.vspx
-*.sap
-
-# Visual Studio Trace Files
-*.e2e
-
-# TFS 2012 Local Workspace
-$tf/
-
-# Guidance Automation Toolkit
-*.gpState
-
-# ReSharper is a .NET coding add-in
-_ReSharper*/
-*.[Rr]e[Ss]harper
-*.DotSettings.user
-
-# JustCode is a .NET coding add-in
-.JustCode
-
-# TeamCity is a build add-in
-_TeamCity*
-
-# DotCover is a Code Coverage Tool
-*.dotCover
-
-# AxoCover is a Code Coverage Tool
-.axoCover/*
-!.axoCover/settings.json
-
-# Visual Studio code coverage results
-*.coverage
-*.coveragexml
-
-# NCrunch
-_NCrunch_*
-.*crunch*.local.xml
-nCrunchTemp_*
-
-# MightyMoose
-*.mm.*
-AutoTest.Net/
-
-# Web workbench (sass)
-.sass-cache/
-
-# Installshield output folder
-[Ee]xpress/
-
-# DocProject is a documentation generator add-in
-DocProject/buildhelp/
-DocProject/Help/*.HxT
-DocProject/Help/*.HxC
-DocProject/Help/*.hhc
-DocProject/Help/*.hhk
-DocProject/Help/*.hhp
-DocProject/Help/Html2
-DocProject/Help/html
-
-# Click-Once directory
-publish/
-
-# Publish Web Output
-*.[Pp]ublish.xml
-*.azurePubxml
-# Note: Comment the next line if you want to checkin your web deploy settings,
-# but database connection strings (with potential passwords) will be unencrypted
-*.pubxml
-*.publishproj
-
-# Microsoft Azure Web App publish settings. Comment the next line if you want to
-# checkin your Azure Web App publish settings, but sensitive information contained
-# in these scripts will be unencrypted
-PublishScripts/
-
-# NuGet Packages
-*.nupkg
-# The packages folder can be ignored because of Package Restore
-**/[Pp]ackages/*
-# except build/, which is used as an MSBuild target.
-!**/[Pp]ackages/build/
-# Uncomment if necessary however generally it will be regenerated when needed
-#!**/[Pp]ackages/repositories.config
-# NuGet v3's project.json files produces more ignorable files
-*.nuget.props
-*.nuget.targets
-
-# Microsoft Azure Build Output
-csx/
-*.build.csdef
-
-# Microsoft Azure Emulator
-ecf/
-rcf/
-
-# Windows Store app package directories and files
-AppPackages/
-BundleArtifacts/
-Package.StoreAssociation.xml
-_pkginfo.txt
-*.appx
-
-# Visual Studio cache files
-# files ending in .cache can be ignored
-*.[Cc]ache
-# but keep track of directories ending in .cache
-!*.[Cc]ache/
-
-# Others
-ClientBin/
-~$*
-*~
-*.dbmdl
-*.dbproj.schemaview
-*.jfm
-*.pfx
-*.publishsettings
-orleans.codegen.cs
-
-# Including strong name files can present a security risk
-# (https://github.com/github/gitignore/pull/2483#issue-259490424)
-#*.snk
-
-# Since there are multiple workflows, uncomment next line to ignore bower_components
-# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
-#bower_components/
-
-# RIA/Silverlight projects
-Generated_Code/
-
-# Backup & report files from converting an old project file
-# to a newer Visual Studio version. Backup files are not needed,
-# because we have git ;-)
-_UpgradeReport_Files/
-Backup*/
-UpgradeLog*.XML
-UpgradeLog*.htm
-ServiceFabricBackup/
-*.rptproj.bak
-
-# SQL Server files
-*.mdf
-*.ldf
-*.ndf
-
-# Business Intelligence projects
-*.rdl.data
-*.bim.layout
-*.bim_*.settings
-*.rptproj.rsuser
-
-# Microsoft Fakes
-FakesAssemblies/
-
-# GhostDoc plugin setting file
-*.GhostDoc.xml
-
-# Node.js Tools for Visual Studio
-.ntvs_analysis.dat
-node_modules/
-
-# Visual Studio 6 build log
-*.plg
-
-# Visual Studio 6 workspace options file
-*.opt
-
-# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
-*.vbw
-
-# Visual Studio LightSwitch build output
-**/*.HTMLClient/GeneratedArtifacts
-**/*.DesktopClient/GeneratedArtifacts
-**/*.DesktopClient/ModelManifest.xml
-**/*.Server/GeneratedArtifacts
-**/*.Server/ModelManifest.xml
-_Pvt_Extensions
-
-# Paket dependency manager
-.paket/paket.exe
-paket-files/
-
-# FAKE - F# Make
-.fake/
-
-# JetBrains Rider
-.idea/
-*.sln.iml
-
-# CodeRush
-.cr/
-
-# Python Tools for Visual Studio (PTVS)
-__pycache__/
-*.pyc
-
-# Cake - Uncomment if you are using it
-# tools/**
-# !tools/packages.config
-
-# Tabs Studio
-*.tss
-
-# Telerik's JustMock configuration file
-*.jmconfig
-
-# BizTalk build output
-*.btp.cs
-*.btm.cs
-*.odx.cs
-*.xsd.cs
-
-# OpenCover UI analysis results
-OpenCover/
-
-# Azure Stream Analytics local run output
-ASALocalRun/
-
-# MSBuild Binary and Structured Log
-*.binlog
-
-# NVidia Nsight GPU debugger configuration file
-*.nvuser
-
-# MFractors (Xamarin productivity tool) working folder
-.mfractor/
-
-# macOS
-.DS_Store
-.AppleDouble
-.LSOverride
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.swo
+*.user
+*.userosscache
+*.sln.docstates
+*.ncrunchproject
+*.ncrunchsolution
+*.swp
+
+# Project XML documentation
+BlazorWebFormsComponents.xml
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+.ionide/
+.vscode/
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# macOS
+.DS_Store
+.AppleDouble
+.LSOverride
diff --git a/README.md b/README.md
index 921785f..d1ebfbc 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,8 @@ Portions of the [original .NET Framework](https://github.com/microsoft/reference
There are a significant number of controls in ASP.NET Web Forms, and we will focus on creating components in the following order:
+ - Editor Controls
+ - [Button](docs/Button.md)
- Data Controls
- Chart(?)
- [DataList](docs/DataList.md)
diff --git a/docs/Button.md b/docs/Button.md
new file mode 100644
index 0000000..a7c1b17
--- /dev/null
+++ b/docs/Button.md
@@ -0,0 +1,3 @@
+# Button
+
+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
diff --git a/samples/AfterBlazorServerSide/Pages/ControlSamples/Button/Index.razor b/samples/AfterBlazorServerSide/Pages/ControlSamples/Button/Index.razor
new file mode 100644
index 0000000..542355e
--- /dev/null
+++ b/samples/AfterBlazorServerSide/Pages/ControlSamples/Button/Index.razor
@@ -0,0 +1,37 @@
+@page "/ControlSamples/Button"
+
+
Button component home page
+
+
+
+@TheContent
+
+@code {
+
+ [Parameter]
+ public string TheContent { get; set; } = "Not clicked yet!";
+ public string bar = "bar";
+
+/*
+ protected override void OnInitialized() {
+ TheContent = "Initialized";
+ Console.WriteLine("Initialized");
+ }
+*/
+
+ void OnClick() {
+
+ TheContent = "I've been clicked";
+
+ }
+
+ void OnCommand(CommandEventArgs args) {
+
+ Console.WriteLine("Command fired");
+ TheContent = $"Command '{args.CommandName}'";
+
+ }
+
+
+
+}
diff --git a/samples/AfterBlazorServerSide/Shared/NavMenu.razor b/samples/AfterBlazorServerSide/Shared/NavMenu.razor
index 55a4ae5..1224e11 100644
--- a/samples/AfterBlazorServerSide/Shared/NavMenu.razor
+++ b/samples/AfterBlazorServerSide/Shared/NavMenu.razor
@@ -14,6 +14,10 @@
+
+
+
+
diff --git a/src/BlazorWebFormsComponents.Test/BaseWebFormsComponent/Parent.razor b/src/BlazorWebFormsComponents.Test/BaseWebFormsComponent/Parent.razor
new file mode 100644
index 0000000..53e873a
--- /dev/null
+++ b/src/BlazorWebFormsComponents.Test/BaseWebFormsComponent/Parent.razor
@@ -0,0 +1,41 @@
+@inherits TestComponentBase
+
+
+
+ @*
+ @Item.Name
+
+
+ Header
+
+ @itemPlaceholder
+
+
+
+ *@
+
+
+
+
+
+@code {
+
+Button MyButton { get; set; }
+
+void FirstTest() {
+
+ var cut = GetComponentUnderTest();
+ cut.FindAll("button").Count().ShouldNotBe(0);
+
+ MyButton.ShouldNotBeNull();
+ MyButton.Parent.ShouldNotBeNull("Cannot find the Parent component");
+
+}
+
+}
diff --git a/src/BlazorWebFormsComponents.Test/Button/Click.razor b/src/BlazorWebFormsComponents.Test/Button/Click.razor
new file mode 100644
index 0000000..29dbbad
--- /dev/null
+++ b/src/BlazorWebFormsComponents.Test/Button/Click.razor
@@ -0,0 +1,37 @@
+ο»Ώ@inherits TestComponentBase
+
+
+
+
+ Click me!
+
+
+
+
+@code {
+
+ public string TheContent { get; set; } = "Not clicked yet!";
+
+ void OnClick()
+ {
+
+ TheContent = "I've been clicked";
+
+ }
+
+ void FirstTest()
+ {
+ // Given
+ var cut = GetComponentUnderTest();
+
+ TheContent.ShouldBe("Not clicked yet!");
+
+ // When
+ cut.Find("button").Click();
+
+ // Then
+ TheContent.ShouldBe("I've been clicked");
+
+ }
+
+}
diff --git a/src/BlazorWebFormsComponents.Test/Button/Command.razor b/src/BlazorWebFormsComponents.Test/Button/Command.razor
new file mode 100644
index 0000000..5320d78
--- /dev/null
+++ b/src/BlazorWebFormsComponents.Test/Button/Command.razor
@@ -0,0 +1,38 @@
+ο»Ώ@inherits TestComponentBase
+
+
+
+
+ Click me!
+
+
+
+
+@code {
+
+ public string TheContent { get; set; } = "No Command yet!";
+ public string CommandArgument = "bar";
+
+ void OnCommand(CommandEventArgs args)
+ {
+
+ TheContent = $"Command '{args.CommandName}' : '{args.CommandArgument.ToString()}'";
+
+ }
+
+ void FirstTest()
+ {
+ // Given
+ var cut = GetComponentUnderTest();
+
+ TheContent.ShouldBe("No Command yet!");
+
+ // When
+ cut.Find("button").Click();
+
+ // Then
+ TheContent.ShouldBe("Command 'Foo' : 'bar'");
+
+ }
+
+}
diff --git a/src/BlazorWebFormsComponents/BaseWebFormsComponent.cs b/src/BlazorWebFormsComponents/BaseWebFormsComponent.cs
index 35eb21e..caaf4ca 100644
--- a/src/BlazorWebFormsComponents/BaseWebFormsComponent.cs
+++ b/src/BlazorWebFormsComponents/BaseWebFormsComponent.cs
@@ -1,248 +1,242 @@
-ο»Ώusing Microsoft.AspNetCore.Components;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace BlazorWebFormsComponents
-{
-
- public abstract class BaseWebFormsComponent : ComponentBase, IAsyncDisposable
- {
-
- #region Obsolete Attributes / Properties
-
- ///
- /// π¨π¨ Use @ref instead of ID π¨π¨
- ///
- [Parameter, Obsolete("Use @ref instead of ID")]
- public string ID { get; set; }
-
- ///
- /// While ViewState is supported by this library, this parameter does nothing
- ///
- [Parameter(), Obsolete("ViewState is supported, but EnableViewState does nothing")]
- public bool EnableViewState { get; set; }
-
- ///
- /// π¨π¨ runat is not available in Blazor π¨π¨
- ///
- [Parameter(), Obsolete("runat is not available in Blazor")]
- public string runat { get; set; }
-
- ///
- /// π¨π¨ DataKeys are not used in Blazor π¨π¨
- ///
- [Parameter(), Obsolete("DataKeys are not used in Blazor")]
- public string DataKeys { get; set; }
-
- ///
- /// π¨π¨ DataSource controls are not used in Blazor π¨π¨
- ///
- [Parameter, Obsolete("DataSource controls are not used in Blazor")]
- public string DataSourceID { get; set; }
-
- ///
- /// π¨π¨ Theming is not available in Blazor π¨π¨
- ///
- [Parameter, Obsolete("Theming is not available in Blazor")]
- public bool EnableTheming { get; set; }
-
- ///
- /// π¨π¨ Theming is not available in Blazor π¨π¨
- ///
- [Parameter, Obsolete("Theming is not available in Blazor")]
- public bool SkinID { get; set; }
-
- #endregion
-
- [Parameter]
- public bool Enabled { get; set; } = true;
-
- [Parameter]
- public short TabIndex { get; set; }
-
- ///
- /// ViewState is supported for compatibility with those components and pages that add and retrieve items from ViewState.!-- It is not binary compatible, but is syntax compatible
- ///
- ///
- [Obsolete("ViewState is supported for compatibility and is discouraged for future use")]
- public Dictionary ViewState { get; } = new Dictionary();
-
- ///
- /// Is the content of this component rendered and visible to your users?
- ///
- [Parameter]
- public bool Visible {
- get
- {
- return visible;
- }
- set
- {
- visible = value;
- StateHasChanged();
- }
- }
-
- [Obsolete("This method doesn't do anything in Blazor")]
- public void DataBind() { }
-
- ///
- /// π¨π¨ Placeholders are not available in Blazor π¨π¨
- ///
- [Parameter, Obsolete("Placeholders are not available in Blazor")]
-
- public string ItemPlaceholderID { get; set; }
-
-
- [Parameter(CaptureUnmatchedValues = true)]
- public Dictionary AdditionalAttributes { get; set; }
-
- #region Custom Events
-
- ///
- /// Event handler to mimic the Web Forms OnInit handler, and is triggered at the beginning of the OnInitialize Blazor event
- ///
- [Parameter]
- public EventCallback OnInit { get; set; }
-
- ///
- /// Event handler to mimic the Web Forms OnLoad handler, and is triggered at the end of the OnInitialize Blazor event
- ///
- [Parameter]
- public EventCallback OnLoad { get; set; }
-
- ///
- /// Event handler to mimic the Web Forms OnPreRender handler, and is triggered at the end of the OnInitialize Blazor event after Load
- ///
- [Parameter]
- public EventCallback OnPreRender { get; set; }
-
- ///
- /// Event handler to mimic the Web Forms OnUnload handler, and is triggered at the end of the OnAfterRender Blazor event
- ///
- [Parameter]
- public EventCallback OnUnload { get; set; }
- private bool _UnloadTriggered = false;
-
- ///
- /// Event handler to mimic the Web Forms OnDisposed handler and triggered in the Dispose method of this class
- ///
- [Parameter]
- public EventCallback OnDisposed { get; set; }
-
- #endregion
-
- #region Blazor Events
-
- protected override void OnInitialized()
- {
- // Adds this component to its parent's children
- ParentControl?.Controls.Add(this);
- base.OnInitialized();
- }
-
- protected override async Task OnInitializedAsync()
- {
-
- if (OnInit.HasDelegate)
- await OnInit.InvokeAsync(EventArgs.Empty);
-
- await base.OnInitializedAsync();
-
- if (OnLoad.HasDelegate)
- await OnLoad.InvokeAsync(EventArgs.Empty);
-
- if (OnPreRender.HasDelegate)
- await OnPreRender.InvokeAsync(EventArgs.Empty);
-
- }
-
- protected override async Task OnAfterRenderAsync(bool firstRender)
- {
-
- await base.OnAfterRenderAsync(firstRender);
-
- if (OnUnload.HasDelegate && !_UnloadTriggered)
- {
- await OnUnload.InvokeAsync(EventArgs.Empty);
- _UnloadTriggered = true;
- }
-
- if (firstRender)
- {
-
- HandleUnknownAttributes();
- StateHasChanged();
-
- }
-
- }
-
- protected virtual void HandleUnknownAttributes() { }
-
-
- #endregion
-
- #region IDisposable Support
- private bool disposedValue = false; // To detect redundant calls
- private bool visible = true;
-
- protected virtual async ValueTask Dispose(bool disposing)
- {
- if (!disposedValue)
- {
- if (disposing)
- {
- if (OnDisposed.HasDelegate)
- {
- await OnDisposed.InvokeAsync(EventArgs.Empty);
- }
- }
- // Remove this control from its parent control
- ParentControl?.Controls.Remove(this);
- disposedValue = true;
- }
- }
-
- ~BaseWebFormsComponent()
- {
- // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
- Dispose(false).GetAwaiter().GetResult();
- }
-
- // This code added to correctly implement the disposable pattern.
- public ValueTask DisposeAsync()
- {
- GC.SuppressFinalize(this);
- return Dispose(true);
- }
- #endregion
-
- public bool LayoutTemplateRendered { get; set; } = false;
-
-
- ///
- /// The component's parent
- ///
- [CascadingParameter(Name = "ParentControl")] public BaseWebFormsComponent ParentControl { get; set; }
-
- ///
- /// The list of child controls
- ///
- public List Controls { get; set; } = new List();
-
- ///
- /// Finds a child control by its ID
- ///
- /// the ID of the child
- ///
- public BaseWebFormsComponent FindControl(string controlId)
- {
- return Controls.Find(control => control.ID == controlId);
- }
- }
-
-}
+ο»Ώusing Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.Rendering;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Text;
+using System.Threading.Tasks;
+using System.Reflection;
+using IComponent = Microsoft.AspNetCore.Components.IComponent;
+
+namespace BlazorWebFormsComponents
+{
+
+ public abstract class BaseWebFormsComponent : ComponentBase, IAsyncDisposable
+ {
+
+ #region Obsolete Attributes / Properties
+
+ ///
+ /// π¨π¨ Use @ref instead of ID π¨π¨
+ ///
+ [Parameter, Obsolete("Use @ref instead of ID")]
+ public string ID { get; set; }
+
+ ///
+ /// While ViewState is supported by this library, this parameter does nothing
+ ///
+ [Parameter(), Obsolete("ViewState is supported, but EnableViewState does nothing")]
+ public bool EnableViewState { get; set; }
+
+ ///
+ /// π¨π¨ runat is not available in Blazor π¨π¨
+ ///
+ [Parameter(), Obsolete("runat is not available in Blazor")]
+ public string runat { get; set; }
+
+ ///
+ /// π¨π¨ DataKeys are not used in Blazor π¨π¨
+ ///
+ [Parameter(), Obsolete("DataKeys are not used in Blazor")]
+ public string DataKeys { get; set; }
+
+ ///
+ /// π¨π¨ DataSource controls are not used in Blazor π¨π¨
+ ///
+ [Parameter, Obsolete("DataSource controls are not used in Blazor")]
+ public string DataSourceID { get; set; }
+
+ ///
+ /// π¨π¨ Theming is not available in Blazor π¨π¨
+ ///
+ [Parameter, Obsolete("Theming is not available in Blazor")]
+ public bool EnableTheming { get; set; }
+
+ ///
+ /// π¨π¨ Theming is not available in Blazor π¨π¨
+ ///
+ [Parameter, Obsolete("Theming is not available in Blazor")]
+ public bool SkinID { get; set; }
+
+ #endregion
+
+ [Parameter]
+ public bool Enabled { get; set; } = true;
+
+ [CascadingParameter(Name= PARENTCOMPONENTNAME)]
+ public virtual BaseWebFormsComponent Parent { get; set; }
+
+ [Parameter]
+ public short TabIndex { get; set; }
+
+ ///
+ /// ViewState is supported for compatibility with those components and pages that add and retrieve items from ViewState.!-- It is not binary compatible, but is syntax compatible
+ ///
+ ///
+ [Obsolete("ViewState is supported for compatibility and is discouraged for future use")]
+ public Dictionary ViewState { get; } = new Dictionary();
+
+ ///
+ /// Is the content of this component rendered and visible to your users?
+ ///
+ [Parameter]
+ public bool Visible { get; set; } = true;
+
+ [Obsolete("This method doesn't do anything in Blazor")]
+ public void DataBind() { }
+
+ ///
+ /// π¨π¨ Placeholders are not available in Blazor π¨π¨
+ ///
+ [Parameter, Obsolete("Placeholders are not available in Blazor")]
+
+ public string ItemPlaceholderID { get; set; }
+
+
+ [Parameter(CaptureUnmatchedValues = true)]
+ public Dictionary AdditionalAttributes { get; set; }
+
+ #region Custom Events
+
+ ///
+ /// Event handler to mimic the Web Forms OnInit handler, and is triggered at the beginning of the OnInitialize Blazor event
+ ///
+ [Parameter]
+ public EventCallback OnInit { get; set; }
+
+ ///
+ /// Event handler to mimic the Web Forms OnLoad handler, and is triggered at the end of the OnInitialize Blazor event
+ ///
+ [Parameter]
+ public EventCallback OnLoad { get; set; }
+
+ ///
+ /// Event handler to mimic the Web Forms OnPreRender handler, and is triggered at the end of the OnInitialize Blazor event after Load
+ ///
+ [Parameter]
+ public EventCallback OnPreRender { get; set; }
+
+ ///
+ /// Event handler to mimic the Web Forms OnUnload handler, and is triggered at the end of the OnAfterRender Blazor event
+ ///
+ [Parameter]
+ public EventCallback OnUnload { get; set; }
+ private bool _UnloadTriggered = false;
+
+ ///
+ /// Event handler to mimic the Web Forms OnDisposed handler and triggered in the Dispose method of this class
+ ///
+ [Parameter]
+ public EventCallback OnDisposed { get; set; }
+
+ #endregion
+
+ [Parameter]
+ public RenderFragment ChildComponents { get; set; }
+
+ #region Blazor Events
+
+ protected override async Task OnInitializedAsync()
+ {
+
+ if (OnInit.HasDelegate)
+ await OnInit.InvokeAsync(EventArgs.Empty);
+
+ await base.OnInitializedAsync();
+
+ if (OnLoad.HasDelegate)
+ await OnLoad.InvokeAsync(EventArgs.Empty);
+
+ if (OnPreRender.HasDelegate)
+ await OnPreRender.InvokeAsync(EventArgs.Empty);
+
+ }
+
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+
+ await base.OnAfterRenderAsync(firstRender);
+
+ if (OnUnload.HasDelegate && !_UnloadTriggered)
+ {
+ await OnUnload.InvokeAsync(EventArgs.Empty);
+ _UnloadTriggered = true;
+ }
+
+ if (firstRender)
+ {
+
+ HandleUnknownAttributes();
+ StateHasChanged();
+
+ }
+
+ }
+
+ protected virtual void HandleUnknownAttributes() { }
+
+
+ #endregion
+
+ #region IDisposable Support
+ private bool disposedValue = false; // To detect redundant calls
+
+ protected virtual async ValueTask Dispose(bool disposing)
+ {
+ if (!disposedValue)
+ {
+ if (disposing)
+ {
+ if (OnDisposed.HasDelegate)
+ {
+ await OnDisposed.InvokeAsync(EventArgs.Empty);
+ }
+ }
+
+ disposedValue = true;
+ }
+ }
+
+ ~BaseWebFormsComponent()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(false).GetAwaiter().GetResult();
+ }
+
+ // This code added to correctly implement the disposable pattern.
+ public ValueTask DisposeAsync()
+ {
+ GC.SuppressFinalize(this);
+ return Dispose(true);
+ }
+ #endregion
+
+ public bool LayoutTemplateRendered { get; set; } = false;
+ private const string BASEFRAGMENTFIELDNAME = "_renderFragment";
+ private const string PARENTCOMPONENTNAME = "ParentComponent";
+
+ // Get Access to the ComponentBase field we need to wrap every component in a CascadingValue
+ private static readonly FieldInfo _renderFragmentField = typeof(ComponentBase).GetField(BASEFRAGMENTFIELDNAME, BindingFlags.NonPublic | BindingFlags.Instance);
+ private readonly RenderFragment _baseRenderFragment;
+
+ public BaseWebFormsComponent ()
+ {
+ // Grab a copy of the default RenderFragment to go into the CascadingValue
+ _baseRenderFragment = (RenderFragment)_renderFragmentField.GetValue(this);
+
+ // Override the default RenderFragment with our Special Sauce version
+ _renderFragmentField.SetValue(this, (RenderFragment)ParentWrappingBuildRenderTree);
+
+ void ParentWrappingBuildRenderTree(RenderTreeBuilder builder)
+ {
+ builder.OpenComponent(1, typeof(CascadingValue));
+ builder.AddAttribute(2, nameof(CascadingValue