前言

Blazor正式版的发布已经有一段时间了,.NET社区的各路高手也创建了一个又一个的Blazor组件库,其中就包括了我和其他小伙伴一起参与的AntDesign组件库,于上周终于发布了第一个版本0.1.0,共计完成了59个常用组件,那么今天就来聊一聊如何在ASP.NET Core MVC项目中使用这些Blazor组件吧

环境搭建

.NET Core SDK 3.0.301

Vistual Studio 2019.16.6.3

调用Blazor组件

创建ASP.NET Core MVC项目,如果想要在已有的项目上使用AntDesign,需要确保Target Framework是netcoreapp3.1,然后在Nuget中搜索并安装AntDesign 0.1.0版本。

修改Startup.cs

在ConfigureServices方法中添加

 // add for balzor
services.AddServerSideBlazor();
// add for AntDesign
services.AddAntDesign();

在Configure方法中添加

 app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
// add for blazor
endpoints.MapBlazorHub();
});

修改./Views/Shared/_Layout.cshtml

在head区域添加

 <!--add for AntDesign-->
<link href="/_content/AntDesign/css/ant-design-blazor.css" rel="stylesheet">
<base href="/" />

在script区域添加

 <!--add for blazor-->
<script src="~/_framework/blazor.server.js"></script>

这里我们需要利用一个中间层,否则直接在View里添加组件会有很多限制,不太方便

创建一个razor文件./Components/HelloWorld.razor

 @using AntDesign

 <Button type="primary" OnClick="(e)=>OnClick(e)">@_content</Button>

 @code{
private string _content = "Primay";
private void OnClick(Microsoft.AspNetCore.Components.Web.MouseEventArgs args)
{
_content += "*";
}
}

最后在View中添加刚刚新建的中间组件

修改./Views/Home/Index.cshtml,添加代码

 <component type="typeof(HelloWorld)" render-mode="ServerPrerendered" />

Build & Run

这时候主页应该会出现一个ant-design风格的button,点击后button的内容会变为Priamary*,每点击一次就多一个*,效果如下

 

小结

一般来说,在MVC项目中,先将界面需要使用的组件组合在一起,然后整体包装在一个中间组件(HelloWolrd.razor)中,最后在调用的View中展示中间组件。可以理解为组件库为我们提供了各种各样的零件,中间层将这些零件(以及原生HTML标签)组合成一个产品,最后在View中展示产品。

创建一个播放器组件

首先我们创建好需要用到的JavaScript脚本

Nuget安装Microsoft.TypeScript.MSBuild

创建文件main.ts

 interface Window {
SoBrian: any;
} function Play(element, flag) {
var dom = document.querySelector(element);
if (flag) {
dom.play();
}
else {
dom.pause();
}
} function GetMusicTime(element) {
var dom = document.querySelector(element);
let obj = {
currentTime: dom.currentTime,
duration: dom.duration
}
let json = JSON.stringify(obj); return json
} function SetMusicTime(element, time) {
var dom = document.querySelector(element);
dom.currentTime = time;
} window.Music = {
print: Print,
play: Play,
getMusicTime: GetMusicTime,
setMusicTime: SetMusicTime
}

创建文件tsconfig.json

{
"compileOnSave": true,
"compilerOptions": {
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"sourceMap": false,
"target": "es2015",
"outDir": "wwwroot/js"
},
"files": [ "main.ts" ],
"exclude": [
"node_modules",
"wwwroot"
]
}

创建文件夹./wwwroot/music/

放入几首你喜欢的音乐,但要注意支持的文件格式

<audio> can be used to play sound files in the following formats:

.mp3: Supported by all modern browsers.
.wav: Not supported by Internet Explorer.
.ogg: Not supported by Internet Explorer and Safari.
 

创建./Components/MusicPlayer.razor

 @namespace SoBrian.MVC.Components
@inherits AntDesign.AntDomComponentBase <audio id="audio" preload="auto" src="@_currentSrc"></audio>
<div>
<AntDesign.Row Justify="center" Align="middle">
<AntDesign.Col Span="4">
<p>@System.IO.Path.GetFileNameWithoutExtension(_currentSrc)</p>
</AntDesign.Col>
<AntDesign.Col Span="4">
<AntDesign.Space>
<AntDesign.SpaceItem>
<AntDesign.Button Type="primary" Shape="circle" Icon="left" OnClick="OnLast" />
</AntDesign.SpaceItem>
<AntDesign.SpaceItem>
<AntDesign.Button Type="primary" Shape="circle" Icon="@PlayPauseIcon" Size="large" OnClick="OnPlayPause" />
</AntDesign.SpaceItem>
<AntDesign.SpaceItem>
<AntDesign.Button Type="primary" Shape="circle" Icon="right" OnClick="OnNext" />
</AntDesign.SpaceItem>
</AntDesign.Space>
</AntDesign.Col>
<AntDesign.Col Span="9">
<AntDesign.Slider Value="@_currentTimeSlide" OnAfterChange="OnSliderChange" />
</AntDesign.Col>
<AntDesign.Col Span="3">
<p>@($"{_currentTime.ToString("mm\\:ss")} / {_duration.ToString("mm\\:ss")}")</p>
</AntDesign.Col>
</AntDesign.Row>
</div>

创建./Components/MusicPlayer.razor.cs

 public partial class MusicPlayer : AntDomComponentBase
{
private bool _isPlaying = false;
private bool _canPlayFlag = false;
private string _currentSrc;
private List<string> _musicList = new List<string>
{
"music/周杰伦 - 兰亭序.mp3",
"music/周杰伦 - 告白气球.mp3",
"music/周杰伦 - 听妈妈的话.mp3",
"music/周杰伦 - 园游会.mp3",
"music/周杰伦 - 夜曲.mp3",
"music/周杰伦 - 夜的第七章.mp3",
"music/周杰伦 - 搁浅.mp3"
};
private Timer _timer;
private double _currentTimeSlide = ;
private TimeSpan _currentTime = new TimeSpan();
private TimeSpan _duration = new TimeSpan();
private string PlayPauseIcon { get => _isPlaying ? "pause" : "caret-right"; }
private Action _afterCanPlay;
[Inject]
private DomEventService DomEventService { get; set; } protected override void OnInitialized()
{
base.OnInitialized(); _currentSrc = _musicList[];
_afterCanPlay = async () =>
{
// do not use _isPlaying, this delegate will be triggered when user clicked play button
if (_canPlayFlag)
{
try
{
await JsInvokeAsync("Music.play", "#audio", true);
_canPlayFlag = false;
}
catch (Exception ex)
{
}
}
};
} protected override Task OnFirstAfterRenderAsync()
{
// cannot listen to dom events in OnInitialized while render-mode is ServerPrerendered
DomEventService.AddEventListener<JsonElement>("#audio", "timeupdate", OnTimeUpdate);
DomEventService.AddEventListener<JsonElement>("#audio", "canplay", OnCanPlay);
DomEventService.AddEventListener<JsonElement>("#audio", "play", OnPlay);
DomEventService.AddEventListener<JsonElement>("#audio", "pause", OnPause);
DomEventService.AddEventListener<JsonElement>("#audio", "ended", OnEnd);
return base.OnFirstAfterRenderAsync();
} #region Audio EventHandlers private async void OnPlayPause(MouseEventArgs args)
{
try
{
await JsInvokeAsync("Music.play", "#audio", !_isPlaying);
}
catch (Exception ex)
{
}
} private async void OnCanPlay(JsonElement jsonElement)
{
try
{
string json = await JsInvokeAsync<string>("Music.getMusicTime", "#audio");
jsonElement = JsonDocument.Parse(json).RootElement;
_duration = TimeSpan.FromSeconds(jsonElement.GetProperty("duration").GetDouble()); _afterCanPlay();
}
catch (Exception)
{
}
} private void OnPlay(JsonElement jsonElement)
{
_isPlaying = true;
} private async void OnLast(MouseEventArgs args)
{
_canPlayFlag = true;
int index = _musicList.IndexOf(_currentSrc);
index = index == ? _musicList.Count - : index - ;
_currentSrc = _musicList[index];
} private async void OnNext(MouseEventArgs args)
{
_canPlayFlag = true;
int index = _musicList.IndexOf(_currentSrc);
index = index == _musicList.Count - ? : index + ;
_currentSrc = _musicList[index];
} private void OnPause(JsonElement jsonElement)
{
_isPlaying = false;
StateHasChanged();
} private void OnEnd(JsonElement jsonElement)
{
_isPlaying = false;
StateHasChanged(); OnNext(new MouseEventArgs());
} private async void OnTimeUpdate(JsonElement jsonElement)
{
// do not use the timestamp from timeupdate event, which is the total time the audio has been working
// use the currentTime property from audio element
string json = await JsInvokeAsync<string>("Music.getMusicTime", "#audio");
jsonElement = JsonDocument.Parse(json).RootElement;
_currentTime = TimeSpan.FromSeconds(jsonElement.GetProperty("currentTime").GetDouble());
_currentTimeSlide = _currentTime / _duration * ; StateHasChanged();
} #endregion private async void OnSliderChange(OneOf<double, (double, double)> value)
{
_currentTime = value.AsT0 * _duration / ;
_currentTimeSlide = _currentTime / _duration * ;
await JsInvokeAsync("Music.setMusicTime", "#audio", _currentTime.TotalSeconds);
}
}

创建./Controllers/MusicController.cs

 public class MusicController : Controller
{
public IActionResult Index(string name)
{
return View();
}
}

创建./Views/Music/Index.cshtml

 <component type="typeof(MusicPlayer)" render-mode="Server" />

修改./Views/Shared/_Layout.cshtml,添加以下代码

 <li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Music" asp-action="Index">Music</a>
</li>

Build & Run

点击菜单栏的Music,效果如下

总结

WebAssembly并不是JavaScript的替代品,Blazor当然也不是,在开发Blazor组件的过程中,大部分情况下,仍然要通过TypeScript / JavaScript来与DOM进行交互,比如在这个播放器的案例中,还是需要JavaScript来调用audio的play,pause等方法。但是在View层面使用播放器这个组件时,我们几乎可以不再关心JavaScript的开发。这让前端的开发更类似于开发WPF的XAML界面。事实上,社区里也有这样的项目,致力于提供一种类WPF界面开发的组件库。

同时,也希望大家能多多关注国内小伙伴们共同参与开发的AntDesign,作为最热门的Blazor组件库之一,在今年的MS Build大会上也获得了微软官方的认可。虽然目前组件还有不少BUG和性能问题,但是在社区的努力下,相信它会越来越好,让我们一起为.NET生态添砖加瓦!

参考:

https://catswhocode.com/html-audio-tag

https://www.w3schools.com/TAGS/tag_audio.asp

https://github.com/ant-design-blazor/ant-design-blazor

