1. 程式人生 > >通過Blazor使用C#開發SPA單頁面應用程式(4) - Ant Design Button

通過Blazor使用C#開發SPA單頁面應用程式(4) - Ant Design Button

  前面學習了Blazor的特點、環境搭建及基礎知識,現在我們嘗試的做個實際的元件。

  Ant Design是螞蟻金服是基於Ant Design設計體系的 UI 元件庫,主要用於研發企業級中後臺產品。目前官方是基於React和Angular實現的,今年也推出了Vue的實現。其元件涵蓋面較廣,其元件風格及互動效果還是比較驚豔的,後面準備利用Ant Design的樣式檔案利用Blazor模仿幾個元件的實現。

  由於也是新學的Blazor開發,可能實現的方式有些笨拙,希望高手提出寶貴意見,先看看實現的Button 按鈕、Grid 柵格、導航欄的效果。

 先來看看Button按鈕,它支援多種風格,是否只顯示圖示,loading狀態等。實現步驟及主要程式碼且聽我娓娓道來,

 1、引用樣式檔案

  首先去antd.css cdn 下載穩定版的css檔案,放到 wwwroot 資料夾下。再 _Host.cshtml 引用該檔案。

2、建立 AButtonBase 類

  AButtonBase類定義了按鈕的屬性引數;註冊了class名稱(例如:class="ant-btn ant-btn-primary")的計算表示式,class內容是根據屬性引數的設定情況計算出來的。

  屬性set 的 ClassMapper.Dirty() 是通知樣式名生成方法屬性改變了需要重新生成樣式名稱。

  而ClassMapper是用於註冊元件需要用到的樣式構建類,PrefixCls()是新增樣式共用的字首,Add有多個過載,可以是直接的樣式名稱;也可以是根據第一個引數的bool表示式來確定,第二個引數的樣式是否啟用。

 

using BlazorAntDesign.Core;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.RenderTree;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BlazorAntDesign.General
{    public class AButtonBase : BaseComponent
    {
        #region Properties
        /// <summary>
        /// 設定按鈕大小,可選值為 Small、Large 或者不設
        /// </summary>
        [Parameter]
        protected AButtonSizes ButtonSize
        {
            get => buttonSize;
            set
            {
                buttonSize = value;
                ClassMapper.Dirty();
            }
        }
        private AButtonSizes buttonSize = AButtonSizes.Default;

        /// <summary>
        /// 設定按鈕型別,可選值為 Primary、Dashed、Danger 或者不設
        /// </summary>
        [Parameter]
        protected AButtonTypes ButtonType
        {
            get => buttonType;
            set
            {
                buttonType = value;
                ClassMapper.Dirty();
            }
        }
        private AButtonTypes buttonType = AButtonTypes.Default;

        /// <summary>
        /// 設定按鈕形狀,可選值為 Circle、 Round 或者不設
        /// </summary>
        [Parameter]
        protected AButtonShapes ButtonShape
        {
            get => buttonShape;
            set
            {
                buttonShape = value;
                ClassMapper.Dirty();
            }
        }
        private AButtonShapes buttonShape = AButtonShapes.Default;

        /// <summary>
        /// 設定 button 原生的 type 值,可選值請參考 HTML 標準
        /// </summary>
        [Parameter]
        protected AButtonHTMLTypes HtmlType { get; set; } = AButtonHTMLTypes.Button;

        /// <summary>
        /// 按鈕標題
        /// </summary>
        [Parameter]
        protected string Title
        {
            get => title;
            set
            {
                title = value;
                ClassMapper.Dirty();
            }
        }
        private string title;

        /// <summary>
        /// 設定按鈕的圖示名稱
        /// </summary>
        [Parameter]
        protected string IconName { get; set; }

        /// <summary>
        /// 將按鈕寬度調整為其父寬度的選項
        /// </summary>
        [Parameter]
        protected bool Block
        {
            get => block;
            set
            {
                block = value;
                ClassMapper.Dirty();
            }
        }
        private bool block;

        /// <summary>
        /// 幽靈屬性,使按鈕背景透明。幽靈按鈕將按鈕的內容反色,背景變為透明,常用在有色背景上。
        /// </summary>
        [Parameter]
        protected bool Ghost
        {
            get => ghost;
            set
            {
                ghost = value;
                ClassMapper.Dirty();
            }
        }
        private bool ghost;

        /// <summary>
        /// 設定按鈕載入狀態
        /// </summary>
        [Parameter]
        protected bool Loading
        {
            get => loading;
            set
            {
                loading = value;
                ClassMapper.Dirty();
            }
        }
        private bool loading;

        /// <summary>
        /// 按鈕失效狀態
        /// </summary>
        [Parameter]
        protected bool Disabled { get; set; }

        [Parameter]
        protected RenderFragment ChildContent { get; set; }

        /// <summary>
        /// 點選按鈕時的回撥
        /// </summary>
        [Parameter]
        public EventCallback<UIMouseEventArgs> OnClick { get; set; }
        //protected System.Action Clicked { get; set; }


        protected bool click_animating { get; set; }
        #endregion

        protected override void RegisterClasses()
        {
            ClassMapper.PrefixCls("ant-btn")
                    .Add(() => ClassMapper.GetEnumName(buttonSize))
                    .Add(() => ClassMapper.GetEnumName(buttonType))
                    .Add(() => ClassMapper.GetEnumName(buttonShape))
                    .Add(() => Block, () => "block")
                    .Add(() => Ghost, () => "background-ghost")
                    .Add(() => Loading, () => "loading")
                    .Add(() => string.IsNullOrEmpty(title) && ChildContent == null, () => "icon-only");

            base.RegisterClasses();
        }

        protected void buttonDown()
        {
            click_animating = false;
        }

        protected void buttonUp()
        {
            click_animating = true;
        }


    }
}

 AButtonSizes、AButtonTypes、AButtonShapes、AButtonHTMLTypes 屬性引數是定義的列舉,例如:

