Maintainers
Pricing
Enterprise
More
More
Documentation
Try Live
Rankings
Install
Blog
Docs7
Ask
New
Add Docs
DotVVM
https://github.com/riganti/dotvvm-docs
Admin
MVVM framework for ASP.NET Core and ASP.NET
Tokens:
375,764
Snippets:
3,412
Trust Score:
8.8
License:
Apache-2.0
Update:
1 week ago
Context
Chat
Benchmark
56.5
Latest
Show doc for...
Code
Info
Show Results
Tokens:
Raw
Copy
Link
Tokens:
Raw
Copy
Link
### ControlTestHelper setup and RunPage example Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/control-development/testing-controls.md Sets up a ControlTestHelper with a custom control and runs a page to verify rendered HTML. The RunPage method executes the DotVVM page and returns parsed HTML via AngleSharp. ```csharp static readonly ControlTestHelper cth = new ControlTestHelper(config: config => { config.Markup.AddCodeControls("cc", exampleControl: typeof(MyControl)); }); [TestMethod] public async Task BasicWrappedHtmlControl() { var r = await cth.RunPage(typeof(BasicTestViewModel), """ <cc:MyControl MyProperty="my-class" /> """ ); Assert.IsNotNull(r.Html.QuerySelector("a.my-class")); } ``` -------------------------------- ### ConfigureServices method with AddBusinessPack Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/business-pack/getting-started.md Add this line in the ConfigureServices method of DotvvmStartup.cs to register all Business Pack controls under the <bp:* tag prefix and register several Business Pack resources. ```CSHARP public void ConfigureServices(IDotvvmServiceCollection options) { options.AddBusinessPack(); ... } ``` -------------------------------- ### Install DotVVM CLI and TypeScript compiler Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/respond-to-user-actions/rest-api-bindings/overview.md Install the DotVVM CLI and TypeScript compiler globally. Required for generating and compiling client classes. ```bash dotnet tool install -g DotVVM.CommandLine npm install -g typescript ``` -------------------------------- ### Add using directive for DotVVM.Bootstrap5.Controls Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/bootstrap-for-dotvvm/v5/getting-started.md Add this using directive at the beginning of the file if needed. ```CSHARP using DotVVM.Bootstrap5.Controls; ``` -------------------------------- ### Install DotVVM.AspNetCore NuGet package Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/quick-starts/modernize/add-dotvvm-to-existing-app.md Use this command in the Package Manager Console to install the DotVVM.AspNetCore package into an existing ASP.NET Core project. It also references the dependent packages DotVVM and DotVVM.Core. ```powershell Install-Package DotVVM.AspNetCore ``` -------------------------------- ### Add Bootstrap 5 configuration in DotvvmStartup.cs Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/bootstrap-for-dotvvm/v5/getting-started.md Add this line at the beginning of the Configure method to register Bootstrap 5 controls and resources. ```CSHARP config.AddBootstrap5Configuration(); ``` -------------------------------- ### Command sample - ViewModel Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/respond-to-user-actions/overview.md C# viewmodel for the command example. It defines Location and Weather properties and an async Refresh method that updates the weather data. ```CSHARP public class CommandSampleViewModel { public string Location { get; set; } public WeatherInfo Weather { get; set; } public async Task Refresh() { // you can access any information from the viewmodel Weather = _weatherService.GetWeather(Location); } } ``` -------------------------------- ### Install DotVVM project template Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/quick-starts/build/create-new-project.md Installs the DotVVM project template using the .NET CLI. Run this command before creating a new project from the command line. ```bash dotnet new --install DotVVM.Templates ``` -------------------------------- ### Install MiniProfiler packages for OWIN Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/diagnostics-and-profiling/miniprofiler.md Run these commands in the Package Manager Console to install the MiniProfiler package and the DotVVM tracing integration for OWIN. ```powershell Install-Package MiniProfiler Install-Package DotVVM.Tracing.MiniProfiler.Owin ``` -------------------------------- ### Install DotVVM.Api.Swashbuckle.Owin NuGet package Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/respond-to-user-actions/rest-api-bindings/provide-api-metadata.md Install the DotVVM.Api.Swashbuckle.Owin NuGet package to enable DotVVM integration with Swashbuckle for OWIN. ```powershell Install-Package DotVVM.Api.Swashbuckle.Owin ``` -------------------------------- ### Install DotVVM CLI globally with dotnet tool install Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/cli/install.md Run this command in the terminal to install the DotVVM Command-Line tool globally. This is the preferred installation method. ```bash dotnet tool install -g DotVVM.CommandLine ``` -------------------------------- ### Install DotVVM Electron NuGet package Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/community-add-ons/dotvvm-electron.md Install the DotVVM.Framework.Integration.Electron NuGet package using the Package Manager Console. ```powershell Install-Package DotVVM.Framework.Integration.Electron ``` -------------------------------- ### Install DotVVM.Tracing.ApplicationInsights.Owin Package Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/diagnostics-and-profiling/application-insights.md Run this command in the Package Manager Console after updating the ApplicationInsights.config file to install the DotVVM tracing reporter for OWIN. ```powershell Install-Package DotVVM.Tracing.ApplicationInsights.Owin ``` -------------------------------- ### Create DotVVM project Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/quick-starts/build/create-new-project.md Creates a new DotVVM project in the current folder using the installed template. ```bash dotnet new dotvvm ``` -------------------------------- ### Install MiniProfiler packages for ASP.NET Core Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/diagnostics-and-profiling/miniprofiler.md Run these commands in the Package Manager Console to install the MiniProfiler ASP.NET Core MVC package and the DotVVM tracing integration for ASP.NET Core. ```powershell Install-Package MiniProfiler.AspNetCore.Mvc Install-Package DotVVM.Tracing.MiniProfiler.AspNetCore ``` -------------------------------- ### Install DotVVM.DynamicData NuGet package Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/community-add-ons/dotvvm-dynamic-data.md Install the DotVVM.DynamicData NuGet package in your project to enable Dynamic Data functionality. ```powershell Install-Package DotVVM.DynamicData ``` -------------------------------- ### Example: Create master page, page, and control Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/cli/create-pages-and-controls.md These commands create a master page, a page embedded in that master, and a control with a code-behind file. The page uses the -m option to reference the master page, and the control uses -c to generate the code-behind. ```bash dotnet dotvvm add master Site ``` ```bash dotnet dotvvm add page -m Site Page1 ``` ```bash dotnet dotvvm add control -c Controls/MyControl.dotcontrol ``` -------------------------------- ### Install DotVVM.Api.Swashbuckle.AspNetCore and optional Newtonsoft package Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/respond-to-user-actions/rest-api-bindings/provide-api-metadata.md Install the DotVVM Swashbuckle integration package in the REST API project. The optional Swashbuckle.AspNetCore.Newtonsoft package is only needed for DotVVM 2.5+ with ASP.NET Core 2.x or projects using Newtonsoft.Json with ASP.NET Core 3.x. ```powershell Install-Package DotVVM.Api.Swashbuckle.AspNetCore # optional - install only if you are using Newtonsoft.Json Install-Package Swashbuckle.AspNetCore.Newtonsoft ``` -------------------------------- ### Command sample - DotHTML view Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/respond-to-user-actions/overview.md DotHTML markup for a command example. It binds a ComboBox to the Location property, displays weather data, and a Button triggers the Refresh command. ```DOTHTML <dot:ComboBox SelectedValue="{value: Location}" ... /> <p>Current temperature: {{value: Weather.Temperature}} °C</p> <p>Conditions: {{value: Weather.Conditions}}</p> <dot:Button Text="Refresh weather" Click="{command: Refresh()}" /> ``` -------------------------------- ### menu.xml example Source: https://github.com/riganti/dotvvm-docs/wiki/Docs-repo-structure Shows the XML structure for the docs menu. Supports DocStaticMenuItem, DocPageMenuItem, and DocControlCategory elements. The RouteName attribute must match the filesystem structure. ```xml <DocMenu Version="2.0"> <DocStaticMenuItem RouteName="Home" PageName="Home" /> <!-- link to docs home page --> <DocPageMenuItem RouteName="section1" PageName="Section 1"> <!-- section header only --> <DocPageMenuItem RouteName="page1" PageName="Page 1" /> <!-- this will point to Pages/section1/page1.md file --> <DocControlCategory Category="builtin" /> <!-- under this section, all controls from the "builtin" category will be listed --> </DocPageMenuItem> </DocMenu> ``` -------------------------------- ### Install ApplicationInsights.OwinExtensions Package Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/diagnostics-and-profiling/application-insights.md Run this command in the Package Manager Console to install the OWIN extensions for Application Insights after adding Application Insights to your ASP.NET project. ```powershell Install-Package ApplicationInsights.OwinExtensions ``` -------------------------------- ### Import namespaces with @import directive Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/data-binding/overview.md Use @import to import namespaces, similar to C# using. This example imports MyWebApp.Resources.Web to access resource strings. ```DOTHTML @import MyWebApp.Resources.Web {{resource: Strings1.SomeResource}} {{resource: Strings2.SomeResource}} ``` -------------------------------- ### Updating the generated clients Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/cli/generate-rest-api-clients.md Example command that regenerates all API clients defined in the config file. ```bash dotnet dotvvm api regen ``` -------------------------------- ### Registering the API client Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/cli/generate-rest-api-clients.md Example command that creates an API client from a local Swagger URL, specifying the namespace, C# client path, and TypeScript client path. ```bash dotnet dotvvm api create http://localhost:43852/swagger/v1/swagger.json DotVVM2.Demo.RestApi.Api Api/ApiClient.cs wwwroot/Scripts/ApiClient.ts ``` -------------------------------- ### OrderDetailViewModel with security protections Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/security/recommendations-for-viewmodels.md Shows how to protect viewmodel data using bind directions, signing protection, and business-layer permission checks. The OrderService must throw exceptions for unauthorized access, which can be handled by exception filters. The example includes loading data on the first request and handling postbacks. ```csharp public class OrderDetailViewModel : DotvvmViewModelBase { private readonly OrderService orderService; // the user won't be able to change the OrderId in the viewmodel, DotVVM always reads it from the route // marking the setter as private is optional, but we like it as it helps to express that the value cannot be set from the client-side // however, the user can change the ID in the address bar of the browser // we'll have to make sure the business layer throw an exception if the user cannot display the order [FromRoute("Id")] public int OrderId { get; private set; } // we are using signing protection, so the user won't be able to change the value // otherwise, we'd have to load the associated CustomerId on every request [Protect(ProtectMode.SignData)] public int CustomerId { get; set; } // this object contains ONLY the properties that are actually needed by the UI // the bind direction tells that this data is sent to the client on the first request, and cannot be changed next time [Bind(Direction.ServerToClientFirstRequest)] public UserBasicInfoDto UserInfo { get; } // this object contains ONLY the properties that are actually needed by the UI public OrderDetailsDto OrderDetails { get; set; } // private set sets the bind direction to ServerToClient // the user will be able to change the value on the client, so the "Change Shipping Address" button may appear public bool CanChangeShippingAddress { get; private set; } public OrderDetailViewModel(OrderService orderService) { this.orderService = orderService; } public override Task Load() { // we get this info on all requests, including postbacks CanChangeShippingAddress = orderService.CanChangeShippingAddress(OrderId); base.Load(); } public override Task PreRender() { if (!Context.IsPostBack) { // the OrderService should verify the user has permissions to get the order details // it can throw e.g. UnauthorizedAccessException which will be caught by exception filter OrderDetails = orderService.GetOrderDetails(OrderId); // this property is not used on postbacks, it's used only on the first request // DotVVM will ignore it on postbacks, so it doesn't need to be protected UserInfo = orderService.GetUserInfo(OrderId); // this property is needed on postbacks, so we are signing it // the user won't be able to change it // alternatively, we'd have to load the CustomerId on every request (in the Load phase) CustomerId = orderService.GetOrderCustomer(OrderId).CustomerId; } base.PreRender(); } // not all properties of OrderDetails can be changed (e. g. order creation date) // the viewmodel and the UI is designed to perform only "possible and meaningful" actions, instead of just saving the entire OrderDetailsDto public void ChangeShippingAddress() { // we have created a special DTO that represents address change request var newAddress = new AddressChangeDto() { Street = OrderDetails.ShippingAddress.Street, ... }; // the OrderService must check the permissions // * can the user modify that order? // * can the address be changed in this state of the order? orderService.ChangeShippingAddress(OrderId, newAddress) } ... } ``` -------------------------------- ### AddressViewModel and Customer classes (AddressViewModel.cs) Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/data-binding/binding-context.md ViewModel for the Address example, containing a Customer property with a Name property. ```CSHARP public class AddressViewModel { public Customer Customer { get; set; } } public class Customer { public string Name { get; set; } } ``` -------------------------------- ### Example PropertyDisplayNames.resx content Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/community-add-ons/dotvvm-dynamic-data.md Shows the structure of PropertyDisplayNames.resx. The provider looks for keys in order 'TypeName_PropertyName' then 'PropertyName' to use as display names. ```text Resource Key Value ------------------------------------------------------------------------------------- FirstName Given Name ManagerDTO_FirstName First Name ... ``` -------------------------------- ### EnableDotvvmIntegration in Swashbuckle configuration (Startup.cs) Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/respond-to-user-actions/rest-api-bindings/provide-api-metadata.md Configure DotVVM API options and enable DotVVM integration in the Swashbuckle setup. The TODO comment indicates where to configure DotVVM Swashbuckle options. ```csharp services.Configure<DotvvmApiOptions>(opt => { // TODO: configure DotVVM Swashbuckle options }); services.AddSwaggerGen(options => { ... options.EnableDotvvmIntegration(); }); ``` -------------------------------- ### Markup control example: AddressEditor Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/control-development/overview.md Markup controls are DOTHTML snippets stored in their own file and reused across pages. This example shows a control with a Text property and a RouteLink, used for building reusable UI like an address editor. ```DOTHTML @viewmodel object @property string Text <dot:RouteLink RouteName="MyRoute" Text={value: _control.Text} ``` -------------------------------- ### Static Command Example - CommandSample.dothtml Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/respond-to-user-actions/overview.md DotHTML view using a static command to call a service method. The viewmodel is not sent to the server; all arguments must be passed explicitly. Uses the fetch API for AJAX communication. ```DOTHTML @service _weatherService = MyApp.Services.WeatherService <dot:ComboBox SelectedValue="{value: Location}" ... /> <p>Current temperature: {{value: Weather.Temperature}} °C</p> <p>Conditions: {{value: Weather.Conditions}}</p> <!-- You need to pass all arguments to the called method - the viewmodel is not sent to the server --> <dot:Button Text="Increment" Click="{staticCommand: Weather = _weatherService.GetWeather(Location)}" /> ``` -------------------------------- ### Install DotVVM Application Insights tracing package Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/diagnostics-and-profiling/application-insights.md Run this command in the Package Manager Console to install the DotVVM tracing package for Application Insights in an ASP.NET Core project. ```powershell Install-Package DotVVM.Tracing.ApplicationInsights.AspNetCore ``` -------------------------------- ### Register Select2 configuration in DotvvmStartup.cs Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Controls/bootstrap5-select2/Select2MultiSelect/control.md Install the DotVVM.Controls.Bootstrap5.Select2 package and register it after Bootstrap 5 configuration in DotvvmStartup.cs. ```CSHARP config.AddBootstrap5Configuration(); config.AddBootstrap5Select2Configuration(); ``` -------------------------------- ### Register DotVVM in Startup.cs (older method) Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/quick-starts/modernize/add-dotvvm-to-existing-app.md Add these lines in the ConfigureServices and Configure methods of Startup.cs to register DotVVM services and middleware. The Configure method receives the content root path and returns a configuration object. ```csharp // Startup.cs ... public void ConfigureServices() { ... services.AddDotVVM<DotvvmStartup>(); } public void Configure(IWebHostEnvironment env) { ... var config = app.UseDotVVM<DotvvmStartup>(env.ContentRootPath); } ``` -------------------------------- ### Override Bootstrap CSS variables Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/bootstrap-for-dotvvm/v5/getting-started.md Example of overriding global and component-specific CSS variables in your CSS file. ```CSS /* Override global variable */ body { --bs-body-font-size: 2rem; } /* Override accordion variable */ .accordion-button:not(.collapsed) { --bs-accordion-active-bg: #00ff90 !important; } ``` -------------------------------- ### Configure Google authentication in Startup.cs Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/security/authentication-and-authorization/aspnetcore.md Add this code in the Startup.cs file to configure Google authentication. It sets the ClientId and ClientSecret from configuration and uses DotvvmAuthenticationHelper.ApplyRedirectResponse in the OnRedirectToAuthorizationEndpoint event to handle the redirect response. ```CSHARP services.AddAuthentication(...) ... .AddGoogle(googleOptions => { googleOptions.ClientId = configuration["Google:ClientId"]; googleOptions.ClientSecret = configuration["Google:ClientSecret"]; googleOptions.Events = new OAuthEvents() { OnRedirectToAuthorizationEndpoint = context => DotvvmAuthenticationHelper.ApplyRedirectResponse(context.HttpContext, context.RedirectUri) }; }) ``` -------------------------------- ### DotvvmStartup class implementation Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/quick-starts/modernize/add-dotvvm-to-existing-app.md This class implements IDotvvmStartup and IDotvvmServiceConfigurator to configure DotVVM routes, controls, resources, and services. The Configure method is where you register routes, controls, and resources; ConfigureServices adds default temp storages. ```csharp using DotVVM.Framework; using DotVVM.Framework.Configuration; using DotVVM.Framework.ResourceManagement; using DotVVM.Framework.Routing; namespace DotvvmDemo { public class DotvvmStartup : IDotvvmStartup, IDotvvmServiceConfigurator { public void Configure(DotvvmConfiguration config, string applicationPath) { // register your routes, controls and resources here } public void ConfigureServices(IDotvvmServiceCollection services) { services.AddDefaultTempStorages("Temp"); } } } ``` -------------------------------- ### Custom attributes usage example Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/control-development/code-only-controls.md Shows how custom attributes like style, class, and placeholder are passed to the control. The default AddAttributesToRender implementation adds these attributes to the HtmlWriter. ```DOTHTML <cc:SimpleTextBox Text="{value: FirstName}" style="border: none" class="txb1" placeholder="Enter first name" /> ``` -------------------------------- ### Custom route strategy for naming conventions Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/routing/auto-discover-routes.md Override GetRouteUrl to implement naming conventions: pages ending with 'detail' get an 'id' parameter. This example shows how to add the parameter. ```csharp public class MyRouteStrategy : DefaultRouteStrategy { protected override string GetRouteUrl(RouteStrategyMarkupFileInfo file) { var url = base.GetRouteUrl(file); if (url.EndsWith("detail", StringComparison.CurrentCultureIgnoreCase)) { // add "id" parameter to the route return url + "/{id}"; } } } ``` -------------------------------- ### Place title on top of the table Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/business-pack/exporting-data.md Sets the start address and adds a rule to write a title cell with custom font size. ```CSHARP var settings = GridViewExportExcelSettings<T>.Default .WithStartAddress(3, 1) .WithRule(onDataExported: (worksheet, _) => { var cell = worksheet.Cell(1, 1); cell.SetValue("Time Tracking report"); cell.Style.Font.SetFontSize(20); }); ``` -------------------------------- ### Post-process existing properties with WrapExpression resolver Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/control-development/binding-extensibility.md Register a resolver with this signature to post-process binding properties after they are resolved. The example increments all bindings by one, but the technique can be used for more powerful metaprogramming. ```CSHARP public ParsedExpressionBindingProperty WrapExpression(ParsedExpressionBindingProperty prop, some other dependencies) { return new ParsedExpressionBindingProperty(Expression.Add(prop.Expression, Expression.Constant(1))); } ``` -------------------------------- ### Example JSON posted to server with IfInPostBackPath Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/viewmodels/binding-direction.md Shows the JSON structure posted to the server when using IfInPostBackPath: only the clicked user object is populated, others are null. ```json { "Users": [ ..., null, { "UserName": "the user being edited", "IsEnabled": true }, null, ... ] } ``` -------------------------------- ### Register DotVVM in Program.cs (new method) Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/quick-starts/modernize/add-dotvvm-to-existing-app.md Add these lines in Program.cs to register DotVVM services and middleware in the request pipeline. The builder.Services.AddDotVVM<DotvvmStartup>() call adds services, and app.UseDotVVM<DotvvmStartup>() adds the middleware. ```csharp // Program.cs ... builder.Services.AddDotVVM<DotvvmStartup>(); ... app.UseDotVVM<DotvvmStartup>(); ... ``` -------------------------------- ### Register Tailwind controls in DotvvmStartup.cs Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/tailwind-for-dotvvm/getting-started.md Add this line in the Configure method of DotvvmStartup.cs to register all Tailwind controls under the <t:* tag prefix and register required Tailwind resources and server-side styles. ```csharp public void Configure(DotvvmConfiguration config, string applicationPath) { config.AddTailwindControls(); ... } ``` -------------------------------- ### Register DotVVM-related services in DotvvmStartup Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/configuration/dependency-injection/aspnetcore.md Registers DotVVM-specific services like IUploadedFileStorage using the ConfigureServices method in DotvvmStartup. Example adds temp file storages. ```CSHARP public void ConfigureServices(IDotvvmServiceCollection options) { options.AddTempFileStorages("Temp"); } ``` -------------------------------- ### Create project directory and navigate Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/quick-starts/build/create-new-project.md Creates a directory for the new DotVVM app and navigates into it. ```bash mkdir SampleDotvvmApp cd SampleDotvvmApp ``` -------------------------------- ### Configure DotVVM AMP options in DotvvmStartup.cs Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/community-add-ons/dotvvm-amp.md Configures how the AMP package handles invalid constructs. This example sets attribute and HTML tag handling to LogAndIgnore and removes forbidden !important styles. Place in the ConfigureServices method. ```CSHARP public void ConfigureServices(IDotvvmServiceCollection options) { ... options.AddDotvvmAmpSupport((config,_) => { config.AttributeHandlingMode = ErrorHandlingMode.LogAndIgnore; config.HtmlTagHandlingMode = ErrorHandlingMode.LogAndIgnore; config.StyleRemoveForbiddenImportant = true; }); ... } ``` -------------------------------- ### Enable session in OWIN Startup.cs Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/quick-starts/modernize/interop/session-and-cookies.md Place this code in Startup.cs before any middleware is registered to enable session state. Without it, HttpContext.Current.Session would be null. ```C# app.Use((context, next) => { var httpContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName); httpContext.SetSessionStateBehavior(SessionStateBehavior.Required); return next(); }); app.UseStageMarker(PipelineStage.MapHandler); ``` -------------------------------- ### Return a file with Context.ReturnFileAsync Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/upload-and-download-files/return-file-from-viewmodel.md Saves the file to temporary storage and redirects the user to a special URL to download it. The file ID is a random Guid. Interrupts the current command response. ```CSHARP await Context.ReturnFileAsync(file, "export.pdf", "application/pdf"); ``` -------------------------------- ### Install TypeScript globally with npm install -g typescript Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/client-side-development/typescript-declarations.md Installs the TypeScript CLI tool globally. Check if it is already installed by running `tsc --version`. ```bash npm install -g typescript ``` -------------------------------- ### Run DotVVM project Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/quick-starts/build/create-new-project.md Runs the DotVVM application. ```bash dotnet run ``` -------------------------------- ### SampleRequestTracer implementation Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/diagnostics-and-profiling/custom-tracing.md Implements IRequestTracer to log event timings to standard output. Starts a Stopwatch on BeginRequest and prints elapsed time for each event. Both EndRequest overloads are implemented for success and error cases. ```csharp public class SampleRequestTracer : IRequestTracer { Stopwatch sw = new Stopwatch(); public Task TraceEvent(string eventName, IDotvvmRequestContext context) { if (eventName == "BeginRequest") sw.Start(); Console.WriteLine($"Trace {sw.Elapsed}: {eventName}"); return Task.CompletedTask; } public Task EndRequest(IDotvvmRequestContext context) { Console.WriteLine($"Trace {sw.Elapsed}: End of request"); return Task.CompletedTask; } public Task EndRequest(IDotvvmRequestContext context, Exception exception) { Console.WriteLine($"Trace {sw.Elapsed}: Error occurred ({exception})"); return Task.CompletedTask; } } ``` -------------------------------- ### Remove DotNetCliToolReference from .csproj Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/cli/install.md If you used ASP.NET Core with .NET Core 2.2 or lower, your .csproj file may contain this DotNetCliToolReference entry. It can be safely removed because DotNetCliToolReference is deprecated. ```xml <ItemGroup> <DotNetCliToolReference Include="DotVVM.CommandLine" Version="2.0.0" /> </ItemGroup> ``` -------------------------------- ### Using prefixed capability properties in DotHTML Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/control-development/control-capabilities.md Shows how to use the prefixed properties from the ExampleCapability on a control. The prefix 'Example-' is added to each property name. ```DOTHTML <cc:MyControl Example-MyProperty="A" Example-MyAnotherProperty="something" /> ``` -------------------------------- ### Common syntax for dotnet dotvvm add Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/cli/create-pages-and-controls.md All creation commands follow this structure. The target is an optional path to the DotVVM project; if omitted, the current working directory is used. The kind specifies the item type, options are kind-specific, and the name is the item's name. ```bash dotnet dotvvm add [<target>] <kind> [options] <name> ``` -------------------------------- ### Lint diagnostic output example Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/cli/lint-dothtml.md Example output from the linter showing a missing @viewModel directive. Diagnostics include the markup file and location. ```text Views/Errors/MissingViewModel.dothtml(1,0): error: The @viewModel directive is missing in the page 'Views/Errors/MissingViewModel.dothtml'! 1: <!DOCTYPE html> 2: <html> 3: <body> 4: Content 5: </body> 6: </html> ``` -------------------------------- ### DotvvmStartup class implementing IDotvvmStartup and IDotvvmServiceConfigurator Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/configuration/overview.md Default project template for the DotvvmStartup class. Implements both interfaces and shows how to configure routes, custom controls, and script resources. The Configure method is executed during build in Visual Studio, so avoid side effects. ```CSHARP public class DotvvmStartup : IDotvvmStartup, IDotvvmServiceConfigurator { // IDotvvmStartup implementation public void Configure(DotvvmConfiguration config, string applicationPath) { ConfigureRoutes(config, applicationPath); ConfigureControls(config, applicationPath); ConfigureResources(config, applicationPath); } private void ConfigureRoutes(DotvvmConfiguration config, string applicationPath) { config.RouteTable.Add("Default", "", "Views/default.dothtml"); // Uncomment the following line to auto-register all dothtml files in the Views folder // config.RouteTable.AutoDiscoverRoutes(new DefaultRouteStrategy(config)); } private void ConfigureControls(DotvvmConfiguration config, string applicationPath) { // register code-only controls and markup controls config.Markup.AddCodeControls("cc", typeof(MyCustomControl)); } private void ConfigureResources(DotvvmConfiguration config, string applicationPath) { // register custom resources and adjust paths to the built-in resources config.Resources.Register("myscript", new ScriptResource() { Location = new FileResourceLocation("~/wwwroot/Scripts/myscript.js") }); } // IDotvvmServiceConfigurator implementation public void ConfigureServices(IDotvvmServiceCollection services) { // configure all DotVVM-related services services.AddDefaultTempStorages("Temp"); } } ``` -------------------------------- ### Static Command ViewModel - CommandSampleViewModel.cs Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/respond-to-user-actions/overview.md C# viewmodel class with Location and Weather properties used by the static command example. The viewmodel is not sent to the server when a static command is invoked. ```CSHARP public class CommandSampleViewModel { public string Location { get; set; } public WeatherInfo Weather { get; set; } } ``` -------------------------------- ### Initialize npm project Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/client-side-development/js-directive/use-typescript-to-declare-modules.md Creates a default package.json file in the root folder of the app. Run this command if package.json is not already present. ```bash npm init -y ``` -------------------------------- ### Install rollup and plugins Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/client-side-development/js-directive/use-typescript-to-declare-modules.md Install rollup and its plugins as dev dependencies. Required for bundling TypeScript modules into ES6 modules for DotVVM. ```bash npm install --save-dev rollup rollup-plugin-commonjs rollup-plugin-node-resolve rollup-plugin-terser @rollup/plugin-typescript ``` -------------------------------- ### Syntax for adding a page or master page Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/cli/create-pages-and-controls.md Use these commands to add a page or master page. The -m option specifies the master page for the new page, and -d sets the directory (default: Views/). ```bash dotnet dotvvm add [<target>] page [options] <name> ``` ```bash dotnet dotvvm add [<target>] master [options] <name> ``` -------------------------------- ### Register TodoList route in DotVVM startup Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/quick-starts/build/build-to-do-list-app.md Adds the TodoList page route to the DotVVM configuration. Place this line inside the ConfigureRoutes method in DotvvmStartup.cs. After registering, run the app and navigate to /todos to see the to-do list application. ```csharp config.RouteTable.Add("TodoList", "todos", "Views/TodoList.dothtml", new { }); ``` -------------------------------- ### Add DotVVM Private NuGet Feed via command line Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/dotvvm-for-visual-studio/dotvvm-private-nuget-feed.md Run this command to manually add the DotVVM Private NuGet Feed as a NuGet source. Replace YOUR EMAIL ADDRESS and YOUR PASSWORD with your DotVVM.com credentials. Requires .NET SDK to be installed. ```bash dotnet nuget add source "https://www.dotvvm.com/nuget/v3/index.json" --name "Dotvvm Feed" --username "YOUR EMAIL ADDRESS" --password "YOUR PASSWORD" ``` -------------------------------- ### Composite control example: MyLink Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/control-development/overview.md A simple CompositeControl that creates a RouteLink to 'MyRoute' with the given text. The GetContents method returns a control tree and can perform any processing, offering more flexibility than DotHTML at the cost of HTML syntax. Composite controls are recommended as the default way of writing code-only controls. ```CSHARP /// Simple CompositeControl example /// * creates a link to MyRoute with the given text public class MyLink : CompositeControl { public DotvvmControl GetContents( ValueOrBinding<string> text, ) { return new RouteLink() .SetProperty(r => r.RouteName, "MyRoute") .SetProperty(r => r.Text, text); } } ``` -------------------------------- ### Bind URLs to plain hyperlinks for dynamic menu Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/data-binding/resource-binding.md For fully dynamic menus, build URLs on the server and include them in the viewmodel, then bind to plain hyperlinks. This example uses value binding for Url and Title. ```DOTHTML <dot:Repeater DataSource="{value: MenuItems}" WrapperTagName="ul"> <li> <a href="{value: Url}">{{value: Title}}</a> </li> </dot:Repeater> ``` -------------------------------- ### Sample directory structure Source: https://github.com/riganti/dotvvm-docs/wiki/Docs-repo-structure Shows the required files inside each sample directory, including sample.json and sample.md. ```text Directory Controls/builtin/TextBox/Sample1: sample.json // contains list of files in the sample and other metadata (required) sample.md // info about the sample (required) ... // any other files listed in sample.json ``` -------------------------------- ### RoleView wrapper tag rendering example Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Controls/builtin/RoleView/output.md Shows the default wrapper tag structure rendered by the RoleView control. The control renders the contents of either IsMemberTemplate or IsNotMemberTemplate inside a div, and the wrapper tag can be customized using RenderWrapperTag and WrapperTagName properties. ```DOTHTML <div> <!-- Contents of the IsMemberTemplate or IsNotMemberTemplate --> </div> ``` -------------------------------- ### Install TypeScript and type definitions Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/client-side-development/js-directive/use-typescript-to-declare-modules.md Installs TypeScript, DotVVM type definitions, and Knockout type definitions as dev dependencies. Required for writing JS directive modules in TypeScript. ```bash npm install --save-dev typescript dotvvm-types @types/knockout ``` -------------------------------- ### Install DotVVM type declarations with npm install --save-dev dotvvm-types Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/client-side-development/typescript-declarations.md Adds the DotVVM TypeScript declaration files as a dev dependency to enable type checking and IntelliSense for DotVVM client-side APIs and Knockout observables. ```bash npm install --save-dev dotvvm-types ``` -------------------------------- ### Syntax for adding a ViewModel Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/cli/create-pages-and-controls.md Use this command to add a ViewModel. The -d option sets the directory (default: ViewModels/), and -b specifies the base class. ```bash dotnet dotvvm add [<target>] viewmodel [options] <name> ``` -------------------------------- ### Define a BindingExtensionParameter subclass Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/control-development/binding-extension-parameters.md Start by creating a class that inherits from BindingExtensionParameter. The class will implement the abstract members required by the API. ```CSHARP public class InjectedServiceExtensionParameter : BindingExtensionParameter { ... } ``` -------------------------------- ### Register AirDatePicker in DotvvmStartup.cs Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Controls/bootstrap5-airdatepicker/AirDatePicker/control.md Install the DotVVM.Controls.Bootstrap5.AirDatePicker package and register it in DotvvmStartup.cs after Bootstrap 5 configuration. ```CSHARP config.AddBootstrap5Configuration(); config.AddBootstrap5AirDatePickerConfiguration(); ``` -------------------------------- ### Format of sample.json Source: https://github.com/riganti/dotvvm-docs/wiki/Docs-repo-structure JSON structure for sample metadata, including file list and regex replacements for master pages. ```json { "files": [ // list of files in the sample { "name": "page.dothtml" }, { "name": "ViewModel.cs" }, { "name": "master.dotmaster", "replacements": [ // regex replacements that are done in the file before showing on the site { "pattern": "(\\<!.*\\<body\\>)", "replacement": "", "singleLine": true }, { "pattern": "(\\</body\\>.*)$", "replacement": "", "singleLine": true } ] } ] } ``` -------------------------------- ### Configure Facebook authentication in Startup.cs Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/security/authentication-and-authorization/owin.md Configure the Facebook authentication middleware in the OWIN startup. The OnApplyRedirect handler uses DotvvmAuthenticationHelper.ApplyRedirectResponse to properly handle the redirect for DotVVM. ```CSHARP app.UseFacebookAuthentication(new FacebookAuthenticationOptions() { ... Provider = new FacebookAuthenticationProvider() { OnApplyRedirect = context => DotvvmAuthenticationHelper.ApplyRedirectResponse(context.OwinContext, context.RedirectUri), ... } }); ``` -------------------------------- ### Repeater server-side rendering Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/server-side-rendering.md In server-side rendering mode, the Repeater renders each row directly into the HTML output, as shown in this example. ```DOTHTML <!-- Repeater in server side rendering --> <tbody data-bind="foreach: Rows"> <tr> <td>Row 1</td> </tr> <tr> <td>Row 2</td> </tr> <tr> <td>Row 3</td> </tr> </tbody> ``` -------------------------------- ### Implement IDotvvmServiceConfigurator Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/upgrading-from-older-versions/from-1-1-to-2-0.md Make the DotvvmStartup class implement IDotvvmServiceConfigurator in addition to IDotvvmStartup. ```csharp public class DotvvmStartup : IDotvvmStartup, IDotvvmServiceConfigurator { ... } ``` -------------------------------- ### LoginViewModel with manual credential verification Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/security/authentication-and-authorization/aspnetcore.md Verifies user credentials and creates a ClaimsIdentity with roles, then signs in via Context.GetAuthentication().SignInAsync. Redirects to returnUrl or default route. Includes placeholder methods for verification and identity creation. ```CSHARP public class LoginViewModel : DotvvmViewModelBase { public string UserName { get; set; } public string Password { get; set; } [FromQuery("returnUrl")] public string ReturnUrl { get; set; } public async Task Login() { if (VerifyCredentials(UserName, Password)) { // the CreateIdentity is your own method which creates the IIdentity representing the user var identity = CreateIdentity(UserName); await Context.GetAuthentication().SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity)); if (!string.IsNullOrEmpty(ReturnUrl)) { Context.RedirectToLocalUrl(ReturnUrl); } else { Context.RedirectToRoute("Default"); } } else { // show the error to the user } } private bool VerifyCredentials(string username, string password) { // verify credentials and return true or false } private ClaimsIdentity CreateIdentity(string username) { var identity = new ClaimsIdentity( new[] { new Claim(ClaimTypes.Name, username), // add claims for each user role new Claim(ClaimTypes.Role, "administrator"), }, CookieAuthenticationDefaults.AuthenticationScheme); return identity; } } ``` -------------------------------- ### EditCompanyViewModel and Company classes (EditCompanyViewModel.cs) Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/data-binding/binding-context.md ViewModel for the EditCompany example, containing a Company property and a DeleteAddress method that accepts a Company parameter. ```CSHARP public class EditCompanyViewModel { public Company Company { get; set; } public void DeleteAddress(Company company) { // ... } } public class Company { public int Id { get; set; } public string Name { get; set; } } ``` -------------------------------- ### Configure default temp storage in DotvvmStartup.cs Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/upload-and-download-files/return-file-from-viewmodel.md Registers uploaded and returned file storage using the local file system. The commented lines show equivalent individual registrations. ```CSHARP public void ConfigureServices(IDotvvmServiceCollection options) { options.AddDefaultTempStorage("temp"); // this is equivalent to registering both uploaded and returned file storage // options.AddUploadedFileStorage("temp"); // options.AddReturnedFileStorage("temp"); } ``` -------------------------------- ### GridView with IfInPostBackPath example Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/viewmodels/binding-direction.md A GridView with a button that calls EnableDisableUser, used with [Bind(Direction.IfInPostBackPath)] on the Users collection to transfer only the clicked row. ```DOTHTML <dot:GridView DataSource="{value: Users}"> <dot:GridViewTextColumn ValueBinding="{value: UserName}" HeaderText="User Name" /> <dot:GridViewCheckBoxColumn ValueBinding="{value: IsEnabled}" HeaderText="Is Enabled?" /> <dot:GridViewTemplateColumn> <dot:LinkButton Text="Enable / Disable" Click="{command: _parent.EnableDisableUser(_this)}" /> </dot:GridViewTemplateColumn> </dot:GridView> ``` -------------------------------- ### Configure AfterApplicationStart with CompileInParallel and BackgroundCompilationDelay Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/configuration/view-compilation-modes.md Extends the previous configuration to disable parallel compilation and delay background compilation by 30 seconds, useful for machines with few CPU cores or when other startup tasks need to run first. ```csharp var dotvvmConfiguration = app.UseDotVVM<DotvvmStartup>(env.ContentRootPath, modifyConfiguration: config => { if (!config.Debug) { config.Markup.ViewCompilation.Mode = ViewCompilationMode.AfterApplicationStart; // we are running on a machine with only 2 CPU cores, and the apps loads some data from the database after the startup // compile on one core and delay it for 30 seconds config.Markup.ViewCompilation.CompileInParallel = false; config.Markup.ViewCompilation.BackgroundCompilationDelay = TimeSpan.FromSeconds(30); } }); ``` -------------------------------- ### Invalid binding in attribute value Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/data-binding/overview.md This example shows that a binding expression cannot be combined with other content in an HTML attribute value. The whole attribute value must be a data binding. ```DOTHTML <!-- This does not work - the whole attribute value has to be a data binding! --> <a class="this-does-not-work {value: DoNotUseThis}">...</a> ``` -------------------------------- ### Register resources in Startup.cs Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/control-development/interactivity.md Registers CSS and JavaScript resources for a custom control in DotVVM. Set the Build Action of the files to Embedded Resource and replace the placeholder assembly and URL with your project's names. The dependencies ensure jQuery, Bootstrap, and DotVVM are loaded before the control's scripts. ```CSHARP // plugin css file configuration.Resources.Register("myDatePicker-css", new StylesheetResource() { EmbeddedResourceAssembly = "", // use your project name Url = "ProjectName.Folder.Folder2.datepicker.css" // full name of the embedded resource }); // plugin javascript file configuration.Resources.Register("myDatePicker-js", new ScriptResource() { EmbeddedResourceAssembly = "", // use your project name Url = "ProjectName.Folder.Folder2.datepicker.js", // full name of the embedded resource Dependencies = new[] { "jquery", "bootstrap" } }); // custom knockout handler file configuration.Resources.Register("myDatePicker", new ScriptResource() { EmbeddedResourceAssembly = "", // use your project name Url = "ProjectName.Folder.Folder2.myDatePicker.js", // full name of the embedded resource Dependencies = new[] { "dotvvm", "myDatePicker-js", "myDatePicker-css" } }); ``` -------------------------------- ### Configure cookie authentication in ASP.NET Core Startup Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/security/authentication-and-authorization/aspnetcore.md Registers cookie authentication with DotVVM-compatible redirect and status code responses. Must be registered before DotVVM in the pipeline. Uses DotvvmAuthenticationHelper to handle AJAX redirects. ```CSHARP public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(sharedOptions => { sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { options.Events = new CookieAuthenticationEvents { OnRedirectToReturnUrl = c => DotvvmAuthenticationHelper.ApplyRedirectResponse(c.HttpContext, c.RedirectUri), OnRedirectToAccessDenied = c => DotvvmAuthenticationHelper.ApplyStatusCodeResponse(c.HttpContext, 403), OnRedirectToLogin = c => DotvvmAuthenticationHelper.ApplyRedirectResponse(c.HttpContext, c.RedirectUri), OnRedirectToLogout = c => DotvvmAuthenticationHelper.ApplyRedirectResponse(c.HttpContext, c.RedirectUri) }; options.LoginPath = "/login"; }); ... } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseAuthentication(); app.UseAuthorization(); ... app.UseDotVVM<DotvvmStartup>(...); } ``` -------------------------------- ### Initialize TypeScript configuration Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/respond-to-user-actions/rest-api-bindings/overview.md Create a tsconfig.json file in the project directory when building with tsc manually or using a bundler. ```bash cd <your-project-directory> tsc init ``` -------------------------------- ### Call async function from DotVVM Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/client-side-development/js-directive/call-js-from-dotvvm.md Invoke the async function using `_js.InvokeAsync<int>` and access the `Result` property to get the returned value. ```DOTHTML <dot:Button Text="Refresh" Click="{staticCommand: Count = _js.InvokeAsync<int>("getNumberOfAttendees").Result}" /> ``` -------------------------------- ### Validation attributes example Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/validation/overview.md Apply validation attributes from System.ComponentModel.DataAnnotations to viewmodel properties. Attributes can specify ErrorMessage directly or use ErrorMessageResourceType and ErrorMessageResourceName for localized messages. ```CSHARP [Required] public string NewTaskTitle { get; set; } [Required(ErrorMessage = "The password is required!")] public string Password { get; set; } [Required(ErrorMessageResourceType = typeof(MyResourceFile), ErrorMessageResourceName = "PasswordLabel")] public string Password { get; set; } ``` -------------------------------- ### Registering custom services in UseDotVVM options Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/upgrading-from-older-versions/from-1-0-to-1-1.md Replaces DotvvmConfiguration.ServiceLocator registrations. Requires importing Microsoft.Extensions.DependencyInjection. Services can only be registered in the UseDotVVM method; adding services later may fail after the first service is resolved. ```CSHARP var dotvvmConfiguration = app.UseDotVVM<DotvvmStartup>(applicationPhysicalPath, options: options => { options.Services.AddSingleton<IViewModelLoader>(serviceProvider => new WindsorViewModelLoader(container)); }); ``` -------------------------------- ### Provide custom IUploadedFileStorage implementation Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/upload-and-download-files/upload-files.md Registers a custom implementation of IUploadedFileStorage, for example to store files in Azure Blob Storage. Replace MyCustomUploadedFileStorage with your own class. ```csharp public void ConfigureServices(IDotvvmServiceCollection options) { options.Services.AddSingleton<IUploadedFileStorage, MyCustomUploadedFileStorage>(); } ``` -------------------------------- ### Custom route strategy overriding GetRouteUrl Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/routing/auto-discover-routes.md Override GetRouteUrl to customize the URL for specific routes. This example adds a parameter to 'contact-us' and maps 'home' to the root. ```csharp public class MyRouteStrategy : DefaultRouteStrategy { protected override string GetRouteUrl(RouteStrategyMarkupFileInfo file) { var url = base.GetRouteUrl(file); if (url == "contact-us") { // the contact-us route should have a parameter return url + "/{ContactReason}"; } else if (url == "home") { // instead of /home, we need the route to be directly in the website root / return ""; } } } ``` -------------------------------- ### Register custom binding type in ControlResolverBase.BindingTypes Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/control-development/binding-extensibility.md Register the custom binding type in the collection using BindingParserOptions.Create. This example registers ResourceBindingExpression<> with the ParserConstants.ResourceBinding key. ```CSHARP ControlResolverBase.BindingTypes.Add(ParserConstants.ResourceBinding, BindingParserOptions.Create(typeof(ResourceBindingExpression<>))); ``` -------------------------------- ### Register UploadedFileStorage in ConfigureServices Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Controls/businesspack/FileUpload/control.md Required setup to define where temporary uploaded files are stored. Register the UploadedFileStorage in the IDotvvmServiceConfigurator, e.g., in the application directory or temp directory. ```csharp public void ConfigureServices(IDotvvmServiceCollection options) { options.AddUploadedFileStorage("App_Data/Temp"); } ``` -------------------------------- ### Register DotVVM middleware in Configure Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/configuration/overview.md Call this in the Configure method of an ASP.NET Core Startup.cs to initialize the DotVVM middleware. The DotvvmStartup type argument identifies the class containing DotVVM configuration. ```csharp var config = app.UseDotVVM<DotvvmStartup>(); ``` -------------------------------- ### Match by ancestor tag Source: https://github.com/riganti/dotvvm-docs/blob/5.0/Pages/concepts/dothtml-markup/server-side-styles.md Match styles based on tags on ancestor controls. The example sets RenderSpanElement to true for Literal controls that have an ancestor with the tag 'literal-spans'. ```CSHARP c.Styles.Register<Literal>(c => c.HasAncestorWithTag("literal-spans")) .SetProperty(c => c.RenderSpanElement, true); ```