Blazor develops WEB programs

foreword

For back-end programmers, the front-end looks very simple, but there are many frameworks, such as the three popular front-end frameworks Vue.js、Angular.js、React.js. Each framework corresponds to a variety of matching libraries. It is not easy to build a complete knowledge system for front-end development. And it seems that some high-end technologies such as picture technology, distributed computing, cloud rendering, etc. are somewhat related to front-end technology, and the idea of ​​doing full-stack development was born.
To be a full-stack, the best thing is a technology that dominates the world, rather than a completely irrelevant technology system. I have more than ten years of experience in C++ research and development. It can be said that I am relatively clear about the research and development of various scenarios of C++. Some biases from other technologies. Recently, after working on various projects, I found that the development of some languages ​​is really fragrant (C#'s extension method, reflection mechanism, Linq, package management, etc. have greatly improved the development efficiency; NODEJS front-end and back-end are developed together, and a set of codes can be solved by one compilation. Multi-terminal application; Vue framework makes me not care about Dom operation). Technology serves to solve problems, and there is nothing good or bad in itself. It is a good technology to use a specific technology well in a specific scenario, and to be able to solve the problems in the project quickly and quickly. To do full-stack development, the most important thing is to open up the links of various languages ​​(based on C++, to break through the barriers of various languages).
Webassembly technology is a technology that allows back-end personnel to develop front-end programs. Before using Blazor, C++ Webassembly programs were developed through C++'s EMSDK technology (the difficulty is hell level for project development, compiling, debugging, interface Encapsulation is very time consuming). So I recently switched to WebAssembly development in other languages. Only then did I discover the solution of Microsoft's Blazor's C# WebAssembly.

What is Blazor

Blazor is a WEB client UI interaction framework launched by Microsoft in .NET. Using Blazor, you can replace JavaScript to implement your own page interaction logic, and can reuse C# code to a large extent. Blazor is for .NET developers It is a good choice.
Blazor has two hosting modes, one is Blazor Server mode, which is based on asp.net core deployment, and the interaction between client and server is done through SignalR to realize client UI update and behavior interaction.
The other is the Blazor WebAssembly mode, which downloads the Blazor application, its dependencies, and the .NET runtime to the browser (so there is a defect that it may have to be recompiled in a different end), and the application will be executed directly in the browser thread . Has the following benefits:
The Blazor WebAssembly hosting model has the following benefits

There is no .NET server-side dependency, and the application can run normally after being downloaded to the client.
Can make full use of client resources and functions.
Work can be transferred from the server to the client.
Host apps without an ASP.NET Core web server. Serverless deployment scenarios are possible, such as those where applications are served via a content delivery network (CDN).

The Blazor WebAssembly hosting model has the following limitations:

Apps can only use browser functionality.
Requires available client hardware and software (such as WebAssembly support).
The download size is large, and the application takes a long time to load.
The .NET runtime and tooling support is underdeveloped. For example, there are limitations in .NET Standard support and debugging.

This article mainly introduces WebAssembly technology.

Blazor installation

  1. Install the Asp.net environment and confirm that the WebAssembly component is checked

image.pngimage.png

  1. After the installation is complete, open VS to select the Blazor project, select Blazor WebAssemblythe application, and then use the default options

image.png

  1. After creating the project as shown in the figure

image.png

  1. run after compiling

image.png

Blazor project structure

This is a rough template for creating a Blazor WebAssembly
image.png

  • Counter component (Counter.razor): Implements the "Counter" page.
  • FetchData component (FetchData.razor): Implements the Fetch Data page.
  • Index component (Index.razor): implements the home page.
  • Properties/launchSettings.json: Keep the development environment configuration.
  • Shared Folder: Contains the following shared components and stylesheets:
  • MainLayout component (MainLayout.razor): The layout component of the application
  • MainLayout.razor.css: The style sheet that applies the main layout.
  • NavMenu component (NavMenu.razor): implements sidebar navigation. Includes the NavLink component (NavLink), which renders navigation links to other Razor components. The NavLink component automatically indicates the selected state when its components are loaded, which helps the user understand which component is currently displayed.
  • NavMenu.razor.css: Applies the style sheet for the navigation menu.
  • SurveyPrompt component (SurveyPrompt.razor): Blazor survey component (subcomponent)
  • wwwroot: The app's web root folder, which contains the app's public static assets, including appsettings.json and the ambient app settings file for configuration settings. The index.html page is the root page of an application implemented as an HTML page.

When any page of the application is initially requested, it is rendered and returned in the response.
This page specifies where the root App component is rendered. Using the app's id (

Loading…
) renders the component at the position of the div DOM element.

  • _Imports.razor: Contains common Razor directives to be included in the application component (.razor), such as the @using directive for the namespace, which is equivalent to a global reference component command
  • App.razor: The root component of the application, used to use the Router component to set up client routing. The Router component intercepts browser navigation and renders a page matching the requested address.
  • Program.cs: Application entry point for setting up the WebAssembly host:

