在winform blazor hybrid中繪圖

来源:https://www.cnblogs.com/mingupupu/archive/2023/12/13/17900024.html
-Advertisement-
Play Games

前幾天跟大家介紹了在winform中使用blazor hybrid,而且還說配上blazor的ui可以讓我們的winform程式設計的更加好看,接下來我想以一個在winform blazor hybrid中繪圖的例子來進行說明,希望對你有所幫助。 效果 在開始之前,先給大家演示一下效果,如下所示: ...


前幾天跟大家介紹了在winform中使用blazor hybrid,而且還說配上blazor的ui可以讓我們的winform程式設計的更加好看,接下來我想以一個在winform blazor hybrid中繪圖的例子來進行說明,希望對你有所幫助。

效果

在開始之前,先給大家演示一下效果,如下所示:

繪圖效果

image-20231213194416788

具體實現

如果你對具體實現感興趣,可以繼續往下閱讀。

1、引入ant design blazor

該應用中用到的所有組件都來源於ant design blazor。

在本文中我只介紹繪圖部分的實現,首先需要在項目中引入ant design blazor。

安裝 Nuget 包引用,如下所示:

image-20231213195008898

如果需要畫圖的話,還需要引用AntDesign.Charts包引用。

在項目的 Form1.cs 中註冊相關服務:

services.AddAntDesign();

如下所示:

image-20231213195432279

引入靜態樣式和腳本文件:

<link href="_content/AntDesign/css/ant-design-blazor.css" rel="stylesheet">
<script src="_content/AntDesign/js/ant-design-blazor.js"></script>

winform blazor hybrid項目在wwwroot/index.html中引入,如下所示:

image-20231213195710167

這裡我也把AntDesign.Charts的引入了。

_Imports.razor 中加入命名空間:

@using AntDesign

為了動態地顯示彈出組件,需要在 App.razor 中添加一個 <AntContainer /> 組件。

這是官網的說法,在winform blazor hybrid中可以在當做主頁面的razor中添加,我這裡是Index.razor如下所示:

image-20231213200010392

現在就可以使用ant design blazor的組件了。

2、頁面設計

繪圖頁面的設計如下所示:

image-20231213200320397

第一步選擇喜歡的佈局,我選的是官網中的這一款,如下所示:

image-20231213200625398

自己修改一下圖標與名字,那麼現在擺在面前的第一個問題就是,如何實現點擊切換頁面呢?

每一個MenuItem都有一個Key屬性,如下所示:

image-20231213201659940

在這裡每一個Key都是唯一的。點擊不同的MenuItem都會觸發點擊事件,而點擊事件使用了lambda表達式調用了同一個方法,但是參數不同。

現在來看看這個方法:

 int selectedMenuItem = 1;
private void NavigateToContent(int menuItemNumber)
{
    selectedMenuItem = menuItemNumber;
}

很簡單,只是將參數傳給selectedMenuItem。

然後在內容這個地方,使用switch case:

<Content Class="site-layout-background" Style="margin: 24px 16px;padding: 24px;min-height: 450px;">
@switch(selectedMenuItem)
{
  case 1:
   <GetData></GetData>
          break;
  case 2:
    <QueryData></QueryData>
          break;
  case 3:
     <Painting></Painting>
          break;
  case 4:
     <Export></Export>
          break;              
}
</Content>

然後就可以根據不同的selectedMenuItem值顯示不同的組件了。

現在來看看<Painting></Painting>組件的設計。

<Painting></Painting>組件的頁面代碼如下:

<div>
   <GridRow>
       <GridCol Span="8">    
       <Space Direction="DirectionVHType.Vertical">
               <SpaceItem>
                   <Text Strong>開始日期:</Text>
               </SpaceItem>
               <SpaceItem>
                   <DatePicker TValue="DateTime?" Format="yyyy/MM/dd"
                   Mask="yyyy/dd/MM" Placeholder="@("yyyy/dd/MM")"
                   @bind-Value = "Date1"/>
               </SpaceItem>
               <SpaceItem>
                   <Text Strong>結束日期:</Text>
               </SpaceItem>
               <SpaceItem>
                   <DatePicker TValue="DateTime?" Format="yyyy/MM/dd"
                   Mask="yyyy/dd/MM" Placeholder="@("yyyy/dd/MM")"
                   @bind-Value = "Date2"/>
               </SpaceItem>
           <SpaceItem>
               <Text Strong>站名:</Text>
           </SpaceItem>
           <SpaceItem>
               <AutoComplete @bind-Value="@value"
                             Options="@options"
                             OnSelectionChange="OnSelectionChange"
                             OnActiveChange="OnActiveChange"
                             Placeholder="input here"
                             Style="width:150px"/>
           </SpaceItem>
           <SpaceItem>
               <Text Strong>繪圖指標:</Text>
           </SpaceItem>
           <SpaceItem>
               <div>            
                   <AntDesign.CheckboxGroup Options="@ckeckAllOptions" @bind-Value = "selectedValues"/>
               </div>
           </SpaceItem>
           <SpaceItem>
               <Button Type="@ButtonType.Primary" OnClick="Painting_Clicked">繪圖</Button>
           </SpaceItem>
           </Space>  
           </GridCol>
       <GridCol Span="12">          
           <AntDesign.Charts.Line Data="@Data1" Config="Config1" @ref="lineChartRef" />        
       </GridCol>
   </GridRow>    
</div>

3、填充站名

當我們一打開這個組件,就有不同的站名了,如下所示:

image-20231213203211518

這是怎麼實現的呢?

首先使用<AutoComplete>自動完成這個組件,如下所示:

 <AutoComplete @bind-Value="@value"
              Options="@options"
              OnSelectionChange="OnSelectionChange"
              OnActiveChange="OnActiveChange"
              Placeholder="input here"
              Style="width:150px"/>
List<string> options = new List<string>();
protected override void OnInitialized()
{
   options = weatherServer.GetDifferentStations();      
}

在 Blazor 中,OnInitialized 是一個生命周期方法,用於在組件初始化時執行一些邏輯。具體而言,OnInitialized 方法是 Microsoft.AspNetCore.Components.ComponentBase 類中定義的一個虛擬方法,你可以在派生的組件中覆蓋它,以在組件初始化的時候執行一些自定義的操作。

這裡採用了三層架構的方式,分為ui層、業務邏輯層、資料庫訪問層。

其中的weatherServer是我自定義的服務,使用這個服務,需要在開頭添加語句:

@inject IWeatherServer weatherServer;

在 Blazor 中,@inject 是用於在 Razor 頁面或組件中註入服務的指令。通過 @inject,你可以將依賴註入服務引入到 Blazor 頁面或組件中,以便在其中使用這些服務。

當然要使用服務,必須先註冊服務:

services.AddSingleton<IWeatherServer,WeatherServer>();
services.AddSingleton<DataServer>();

這裡一個是業務邏輯的服務一個是數據訪問的服務。

其中IWeatherServer是業務邏輯層的介面,使用介面的好處,大家可以參考一下:

實現多繼承:

C# 中的類只支持單一繼承,但一個類可以實現多個介面。介面提供了一種方式,允許一個類在不同的維度上獲取和實現功能。一個類可以實現多個介面,從而擁有每個介面定義的一組成員。

實現規範:

介面定義了一組規範,要求實現類提供特定的成員。這有助於強制實現類遵循一定的編程規範和標準,從而提高代碼的一致性和可讀性。

提供抽象和靈活性:

介面本身不提供具體的實現,只是定義了成員的契約。這使得介面成為一種強大的抽象工具,讓你可以在不暴露具體實現的情況下描述類的能力。

介面還提供了一種擴展和修改類行為的方法,而無需更改類本身的實現。

實現依賴註入:

介面和依賴註入相結合,使得在應用程式中實現可替代性和可測試性變得更加容易。通過依賴註入框架,你可以在運行時註入不同的實現,從而實現模塊之間的低耦合性。

定義公共契約:

介面提供了一種定義公共契約的方式,使得多個實現可以在系統中一起工作,而不管它們的具體類型如何。這對於插件系統、擴展性和模塊化設計非常有用。

允許多態性:

通過介面,你可以利用 C# 中的多態性機制。當你引用一個對象的介面類型時,你可以在運行時實際上引用該對象的派生類型,從而實現多態行為。

定義事件契約:介面可以包含事件聲明,用於定義類應該提供的事件契約。這有助於規範化事件的使用和處理。

我這裡使用介面,主要是為了明晰服務到底實現了哪些功能,因為具體實現類中會有很多代碼,不好看清楚。

比如跟繪圖相關的介面如下所示:

 public List<string> GetDifferentStations();
public List<WeatherData> GetDataByCondition(Condition condition);

然後在實現類中進行具體實現:

 public List<string> GetDifferentStations()
{
    return dataService.GetDifferentStations();
}


