VwV.Forms.Client
1.0.0-preview.5
dotnet add package VwV.Forms.Client --version 1.0.0-preview.5
NuGet\Install-Package VwV.Forms.Client -Version 1.0.0-preview.5
<PackageReference Include="VwV.Forms.Client" Version="1.0.0-preview.5" />
<PackageVersion Include="VwV.Forms.Client" Version="1.0.0-preview.5" />
<PackageReference Include="VwV.Forms.Client" />
paket add VwV.Forms.Client --version 1.0.0-preview.5
#r "nuget: VwV.Forms.Client, 1.0.0-preview.5"
#:package VwV.Forms.Client@1.0.0-preview.5
#addin nuget:?package=VwV.Forms.Client&version=1.0.0-preview.5&prerelease
#tool nuget:?package=VwV.Forms.Client&version=1.0.0-preview.5&prerelease
VwV.Forms
Built by Vira Web Vorna (VwV) — virawebvorna.ir · arvindev1400@gmail.com. Licensed under MIT.
Reusable .NET 10 form authoring, assessment rendering, validation and response reporting. No Club, EF Core, Identity, database or file-provider dependency. The independent Playground is a runnable WebAssembly integration example, with a small ASP.NET Core host. This is a preview release with pinned preview Bit dependencies; upgrade and test these libraries together.
Packages
| Package | Responsibility |
|---|---|
| VwV.Forms.Contracts | Strong identifiers, public definitions/answers, private scoring rules, scoped reporting and source-generated JSON |
| VwV.Forms.Engine | Validation, exact-set choice grading, deterministic reports and safe CSV; depends only on Contracts |
| VwV.Forms.Client | Bit builder, outline/reordering, desktop/mobile preview, animated runner and reports; depends on Contracts, Engine, Bit UI/Icons/Extras and Bmotion |
Installation from NuGet.org
The first public release is 1.0.0-preview.5, for .NET 10. It uses the latest listed 11.0.0-pre-14 versions of Bit.BlazorUI, Icons, Extras and Bmotion. Keep the package family aligned. A preview is required while Bit dependencies are previews.
After public upload, install without a GitHub token:
dotnet add YourApp.Shared package VwV.Forms.Contracts --version 1.0.0-preview.5
dotnet add YourApp.Server package VwV.Forms.Engine --version 1.0.0-preview.5
dotnet add YourApp.Client package VwV.Forms.Client --version 1.0.0-preview.5
Use your actual project paths. Client depends on Engine and Contracts; install these separately only in projects that need them directly. Server-only validation/grading requires Engine, and shared DTO projects need only Contracts. With central package management add PackageVersion entries to Directory.Packages.props and use versionless PackageReference entries in projects.
NuGet.org is the only configured source in this repository. Remove old mappings that force VwV.Forms.* to the private GitHub feed. Before public upload, use the three .nupkg files attached to the GitHub release as a local NuGet source. A GitHub release does not certify NuGet.org publication.
Independent Playground
From the repository root:
dotnet run --project VwV.Forms.Playground --urls https://localhost:5003
Trust the .NET development certificate if needed and open https://localhost:5003. Builder, Answer form and Reports use synthetic session memory. The demo starts with eight responses, supports RTL and the built-in Bit theme switcher, and connects to no database, identity, upload service, email or SMS. Refreshing creates a new session. Activating a changed form creates a new revision and a separate report; earlier answers are never reinterpreted using changed questions. File inputs explicitly require a host adapter.
Use packages instead of project references:
dotnet run --project VwV.Forms.Playground -p:UsePublishedPackages=true -p:FormsPackageVersion=1.0.0-preview.5 --urls https://localhost:5003
For locally packed packages, first dotnet restore with these properties and --source ./artifacts/packages --source https://api.nuget.org/v3/index.json, then run with --no-restore. The release workflow also restores and builds this package-only consumer from audited release packages.
Blazor registration and assets
The Playground host/client Program.cs, host Components/App.razor and client page code are executable setup examples. Use an interactive render mode; static SSR cannot handle editor events.
using Bit.BlazorUI;
using VwV.Forms.Client;
builder.Services.AddBitBlazorUIExtrasServices();
builder.Services.AddFormsClientServices();
For WASM with prerendering register both in client and server containers. AddFormsClientServices registers Bmotion with the user's reduced-motion preference. The host controls theme, culture, navigation and error presentation.
Include Bit assets and the host's generated scoped stylesheet in its document:
<link rel="stylesheet" href="_content/Bit.BlazorUI/styles/bit.blazorui.css" />
<link rel="stylesheet" href="_content/Bit.BlazorUI.Icons/styles/bit.blazorui.icons.css" />
<link rel="stylesheet" href="_content/Bit.BlazorUI.Extras/styles/bit.blazorui.extras.css" />
<link rel="stylesheet" href="MyApp.styles.css" />
<script src="_content/Bit.BlazorUI/scripts/bit.blazorui.js"></script>
<script src="_content/Bit.BlazorUI.Extras/scripts/bit.blazorui.extras.js"></script>
Include your hosting model's Blazor framework script. The Playground additionally loads Fluent 2, Material and Cupertino styles for BitThemeSwitcher. Bmotion imports its own module; no extra animation script is required. Compiled scoped CSS ships in the package; consuming builds need no Sass or Node. Library developers regenerate paired SCSS/CSS after changing styles.
Strong identifiers and exact scope
using VwV.Forms.Contracts;
using VwV.Forms.Engine;
ApplicationKey project = new("TrainingPortal");
TenantKey academy = new("north-academy");
var scope = FormScope.ForTenant(project, academy);
// A non-tenant consumer instead uses FormScope.SingleTenant(project).
var definition = new FormDefinition
{
Scope = scope, Id = new FormId("feedback"), Revision = 1,
Purpose = FormPurpose.Form, Title = "Course feedback",
Fields =
[
new()
{
Id = new FieldId("recommend"), Position = 1, Kind = FieldKind.SingleChoice,
Prompt = "Would you recommend this course?", Required = true,
Options = [new() { Id = new OptionId("yes"), Text = "Yes" },
new() { Id = new OptionId("no"), Text = "No" }]
}
]
};
FormRules.ValidateDefinition(definition);
Identifiers are distinct value types, serialized as plain JSON strings. Empty/default IDs fail validation. Use FormsJsonContext for source-generated JSON and trimming. The collection values are mutable .NET lists: clone a published snapshot before editing.
ApplicationKey separates projects; TenantKey separates tenants. A null tenant is an exact single-tenant scope, never a wildcard. Derive scope from authenticated server context. Every read, save, export, upload and download must authorize again. Platform access is a separate host policy; build separate exact-scope snapshots rather than treating a null tenant as permission to read everything.
Kinds: true/false, single choice, multiple choice, long/short text, number, email, URL, date, rating and file. Ratings use integer stars from one to a configured maximum of ten; an empty/cleared rating is unanswered, never an automatic one-star answer. Numeric answers use invariant decimal strings; dates use yyyy-MM-dd. Media uses opaque MediaId references; the host enforces ownership, file limits, quarantine and transport.
Builder, preview and response runner
Import VwV.Forms.Client, VwV.Forms.Contracts and Bit.BlazorUI in _Imports.razor:
<FormDefinitionEditor Value="draft" ValueChanged="DraftChanged" OnError="HandleError" />
<FormRunner Definition="published" Answers="answers" AnswerChanged="AnswerChanged"
SaveBeforeNavigate="FlushAsync" SubmitRequested="SubmitAsync" CanSubmit
StateKey="@attemptId.Value" OnError="HandleError" />
Put handlers in .razor.cs. DraftChanged(FormDefinition) stores a local draft; publish a separately validated revision. AnswerChanged(FieldAnswer) updates the answer list and queues persistence. FlushAsync must await pending writes and throw on failure. SubmitAsync must reauthorize, revalidate and atomically freeze participation on the server, handling retries and concurrency. The UI is not a timing/security authority. Change StateKey when participation/tenant/form changes. Disabled blocks incompatible operations; ReadOnly prevents editing submitted answers.
FormDefinitionEditor has Toolbar, PrivateSettings, PromptMedia, OptionMedia and MediaPreview slots. AllowedKinds restricts a consumer. FormFieldEditor.FixedSingleChoiceCount enforces a consumer's four-choice rule; generic forms permit two or more choices.
FormRunner has MediaTemplate, AnswerMediaTemplate and FieldHeader. The media editor receives FormAnswerEditorContext with the field, answer, disabled state and typed Changed callback. Use your document module's authorized uploader/preview here; never send storage credentials in definitions. Pass equivalent media adapters to preview. Preview=true keeps transient answers internally and calls no save or submit API.
Single-choice/true-false acknowledgement and transitions use Bit.Bmotion, advancing after the answer handler and save barrier succeed. Multiple choice, text and files use explicit continuation. Final submission requires review and confirmation. Acknowledgement does not reveal correctness. Move buttons provide a keyboard alternative to outline drag/reordering. With the pinned Bmotion version, pointer reorder is enabled only on WebAssembly: its synchronous interop is unsupported on Blazor Server/WebView. Those renderers show move buttons and suppress the unsupported drag handle. The Playground uses WebAssembly so the real pointer interaction can be tested.
Labels use Text: Func<FormTextKey,string>; supply a localizer-backed delegate and forward it to child components. OnError supplies host feedback for caught handler errors. Defaults are English. RTL support in the Playground is not a complete Persian translation.
Assessment and grading
Use Purpose=FormPurpose.Assessment. Keep FieldScoringRule and manual rubrics on the author/server side. Public FormField has no answer-key property.
var key = new FieldScoringRule
{
FieldId = definition.Fields[0].Id, MaximumScore = 2,
CorrectOptionIds = [new OptionId("yes")]
};
var answer = new FieldAnswer { FieldId = key.FieldId, SelectedOptionIds = [new OptionId("yes")] };
decimal score = FormRules.ExactChoiceSet(definition.Fields[0], key, answer) ? key.MaximumScore : 0;
Exact-set grading requires the entire selected set to match; no implicit partial credit. The host owns manual marking, maximum points, total/passing score, release, audit history and alternative grading policies. Report grade facts require one mark per field and consistent totals. A missing mark is null, not zero. A previous released grade may accompany a newer unreleased correction; its released result remains until the correction is published.
Authorized reports and CSV
Implement IResponseReportSource.ReadAuthorizedAsync. Authorize the actor, derive/check exact scope, pin a definition revision and read one consistent snapshot. Return AuthorizedResponseSnapshot with the definition, response facts and optional whole-roster invited count. Supply the latest saved answer per field, latest current grade and latest released grade per attempt. Historical audit rows are not separate attempts. Never include answer keys in response facts.
// authorizedSource is your IResponseReportSource implementation.
var service = new ResponseReportService(authorizedSource, TimeProvider.System);
var report = await service.ReadAsync(new ResponseReportQuery
{
Scope = scope, FormId = definition.Id, Revision = definition.Revision,
Attempts = ReportAttemptPolicy.LatestFinalizedPerRespondent, Page = 0, PageSize = 20
}, cancellationToken);
string summaryCsv = ResponseCsvExporter.Summary(report);
string displayedPageCsv = ResponseCsvExporter.Page(report); // no respondent identity by default
ResponseReporting.Build is the pure alternative for an already authorized snapshot. It rejects mixed projects/tenants/revisions, duplicate attempt/answer IDs and inconsistent grade totals. Authorize again for exports; enable includeIdentity=true only for entitled actors. Page after aggregation: never supply a single page of source facts and label its aggregate counts as the whole report. Bound oversized jobs explicitly instead of silently truncating them.
ResponseReportView takes Report, PageChanged, ExportSummary, ExportPage and OnError, plus optional AnswerMedia for authorized attachment details. It performs no network/storage operations itself.
- Started attempts, distinct respondents, submitted/expired/in-progress counts, analyzed population and invited roster remain separate.
- Default is latest finalized attempt per respondent. All finalized, latest including drafts and all attempts are explicit alternatives. Stable ordering uses attempt number, start time, then ID. An unfinished retake cannot replace the default finalized result.
- Start filters are inclusive from and exclusive before. Invited roster is not silently date-filtered. Query metadata records policy/range.
- Option denominator is analyzed attempts. Multiple-choice percentages may total above 100%. Invalid and unanswered are separate.
- Grade averages/pass rate use complete released grades. Missing/manual/pending grades are never zero; forms invent no missing grades. Duration uses finalized timestamps.
- Paging affects detail rows, not aggregates. Summary CSV and displayed-page CSV differ. Cells are quoted and spreadsheet formula prefixes neutralized; answer text can still contain personal information, so authorize every export.
Build and release
dotnet run --project VwV.Forms.Tests -c Release
dotnet pack VwV.Forms.Contracts -c Release -o artifacts/packages
dotnet pack VwV.Forms.Engine -c Release -o artifacts/packages
dotnet pack VwV.Forms.Client -c Release -o artifacts/packages
Tests create no database. Dispatch packages.yml with a version such as 1.0.0-preview.5. It tests, packs, verifies a package-only consumer, publishes three packages and rebuilds the consumer from GitHub Packages. Versions are immutable: changes require a new version.
Browser screenshots cannot reliably be blocked. Normal print output hides runner content; personalized host watermarks are another deterrent, not screenshot prevention. Anonymous distribution, invitations, payment, proctoring and host persistence/workflows are outside this core module.
Public releases and support
See the release guide and changelog. Windows/Linux CI runs synthetic tests and an isolated consumer installed from the built packages. Publication is a separate explicit workflow operation that downloads and verifies the existing GitHub release assets; it does not rebuild packages. Configure a scoped NUGET_API_KEY secret or NUGET_USER with NuGet Trusted Publishing before uploading to NuGet.org. Symbols remain in internal workflow artifacts.
Copyright (c) 2026 Vira Web Vorna (VwV). MIT license. Website: virawebvorna.ir. Email: arvindev1400@gmail.com. Third-party dependency licenses are recorded in consumer-proof.json attached to the release.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Bit.BlazorUI (>= 11.0.0-pre-14)
- Bit.BlazorUI.Extras (>= 11.0.0-pre-14)
- Bit.BlazorUI.Icons (>= 11.0.0-pre-14)
- Bit.Bmotion (>= 11.0.0-pre-14)
- Microsoft.AspNetCore.Components.Web (>= 10.0.14)
- VwV.Forms.Contracts (>= 1.0.0-preview.5)
- VwV.Forms.Engine (>= 1.0.0-preview.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0-preview.5 | 49 | 10/27/2026 |
First public Forms release by Vira Web Vorna. See CHANGELOG.md.