The App component is the root component of the application. For the root component collection (builder.RootComponents.Add(“#app”)), use the id of the app (in wwwroot/index.html

Loading…
) specifies the App component as a div DOM element.
A service is added and configured (for example, builder.Services.AddSingleton<IMyDependency, MyDependency>()).

write the first page

Blazor pages are written using razor syntax, not similar to front-end JS code or HTML writing, razor syntax can refer to related articles

Blazor's page structure is somewhat similar to Vue's structure, consisting of page structure + logic code.

@page "/counter"  //页面路由,如果是一个独立的页面需要加上这个页面标记,如果作为一个子组件则没有必要
    
    <PageTitle>Counter</PageTitle>  //
    
    <h1>Counter</h1>
    
    <p role="status">Current count: @currentCount</p> //@是razor的语法,用于绑定C#的属性或者方法来的
    
     <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> //直接调用方法
   
   //C#代码块
   @code {
    
    
         private int currentCount = 0;

         private void IncrementCount()
         {
    
    
             currentCount++;
         }
      }

It will be confusing when you look at it at first. C# and the page code are written together, but the page code can actually call the C# code, and there is no class structure. To understand this interface, we should treat this component as a class object. The page and logic code belong to the same class. Although sometimes it seems to be in two different files, it is a class (inheriting the ComponentBase class) , just written in different places. For the organization of this C#, please refer to
the C# code organization form of the razor component https://www.cnblogs.com/harrychinese/p/blazaor_CSharp_code.html
, here casually introduce the relevant instructions of razor

@code是Razor 组件专用,用于在@code代码块中定义页面类需要的字段,属性,方法等定义。@code是@functions的别名。但是建议在Razor组件中使用@code,而非@functions.

@implements 指令,类比如C# 类的implements.

@inherits: 类比C#的中继承,在Blazor的类的组织方式里面有说明

@inject: 从DI容器中引入需要的类,一个单例类的引入。在C#类中使用[inject]标记

@layout: 指定页面需要继承的布局模板。

@model: 专用于MVC 视图和Razor Page页面,用于指定model

@namespace: 用户指定生成的页面类所处的名称空间。

@page指令:用户razor page项目或者razor 组件,用于定义路由。

@preservewhitespace: 是否保留空白,默认是false.

@section: 用户razor page或者是mvc

Use Skia to implement Blazor's drawing function

To achieve drawing in Blazor, you can use the canvas function similar to html, and use the library Blazor.Extensions.Canvasto achieve it . There are many examples inside. Since the skia library is used more in the project, the skia library is used for Blazor drawing.

1. Skia library installation

Use nuget to install SkiaSharp and SkiaSharp.Views.Blazor libraries, SkiaSharp is the basic library, and SkiaSharp.Views.Blazor is responsible for the display of the surface.

2. Create a drawing page

  1. Add a drawing page, I directly modify the main page skiacanvas.razor
  2. Add a corresponding page code class skiacanvas.razor.cs to realize binding with the skiacanvas.razor page
  3. Add a label to skiacanvas.razor SKCanvasView, set the IgnorePixelScaling property to true (meaning that pixel scaling is ignored, otherwise the offset value obtained in the mouse event is different from the actual value, which is proportional to the system's screen scaling ratio ), specifying @ref="canvasView" the canvasView object is a member variable defined in the C# codeSKCanvasView canvasView
<Layout ShowFooter="false">   // 这里面使用了BootstrapBlazor组件库进行页面布局
	<Header>
		<PageTitle>XDGraph WEB系统</PageTitle>
		<br/>
	</Header>
<Main>
		<SKCanvasView @ref="canvasView" IgnorePixelScaling=true style="@CursorType" OnPaintSurface="OnPaintSurface"  @onkeydown="OnCanvasKeyDown" 
		@onmousemove="OnCanvasMouseMove"  @onwheel="OnCanvasMouseWheel" @onmousedown="OnCanvasMouseDown" tabindex="0"
		@onmouseup="OnCanvasMouseUp"/>
</Main>
</Layout>

Precautions:

  1. By default, the SKCanvasView tag does not support keyboard events (similar to html5 canvas), and to respond to keyboard events, the tabindex attribute needs to be given
  2. The OnPaintSurface event must be implemented, because all drawing is done in the OnPaintSurface event
  3. The initialization of the canvasView binding object is not in the constructor of the corresponding c# code, but after the page initialization is completed.

3. SkiaCanvas.Razor.cs code implementation

The following is the code, the actual code cannot be posted directly in the project.

using SkiaSharp;
using SkiaSharp.Views.Blazor;
public partial class SkiaCanvas
{
    
    
     protected SKCanvasView canvasView=null;

      
      public void OnCanvasKeyDown(KeyboardEventArgs e)
      {
    
    
          //键盘事件
      }
    public void OnCanvasMouseWheel(WheelEventArgs e)
    {
    
    
        
    }
     public void OnCanvasMouseMove(MouseEventArgs e)
    {
    
    
        
        var mouse_event = toEvent(e);

        
        _component.MouseMove(mouse_event);
        
        move_point =new Point(mouse_event.OffsetX,mouse_event.OffsetY);
    }
    public void OnPaintSurface(SKPaintSurfaceEventArgs e)
    {
    
    
        var canvas = e.Surface.Canvas;

        //SKBitmap bitmap = new SKBitmap(e.Info.Width, e.Info.Height);
        canvas.Clear(SKColors.White);

      //调用底层函数绘制
        _component.Graph.View.Redraw(canvas);

        //绘制坐标;
        using var paint = new SKPaint
        {
    
    
            Color = SKColors.Black,
            IsAntialias = true,
            Typeface = SkiaChinaFont.ChinaFont,
            TextSize = 24
        };

        if (move_point != null)
        {
    
    
            //move_point是mousemove事件中获取的,直接在渲染接口中好像没有办法获取到;
            var msg1 = $"x:{
      
      move_point.X.ToString("F2")} y:{
      
      move_point.Y.ToString("F2")}";
            canvas.DrawText(msg1, 0, e.Info.Height-30, paint);
        }
    }
    
}

4. About the adaptive problem of the drawing interface

The window size of SKCanvasView is changed by the browser window. The label size setting in the component is style="width:900px;height=900px"realized through similar code. There is no corresponding event response in the razor component code, including the default front-end language. Generally, it is handled by listening to the window.reszie event. This involves the interaction between C# and JS. Fortunately, there is an open source library that encapsulates it for us. BlazorPro.BlazorSizeThis library solves the problem of adaptability to size changes. See how to use the library for details.

The following code adds the following code to the original code to achieve.

    public static string DefaultStyle= "width:100%;heiht:100%;";
    [Parameter]
    public string CursorType {
    
     get; set; } = DefaultStyle;

   protected override void OnAfterRender(bool firstRender)
    {
    
    
    
        if (firstRender)
        {
    
    
            // Subscribe to the OnResized event. This will do work when the browser is resized.
            listener.OnResized += WindowResized;
        }
    }
    void WindowResized(object? sender,BrowserWindowSize window)
    {
    
    
         
        browser = window;
        int width = browser.Width;
        int height = browser.Height-100;
        DefaultStyle = $"width:100%;height:{
      
      height}px;";
        CursorType = DefaultStyle;
        StateHasChanged();
    }

5. Chinese text display

By default, the Chinese characters drawn by Skia cannot be displayed. At this time, the display of Chinese fonts needs to be added.

 public static class SkiaChinaFont
    {
    
    
        public static SKTypeface ChinaFont {
    
     get; private set; }

        static SkiaChinaFont()
        {
    
    
            //加载资源方案,设置字体文件属性为嵌入的资源(需要注意)
            try
            {
    
    
                //嵌入资源的访问是应用程序名加上路径,路径使用.而不是使用/。
                //如XDGraphWeb.res.DroidSansFallback.ttf 表示的是应用程序名为XDGraphWeb 在当前项目的res文件夹下的DroidSansFallback.ttf文件.
                var fontStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("XDGraphWeb.res.DroidSansFallback.ttf");
                ChinaFont = SKTypeface.FromStream(fontStream);
            }
            catch(Exception e)
            {
    
    
                var error=e.Message;
                int c = 0;
            }
            
        }
    }

6. Results

image.png

Access to static resources of Blazor

There are two kinds of static resources in WebAssembly, one is the embedded resource of C#, and the other is the file access under the wwwroot directory.

1. Embedded resources

We usually come into contact with more local file systems, or file systems running on remote servers such as FTP and object storage. These are non-embedded resources, so embedded resources mainly refer to files without directory hierarchy resource, because it will be "embedded" in the dynamic link library (DLL) at compile time. The resource files introduced into skia before belong to the access of embedded resources.

  1. Select the specified file in Visual Studio, and select the build operation as an embedded resource in its properties window