public List<WeatherData> GetDataByCondition(Condition condition)
{
    return dataService.GetDataByCondition(condition);
}

業務邏輯層中不與資料庫直接相互,使用了資料庫訪問服務:

 public List<string> GetDifferentStations()
{
   return db.Queryable<WeatherData>().Select(x => x.StationName ?? "").Distinct().ToList();
}
public List<WeatherData> GetDataByCondition(Condition condition)
{
    return db.Queryable<WeatherData>()
            .Where(x => x.Date >= condition.StartDate &&
                         x.Date < condition.EndDate.AddDays(1) &&
                         x.StationName == condition.StationName).ToList();
}

這裡資料庫使用的是SQLite,ORM使用的是SQLSugar,具體怎麼設置,在這裡我就不詳細說明瞭,可以查看官網也可以查看歷史文章。

4、繪圖的實現

代碼如下:

async void Painting_Clicked()
{
   if (Date1 != null && Date2 != null && value != null && selectedValues != null)
  {
       if(Data1?.Length > 0)
      {
           Data1 = new object[0];
      }
       if (plotDatas.Count > 0)
      {
           plotDatas.Clear();
      }
       var cofig = new MessageConfig()
          {
               Content = "正在畫圖中...",
               Duration = 0
          };
       var task = _message.Loading(cofig);          
       var condition = new Condition();
       condition.StartDate = (DateTime)Date1;
       condition.EndDate = (DateTime)Date2;
       condition.StationName = value;  
       for(int i = 0;i < selectedValues.Length;i ++)
      {
           switch (selectedValues[i])
          {
               case "Tem_Low":
                   var result1 = weatherServer.GetDataByCondition(condition).Select(x => new PlotData
                  {
                       Date = x.Date,
                       Type = "Tem_Low",
                       Value = Convert.ToDouble(x.Tem_Low)
                  }).ToList();
                   plotDatas.AddRange(result1);
                   break;
               case "Tem_High":
                   var result2 = weatherServer.GetDataByCondition(condition).Select(x => new PlotData
                      {
                           Date = x.Date,
                           Type = "Tem_High",
                           Value = Convert.ToDouble(x.Tem_High)
                      }).ToList();
                   plotDatas.AddRange(result2);
                   break;
               case "Visibility_Low":
                   var result3 = weatherServer.GetDataByCondition(condition).Select(x => new PlotData
                      {
                           Date = x.Date,
                           Type = "Visibility_Low",
                           Value = Convert.ToDouble(x.Visibility_Low)
                      }).ToList();
                   plotDatas.AddRange(result3);
                   break;
               case "Visibility_High":
                   var result4 = weatherServer.GetDataByCondition(condition).Select(x => new PlotData
                      {
                           Date = x.Date,
                           Type = "Visibility_High",
                           Value = Convert.ToDouble(x.Visibility_High)
                      }).ToList();
                   plotDatas.AddRange(result4);
                   break;

          }
      }  
       
       // 將自定義類型的數組投影為 object[] 類型的數組
       Data1 = plotDatas.Select(p => new { date = p.Date, type = p.Type, value = p.Value }).ToArray();
       
       // 更新圖表數據    
       await lineChartRef.ChangeData(Data1);        
       task.Start();
  }
   else
  {
       await _message.Error("請查看開始日期、結束日期、站名與繪圖指標是否都已選擇!!!");
  }  
}

在AntDesign.Charts中畫多條折線圖,官網位置如下所示:

image-20231213210500538

創建一個自定義的畫圖數據類:

 public class PlotData
{
    public DateTime? Date {  get; set; }
    public string? Type { get; set; }
    public double Value { get; set; }
}

然後建一個畫圖數據類的列表:

List<PlotData> plotDatas = new List<PlotData>();

創建一個自定義的條件類:

  public class Condition
{
     public DateTime StartDate{ get; set; }
     public DateTime EndDate { get; set; }
     public string? StationName { get; set; }    
}

然後在我點擊的時候,如果各項不為空,那麼創建一個條件對象:

var condition = new Condition();
condition.StartDate = (DateTime)Date1;
condition.EndDate = (DateTime)Date2;
condition.StationName = value;

該對象包含了我們選擇的開始時間、結束時間與站名。

然後遍歷selectedValues:

 for(int i = 0;i < selectedValues.Length;i ++)

selectedValues是string[]?類型。

string[]? selectedValues;

