目录

介绍

代码

组件渲染

Blazor UI事件

Blazor UI事件模式

总结

附录


介绍

对于刚接触Blazor的程序员来说,最常见的问题之一是UI事件和相关的渲染过程。此类问题每天都会发布在StackOverflow等网站上。希望这篇文章能消除一些疑惑!

代码

本文没有代码库。在本文的附录中有一个单页演示Razor文件,您可以使用它进行测试。

渲染片段

什么是RenderFragment?

对于许多人来说,它看起来像这样:

<div>Hello World
</div>

string——Razor中的标记块。

深入到DotNetCore代码库,你会发现:

public delegate void RenderFragment(RenderTreeBuilder builder);

如果您不完全理解委托,请将其视为一种模式。任何符合模式的函数都可以作为RenderFragment。

该模式规定您的方法必须:

  1. 有一个,而且只有一个,类型为RenderTreeBuilder的参数。
  2. 返回一个void。

让我们看一个例子:

protected void BuildHelloWorld(RenderTreeBuilder builder)
{builder.OpenElement(0, "div");builder.AddContent(1, "Hello World");builder.CloseElement();
}

我们可以将其重写为属性:

protected RenderFragment HelloWorldFragment => (RenderTreeBuilder builder) =>
{builder.OpenElement(0, "div");builder.AddContent(1, "Hello World");builder.CloseElement();
};

或者:

protected RenderFragment HelloWorldFragment => (builder) =>{builder.OpenElement(0, "div");builder.AddContent(1, "Hello World");builder.CloseElement();};

当一个Razor文件被编译时,它被Razor编译器转换成一个C#类文件。

ADiv.razor组件:

<div>Hello World
</div>

被编译成:

namespace Blazr.UIDemo.Pages
{public partial class ADiv : Microsoft.AspNetCore.Components.ComponentBase{protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder){__builder.AddMarkupContent(0, "<div>\r\n Hello World\r\n</div>");}}
}

组件渲染

Razor页面/组件的基本组件是ComponentBase.。这个类有一个public方法StateHasChanged来渲染组件。

一个常见的问题代码片段:

void ButtonClick()
{// set some message saying processingStateHasChanged();// Do some work// set some message saying completeStateHasChanged();
}

出现的唯一消息是完整的。为什么?第一个StateHasChanged不是在调用“Do Some Work”之前重新渲染组件吗?

是的,StateHasChanged确实运行了。但是,要理解这个问题,我们需要仔细查看StateHasChanged和组件渲染片段的缩写版本。

protected void StateHasChanged()
{if (_hasPendingQueuedRender)return;else{_hasPendingQueuedRender = true;_renderHandle.Render(_renderFragment);}
}_renderFragment = builder =>{_hasPendingQueuedRender = false;BuildRenderTree(builder);};

首先,它检查渲染是否已经排队——_hasPendingQueuedRender是false。如果不是,则设置_hasPendingQueuedRender为true并调用_renderHandle.Render传递它_renderFragment(组件的渲染片段)。就是这样。

_hasPendingQueuedRender实际运行渲染片段时设置为false。对于好奇的人,_renderHandle在被附加到RenderTree(Renderer调用Attach)是别传递给组件。

需要理解的重要一点是,StateHasChanged将组件渲染片段_renderFragment作为 要给delegate队列放到Renderer的渲染队列上。它不执行渲染片段。那是Renderer的工作。

如果我们回到按钮点击,它是在UI线程上运行的所有顺序同步代码。renderer不运行——从而服务于它的渲染队列——直到ButtonClick完成。没有屈服。

Blazor UI事件

让我们看另一个常见问题来理解UI事件流程:

async void ButtonClick()
{// set some message saying processing// Call Task.Wait to simulate some yielding async workawait Task.Wait(1000);// set some message saying complete
}

为什么我们只看到第一条消息?在代码末尾添加一个StateHasChanged,它就可以工作了。

async void ButtonClick()
{// set some message saying processing// Call Task.Wait to simulate some yielding async workawait Task.Wait(1000);// set some message saying completeStateHasChanged();
}

您可能已经解决了显示问题,但您还没有解决问题。

Blazor UI事件模式

Blazor UI事件不是一劳永逸的。使用的基本模式是:

var task = InvokeAsync(EventMethod);
StateHasChanged();
if (!task.IsCompleted)
{await task;StateHasChanged();
}

我们的按钮事件得到了一个Task包装器 task。它要么运行到一个yield事件,要么运行到完成。此时,StateHasChanged被调用并且渲染事件排队并执行。如果task尚未完成,则处理程序等待任务,并在完成时调用StateHasChanged。