image.png
In this way, we have completed the definition of embedded resources. The definition of embedded resources is essentially to read and use during runtime, so naturally, we can't help but ask, how to read these embedded resources? In the Assembly class, Microsoft provides us with the following interfaces to handle embedded resources.

public virtual ManifestResourceInfo GetManifestResourceInfo(string resourceName);
public virtual string[] GetManifestResourceNames();//返回资源名称
public virtual Stream GetManifestResourceStream(Type type, string name);//获取资源中的留数据
public virtual Stream GetManifestResourceStream(string name);

Through GetManifestResourceNamesthis method, we found that its resources are accessed through the ABC path.

var assembly = Assembly.GetExecutingAssembly();
var resources = assembly.GetManifestResourceNames();
resources.ToList().ForEach(x => Console.WriteLine(x));
var fileInfo = assembly.GetManifestResourceInfo(resources[0]);
var fileStream = assembly.GetManifestResourceStream(resources[0]);//获取文件的字节流

2. httpClient

The library where the HttpClient class resides is System.Net.Http, and the default template of Blazor webassembly has automatically registered HttpClient into the DI container, which is very convenient to use. The Program.Main function registers the DI container code

builder.Services.AddScoped(sp => new HttpClient {
    
     BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

The problems with using HttpClient directly are:

  • The main problem with HttpClient is that even after Dispose, the socket connection cannot be closed immediately. Under Windows, the socket needs to wait for 240 seconds by default before closing the socket. Using HttpClient in large quantities in a short time will exhaust the socket connections between the client and server. See the analysis of reference document 1. Therefore, client applications generally use the singleton mode to use the HttpClient class. The same is true for Blazor webassembly.
  • If you use the singleton mode, it is very inconvenient to set different headers for different urls.
  • HttpClient will also cache IP, if DNS is updated later, HttpClient will still use the old IP

Therefore, under normal circumstances, HttpClient is registered as a singleton class. After registration as a singleton, the code in Razor.cs is

 //直接获取单例类;
[Inject]
private HttpClient Http {
    
     get; set; }  //一定要设置为get;set 否则会崩溃。HttpClient只能设置在这样的组件类中设置,普通类设置无效还有可能出问题。

Use of HttpClient

// 读取html数据,在wwwroot目录下的数据
var xml_string= await Http.GetStringAsync("/sample-data/defaultConfig.Xml"); 
//获取json数据
 protected override async Task OnInitializedAsync()
{
    
    
    Items = await Http.GetFromJsonAsync<MenuItem[]>("sample-data/menu.json");  
    return;
}

BootstrapBlazor interface library for Blazor

https://www.blazor.zone/introduction BootstrapBlazor interface library provides nearly 200 components, basically covering most interface usage of front-end development.
image.png

  1. Use of dialog boxes
 
@using System.ComponentModel.DataAnnotations
@using Microsoft.Extensions.Localization
@using System.Xml 
<div>
 
    <ValidateForm Model="Project" OnValidSubmit="OnSubmit">
        <div class="row g-3">
            
            <div class="col-12">
                <InputUpload @bind-Value="@Project.Mxe" DisplayText="工程文件名"  />
            </div>
            <div class="col-12">
                <Button ButtonType="@ButtonType.Submit" Text="提交"></Button>
            </div>
        </div>
    </ValidateForm>
</div>
@code
{
    
    
   //表单验证设置
    private class PersonInfo
    {
    
    
        [Required]
        [FileValidation(Extensions = new string[] {
      
       ".mxe" }, FileSize = 2*1024 * 1024)]
        public IBrowserFile? Mxe {
    
     get; set; }
    }

    private PersonInfo Project {
    
     get; set; } = new PersonInfo();
    //对话框的参数传递,返回值必须是object
    [CascadingParameter(Name = "BodyContext")]
    public object? Graph {
    
     get; set; }

    //设置回调函数参数;
    [Parameter]
    public Action? OnClose {
    
     get; set; }

    //执行提交功能,打开本地文件,相当于实现了文件上传功能
    private async Task OnSubmit(EditContext context)
    {
    
    
        var graph=Graph as XDGraph;
        if(graph==null || Project.Mxe==null)
        {
    
    
            return;
        }
        MemoryStream ms = new MemoryStream();

        await  Project.Mxe.OpenReadStream().CopyToAsync(ms);
        //从内存数据流中读取文件;
        XmlDocument docxml = new XmlDocument();
       
        byte[] b = ms.ToArray();
        string s = System.Text.Encoding.UTF8.GetString(b, 0, b.Length);

        docxml.LoadXml(s);

      
        //删除历史;
        graph.Editor().UndoManager().Clear();

        var codec = new XDCodec();
        graph.SetModel(codec.DecodeGraphModel(docxml, graph.Model));
        graph.Repaint();

        if(OnClose!=null)
        {
    
    
            OnClose.Invoke();
        }
         return ;
    }
}
 

Using dialog functions


 public async Task OpenFile()
 {
    
    
    var option = new DialogOption()
    {
    
    
        Title = "打开本地工程文件",
        IsKeyboard=true,
        ShowHeaderCloseButton=true
        
    };
    //也可以当成参数传递;
    option.BodyContext = MainGrpah;
    
    option.BodyTemplate = BootstrapDynamicComponent.CreateComponent<OpenDialogComponent>(new Dictionary<string, object?>
    {
    
    
        [nameof(OpenDialogComponent.OnClose)] = new Action(async () => await option.Dialog.Close())
    }).Render();
    await DialogService.Show(option);
 }

Blazor-related learning sites

Examples of Blazor on Github https://github.com/syncfusion/blazor-samples
Blazor.Extensions.Canvas https://github.com/mizrael/BlazorCanvas
SkiaSharp drawing on the web https://www.cnblogs.com/ sunnytrudeau/p/15574467.html
bibi video tutorial https://www.bilibili.com/video/BV19K4y1e7kd?p=1
Blazor official website https://docs.microsoft.com/zh-cn/aspnet/core/blazor/? view=aspnetcore-6.0
https://github.com/AdrienTorris/awesome-blaz
blazor interface library https://www.blazor.zone/introduction
C# organization of blazor https://www.cnblogs.com/harrychinese/p /blazaor_CSharp_code.html
Blazor's C# and JS interoperability https://www.cnblogs.com/functionMC/p/16552500.html

Guess you like

Origin blog.csdn.net/qq_33377547/article/details/126935824