├── Sz.BlazorRerenderReducers ├── Client │ ├── ProductGridDemo │ │ ├── ProductShareLink.razor │ │ ├── OutOfStockBadge.razor │ │ ├── ProductCode.razor │ │ ├── OtherPropertyHeader.razor │ │ ├── ProductPriceTag.razor │ │ ├── ProductImage.razor │ │ ├── Helpers.cs │ │ ├── OtherPropertyCell.razor │ │ ├── ProductViewModel.cs │ │ ├── ProductGrid.razor │ │ └── ProductTile.razor │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── css │ │ │ ├── open-iconic │ │ │ │ ├── font │ │ │ │ │ ├── fonts │ │ │ │ │ │ ├── open-iconic.eot │ │ │ │ │ │ ├── open-iconic.otf │ │ │ │ │ │ ├── open-iconic.ttf │ │ │ │ │ │ ├── open-iconic.woff │ │ │ │ │ │ └── open-iconic.svg │ │ │ │ │ └── css │ │ │ │ │ │ └── open-iconic-bootstrap.min.css │ │ │ │ ├── ICON-LICENSE │ │ │ │ ├── README.md │ │ │ │ └── FONT-LICENSE │ │ │ └── app.css │ │ └── index.html │ ├── Shared │ │ ├── MainLayout.razor │ │ ├── Helpers.cs │ │ ├── Foo.cs │ │ └── MainLayout.razor.css │ ├── App.razor │ ├── _Imports.razor │ ├── Pages │ │ ├── MainPanel.razor │ │ ├── NestedPanel.razor │ │ ├── Index.razor │ │ └── ProductGridDemo.razor │ ├── Program.cs │ ├── Sz.BlazorRerenderReducers.Client.csproj │ └── Properties │ │ └── launchSettings.json ├── Server │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── Sz.BlazorRerenderReducers.Server.csproj │ ├── Program.cs │ ├── Pages │ │ ├── Error.cshtml.cs │ │ └── Error.cshtml │ ├── Properties │ │ ├── launchSettings.json │ │ ├── PublishProfiles │ │ │ ├── SzBlazorRerenderReducers - Web Deploy.pubxml.user │ │ │ └── SzBlazorRerenderReducers - Web Deploy.pubxml │ │ └── ServiceDependencies │ │ │ └── SzBlazorRerenderReducers - Web Deploy │ │ │ └── profile.arm.json │ └── Startup.cs └── Shared │ ├── Sz.BlazorRerenderReducers.csproj │ ├── ScopeTimer.cs │ ├── README.md │ └── DisplayHashRerenderComponentBase.cs ├── .gitignore ├── .github └── workflows │ └── dotnet.yml ├── Sz.BlazorRerenderReducers.sln └── README.md /Sz.BlazorRerenderReducers/Client/ProductGridDemo/ProductShareLink.razor: -------------------------------------------------------------------------------- 1 | Share 2 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/ProductGridDemo/OutOfStockBadge.razor: -------------------------------------------------------------------------------- 1 |
2 | Out of
3 | stock 4 |
-------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szalapski/BlazorRerenderReducers/HEAD/Sz.BlazorRerenderReducers/Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Oo]bj 2 | [Bb]in 3 | .vs 4 | *.user 5 | *.suo 6 | *.[Cc]ache 7 | *.bak 8 | *.ncb 9 | *.log 10 | /TestResults 11 | *.vspscc 12 | *.vssscc 13 | [Tt]humbs.db 14 | /release -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szalapski/BlazorRerenderReducers/HEAD/Sz.BlazorRerenderReducers/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szalapski/BlazorRerenderReducers/HEAD/Sz.BlazorRerenderReducers/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szalapski/BlazorRerenderReducers/HEAD/Sz.BlazorRerenderReducers/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szalapski/BlazorRerenderReducers/HEAD/Sz.BlazorRerenderReducers/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/ProductGridDemo/ProductCode.razor: -------------------------------------------------------------------------------- 1 |
@Product.Code
2 | @code { 3 | [Parameter, EditorRequired] 4 | public ProductViewModel Product { get; set; } = null!; 5 | } -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/ProductGridDemo/OtherPropertyHeader.razor: -------------------------------------------------------------------------------- 1 | @Item.Key 2 | 3 | @code { 4 | [Parameter, EditorRequired] 5 | public KeyValuePair Item { get; set; } 6 | } -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/ProductGridDemo/ProductPriceTag.razor: -------------------------------------------------------------------------------- 1 | @(Product.Price.ToString("C")) 2 | 3 | @code { 4 | [Parameter, EditorRequired] 5 | public ProductViewModel Product { get; set; } = null!; 6 | } -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 |
5 |
6 | @Body 7 |
8 |
9 |
10 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Server/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/Shared/Helpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Sz.BlazorRerenderReducers.Client.Shared; 4 | 5 | public static class Helpers 6 | { 7 | public static T RandomOf(params T[] items) => items[Random.Shared.Next(items.Length)]; 8 | } 9 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/Shared/Foo.cs: -------------------------------------------------------------------------------- 1 | namespace Sz.BlazorRerenderReducers.Client.Shared 2 | { 3 | public class Foo 4 | { 5 | public string Bar { get; set; } = "BarStartValue"; 6 | public string Qux { get; set; } = "QuxStartValue"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/ProductGridDemo/ProductImage.razor: -------------------------------------------------------------------------------- 1 | @Product.Code 2 | @code { 3 | [Parameter, EditorRequired] 4 | public ProductViewModel Product { get; set; } = null!; 5 | } -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/ProductGridDemo/Helpers.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | 3 | namespace Sz.BlazorRerenderReducers.Client.ProductGridDemo; 4 | 5 | public static class Helpers 6 | { 7 | // simulate an operation that takes a few ms to complete 8 | public static void IntensiveOperation() 9 | { 10 | Thread.Sleep(2); // milliseconds 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/ProductGridDemo/OtherPropertyCell.razor: -------------------------------------------------------------------------------- 1 | @using System.Threading 2 | @Item.Value 3 | 4 | @code { 5 | [Parameter, EditorRequired] 6 | public KeyValuePair Item { get; set; } 7 | 8 | protected override void OnParametersSet() 9 | { 10 | Thread.Sleep(2); // simulate a component that takes just 2 ms to rerender 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Sorry, there's nothing at this address.

8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using Sz.BlazorRerenderReducers.Client 10 | @using Sz.BlazorRerenderReducers.Client.Shared 11 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Server/Sz.BlazorRerenderReducers.Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/Pages/MainPanel.razor: -------------------------------------------------------------------------------- 1 | @using Sz.BlazorRerenderReducers 2 | @inherits DisplayHashRerenderComponentBase 3 | 4 |
5 | @foreach (int i in Enumerable.Range(0, 1000)) 6 | { 7 |
@i
8 | } 9 |
10 | 11 | 12 | MainPanel Bar=@InputFoo.Bar 13 | 14 | 15 | @code { 16 | [Parameter, EditorRequired] public Foo InputFoo { get; set; } = null!; 17 | 18 | protected override string[] GetDisplayItems() 19 | { 20 | return new[] { InputFoo.Bar }; 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Server/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace Sz.BlazorRerenderReducers.Server 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/Pages/NestedPanel.razor: -------------------------------------------------------------------------------- 1 | @using BlazorRerenderReducers 2 | @inherits DisplayHashRerenderComponentBase 3 | 4 |
5 | @foreach (int i in Enumerable.Range(0, 1000)) 6 | { 7 |
@i
8 | } 9 |
10 | 11 | NestedPanel Qux=@InputFoo.Qux 12 | 13 |
14 | @foreach (int i in Enumerable.Range(0, 1000)) 15 | { 16 |
@i
17 | } 18 |
19 | 20 | @code { 21 | [Parameter, EditorRequired] 22 | public Foo InputFoo { get; set; } = null!; 23 | 24 | protected override string GetDisplayHash() => InputFoo.Qux.ToString(); 25 | } 26 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | 7 | namespace Sz.BlazorRerenderReducers.Client 8 | { 9 | public class Program 10 | { 11 | public static async Task Main(string[] args) 12 | { 13 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 14 | builder.RootComponents.Add("#app"); 15 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 16 | await builder.Build().RunAsync(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Server/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using Microsoft.Extensions.Logging; 4 | using System.Diagnostics; 5 | 6 | namespace Sz.BlazorRerenderReducers.Server.Pages 7 | { 8 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 9 | [IgnoreAntiforgeryToken] 10 | public class ErrorModel : PageModel 11 | { 12 | public string? RequestId { get; set; } 13 | 14 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 15 | 16 | private readonly ILogger _logger; 17 | 18 | public ErrorModel(ILogger logger) 19 | { 20 | _logger = logger; 21 | } 22 | 23 | public void OnGet() 24 | { 25 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Sz.BlazorRerenderReducers 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
Loading...
19 | 20 |
21 | An unhandled error has occurred. 22 | Reload 23 | 🗙 24 |
25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/Sz.BlazorRerenderReducers.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ES6 5 | 6 | 7 | 8 | ES6 9 | 10 | 11 | 12 | net6.0 13 | enable 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:18756", 7 | "sslPort": 44327 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "launchUrl": "/product-grid-demo", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | }, 18 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" 19 | }, 20 | "Sz.BlazorRerenderReducers": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "/product-grid-demo", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 28 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 29 | "dotnetRunMessages": "true" 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/ProductGridDemo/ProductViewModel.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | using static Sz.BlazorRerenderReducers.Client.Shared.Helpers; 5 | 6 | namespace Sz.BlazorRerenderReducers.Client.ProductGridDemo; 7 | 8 | public class ProductViewModel 9 | { 10 | public ProductViewModel() 11 | { 12 | this.Code = Guid.NewGuid().ToString()[..6]; 13 | this.Price = new Random().Next(100, 999) / 100m; 14 | OtherProperties.Add("MSRP", (this.Price * 1.2m).ToString()); 15 | OtherProperties.Add("Color", RandomOf("Red", "Green", "Blue", "White", "Gray", "Black")); 16 | OtherProperties.Add("Size", RandomOf("S", "M", "L", "XL")); 17 | OtherProperties.Add("Release", new DateOnly(2022, RandomOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), 1).ToString()); 18 | OtherProperties.Add("OS Ver", RandomOf(1, 2, 3).ToString()); 19 | OtherProperties.Add("Resolution", RandomOf("1600x1200", "1920x1080", "2560x1440")); 20 | } 21 | public string Code { get; set; } 22 | public decimal Price { get; set; } 23 | public bool IsInStock { get; set; } = true; 24 | public Dictionary OtherProperties { get; } = new(); 25 | } -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:18756", 7 | "sslPort": 44327 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 15 | "launchUrl": "product-grid-demo", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "Sz.BlazorRerenderReducers.Server": { 21 | "commandName": "Project", 22 | "dotnetRunMessages": "true", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Server/Properties/PublishProfiles/SzBlazorRerenderReducers - Web Deploy.pubxml.user: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAN9yOMdXXOEWXEd1Rg64yPAAAAAACAAAAAAAQZgAAAAEAACAAAAA3/AXpBpQhFPcD//4UU7yvjr5XdI5xg3qDp4sinZTGaAAAAAAOgAAAAAIAACAAAAAYW+12VjRJqXxiPcc2S/EcJy1WyPmohtMVrmua2LX+gYAAAABq6qG10ZZzUdR0qSMIFbHpLnML1fX7siMOpBwHyLXGjkAWvuYj8rAOdUnXxObwSUnZi8bIhuvGxANXET3Zg2OGIz2dETjoeiJ++z+LnzjWYTM3fOLvkudD6jBt4FlV798Yu6O8fhqYjPrdLHFekUAkAkBFWjW3YMNodIw2RgTlhkAAAAAPp6iOEZg5obOT+mo6ZxipT0TYgtr62PaY4TiRbmacq7MfY4mVuVWiG0cZFQVywBzUxxQ9Zm0BPXgRpA7HTNAn 10 | True|2021-08-21T01:07:23.0094614Z;True|2021-08-20T18:56:30.8457562-05:00;True|2021-08-13T22:31:28.6245412-05:00; 11 | 12 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/ProductGridDemo/ProductGrid.razor: -------------------------------------------------------------------------------- 1 |
2 |
3 | 7 |
8 |
9 | 10 | 14 |
15 |
    16 | 17 | 21 | 22 |
23 |
24 | @code { 25 | [Parameter, EditorRequired] 26 | public List Products { get; set; } = null!; 27 | 28 | public List FilteredProducts => 29 | Products.Where(p => p.Code.Contains(FilterText)).ToList(); 30 | 31 | private bool AvoidUnnecessaryRerenders { get; set; } = true; 32 | private string FilterText { get; set; } = ""; 33 | 34 | private void Add() => Products.Insert(0, new()); 35 | 36 | 37 | } -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | @import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); 2 | 3 | html, body { 4 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 5 | } 6 | 7 | a, .btn-link { 8 | color: #0366d6; 9 | } 10 | 11 | .btn-primary { 12 | color: #fff; 13 | background-color: #1b6ec2; 14 | border-color: #1861ac; 15 | } 16 | 17 | .content { 18 | padding-top: 1.1rem; 19 | } 20 | 21 | .valid.modified:not([type=checkbox]) { 22 | outline: 1px solid #26b050; 23 | } 24 | 25 | .invalid { 26 | outline: 1px solid red; 27 | } 28 | 29 | .validation-message { 30 | color: red; 31 | } 32 | 33 | #blazor-error-ui { 34 | background: lightyellow; 35 | bottom: 0; 36 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 37 | display: none; 38 | left: 0; 39 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 40 | position: fixed; 41 | width: 100%; 42 | z-index: 1000; 43 | } 44 | 45 | #blazor-error-ui .dismiss { 46 | cursor: pointer; 47 | position: absolute; 48 | right: 0.75rem; 49 | top: 0.5rem; 50 | } 51 | 52 | 53 | .product-tile { 54 | display: inline-block; 55 | padding: 10px; 56 | min-width: 200px; 57 | min-height: 224px; 58 | } 59 | 60 | button { 61 | padding: 10px 50px; 62 | } 63 | 64 | input[type=checkbox] { 65 | transform: scale(1.5); 66 | } 67 | 68 | table td, table th { 69 | border: solid lightgrey 1px; 70 | padding: 4px; 71 | } -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/ProductGridDemo/ProductTile.razor: -------------------------------------------------------------------------------- 1 | @using Sz.BlazorRerenderReducers 2 | @inherits DisplayHashRerenderComponentBase 3 |
  • 4 | 5 |
    6 | @if (!Product.IsInStock) 7 | { 8 | 9 | } 10 | 11 | 12 |
    13 |
    14 | 15 | 16 | @foreach (var item in Product.OtherProperties) 17 | { 18 | 19 | } 20 | 21 | 22 | @foreach (var item in Product.OtherProperties) 23 | { 24 | 25 | } 26 | 27 |
    28 |
    29 |
  • 30 | @code { 31 | [Parameter, EditorRequired] 32 | public ProductViewModel Product { get; set; } = null!; 33 | 34 | [Parameter] 35 | public bool AvoidUnnecessaryRerenders { get; set; } = true; 36 | 37 | // when this value is unchanged, suppresses unnecessary re-rendering 38 | protected override string? GetDisplayHash() => 39 | AvoidUnnecessaryRerenders 40 | ? $"{Product.Code};{Product.Price};{Product.IsInStock}" 41 | : base.GetDisplayHash(); 42 | } 43 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/Shared/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | .main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | } 28 | 29 | .top-row a:first-child { 30 | overflow: hidden; 31 | text-overflow: ellipsis; 32 | } 33 | 34 | @media (max-width: 640.98px) { 35 | .top-row:not(.auth) { 36 | display: none; 37 | } 38 | 39 | .top-row.auth { 40 | justify-content: space-between; 41 | } 42 | 43 | .top-row a, .top-row .btn-link { 44 | margin-left: 0; 45 | } 46 | } 47 | 48 | @media (min-width: 641px) { 49 | .page { 50 | flex-direction: row; 51 | } 52 | 53 | .sidebar { 54 | width: 250px; 55 | height: 100vh; 56 | position: sticky; 57 | top: 0; 58 | } 59 | 60 | .top-row { 61 | position: sticky; 62 | top: 0; 63 | z-index: 1; 64 | } 65 | 66 | .main > div { 67 | padding-left: 2rem !important; 68 | padding-right: 1.5rem !important; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 |

    Sz.BlazorRerenderReducers demo

    4 |
    5 | see also 6 | GitHub project page 7 | | NuGet package 8 |
    9 | 10 |
    11 | 12 |
    13 | 14 | @if (UseRerenderControl) 15 | { 16 |
    Using DisplayHashRerender
    17 | } 18 | else 19 | { 20 |
    Not using DisplayHashRerender
    21 | } 22 | 23 |
    24 | 29 |
    30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | @code { 40 | private Foo Foo1 { get; set; } = new Foo(); 41 | private bool UseRerenderControl { 42 | get => DisplayHashRerenderComponentBase.EnableRerenderReductionGlobal; 43 | set => DisplayHashRerenderComponentBase.EnableRerenderReductionGlobal = value; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Shared/Sz.BlazorRerenderReducers.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | true 7 | 0.3.0 8 | Patrick Szalapski 9 | Sz.BlazorRerenderReducers 10 | 11 | 12 | MIT 13 | Provides classes useful for reducing the frequency of component re-renders of Blazor components to a minimum. 14 | 2025 Patrick Szalapski 15 | 16 | 0.1.0 Initial release with only hash-based rerendering control 17 | 0.2.0 Added optional control by array of strings, in addition to string--use either one 18 | 0.3.0 Enhanced for fewer redundant renders; updated dependencies to latest .NET 6.0 19 | 20 | https://szblazorrerenderreducers.azurewebsites.net/ 21 | https://github.com/szalapski/BlazorRerenderReducers 22 | Git 23 | Blazor 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Server/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Sz.BlazorRerenderReducers.Server.Pages.ErrorModel 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Error 11 | 12 | 13 | 14 | 15 | 16 |
    17 |
    18 |

    Error.

    19 |

    An error occurred while processing your request.

    20 | 21 | @if (Model.ShowRequestId) 22 | { 23 |

    24 | Request ID: @Model.RequestId 25 |

    26 | } 27 | 28 |

    Development Mode

    29 |

    30 | Swapping to the Development environment displays detailed information about the error that occurred. 31 |

    32 |

    33 | The Development environment shouldn't be enabled for deployed applications. 34 | It can result in displaying sensitive information from exceptions to end users. 35 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 36 | and restarting the app. 37 |

    38 |
    39 |
    40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to GitHub Pages 2 | 3 | # Run workflow on every push to the master branch 4 | on: 5 | push: 6 | branches: [ main ] 7 | jobs: 8 | deploy-to-github-pages: 9 | runs-on: ubuntu-latest 10 | steps: 11 | 12 | # uses GitHub's checkout action to checkout code form the master branch 13 | - uses: actions/checkout@v2 14 | 15 | - name: Setup .NET SDK 16 | uses: actions/setup-dotnet@v1 17 | with: 18 | dotnet-version: 6.0.x 19 | 20 | - name: Publish .NET Project 21 | run: dotnet publish Sz.BlazorRerenderReducers/Server/ -c Release -o release --nologo 22 | 23 | - name: Change base-tag in index.html from / to the root subfolder 24 | run: sed -i 's///g' release/wwwroot/index.html 25 | 26 | # add .nojekyll file to tell GitHub pages to allow files and folders starting with an underscore) 27 | - name: Add .nojekyll file 28 | run: touch release/wwwroot/.nojekyll 29 | 30 | # copy index.html to 404.html to overcome lack of server-side routing on Github Pages. TODO: 31 | # alternative 1: https://docs.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/webassembly?view=aspnetcore-6.0#github-pages 32 | # alternative 2: https://swimburger.net/blog/dotnet/pre-render-blazor-webassembly-at-build-time-to-optimize-for-search-engines 33 | - name: copy index.html to 404.html 34 | run: cp release/wwwroot/index.html release/wwwroot/404.html 35 | 36 | - name: Commit wwwroot to GitHub Pages 37 | uses: JamesIves/github-pages-deploy-action@3.7.1 38 | with: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | BRANCH: gh-pages 41 | FOLDER: release/wwwroot 42 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Server/Properties/PublishProfiles/SzBlazorRerenderReducers - Web Deploy.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | MSDeploy 9 | /subscriptions/5d2b3eac-ecde-4a88-9b45-12879ff1d75b/resourcegroups/Default-ApplicationInsights-CentralUS/providers/Microsoft.Web/sites/SzBlazorRerenderReducers 10 | Default-ApplicationInsights-CentralUS 11 | AzureWebSite 12 | Release 13 | Any CPU 14 | https://szblazorrerenderreducers.azurewebsites.net 15 | True 16 | False 17 | 6271ada2-db33-415d-a060-fac5578e309c 18 | szblazorrerenderreducers.scm.azurewebsites.net:443 19 | SzBlazorRerenderReducers 20 | 21 | True 22 | WMSVC 23 | True 24 | $SzBlazorRerenderReducers 25 | <_SavePWD>True 26 | <_DestinationType>AzureWebSite 27 | False 28 | 29 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Server/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | 7 | namespace Sz.BlazorRerenderReducers.Server 8 | { 9 | public class Startup 10 | { 11 | public Startup(IConfiguration configuration) 12 | { 13 | Configuration = configuration; 14 | } 15 | 16 | public IConfiguration Configuration { get; } 17 | 18 | // This method gets called by the runtime. Use this method to add services to the container. 19 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 20 | public void ConfigureServices(IServiceCollection services) 21 | { 22 | 23 | services.AddControllersWithViews(); 24 | services.AddRazorPages(); 25 | } 26 | 27 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 28 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 29 | { 30 | if (env.IsDevelopment()) 31 | { 32 | app.UseDeveloperExceptionPage(); 33 | app.UseWebAssemblyDebugging(); 34 | } 35 | else 36 | { 37 | app.UseExceptionHandler("/Error"); 38 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 39 | app.UseHsts(); 40 | } 41 | 42 | app.UseHttpsRedirection(); 43 | app.UseBlazorFrameworkFiles(); 44 | app.UseStaticFiles(); 45 | 46 | app.UseRouting(); 47 | 48 | app.UseEndpoints(endpoints => 49 | { 50 | endpoints.MapFallbackToFile("index.html"); 51 | }); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.13.35931.197 d17.13 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sz.BlazorRerenderReducers.Server", "Sz.BlazorRerenderReducers\Server\Sz.BlazorRerenderReducers.Server.csproj", "{6271ADA2-DB33-415D-A060-FAC5578E309C}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sz.BlazorRerenderReducers.Client", "Sz.BlazorRerenderReducers\Client\Sz.BlazorRerenderReducers.Client.csproj", "{29170647-D40B-4E57-9F9C-AAB713FC1605}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sz.BlazorRerenderReducers", "Sz.BlazorRerenderReducers\Shared\Sz.BlazorRerenderReducers.csproj", "{DEE8283E-2C1F-4FEA-8FA4-A9C25E68E153}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D0D7237C-EC30-4F4A-8580-BDD3CCEEFF71}" 13 | ProjectSection(SolutionItems) = preProject 14 | .gitignore = .gitignore 15 | EndProjectSection 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {6271ADA2-DB33-415D-A060-FAC5578E309C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {6271ADA2-DB33-415D-A060-FAC5578E309C}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {6271ADA2-DB33-415D-A060-FAC5578E309C}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {6271ADA2-DB33-415D-A060-FAC5578E309C}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {29170647-D40B-4E57-9F9C-AAB713FC1605}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {29170647-D40B-4E57-9F9C-AAB713FC1605}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {29170647-D40B-4E57-9F9C-AAB713FC1605}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {29170647-D40B-4E57-9F9C-AAB713FC1605}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {DEE8283E-2C1F-4FEA-8FA4-A9C25E68E153}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {DEE8283E-2C1F-4FEA-8FA4-A9C25E68E153}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {DEE8283E-2C1F-4FEA-8FA4-A9C25E68E153}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {DEE8283E-2C1F-4FEA-8FA4-A9C25E68E153}.Release|Any CPU.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(SolutionProperties) = preSolution 37 | HideSolutionNode = FALSE 38 | EndGlobalSection 39 | GlobalSection(ExtensibilityGlobals) = postSolution 40 | SolutionGuid = {54000CA6-D09D-4F35-BA4C-952C51B014EE} 41 | EndGlobalSection 42 | EndGlobal 43 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Shared/ScopeTimer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | 7 | namespace Sz.BlazorRerenderReducers 8 | { 9 | /// 10 | /// A timer that starts running when created and then stops and outputs its elapsed time to the console 11 | /// when disposed. Designed to be used to measure the time spent inside a Using block, or between a pair 12 | /// of statements (`new` and `.Dispose()`). 13 | /// 14 | /// 15 | /// 16 | /// using (new ScopeTimer("Loading foo into memory")} 17 | /// await LoadFoo(); 18 | /// } 19 | /// 20 | /// This will output the label "LoadFoo:" and the time running LoadFoo, in milliseconds, to the console. 21 | /// 22 | public class ScopeTimer : IDisposable 23 | { 24 | public Stopwatch Stopwatch1 { get; } = new Stopwatch(); 25 | public string ScopeLabel { get; } 26 | public List StackFrames { get; } 27 | public string StackNames => string.Join(" <- ", StackFrames.Select(f => f.GetMethod()?.Name ?? "Unk")); 28 | public int Id { get; set; } 29 | private static int NextId { get; set; } = 1; 30 | private static ConcurrentDictionary RunCounts { get; } = new(); 31 | private readonly bool writeStart; 32 | 33 | public ScopeTimer(string? scopeLabel = null, int stackLevelsToWrite = 1, bool writeStart = false) 34 | { 35 | ScopeLabel = scopeLabel ?? StackFrames?.First().GetMethod()?.Name ?? ""; 36 | this.writeStart = writeStart; 37 | StackFrames = new StackTrace().GetFrames().Skip(1).Take(stackLevelsToWrite).ToList(); 38 | Init(); 39 | } 40 | 41 | public void Init() 42 | { 43 | Id = NextId++; 44 | 45 | if (RunCounts.ContainsKey(ScopeLabel)) ++RunCounts[ScopeLabel]; 46 | else RunCounts[ScopeLabel] = 1; 47 | 48 | if (writeStart) Console.WriteLine($"Scope {Id} {ScopeLabel}: Starting {StackNames}"); 49 | Stopwatch1.Restart(); 50 | } 51 | 52 | public void Lap() 53 | { 54 | Stop(); 55 | Init(); 56 | } 57 | 58 | public void Stop() 59 | { 60 | Stopwatch1.Stop(); 61 | 62 | // Do not comment out this line. If you want to get rid of this message, get rid of your ScopeTimer instance. 63 | Console.WriteLine($"{(int)Stopwatch1.ElapsedMilliseconds} ms to run scope {Id}, '{ScopeLabel}', run count {RunCounts[ScopeLabel]}"); 64 | } 65 | 66 | public void Dispose() => Lap(); 67 | 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/wwwroot/css/open-iconic/README.md: -------------------------------------------------------------------------------- 1 | [Open Iconic v1.1.1](http://useiconic.com/open) 2 | =========== 3 | 4 | ### Open Iconic is the open source sibling of [Iconic](http://useiconic.com). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](http://useiconic.com/open#icons) 5 | 6 | 7 | 8 | ## What's in Open Iconic? 9 | 10 | * 223 icons designed to be legible down to 8 pixels 11 | * Super-light SVG files - 61.8 for the entire set 12 | * SVG sprite—the modern replacement for icon fonts 13 | * Webfont (EOT, OTF, SVG, TTF, WOFF), PNG and WebP formats 14 | * Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats 15 | * PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px. 16 | 17 | 18 | ## Getting Started 19 | 20 | #### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](http://useiconic.com/open#icons) and [Reference](http://useiconic.com/open#reference) sections. 21 | 22 | ### General Usage 23 | 24 | #### Using Open Iconic's SVGs 25 | 26 | We like SVGs and we think they're the way to display icons on the web. Since Open Iconic are just basic SVGs, we suggest you display them like you would any other image (don't forget the `alt` attribute). 27 | 28 | ``` 29 | icon name 30 | ``` 31 | 32 | #### Using Open Iconic's SVG Sprite 33 | 34 | Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack. 35 | 36 | Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `` *tag and a unique class name for each different icon in the* `` *tag.* 37 | 38 | ``` 39 | 40 | 41 | 42 | ``` 43 | 44 | Sizing icons only needs basic CSS. All the icons are in a square format, so just set the `` tag with equal width and height dimensions. 45 | 46 | ``` 47 | .icon { 48 | width: 16px; 49 | height: 16px; 50 | } 51 | ``` 52 | 53 | Coloring icons is even easier. All you need to do is set the `fill` rule on the `` tag. 54 | 55 | ``` 56 | .icon-account-login { 57 | fill: #f00; 58 | } 59 | ``` 60 | 61 | To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.com/svg-sprites-use-better-icon-fonts/). 62 | 63 | #### Using Open Iconic's Icon Font... 64 | 65 | 66 | ##### …with Bootstrap 67 | 68 | You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}` 69 | 70 | 71 | ``` 72 | 73 | ``` 74 | 75 | 76 | ``` 77 | 78 | ``` 79 | 80 | ##### …with Foundation 81 | 82 | You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css, less, scss, styl}` 83 | 84 | ``` 85 | 86 | ``` 87 | 88 | 89 | ``` 90 | 91 | ``` 92 | 93 | ##### …on its own 94 | 95 | You can find our default stylesheets in `font/css/open-iconic.{css, less, scss, styl}` 96 | 97 | ``` 98 | 99 | ``` 100 | 101 | ``` 102 | 103 | ``` 104 | 105 | 106 | ## License 107 | 108 | ### Icons 109 | 110 | All code (including SVG markup) is under the [MIT License](http://opensource.org/licenses/MIT). 111 | 112 | ### Fonts 113 | 114 | All fonts are under the [SIL Licensed](http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web). 115 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sz.BlazorRerenderReducers 2 | 3 | Provides classes useful for reducing the frequency of component re-renders of Blazor components to a minimum. 4 | 5 | ## Usage 6 | 7 | Suppose that you have a component that is being rerendered undesirably often. First, you should take the ordinary steps to enable Blazor to efficiently avoid rerendering (listed below in "How to optimize..." for your reference). But, if you are unable or unwilling to make such changes, do the following on the desired components: 8 | 9 | 1. Install the NuGet Package by finding it in NuGet Package Explorer or by using the Nuget Console and running `Install-Package Sz.BlazorRerenderReducers -Version 0.x.y` 10 | 11 | 2. Inherit from `DisplayHashRerenderComponentBase` on the component that you wish to reduce rerender frequency: 12 | 13 | ```c# 14 | @inherits DisplayHashRerenderComponentBase 15 | ``` 16 | 17 | 3. Override the abstract method `GetDisplayHash` to return a string that represents *all* the displayed state of the component. That is, it should return a different value when any displayable state changes, and the same value when no visible change happens. 18 | 19 | ```c# 20 | @code { 21 | protected override string GetDisplayHash() => InputFoo.Bar.ToString(); 22 | } 23 | ``` 24 | Now your component should be rerendered only when something in the displayable state changes.. To see such rerendering logged, you might choose to override AfterRender on your component. 25 | 26 | Note that child components will not rerender if the current component doesn't rerender, as the current component will not set any parameters on its children unless it rerenders. 27 | 28 | ## What not to do 29 | 30 | * Do not use on components that are already fast to rerender and thus won't benefit much. 31 | * Do not use on components that take only primitive types, strings, decimals, DateTimes, or Guids as `[Parameter]`s (Blazor will optimize these automatically). 32 | * Do not use on components that have a very complicated display state, as it will be too hard to maintain. Consider refactoring such components into several subcomponents. 33 | 34 | ## Background 35 | 36 | By default, Razor components inherit from the `Microsoft.AspNetCore.Components.ComponentBase` base class, which contains logic to trigger rerendering at the following times: 37 | 38 | * After applying an updated set of parameters from a parent component. 39 | * After applying an updated value for a cascading parameter. 40 | * After notification of an event and invoking one of its own event handlers. 41 | * After a call to its own `StateHasChanged` method. 42 | 43 | Components inherited from `Microsoft.AspNetCore.Components.ComponentBase` skip rerenders due to parameter updates if either of the following are true: 44 | 45 | * All of the parameter values are of known immutable primitive types, such as int, string, DateTime, and haven't changed since the previous set of parameters were set, or 46 | * The component's ShouldRender method returns false. 47 | 48 | This package enables some convenient ways to avoid a complicated, onerous, or repetitive implementation of ShouldRender in the cases where rerendering needs to be optimized. 49 | 50 | ## Demo 51 | 52 | https://szalapski.github.io/BlazorRerenderReducers/ 53 | 54 | ## How to optimize rerendering without using this package 55 | 56 | * Make your parameters entirely known immutable primitive types. If you do this, your component will automatically avoid rerendering unless the value of any parameter changes. 57 | * If your component does not ever need to change after its first render, you can simply override ShouldRender to return false: `protected override bool ShouldRender() => false;` 58 | * If your component can determine the need to rerender based on some other logic, you can simply override ShouldRender to return true or false based on such logic. 59 | 60 | See also: 61 | * https://docs.microsoft.com/en-us/aspnet/core/blazor/webassembly-performance-best-practices 62 | * https://docs.microsoft.com/en-us/aspnet/core/blazor/components/rendering 63 | * https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/blazor/components/rendering.md 64 | 65 | 66 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/wwwroot/css/open-iconic/FONT-LICENSE: -------------------------------------------------------------------------------- 1 | SIL OPEN FONT LICENSE Version 1.1 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | PREAMBLE 6 | The goals of the Open Font License (OFL) are to stimulate worldwide 7 | development of collaborative font projects, to support the font creation 8 | efforts of academic and linguistic communities, and to provide a free and 9 | open framework in which fonts may be shared and improved in partnership 10 | with others. 11 | 12 | The OFL allows the licensed fonts to be used, studied, modified and 13 | redistributed freely as long as they are not sold by themselves. The 14 | fonts, including any derivative works, can be bundled, embedded, 15 | redistributed and/or sold with any software provided that any reserved 16 | names are not used by derivative works. The fonts and derivatives, 17 | however, cannot be released under any other type of license. The 18 | requirement for fonts to remain under this license does not apply 19 | to any document created using the fonts or their derivatives. 20 | 21 | DEFINITIONS 22 | "Font Software" refers to the set of files released by the Copyright 23 | Holder(s) under this license and clearly marked as such. This may 24 | include source files, build scripts and documentation. 25 | 26 | "Reserved Font Name" refers to any names specified as such after the 27 | copyright statement(s). 28 | 29 | "Original Version" refers to the collection of Font Software components as 30 | distributed by the Copyright Holder(s). 31 | 32 | "Modified Version" refers to any derivative made by adding to, deleting, 33 | or substituting -- in part or in whole -- any of the components of the 34 | Original Version, by changing formats or by porting the Font Software to a 35 | new environment. 36 | 37 | "Author" refers to any designer, engineer, programmer, technical 38 | writer or other person who contributed to the Font Software. 39 | 40 | PERMISSION & CONDITIONS 41 | Permission is hereby granted, free of charge, to any person obtaining 42 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 43 | redistribute, and sell modified and unmodified copies of the Font 44 | Software, subject to the following conditions: 45 | 46 | 1) Neither the Font Software nor any of its individual components, 47 | in Original or Modified Versions, may be sold by itself. 48 | 49 | 2) Original or Modified Versions of the Font Software may be bundled, 50 | redistributed and/or sold with any software, provided that each copy 51 | contains the above copyright notice and this license. These can be 52 | included either as stand-alone text files, human-readable headers or 53 | in the appropriate machine-readable metadata fields within text or 54 | binary files as long as those fields can be easily viewed by the user. 55 | 56 | 3) No Modified Version of the Font Software may use the Reserved Font 57 | Name(s) unless explicit written permission is granted by the corresponding 58 | Copyright Holder. This restriction only applies to the primary font name as 59 | presented to the users. 60 | 61 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 62 | Software shall not be used to promote, endorse or advertise any 63 | Modified Version, except to acknowledge the contribution(s) of the 64 | Copyright Holder(s) and the Author(s) or with their explicit written 65 | permission. 66 | 67 | 5) The Font Software, modified or unmodified, in part or in whole, 68 | must be distributed entirely under this license, and must not be 69 | distributed under any other license. The requirement for fonts to 70 | remain under this license does not apply to any document created 71 | using the Font Software. 72 | 73 | TERMINATION 74 | This license becomes null and void if any of the above conditions are 75 | not met. 76 | 77 | DISCLAIMER 78 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 79 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 81 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 82 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 83 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 84 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 85 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 86 | OTHER DEALINGS IN THE FONT SOFTWARE. 87 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Server/Properties/ServiceDependencies/SzBlazorRerenderReducers - Web Deploy/profile.arm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "metadata": { 5 | "_dependencyType": "appService.windows" 6 | }, 7 | "parameters": { 8 | "resourceGroupName": { 9 | "type": "string", 10 | "defaultValue": "Default-ApplicationInsights-CentralUS", 11 | "metadata": { 12 | "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking." 13 | } 14 | }, 15 | "resourceGroupLocation": { 16 | "type": "string", 17 | "defaultValue": "centralus", 18 | "metadata": { 19 | "description": "Location of the resource group. Resource groups could have different location than resources, however by default we use API versions from latest hybrid profile which support all locations for resource types we support." 20 | } 21 | }, 22 | "resourceName": { 23 | "type": "string", 24 | "defaultValue": "SzBlazorRerenderReducers", 25 | "metadata": { 26 | "description": "Name of the main resource to be created by this template." 27 | } 28 | }, 29 | "resourceLocation": { 30 | "type": "string", 31 | "defaultValue": "[parameters('resourceGroupLocation')]", 32 | "metadata": { 33 | "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there." 34 | } 35 | } 36 | }, 37 | "variables": { 38 | "appServicePlan_name": "[concat('Plan', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]", 39 | "appServicePlan_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/serverFarms/', variables('appServicePlan_name'))]" 40 | }, 41 | "resources": [ 42 | { 43 | "type": "Microsoft.Resources/resourceGroups", 44 | "name": "[parameters('resourceGroupName')]", 45 | "location": "[parameters('resourceGroupLocation')]", 46 | "apiVersion": "2019-10-01" 47 | }, 48 | { 49 | "type": "Microsoft.Resources/deployments", 50 | "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]", 51 | "resourceGroup": "[parameters('resourceGroupName')]", 52 | "apiVersion": "2019-10-01", 53 | "dependsOn": [ 54 | "[parameters('resourceGroupName')]" 55 | ], 56 | "properties": { 57 | "mode": "Incremental", 58 | "template": { 59 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 60 | "contentVersion": "1.0.0.0", 61 | "resources": [ 62 | { 63 | "location": "[parameters('resourceLocation')]", 64 | "name": "[parameters('resourceName')]", 65 | "type": "Microsoft.Web/sites", 66 | "apiVersion": "2015-08-01", 67 | "tags": { 68 | "[concat('hidden-related:', variables('appServicePlan_ResourceId'))]": "empty" 69 | }, 70 | "dependsOn": [ 71 | "[variables('appServicePlan_ResourceId')]" 72 | ], 73 | "kind": "app", 74 | "properties": { 75 | "name": "[parameters('resourceName')]", 76 | "kind": "app", 77 | "httpsOnly": true, 78 | "reserved": false, 79 | "serverFarmId": "[variables('appServicePlan_ResourceId')]", 80 | "siteConfig": { 81 | "metadata": [ 82 | { 83 | "name": "CURRENT_STACK", 84 | "value": "dotnetcore" 85 | } 86 | ] 87 | } 88 | }, 89 | "identity": { 90 | "type": "SystemAssigned" 91 | } 92 | }, 93 | { 94 | "location": "[parameters('resourceLocation')]", 95 | "name": "[variables('appServicePlan_name')]", 96 | "type": "Microsoft.Web/serverFarms", 97 | "apiVersion": "2015-08-01", 98 | "sku": { 99 | "name": "S1", 100 | "tier": "Standard", 101 | "family": "S", 102 | "size": "S1" 103 | }, 104 | "properties": { 105 | "name": "[variables('appServicePlan_name')]" 106 | } 107 | } 108 | ] 109 | } 110 | } 111 | } 112 | ] 113 | } -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Shared/README.md: -------------------------------------------------------------------------------- 1 | # Sz.BlazorRerenderReducers 2 | 3 | Provides classes useful for reducing the frequency of re-renders of Blazor components to a minimum. 4 | 5 | ## Usage 6 | 7 | Suppose that you have a component that is being rerendered undesirably often. First, you should take the ordinary steps to enable Blazor to efficiently avoid rerendering (listed below in "How to optimize..." for your reference). But, if you are unable or unwilling to make such changes, do the following on the desired components: 8 | 9 | 1. Inherit from `DisplayHashRerenderComponentBase` on the component that you wish to reduce rerender frequency: 10 | 11 | ```c# 12 | @inherit DisplayHashRerenderComponentBase 13 | ``` 14 | 15 | If any such component already has an override for OnAfterRender, ensure it calls the method it is overriding on the base, as one might for any overridden method: 16 | ```c# 17 | protected override void OnAfterRender(bool firstRender) 18 | { 19 | base.OnAfterRender(firstRender); 20 | // ...any other calls you might have 21 | } 22 | ``` 23 | 24 | 2. Override the abstract method `GetDisplayHash` to return a string that represents *all* the displayed state of the component. That is, it should return a different value when any displayable state changes, and the same value when no visible change happens. 25 | 26 | ```c# 27 | @code { 28 | protected override string? GetDisplayHash() => InputFoo.Bar.ToString(); 29 | } 30 | ``` 31 | 32 | Alternatively, override GetDisplayItems with several strings that represent, collectively, *all* the displayed state of the component. 33 | ```c# 34 | @code { 35 | protected override string[]? GetDisplayItems() => new [] { InputFoo.Bar, InputFoo.Qux } 36 | } 37 | ``` 38 | 39 | Now your component should be rerendered only when the value of GetDisplayHash changes. To see such rerendering logged, you might choose to override AfterRender on your component. 40 | 41 | Note that child components will not rerender if the current component doesn't rerender, as the current component will not set any parameters on its children unless it rerenders. 42 | 43 | ## What not to do 44 | 45 | * Do not use on components that are already fast to rerender and thus won't benefit much. 46 | * Do not use on components that take only primitives as `[Parameter]`s (Blazor will optimize these). 47 | * Do not use on components that have a very complicated display state, as it will be too hard to maintain. Cconsider refactoring such components into several subcomponents. 48 | 49 | ## Background 50 | 51 | By default, Razor components inherit from the `Microsoft.AspNetCore.Components.ComponentBase` base class, which contains logic to trigger rerendering at the following times: 52 | 53 | * After applying an updated set of parameters from a parent component. 54 | * After applying an updated value for a cascading parameter. 55 | * After notification of an event and invoking one of its own event handlers. 56 | * After a call to its own `StateHasChanged` method. 57 | 58 | Components inherited from `Microsoft.AspNetCore.Components.ComponentBase` skip rerenders due to parameter updates if either of the following are true: 59 | 60 | * All of the parameter values are of known immutable primitive types, such as int, string, DateTime, and haven't changed since the previous set of parameters were set, or 61 | * The component's ShouldRender method returns false. 62 | 63 | This package provides a convenient way to avoid a complicated, onerous, or repetitive implementation of ShouldRender in the cases where rerendering needs to be optimized. 64 | 65 | No .NET Standard, .NET Core, or .NET Framework package is provided, as it is expected that anyone working on Blazor should be on .NET 5 or later. 66 | 67 | ## Demo 68 | 69 | See it in action at https://szalapski.github.io/BlazorRerenderReducers. 70 | 71 | ## How to optimize rerendering without using this package 72 | 73 | * Make your parameters entirely known immutable primitive types. If you do this, your component will automatically avoid rerendering unless the value of any parameter changes. 74 | * Many simple components will render very fast without any need for optimization. Measure render speed first; this information will help you avoid naively optimizing any component. 75 | * If your component does not ever need to change after its first render, you can simply override ShouldRender to return false: `protected override bool ShouldRender() => false;` 76 | * If your component can determine the need to rerender based on some other logic, you can simply override ShouldRender to return true or false based on such logic. 77 | 78 | See also: 79 | * https://docs.microsoft.com/en-us/aspnet/core/blazor/webassembly-performance-best-practices 80 | * https://docs.microsoft.com/en-us/aspnet/core/blazor/components/rendering 81 | * https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/blazor/components/rendering.md 82 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Shared/DisplayHashRerenderComponentBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Components; 3 | 4 | namespace Sz.BlazorRerenderReducers 5 | { 6 | /// 7 | /// Components that inherit have their re-render controlled by a "display hash", or a string representation 8 | /// of all info that could affect their display state. Useful for reducing the frequency of component re-renders to 9 | /// a minimum. 10 | /// 11 | /// Do not inherit this on components that are already fast to rerender and thus won't benefit much. 12 | /// Do not inherit this on components that take only primitives as Parameters (Blazor will optimize these). 13 | /// Do not inherit this on components that have a very complicated display state, as it will be too hard to maintain. 14 | /// (consider refactoring such components into several subcomponents) 15 | /// 16 | /// Also includes optional render profiling code (so you can see how often a re-render happens) 17 | public abstract class DisplayHashRerenderComponentBase : ComponentBase 18 | { 19 | /// 20 | /// Returns a string that, accross successive calls, would indicate when to re-render: no change would indicate 21 | /// a re-render is not needed, and a change would indicate a re-render is needed. 22 | /// When it returns null, then GetDisplayItems will be checked to see re-render is needed 23 | /// 24 | /// Override this method to return a unique value for each unique display state of the component--i.e. 25 | /// a string that is different when any displayable state changes, 26 | /// and the same value when no visible change happens. 27 | /// 28 | /// Note that child components will not re-render if the current component doesn't re-render, as the current component will not 29 | /// set any parameters on its children unless it rerenders. 30 | protected virtual string? GetDisplayHash() => null; 31 | 32 | /// 33 | /// Returns an array of strings that, accross successive calls, would indicate when to re-render: no change would indicate 34 | /// a re-render is not needed, and a change would indicate a re-render is needed. 35 | /// Only when GetDisplayHash returns null is this checked (GetDisplayHash has precedence) 36 | /// When it returns a null array, should also trigger a re-render regardless of whether it is a change. 37 | /// 38 | /// Override this method to return a unique array for each unique display state of the component--i.e. 39 | /// a string that is different when any displayable state changes, 40 | /// and the same value when no visible change happens. 41 | /// 42 | /// Note that child components will not rerender if the current component doesn't re-render, as the current component will not 43 | /// set any parameters on its children unless it re-renders. 44 | protected virtual string[]? GetDisplayItems() => null; 45 | 46 | 47 | /// 48 | /// Returns a flag to indicate whether the component should re-render based on the current and previous values 49 | /// returned by GetDisplayHash. 50 | /// 51 | /// Should not need to call explicitly nor override, but you can if needed. 52 | protected override bool ShouldRender() 53 | { 54 | if (!EnableRerenderReductionGlobal) return true; 55 | 56 | string? displayHash = GetDisplayHash(); 57 | 58 | if (displayHash == null) 59 | { 60 | string[]? items = GetDisplayItems(); 61 | if (items == null) return true; 62 | displayHash = string.Join(",", items!); 63 | } 64 | //Console.WriteLine($"{GetType()} GetDisplayHash='{displayHash ?? "null"}'"); // may want this just to look under the hood a bit 65 | 66 | bool result = PreviousDisplayHash == null || PreviousDisplayHash != displayHash; 67 | PreviousDisplayHash = displayHash; 68 | return result; 69 | } 70 | 71 | /// 72 | /// True to enable rerender reduction globally (the default). False to disable rerender reduction 73 | /// and therefore always render whenever Blazor deems possibly necessary (ordinary behavior without this library). 74 | /// 75 | public static bool EnableRerenderReductionGlobal { get; set; } = true; 76 | 77 | private string? PreviousDisplayHash { get; set; } = null; 78 | 79 | 80 | #region rudimentary render profiling code 81 | 82 | private bool _outputRenderProfiling = true; 83 | 84 | /// 85 | /// Whether to output rudimentary render profiling info to console. 86 | /// 87 | protected bool OutputRenderProfiling 88 | { 89 | get => _outputRenderProfiling; 90 | set 91 | { 92 | _outputRenderProfiling = value; 93 | //if (_outputRenderProfiling) RenderTimer = new(); // uncomment this to report time spent rendering on every render profile console line 94 | } 95 | } 96 | 97 | private ScopeTimer? RenderTimer { get; set; } = null; 98 | 99 | protected override void OnInitialized() 100 | { 101 | // Blazor doesn't call ShouldRender on first render, but we want it to do so, just to initialize PreviousDisplayHash. 102 | ShouldRender(); 103 | } 104 | 105 | protected override void OnParametersSet() 106 | { 107 | if (!OutputRenderProfiling) return; 108 | // RenderTimer = new ScopeTimer($"Rendered {GetType().Name}"); // uncomment this to report time spent rendering on every render profile console line 109 | } 110 | 111 | protected override void OnAfterRender(bool firstRender) 112 | { 113 | // ensures the first render doesn't result in an immediate rerender in some cases 114 | if (firstRender) PreviousDisplayHash = GetDisplayHash(); 115 | 116 | if (!OutputRenderProfiling) return; 117 | if (RenderTimer == null) Console.WriteLine($"Rendered {GetType().Name}"); 118 | else RenderTimer?.Lap(); 119 | } 120 | 121 | #endregion 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/Pages/ProductGridDemo.razor: -------------------------------------------------------------------------------- 1 | @page "/product-grid-demo" 2 | @using Sz.BlazorRerenderReducers.Client.ProductGridDemo 3 | 4 | 5 | 6 | @code { 7 | 8 | // imagine a thousand products... 9 | private List Products = new () 10 | { 11 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 12 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 13 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 14 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 15 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 16 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 17 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 18 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 19 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 20 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 21 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 22 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 23 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 24 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 25 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 26 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 27 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 28 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 29 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 30 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 31 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 32 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 33 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 34 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 35 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 36 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 37 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 38 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 39 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 40 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 41 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 42 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 43 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 44 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 45 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 46 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 47 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 48 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 49 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 50 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 51 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 52 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 53 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 54 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 55 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 56 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 57 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 58 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 59 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 60 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 61 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 62 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 63 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 64 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 65 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 66 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 67 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 68 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 69 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 70 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 71 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 72 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 73 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 74 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 75 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 76 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 77 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 78 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 79 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 80 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 81 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 82 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 83 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 84 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 85 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 86 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 87 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 88 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 89 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 90 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 91 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 92 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 93 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 94 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 95 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 96 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 97 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 98 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 99 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 100 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 101 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 102 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 103 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 104 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 105 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 106 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 107 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 108 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 109 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 110 | new(), new(), new(), new (), new(), new(), new(), new (), new(), new(), 111 | }; 112 | } 113 | -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:Icons;src:url(../fonts/open-iconic.eot);src:url(../fonts/open-iconic.eot?#iconic-sm) format('embedded-opentype'),url(../fonts/open-iconic.woff) format('woff'),url(../fonts/open-iconic.ttf) format('truetype'),url(../fonts/open-iconic.otf) format('opentype'),url(../fonts/open-iconic.svg#iconic-sm) format('svg');font-weight:400;font-style:normal}.oi{position:relative;top:1px;display:inline-block;speak:none;font-family:Icons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.oi:empty:before{width:1em;text-align:center;box-sizing:content-box}.oi.oi-align-center:before{text-align:center}.oi.oi-align-left:before{text-align:left}.oi.oi-align-right:before{text-align:right}.oi.oi-flip-horizontal:before{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.oi.oi-flip-vertical:before{-webkit-transform:scale(1,-1);-ms-transform:scale(-1,1);transform:scale(1,-1)}.oi.oi-flip-horizontal-vertical:before{-webkit-transform:scale(-1,-1);-ms-transform:scale(-1,1);transform:scale(-1,-1)}.oi-account-login:before{content:'\e000'}.oi-account-logout:before{content:'\e001'}.oi-action-redo:before{content:'\e002'}.oi-action-undo:before{content:'\e003'}.oi-align-center:before{content:'\e004'}.oi-align-left:before{content:'\e005'}.oi-align-right:before{content:'\e006'}.oi-aperture:before{content:'\e007'}.oi-arrow-bottom:before{content:'\e008'}.oi-arrow-circle-bottom:before{content:'\e009'}.oi-arrow-circle-left:before{content:'\e00a'}.oi-arrow-circle-right:before{content:'\e00b'}.oi-arrow-circle-top:before{content:'\e00c'}.oi-arrow-left:before{content:'\e00d'}.oi-arrow-right:before{content:'\e00e'}.oi-arrow-thick-bottom:before{content:'\e00f'}.oi-arrow-thick-left:before{content:'\e010'}.oi-arrow-thick-right:before{content:'\e011'}.oi-arrow-thick-top:before{content:'\e012'}.oi-arrow-top:before{content:'\e013'}.oi-audio-spectrum:before{content:'\e014'}.oi-audio:before{content:'\e015'}.oi-badge:before{content:'\e016'}.oi-ban:before{content:'\e017'}.oi-bar-chart:before{content:'\e018'}.oi-basket:before{content:'\e019'}.oi-battery-empty:before{content:'\e01a'}.oi-battery-full:before{content:'\e01b'}.oi-beaker:before{content:'\e01c'}.oi-bell:before{content:'\e01d'}.oi-bluetooth:before{content:'\e01e'}.oi-bold:before{content:'\e01f'}.oi-bolt:before{content:'\e020'}.oi-book:before{content:'\e021'}.oi-bookmark:before{content:'\e022'}.oi-box:before{content:'\e023'}.oi-briefcase:before{content:'\e024'}.oi-british-pound:before{content:'\e025'}.oi-browser:before{content:'\e026'}.oi-brush:before{content:'\e027'}.oi-bug:before{content:'\e028'}.oi-bullhorn:before{content:'\e029'}.oi-calculator:before{content:'\e02a'}.oi-calendar:before{content:'\e02b'}.oi-camera-slr:before{content:'\e02c'}.oi-caret-bottom:before{content:'\e02d'}.oi-caret-left:before{content:'\e02e'}.oi-caret-right:before{content:'\e02f'}.oi-caret-top:before{content:'\e030'}.oi-cart:before{content:'\e031'}.oi-chat:before{content:'\e032'}.oi-check:before{content:'\e033'}.oi-chevron-bottom:before{content:'\e034'}.oi-chevron-left:before{content:'\e035'}.oi-chevron-right:before{content:'\e036'}.oi-chevron-top:before{content:'\e037'}.oi-circle-check:before{content:'\e038'}.oi-circle-x:before{content:'\e039'}.oi-clipboard:before{content:'\e03a'}.oi-clock:before{content:'\e03b'}.oi-cloud-download:before{content:'\e03c'}.oi-cloud-upload:before{content:'\e03d'}.oi-cloud:before{content:'\e03e'}.oi-cloudy:before{content:'\e03f'}.oi-code:before{content:'\e040'}.oi-cog:before{content:'\e041'}.oi-collapse-down:before{content:'\e042'}.oi-collapse-left:before{content:'\e043'}.oi-collapse-right:before{content:'\e044'}.oi-collapse-up:before{content:'\e045'}.oi-command:before{content:'\e046'}.oi-comment-square:before{content:'\e047'}.oi-compass:before{content:'\e048'}.oi-contrast:before{content:'\e049'}.oi-copywriting:before{content:'\e04a'}.oi-credit-card:before{content:'\e04b'}.oi-crop:before{content:'\e04c'}.oi-dashboard:before{content:'\e04d'}.oi-data-transfer-download:before{content:'\e04e'}.oi-data-transfer-upload:before{content:'\e04f'}.oi-delete:before{content:'\e050'}.oi-dial:before{content:'\e051'}.oi-document:before{content:'\e052'}.oi-dollar:before{content:'\e053'}.oi-double-quote-sans-left:before{content:'\e054'}.oi-double-quote-sans-right:before{content:'\e055'}.oi-double-quote-serif-left:before{content:'\e056'}.oi-double-quote-serif-right:before{content:'\e057'}.oi-droplet:before{content:'\e058'}.oi-eject:before{content:'\e059'}.oi-elevator:before{content:'\e05a'}.oi-ellipses:before{content:'\e05b'}.oi-envelope-closed:before{content:'\e05c'}.oi-envelope-open:before{content:'\e05d'}.oi-euro:before{content:'\e05e'}.oi-excerpt:before{content:'\e05f'}.oi-expand-down:before{content:'\e060'}.oi-expand-left:before{content:'\e061'}.oi-expand-right:before{content:'\e062'}.oi-expand-up:before{content:'\e063'}.oi-external-link:before{content:'\e064'}.oi-eye:before{content:'\e065'}.oi-eyedropper:before{content:'\e066'}.oi-file:before{content:'\e067'}.oi-fire:before{content:'\e068'}.oi-flag:before{content:'\e069'}.oi-flash:before{content:'\e06a'}.oi-folder:before{content:'\e06b'}.oi-fork:before{content:'\e06c'}.oi-fullscreen-enter:before{content:'\e06d'}.oi-fullscreen-exit:before{content:'\e06e'}.oi-globe:before{content:'\e06f'}.oi-graph:before{content:'\e070'}.oi-grid-four-up:before{content:'\e071'}.oi-grid-three-up:before{content:'\e072'}.oi-grid-two-up:before{content:'\e073'}.oi-hard-drive:before{content:'\e074'}.oi-header:before{content:'\e075'}.oi-headphones:before{content:'\e076'}.oi-heart:before{content:'\e077'}.oi-home:before{content:'\e078'}.oi-image:before{content:'\e079'}.oi-inbox:before{content:'\e07a'}.oi-infinity:before{content:'\e07b'}.oi-info:before{content:'\e07c'}.oi-italic:before{content:'\e07d'}.oi-justify-center:before{content:'\e07e'}.oi-justify-left:before{content:'\e07f'}.oi-justify-right:before{content:'\e080'}.oi-key:before{content:'\e081'}.oi-laptop:before{content:'\e082'}.oi-layers:before{content:'\e083'}.oi-lightbulb:before{content:'\e084'}.oi-link-broken:before{content:'\e085'}.oi-link-intact:before{content:'\e086'}.oi-list-rich:before{content:'\e087'}.oi-list:before{content:'\e088'}.oi-location:before{content:'\e089'}.oi-lock-locked:before{content:'\e08a'}.oi-lock-unlocked:before{content:'\e08b'}.oi-loop-circular:before{content:'\e08c'}.oi-loop-square:before{content:'\e08d'}.oi-loop:before{content:'\e08e'}.oi-magnifying-glass:before{content:'\e08f'}.oi-map-marker:before{content:'\e090'}.oi-map:before{content:'\e091'}.oi-media-pause:before{content:'\e092'}.oi-media-play:before{content:'\e093'}.oi-media-record:before{content:'\e094'}.oi-media-skip-backward:before{content:'\e095'}.oi-media-skip-forward:before{content:'\e096'}.oi-media-step-backward:before{content:'\e097'}.oi-media-step-forward:before{content:'\e098'}.oi-media-stop:before{content:'\e099'}.oi-medical-cross:before{content:'\e09a'}.oi-menu:before{content:'\e09b'}.oi-microphone:before{content:'\e09c'}.oi-minus:before{content:'\e09d'}.oi-monitor:before{content:'\e09e'}.oi-moon:before{content:'\e09f'}.oi-move:before{content:'\e0a0'}.oi-musical-note:before{content:'\e0a1'}.oi-paperclip:before{content:'\e0a2'}.oi-pencil:before{content:'\e0a3'}.oi-people:before{content:'\e0a4'}.oi-person:before{content:'\e0a5'}.oi-phone:before{content:'\e0a6'}.oi-pie-chart:before{content:'\e0a7'}.oi-pin:before{content:'\e0a8'}.oi-play-circle:before{content:'\e0a9'}.oi-plus:before{content:'\e0aa'}.oi-power-standby:before{content:'\e0ab'}.oi-print:before{content:'\e0ac'}.oi-project:before{content:'\e0ad'}.oi-pulse:before{content:'\e0ae'}.oi-puzzle-piece:before{content:'\e0af'}.oi-question-mark:before{content:'\e0b0'}.oi-rain:before{content:'\e0b1'}.oi-random:before{content:'\e0b2'}.oi-reload:before{content:'\e0b3'}.oi-resize-both:before{content:'\e0b4'}.oi-resize-height:before{content:'\e0b5'}.oi-resize-width:before{content:'\e0b6'}.oi-rss-alt:before{content:'\e0b7'}.oi-rss:before{content:'\e0b8'}.oi-script:before{content:'\e0b9'}.oi-share-boxed:before{content:'\e0ba'}.oi-share:before{content:'\e0bb'}.oi-shield:before{content:'\e0bc'}.oi-signal:before{content:'\e0bd'}.oi-signpost:before{content:'\e0be'}.oi-sort-ascending:before{content:'\e0bf'}.oi-sort-descending:before{content:'\e0c0'}.oi-spreadsheet:before{content:'\e0c1'}.oi-star:before{content:'\e0c2'}.oi-sun:before{content:'\e0c3'}.oi-tablet:before{content:'\e0c4'}.oi-tag:before{content:'\e0c5'}.oi-tags:before{content:'\e0c6'}.oi-target:before{content:'\e0c7'}.oi-task:before{content:'\e0c8'}.oi-terminal:before{content:'\e0c9'}.oi-text:before{content:'\e0ca'}.oi-thumb-down:before{content:'\e0cb'}.oi-thumb-up:before{content:'\e0cc'}.oi-timer:before{content:'\e0cd'}.oi-transfer:before{content:'\e0ce'}.oi-trash:before{content:'\e0cf'}.oi-underline:before{content:'\e0d0'}.oi-vertical-align-bottom:before{content:'\e0d1'}.oi-vertical-align-center:before{content:'\e0d2'}.oi-vertical-align-top:before{content:'\e0d3'}.oi-video:before{content:'\e0d4'}.oi-volume-high:before{content:'\e0d5'}.oi-volume-low:before{content:'\e0d6'}.oi-volume-off:before{content:'\e0d7'}.oi-warning:before{content:'\e0d8'}.oi-wifi:before{content:'\e0d9'}.oi-wrench:before{content:'\e0da'}.oi-x:before{content:'\e0db'}.oi-yen:before{content:'\e0dc'}.oi-zoom-in:before{content:'\e0dd'}.oi-zoom-out:before{content:'\e0de'} -------------------------------------------------------------------------------- /Sz.BlazorRerenderReducers/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014 9 | By P.J. Onori 10 | Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net) 11 | 12 | 13 | 14 | 27 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 45 | 47 | 49 | 51 | 53 | 55 | 57 | 59 | 61 | 63 | 65 | 67 | 69 | 71 | 74 | 76 | 79 | 81 | 84 | 86 | 88 | 91 | 93 | 95 | 98 | 100 | 102 | 104 | 106 | 109 | 112 | 115 | 117 | 121 | 123 | 125 | 127 | 130 | 132 | 134 | 136 | 138 | 141 | 143 | 145 | 147 | 149 | 151 | 153 | 155 | 157 | 159 | 162 | 165 | 167 | 169 | 172 | 174 | 177 | 179 | 181 | 183 | 185 | 189 | 191 | 194 | 196 | 198 | 200 | 202 | 205 | 207 | 209 | 211 | 213 | 215 | 218 | 220 | 222 | 224 | 226 | 228 | 230 | 232 | 234 | 236 | 238 | 241 | 243 | 245 | 247 | 249 | 251 | 253 | 256 | 259 | 261 | 263 | 265 | 267 | 269 | 272 | 274 | 276 | 280 | 282 | 285 | 287 | 289 | 292 | 295 | 298 | 300 | 302 | 304 | 306 | 309 | 312 | 314 | 316 | 318 | 320 | 322 | 324 | 326 | 330 | 334 | 338 | 340 | 343 | 345 | 347 | 349 | 351 | 353 | 355 | 358 | 360 | 363 | 365 | 367 | 369 | 371 | 373 | 375 | 377 | 379 | 381 | 383 | 386 | 388 | 390 | 392 | 394 | 396 | 399 | 401 | 404 | 406 | 408 | 410 | 412 | 414 | 416 | 419 | 421 | 423 | 425 | 428 | 431 | 435 | 438 | 440 | 442 | 444 | 446 | 448 | 451 | 453 | 455 | 457 | 460 | 462 | 464 | 466 | 468 | 471 | 473 | 477 | 479 | 481 | 483 | 486 | 488 | 490 | 492 | 494 | 496 | 499 | 501 | 504 | 506 | 509 | 512 | 515 | 517 | 520 | 522 | 524 | 526 | 529 | 532 | 534 | 536 | 539 | 542 | 543 | 544 | --------------------------------------------------------------------------------