表示的是多選框中選中的值。

 static CheckboxOption[] ckeckAllOptions = new CheckboxOption[]{
    new CheckboxOption{ Label="最低溫度(℃)",Value="Tem_Low" },
    new CheckboxOption{ Label="最高溫度(℃)", Value="Tem_High" },
    new CheckboxOption{ Label="最低可見度(km)", Value="Visibility_Low"},
    new CheckboxOption{ Label="最高可見度(km)", Value="Visibility_High" },
};

選擇的Label都有對應的value。

 switch (selectedValues[i])
{
    case "Tem_Low":
        var result1 = weatherServer.GetDataByCondition(condition).Select(x => new PlotData
        {
            Date = x.Date,
            Type = "Tem_Low",
            Value = Convert.ToDouble(x.Tem_Low)
        }).ToList();
        plotDatas.AddRange(result1);
        break;
    case "Tem_High":
        var result2 = weatherServer.GetDataByCondition(condition).Select(x => new PlotData
            {
                Date = x.Date,
                Type = "Tem_High",
                Value = Convert.ToDouble(x.Tem_High)
            }).ToList();
        plotDatas.AddRange(result2);
        break;
    case "Visibility_Low":
        var result3 = weatherServer.GetDataByCondition(condition).Select(x => new PlotData
            {
                Date = x.Date,
                Type = "Visibility_Low",
                Value = Convert.ToDouble(x.Visibility_Low)
            }).ToList();
        plotDatas.AddRange(res
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 題目:輸入兩個正整數 m 和 n,求其最大公約數和最小公倍數。 求出最大公約數就行,最小公倍數用m*n除以最大公約數就行 package myself; import java.util.Scanner; /** * @Auther QY * @Date 2023/12/11 */ public c ...
  • 開始 日期格式化可以說是最常用的一個小知識了,例如格式化成"年-月-日"、"年-月-日 時-分-秒"、“年/月/日”等等,隨之也就出現了“yyyy-MM-dd”、"yyyy-MM-dd HH-mm-ss"等格式,使用不當就會一臉懵逼。 運行 public static void main(Strin ...
  • Qt 是一個跨平臺C++圖形界面開發庫,利用Qt可以快速開發跨平臺窗體應用程式,在Qt中我們可以通過拖拽的方式將不同組件放到指定的位置,實現圖形化開發極大的方便了開發效率,本章將重點介紹CheckBox單行輸入框組件的常用方法及靈活運用。QCheckBox是 Qt 中用於實現覆選框的組件,它提供了豐... ...
  • 隨著金融科技的不斷發展,越來越多的線上查詢工具被應用到汽車管理領域。一款名為汽車管理線上查詢工具,定位車輛,輕鬆追蹤的工具就是其中之一。此工具通過API介面代碼實現了車牌號查車輛信息、車輛故障碼、VIN查詢汽車品牌以及二手車估值等功能,為用戶提供了準確、便捷、高效的汽車管理服務。 首先,車牌號查車輛 ...
  • 數據的預處理是數據分析,或者機器學習訓練前的重要步驟。通過數據預處理,可以 提高數據質量,處理數據的缺失值、異常值和重覆值等問題,增加數據的準確性和可靠性 整合不同數據,數據的來源和結構可能多種多樣,分析和訓練前要整合成一個數據集 提高數據性能,對數據的值進行變換,規約等(比如無量綱化),讓演算法更加 ...
  • 1. 同城雙活是什麼 同城雙活是一種容災架構的設計模式,主要用於提高系統的可用性和容錯性。它通常涉及在同一個城市內建立兩個數據中心(機房),這兩個數據中心同時對外提供服務,實現了高可用性和冗餘。 關鍵特點和優勢包括: 雙活部署: 兩個數據中心都處於活躍狀態,同時處理用戶請求。這樣,當一個數據中心發生 ...
  • 公司某規則引擎系統,在每次發版啟動會手動預熱,預熱完成當流量切進來之後會偶發的出現一次長達1-2秒的Young GC(流量並不大,並且LB下的每個節點都會出現該情況)在這次長暫停之後,每一次的年輕代GC暫停時間又都恢覆在20-100ms以內2秒雖然看起來不算長吧,但規則引擎每次執行也才幾毫秒,這誰能... ...
  • Qt 是一個跨平臺C++圖形界面開發庫,利用Qt可以快速開發跨平臺窗體應用程式,在Qt中我們可以通過拖拽的方式將不同組件放到指定的位置,實現圖形化開發極大的方便了開發效率,本章將重點介紹`QRadioButton`單選框組件以及與之交互的`QButtonGroup`類的常用方法及靈活運用。`QRad... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...