ButtonClick的问题是它产生了,但是给事件处理程序传递一个void,事件处理程序没有什么可等待的。它在产生代码运行完成之前运行完成。没有第二个渲染事件。

解决方案是让ButtonClick返回一个Task:

async Task ButtonClick()
{// set some message saying processing// Call Task.Wait to simulate some yielding async workawait Task.Wait(1000);// set some message saying completeStateHasChanged();
}

现在事件处理程序task有一些东西要等待。

几乎所有UI事件都使用相同的模式。您还可以看到它在OnInitializedAsync和OnParametersSetAsync中使用。

那么最佳实践是什么?何时在事件处理程序中使用void和Task?

通常,不要将async关键字与void关键字混在一起。如果有疑问,请通过Task.

总结

从这篇文章中获取的关键信息是:

  1. RenderFragment是一个delegate——它是一个使用RenderTreeBuilder来构建html标记的代码块。
  2. StateHasChanged不渲染组件或执行RenderFragment.。它将一个RenderFragment推送到Renderer的队列中。
  3. UI事件处理程序需要让出给Renderer线程时间来运行它的渲染队列。
  4. UI事件处理程序不是一劳永逸的。
  5. 不要像这样声明事件处理程序:async void UiEvent()。如果是async,那么就是async Task UiEvent()。

附录

演示页面

这是一个独立的页面,展示了上面讨论的一些问题和解决方案。长时间运行的任务是实数运算方法(寻找质数),以演示真正的同步和异步长时间运行的操作。Task.Yield每次找到质数时,异步版本都会调用以产生执行控制。您可以使用此页面来测试各种场景。

@page "/"
@using System.Diagnostics;
@using Microsoft.AspNetCore.Components.Rendering;<h1>UI Demo</h1>@MyDiv@MyOtherDiv<div class="container"><div class="row"><div class="col-4"><span class="col-form-label">Primes to Calculate: </span><input class="form-control" @bind-value="this.primesToCalculate" /></div><div class="col-8"><button class="btn @buttoncolour" @onclick="Clicked1">Click Event</button><button class="btn @buttoncolour" @onclick="Clicked2">Click Async Void Event</button><button class="btn @buttoncolour ms-2" @onclick="ClickedAsync">Click Async Task Event</button><button class="btn @buttoncolour" @onclick="Reset">Reset</button></div></div>
</div>
@code{bool workingstate;string buttoncolour => workingstate ? "btn-danger" : "btn-success";string MyDivColour => workingstate ? "bg-warning" : "bg-primary";string myOtherDivColour => workingstate ? "bg-danger" : "bg-dark";long tasklength = 0;long primesToCalculate = 10;string message = "Waiting for some action!";private async Task Reset(){message = "Waiting for some action!";workingstate = false;}private async Task ClickedAsync(){workingstate = true;message = "Processing";await LongYieldingTaskAsync();message = $"Complete : {DateTime.Now.ToLongTimeString()}";workingstate = false;}private void Clicked1(){workingstate = true;message = "Processing";LongTaskAsync();message = $"Complete : {DateTime.Now.ToLongTimeString()}";workingstate = false;}private async void Clicked2(){workingstate = true;message = "Processing";await Task.Yield();await LongTaskAsync();message = $"Complete : {DateTime.Now.ToLongTimeString()}";workingstate = false;}private RenderFragment MyDiv => (RenderTreeBuilder builder) =>{builder.AddMarkupContent(0, $"<div class='text-white {MyDivColour} m-2 p-2'>{message}</div>");};private RenderFragment MyOtherDiv => (builder) =>{builder.OpenElement(0, "div");builder.AddAttribute(1, "class", $"text-white {myOtherDivColour} m-2 p-2");builder.AddMarkupContent(0, message);builder.CloseElement();};public Task LongTaskAsync(){var watch = new Stopwatch();var num = primesToCalculate * 1;watch.Start();var counter = 0;for (long x = 0; x <= num; x++){for (long i = 0; i <= (10000); i++){bool isPrime = true;for (long j = 2; j < i; j++){if (i % j == 0){isPrime = false;break;}}if (isPrime){counter++;}}}watch.Stop();tasklength = watch.ElapsedMilliseconds;return Task.CompletedTask;}public async Task LongYieldingTaskAsync(){var watch = new Stopwatch();var num = primesToCalculate * 1;watch.Start();var counter = 0;for (long x = 0; x <= num; x++){for (long i = 0; i <= (10000); i++){bool isPrime = true;for (long j = 2; j < i; j++){if (i % j == 0){isPrime = false;break;}}if (isPrime){counter++;await Task.Yield();}}}watch.Stop();tasklength = watch.ElapsedMilliseconds;}
}

https://www.codeproject.com/Articles/5310624/Blazor-UI-Events-and-Rendering

Blazor UI事件和渲染相关推荐

  1. Android学习笔记--处理UI事件

    Handling UI Events 在Android里, 有不只一种方式可以截获用户与你的应用程序交互的事件. 在你的界面上处理事件时,你需要捕获用户与某个View实例交互时所产生的事件.View类 ...

  2. UI事件与内容,舞台与演员

    UI事件:创建/清除/显示/隐藏/填充内容/位置变化/形态变化/尺寸变化/颜色变化/ 非UI事件:点击/输入/拖动/ 转载于:https://www.cnblogs.com/jcode/p/62469 ...

  3. unity UI事件

    由于工作需要到持续按键,所以了解了一下unity UI事件,本文主要转载于http://www.cnblogs.com/zou90512/p/3995932.html?utm_source=tuico ...

  4. 正式发布! .NET开发控件集ComponentOne 新版本加入Blazor UI

    近期,由葡萄城推出的ComponentOne .NET开发控件集正式发布最新版本! ComponentOne 是一套专注于企业 .NET开发.支持 .NET Core 平台,并完美集成于 Visual ...

  5. Android UI事件传递就是这么个事儿

    正文 ###聊聊UI事件传递 什么是UI事件? 触摸屏幕中UI控件的那一刻即为事件发生 MotionEvent对象包含了所有的触摸事件,如触摸的位置.多指触摸等 MotionEvent描述了当前的操作 ...

  6. 事件类型-UI事件、焦点事件

    DOM3级事件包括以下几类事件: UI事件:当用户与页面上的元素交互时触发 焦点事件:当元素获得或失去焦点时触发 鼠标事件:当用户通过鼠标在页面上执行操作时触发 滚轮事件:当使用鼠标滚轮时触发 文本事 ...

  7. 2020.11.18第十三章 事件(事件处理程序;事件对象;事件类型:UI事件;焦点事件;鼠标与键盘事件 变动事件 模拟事件)

    事件处理程序 html事件处理程序(直接在html中书写script) <!-- //#### 事件处理程序 //html时间程序 可以直接在HTML文件中写,而且还可以引用其他地方的方法和函数 ...

  8. Electron 入门,主进程向渲染进程发送事件,渲染进程向主进程发送事件

    Electron 入门,主进程向渲染进程发送事件,渲染进程向主进程发送事件 相关教程: Electron教程(二)启动过程:主进程,渲染进程是什么 刚入门 electon ,整 electon + v ...

  9. uGUI学习篇: UI元素的渲染与性能

    Draw Call 为将对象渲染到画面中,Unity的渲染引擎针对OpenGL.Direct3D等图形API发行了渲染调用 (Draw Call). Unity引擎可按特定的顺序在一帧内渲染多个多边形 ...

最新文章

  1. Qt编程之通过鼠标滚轮事件缩放QGraphicsView里面的Item
  2. python 读图片性能测试
  3. 列表导航栏实例(02)——精美电子商务网站赏析
  4. qxidc项目/crtsurfdata程序 杀青总结
  5. TREX搜索的执行最后也是通过HTTP call来完成的
  6. 7.9 规划Varnish缓存
  7. Redis-主从复制
  8. quercus mysql_让PHP运行在Glassfish中:quercus配置
  9. Sql语句中两个比较迷糊的概念:“连接查询” 与 “外键约束”
  10. Pandas period_range
  11. python regex应用
  12. MATLAB 写入 txt
  13. 【区块链】2018全年比特币网络重要指标图表分析
  14. 今日头条架构演进之路
  15. 数据之路 - Python爬虫 - PyQuery库
  16. 数据类型和Json格式[zt]
  17. python github库_让pip使用git和github存储库
  18. 分布式架构网络通信——netty
  19. Oracle第二财季业绩表现抢眼 | 搜狗推出“唇语识别”技术 | FF宣布完成超10亿美元A轮融资
  20. 错误:VM5729:1 Blocked script execution in ‘about:blank‘

热门文章

  1. c语言的二分查找,C语言二分查找法
  2. python怎样填充颜色_python – 使用颜色填充Tkinter画布对象之间的空间
  3. 设计灵感|引导页设计中如何借助图形来展现场景?
  4. UI实用干货素材|工作管理、日程日历专辑
  5. 华为交换机VLAN的配置和实施
  6. 爬虫职业道德----查看Robots.txt
  7. MyBatis的总结(下)
  8. Linux进程管理:进程和线程基础知识
  9. 如何用指针判定系统是32bit还是64bit?一次惨痛的经历,想想都脸红
  10. MySQL查询语句(select)详解