namespace BlazorAntDesign.General
{
    /// <summary>
    /// 按鈕尺寸選項
    /// </summary>
    public enum AButtonSizes
    {
        /// <summary>
        /// 預設,中
        /// </summary>
        [ClassNamePart("")]
        Default,

        /// <summary>
        /// 大
        /// </summary>
        [ClassNamePart("lg")]
        Large,

        /// <summary>
        /// 小
        /// </summary>
        [ClassNamePart("sm")]
        Small
    }
}

 

namespace BlazorAntDesign.General
{
    /// <summary>
    /// 按鈕型別選項
    /// </summary>
    public enum AButtonTypes
    {
        /// <summary>
        /// 預設,次按鈕
        /// </summary>
        [ClassNamePart("")]
        Default,

        /// <summary>
        /// 主按鈕
        /// </summary>
        Primary,

        /// <summary>
        /// 
        /// </summary>
        Ghost,

        /// <summary>
        /// 虛線按鈕
        /// </summary>
        Dashed,

Danger } }

     AButtonBase類繼承自自定義的BaseComponent,BaseComponent繼承自Blazor的ComponentBase類。

  BaseComponent主要定義了所有元件共用的屬性,ElementId、Style、ClassMapper等,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;

namespace BlazorAntDesign.Core
{
    public abstract class BaseComponent : ComponentBase, IDisposable
    {
        #region Members

        private bool disposed;


        private ElementRef elementRef;

        #endregion

        #region Constructors

        public BaseComponent()
        {
            ElementId = Utils.IDGenerator.Instance.Generate;

            ClassMapper = new ClassMapper();
            RegisterClasses();
        }

        #endregion

        #region Methods
        protected virtual void RegisterClasses()
        {
            ClassMapper.AddCustomClass(() => customClass);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    if (ClassMapper != null)
                    {
                        //ClassMapper.Dispose();
                        ClassMapper = null;
                    }
                }

                disposed = true;
            }
        }

        #endregion

        #region Properties
        
        /// <summary>
        /// 定義使用者自定義的 ClassName
        /// </summary>
        [Parameter]
        protected string ClassName
        {
            get => customClass;
            set
            {
                customClass = value;
                ClassMapper.Dirty();
            }
        }
        private string customClass;

        /// <summary>
        /// Gets the reference to the rendered element.
        /// </summary>
        public ElementRef ElementRef { get => elementRef; protected set => elementRef = value; }

        /// <summary>
        /// 獲取 element 唯一 Id.
        /// </summary>
        public string ElementId { get; }

        [Parameter]
        protected string Style { get; set; }

        /// <summary>
        /// 獲取 ClassMapper.
        /// </summary>
        protected ClassMapper ClassMapper { get; private set; }

        #endregion
    }
}

 

3、建立 AButton.razor

@inherits BlazorAntDesign.General.AButtonBase