【Blazor】在ASP.NET Core中使用Blazor组件 - 创建一个音乐播放器的更多相关文章

  1. 玩转ASP.NET Core中的日志组件

    简介 日志组件,作为程序员使用频率最高的组件,给程序员开发调试程序提供了必要的信息.ASP.NET Core中内置了一个通用日志接口ILogger,并实现了多种内置的日志提供器,例如 Console ...

  2. Asp.Net Core中利用Seq组件展示结构化日志功能

    在一次.Net Core小项目的开发中,掌握的不够深入,对日志记录并没有好好利用,以至于一出现异常问题,都得跑动服务器上查看,那时一度怀疑自己肯定没学好,不然这一块日志不可能需要自己扒服务器日志来查看 ...

  3. ASP.NET Core中使用表达式树创建URL

    当我们在ASP.NET Core中生成一个action的url会这样写: var url=_urlHelper.Action("Index", "Home"); ...

  4. Redis系列文章总结:ASP.Net Core 中如何借助CSRedis实现一个安全高效的分布式锁

    引言:最近回头看了看开发的.Net Core 2.1项目的复盘总结,其中在多处用到Redis实现的分布式锁,虽然在OnResultExecuting方法中做了防止死锁的处理,但在某些场景下还是会发生死 ...

  5. ASP.NET Core中的依赖注入(3): 服务的注册与提供

    在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...

  6. ASP.NET Core中的ActionFilter与DI

    一.简介 前几篇文章都是讲ASP.NET Core MVC中的依赖注入(DI)与扩展点的,也许大家都发现在ASP.NET CORE中所有的组件都是通过依赖注入来扩展的,而且面向一组功能就会有一组接口或 ...

  7. ASP.NET Core中使用GraphQL - 第三章 依赖注入

    ASP.NET Core中使用GraphQL ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中间件 SOL ...

  8. ASP.NET Core 中 HttpContext 详解与使用 | Microsoft.AspNetCore.Http 详解 (转载)

    “传导体” HttpContext 要理解 HttpContext 是干嘛的,首先,看图 图一 内网访问程序 图二 反向代理访问程序 ASP.NET Core 程序中,Kestrel 是一个基于 li ...

  9. ASP.NET Core 中 HttpContext 详解与使用 | Microsoft.AspNetCore.Http 详解

    笔者没有学 ASP.NET,直接学 ASP.NET Core ,学完 ASP.NET Core MVC 基础后,开始学习 ASP.NET Core 的运行原理.发现应用程序有一个非常主要的 “传导体” ...

随机推荐

  1. @Ajax.ActionLink跳转页面的问题解决方案 MVC Ajax不支持问题

    [JavaScriptResult]在客户端执行服务器返回的JavaScript代码当一个内置的Ajax辅助方法请求一个操作方法,该方法会返回一个在客户端执行立即的脚本. public ActionR ...

  2. void out2() const{

    include "stdafx.h" include using namespace std; class aa{ int num; public: aa(){ int b =10 ...

  3. mysql where与 having的区别

    where是针对磁盘的数据文件,having是针对存在内存的结果集的筛选. 例如: select name ,(xxx - xxx) as a from table where a > 10; ...

  4. 用Springboot干掉IBM的WAS-为公司省点钱

    1 那一夜,你伤害了我 今夜的雨下得凉快,小南睡得正香,突然收到远洋运维小周的电话:Hello, Are you OK? WAS有issue,快起来help me! 只见小南登陆WAS机,查看了机器日 ...

  5. spark源码解析大全

      第1章 Spark 整体概述 1.1 整体概念   Apache Spark 是一个开源的通用集群计算系统,它提供了 High-level 编程 API,支持 Scala.Java 和 Pytho ...

  6. Jlink设置正确,但下载程序失败

    [图中reset and run]勾选后即每次·下载程序后会自动复位,不需要再在硬件上进行复位 各参数设置正确 但依然下载失败. 原因是需要重新再编译一次,因为上次设置错误,编译后目标未创建! 重新编 ...

  7. Android学习笔记BroadcastReceiver(广播接收者)

    Android发送广播的过程 代码实现 MainActivity.java import androidx.appcompat.app.AppCompatActivity; import androi ...

  8. 从字符串到常量池,一文看懂String类设计

    从一道面试题开始 看到这个标题,你肯定以为我又要讲这道面试题了 // 这行代码创建了几个对象? String s3 = new String("1"); 是的,没错,我确实要从这里 ...

  9. 【JMeter_01】JMeter介绍与环境搭建

    JMeter介绍 Apache JMeter™应用开源软件,100%纯Java应用程序,设计之初是用于负载功能测试和性能测试.但因它在实现对各种接口的调用方面比较成熟,因此,常被用做接口功能测试. J ...

  10. Java根据模板生成Word文档

    一,首先制作模板 1.先做一个Word文档, 2.打开Word,然后另存为*.xml文件 3.最后修改*.xml文件的后缀名为*.ftl 二,打开项目编辑器Idea,在pom文件中引入相关架包依赖(我 ...