<button class="@ClassMapper.Class"
        type="@(Enum.GetName(typeof(AButtonHTMLTypes), HtmlType).ToLower())"
        ant-click-animating-without-extra-node="@(click_animating ? "true" : "")"
        disabled="@Disabled"
        @onkeydown="@buttonDown"
        @onmousedown="@buttonDown"
        @onkeyup="@buttonUp"
        @onmouseup="@buttonUp"
        @onclick="@OnClick">
    @*圖示*@
    @if (Loading)
    {<AIcon IconName="loading"></AIcon>}
    else
    {
        if (!string.IsNullOrEmpty(IconName))
        {<AIcon IconName="@IconName"></AIcon>}
    }
    @*標題*@
    @if (!string.IsNullOrEmpty(Title))
    {<span>@Title</span>}
@*子內容*@
    @ChildContent</button>

  其中  @inherits BlazorAntDesign.General.AButtonBase 是程式碼隱藏方式,指定後臺程式碼繼承自AButtonBase類,button class="@ClassMapper.Class",將採用的樣式根據指定的屬性值計算出來賦值給button的class屬性。下面根據屬性設定判斷是否顯示圖示、標題、子內容。

4、使用 AButton

 建立DemoButton.razor檔案,樣例程式碼如下:

 

@page "/DemoButton"
@using BlazorAntDesign.General;


<div style="border:1px solid beige;padding:10px">
    <AButton Title="Default"></AButton>
    <AButton Title="Primary" ButtonType="AButtonTypes.Primary"></AButton>
    <AButton Title="Danger" ButtonType="AButtonTypes.Danger"></AButton>
    <AButton Title="Dashed" ButtonType="AButtonTypes.Dashed"></AButton>
    <AButton Title="Disabled" Disabled="true"></AButton>
    <AButton ButtonType="AButtonTypes.Primary" IconName="download" ButtonShape="AButtonShapes.Circle"></AButton>
    <AButton Title="下載" ButtonType="AButtonTypes.Primary" IconName="download" ButtonShape="AButtonShapes.Round"></AButton>
    <AButton Title="下載" ButtonType="AButtonTypes.Primary" IconName="download"></AButton>
    <AButtonGroup><AButton ButtonType="AButtonTypes.Primary" IconName="left">Backward</AButton><AButton ButtonType="AButtonTypes.Primary">Forward<AIcon IconName="right" /></AButton></AButtonGroup>
    <AButton Title="Loading" ButtonType="AButtonTypes.Primary" Loading="true"></AButton>
    <AButton Title="Click me!" ButtonType="AButtonTypes.Primary" Loading="@isLoading" OnClick="@(()=> { isLoading = true; })"></AButton>
</div>
<div style="border:1px solid beige;padding:10px;margin-top:5px">
    <AButton IconName="search" ButtonType="AButtonTypes.Primary" ButtonShape="AButtonShapes.Circle"></AButton>
    <AButton IconName="search" ButtonType="AButtonTypes.Primary">按鈕</AButton>
    <AButton IconName="search" ButtonShape="AButtonShapes.Circle"></AButton>
    <AButton IconName="search">按鈕</AButton>
    <AButton IconName="search" ButtonType="AButtonTypes.Dashed" ButtonShape="AButtonShapes.Circle"></AButton>
    <AButton IconName="search" ButtonType="AButtonTypes.Primary">按鈕</AButton>
</div>

@code{
    private bool isLoading = false;

    private void LoadingClick()
    {
        isLoading = true;
    }
}

 

@page "/DemoButton"   為元件路由

@using BlazorAntDesign.General;       為應用元件名稱空間

 

   好了Button的實現思路今天就學習到這了,希望有興趣的同仁共同學習探討,讓Blazor真正走向SPA開發的舞臺,讓.net core七龍珠面向全棧開發,更好的擁抱社群,重新整理業界對.net體系的認知。

 

   番   外   篇   

   Blazor 在發展:

  微軟已將 Blazor 移出了實驗階段,進入了官方預覽版。使用元件模型進行伺服器端渲染的 Blazor 版本將與.NET Core 3 的最終版本一起釋出(請參閱.NET Core 路線圖),客戶端版本將在隨後不久釋出。還有工作要完成:除錯體驗極其有限,必須改進;有機會通過提前編譯生成本機 Wasm 來優化程式碼效能;在將未使用的程式碼庫傳送到瀏覽器之前,需要從庫中刪除未使用的程式碼,從而降低總體大小(這個過程稱為樹抖動)。對 WebAsssembly 的興趣和採用與日俱增,藉助 Blazor,編寫可以在任何地方執行的 C#和.NET 程式碼的夢想終於實現了。

  .net core將踏上新的征程: