TensorRT C# API 項目介紹:基於C#與TensorRT部署深度學習模型

来源:https://www.cnblogs.com/guojin-blogs/p/18108220
-Advertisement-
Play Games

開發了TensorRT C# API 2.0版本,該版本在開發時充分考慮了上一版本應用時出現的問題,併進行了改進。同時在本版本中,我們對介面進行了優化,使用起來更加簡單,並同時提供了相關的應用案例,方便開發者進行使用。 ...


TensorRT C# API 項目介紹:基於C#與TensorRT部署深度學習模型

1. 項目介紹

   NVIDIA® TensorRT™ 是一款用於高性能深度學習推理的 SDK,包括深度學習推理優化器和運行時,可為推理應用程式提供低延遲和高吞吐量。基於 NVIDIA TensorRT 的應用程式在推理過程中的執行速度比純 CPU 平臺快 36 倍,使您能夠優化在所有主要框架上訓練的神經網路模型,以高精度校準低精度,並部署到超大規模數據中心、嵌入式平臺或汽車產品平臺。

  TensorRT 基於 NVIDIA CUDA® 並行編程模型構建,使您能夠在 NVIDIA GPU 上使用量化、層和張量融合、內核調整等技術來優化推理。TensorRT 提供 INT8 使用量化感知訓練和訓練後量化和浮點 16 (FP16) 優化,用於部署深度學習推理應用程式,例如視頻流、推薦、欺詐檢測和自然語言處理。低精度推理可顯著降低延遲,這是許多實時服務以及自主和嵌入式應用所必需的。TensorRT 與 PyTorch 和 TensorFlow 集成,因此只需一行代碼即可實現 6 倍的推理速度。TensorRT 提供了一個 ONNX 解析器,因此您可以輕鬆地將 ONNX 模型從常用框架導入 TensorRT。它還與 ONNX 運行時集成,提供了一種以 ONNX 格式實現高性能推理的簡單方法。

  基於這些優勢,TensorRT目前在深度模型部署應用越來越廣泛。但是TensorRT目前只提供了C++與Python介面,對於跨語言使用十分不便。目前C#語言已經成為當前編程語言排行榜上前五的語言,也被廣泛應用工業軟體開發中。為了能夠實現在C#中調用TensorRT部署深度學習模型,我們在之前的開發中開發了TensorRT C# API。雖然實現了該介面,但由於數據傳輸存在問題,當時開發的版本在應用時存在較大的問題。

  基於此,我們開發了TensorRT C# API 2.0版本,該版本在開發時充分考慮了上一版本應用時出現的問題,併進行了改進。同時在本版本中,我們對介面進行了優化,使用起來更加簡單,並同時提供了相關的應用案例,方便開發者進行使用。

  • TensorRT C# API 項目源碼:
https://github.com/guojin-yan/TensorRT-CSharp-API.git
  • TensorRT C# API 項目應用源碼:
https://github.com/guojin-yan/TensorRT-CSharp-API-Samples.git

2. 介面介紹

  下麵簡單介紹一下該項目封裝的介面:

  • class Nvinfer

  • 模型推理類: 該類主要是封裝了轉換後的介面,用戶可以直接調用該類進行初始化推理引擎。

  • **public static void OnnxToEngine(string modelPath, int memorySize) **

    • 模型轉換介面:可以調用封裝的TensorRT中的ONNX 解釋器,對ONNX模型進行轉換,並根據本機設備信息,編譯本地模型,將模型轉換為TensorRT 支持的engine格式。
    • string modelPath: 本地ONNX模型地址,只支持ONNX格式,且ONNX模型必須為確定的輸入輸出,暫不支持動態輸入。
    • int memorySize: 模型轉換時分配的記憶體大小
  • **public Nvinfer(string modelPath) **

    • Nvinfer 初始化介面: 初始化Nvinfer類,主要初始化封裝的推理引擎,該推理引擎中封裝了比較重要的一些類和指針。
    • string modelPath: engine模型路徑。
  • **public Dims GetBindingDimensions(int index)/GetBindingDimensions(string nodeName) **

    • 獲取節點維度介面: 通過埠編號或者埠名稱,獲取綁定的埠的形狀信息.
    • int index: 綁定埠的編號
    • string nodeName: 綁定埠的名稱
    • return Dims: 介面返回一個Dims結構體,該結構體包含了節點的維度大小以及每個維度的具體大小。
  • public void LoadInferenceData(string nodeName, float[] data)/LoadInferenceData(int nodeIndex, float[] data)

    • 載入待推理數據介面: 通過埠編號或者埠名稱,將處理好的帶推理數據載入到推理通道上。
    • string nodeName: 待載入推理數據埠的名稱。
    • **int nodeIndex: **待載入推理數據埠的編號。
    • float[] data: 處理好的待推理數據,由於目前使用的推理數據多為float類型,因此此處目前只做了該類型介面。
  • public void infer()

    • 模型推理介面: 調用推理介面,對載入到推理通道的數據進行推理。
  • public float[] GetInferenceResult(string nodeName)/GetInferenceResult(int nodeIndex)

    • 獲取推理結果: 通過埠編號或者埠名稱,讀取推理好的結果數據。
    • string nodeName: 推理結果數據埠的名稱。
    • **int nodeIndex: **推理結果數據埠的編號。
    • return float[]: 返回值為指定節點的推理結果數據。

3. 安裝流程

  下麵演示一下安裝方式,下文所有演示都是基於以下環境進行配置的:

  • 操作系統:Windows 11

  • 編譯平臺:Visual Studio 2022

  • 顯卡型號:RTX 2060

  • CUDA型號:12.2

  • Cudnn:8.9.3

  • TensorRT:8.6.1.6

  對於CUDA以及Cudnn的安裝此處不再作過多演示,大家可以自行安裝。

3.1 TensorRT安裝

  首先確定安裝的CUDA版本,在命令提示符中輸入nvcc -V指令,便可以獲取安裝的CUDA版本。

  接下來訪問TensorRT Download | NVIDIA Developer下載安裝包,此處安裝8.x系列,安裝最新的版本8.6,如下圖所示,通過下載Zip文件進行安裝,大家可以根據自己的CUDN版本進行下載。

  由於下載的是編譯好的文件,因此在下載完成後,大家可以將其解壓到常用的安裝目錄,然後將路徑添加到環境變數即可,下麵路徑為本機安裝的TensorRT路徑,將其添加到本機的Path環境變數中。

D:\Program Files\TensorRT\TensorRT-8.6.1.6\lib

3.2 下載項目源碼

  由於CUDA以及TensorRT程式集文件較大,無法打包成NuGet Package,因此需要用戶自己進行編譯。

  首先第一步下載項目源碼,使用Git命令將源碼下載到本地,如下圖所示

git clone https://github.com/guojin-yan/TensorRT-CSharp-API.git

  然後使用Visual Studio 2022打開解決方案文件,如下圖所示:

  該解決方案中包含兩個項目,一個是C++項目,該項目是封裝的TensorRT介面,將介面封裝到動態鏈接庫中;另一個是C#項目,該項目是讀取動態鏈接庫中的C++介面,然後重新封裝該介面。

3.3 配置C++項目

  接下來配置C++項目,主要分為以下幾步:

第一步:設置項目輸出類型

  C++項目主要用於生成動態鏈接庫文件,因此將配置類型改為動態庫(.dll)

第二部:設置包含目錄

  當前C++項目主要使用兩個依賴庫,主要是CUDA(CUDNN)以及TensorRT,因此此處主要配置這兩個依賴項,用戶根據自己的安裝情況進行配置即可。

  以下是本設備CUDA(CUDNN)以及TensorRT的包含目錄位置,用戶在使用時這需要根據自己本機安裝情況進行設置即可。

C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.2\include
D:\Program Files\TensorRT\TensorRT-8.6.1.6\include

第三步:設置庫目錄

  下麵設置安裝的CUDA(CUDNN)以及TensorRT的庫目錄情況,如下圖所示。

  以下是本設備CUDA(CUDNN)以及TensorRT的庫目錄位置,用戶在使用時這需要根據自己本機安裝情況進行設置即可。

C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.2\lib\x64
D:\Program Files\TensorRT\TensorRT-8.6.1.6\lib

第四步:設置附加依賴項

  下麵設置附加依賴項,附加依賴項文件在上一步設置的庫目錄路徑中,此處主要添加.lib文件。

  以下是CUDA(CUDNN)以及TensorRT的附加依賴項目錄,但不同版本可能會不一致,因此用戶在使用時需要根據自己的安裝情況進行設置。

cublas.lib
cublasLt.lib
cuda.lib
cudadevrt.lib
cudart.lib
cudart_static.lib
cudnn.lib
cudnn64_8.lib
cudnn_adv_infer.lib
cudnn_adv_infer64_8.lib
cudnn_adv_train.lib
cudnn_adv_train64_8.lib
cudnn_cnn_infer.lib
cudnn_cnn_infer64_8.lib
cudnn_cnn_train.lib
cudnn_cnn_train64_8.lib
cudnn_ops_infer.lib
cudnn_ops_infer64_8.lib
cudnn_ops_train.lib
cudnn_ops_train64_8.lib
cufft.lib
cufftw.lib
cufilt.lib
curand.lib
cusolver.lib
cusolverMg.lib
cusparse.lib
nppc.lib
nppial.lib
nppicc.lib
nppidei.lib
nppif.lib
nppig.lib
nppim.lib
nppist.lib
nppisu.lib
nppitc.lib
npps.lib
nvblas.lib
nvJitLink.lib
nvJitLink_static.lib
nvjpeg.lib
nvml.lib
nvptxcompiler_static.lib
nvrtc-builtins_static.lib
nvrtc.lib
nvrtc_static.lib
OpenCL.lib
nvinfer.lib
nvinfer_dispatch.lib
nvinfer_lean.lib
nvinfer_plugin.lib
nvinfer_vc_plugin.lib
nvonnxparser.lib
nvparsers.lib

第五步:設置預處理器

  由於項目中應用了一些不安全方法,所以需要在巨集定義中添加以下定義,如下圖所示:

_CRT_SECURE_NO_WARNINGS

第六步:編譯項目源碼

  接下來生成C++項目,此處選擇生成,不要選擇運行,如下圖所示:

  最終可以看出生成的動態鏈接庫文件名稱以及文件路徑,這個路徑在下一步中十分重要。

3.4 編譯C#項目

  接下來編譯C#項目,C#項目只需要修改一下位置即可,修改NativeMethods.cs文件中的dll文件路徑,該路徑及上一步中C++項目生成的動態鏈接庫文件,如下圖所示:

E:\GitSpace\TensorRT-CSharp-API\x64\Release\TensorRtExtern.dll

  接下來就可以運行C#項目,生成類庫文件,如下圖所示:

4. 介面調用

4.1 創建並配置C#項目

  首先創建一個簡單的C#項目,然後添加項目配置。

  首先是添加TensorRT C# API 項目引用,如下圖所示,添加上文中C#項目生成的dll文件即可。

  接下來添加OpenCvSharp,此處通過NuGet Package安裝即可,此處主要安裝以下兩個程式包即可:

  配置好項目後,項目的配置文件如下所示:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <RootNamespace>TensorRT_CSharp_API_demo</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="OpenCvSharp4.Extensions" Version="4.9.0.20240103" />
    <PackageReference Include="OpenCvSharp4.Windows" Version="4.9.0.20240103" />
  </ItemGroup>

  <ItemGroup>
    <Reference Include="TensorRtSharp">
      <HintPath>E:\GitSpace\TensorRT-CSharp-API\src\TensorRtSharp\bin\Release\net6.0\TensorRtSharp.dll</HintPath>
    </Reference>
  </ItemGroup>

</Project>

4.2 添加推理代碼

  此處演示一個簡單的圖像分類項目,以Yolov8-cls項目為例:

static void Main(string[] args)
{
    Nvinfer predictor = new Nvinfer("E:\\Model\\yolov8\\yolov8s-cls_2.engine");
    Dims InputDims = predictor.GetBindingDimensions("images");
    int BatchNum = InputDims.d[0];

    Mat image1 = Cv2.ImRead("E:\\ModelData\\image\\demo_4.jpg");
    Mat image2 = Cv2.ImRead("E:\\ModelData\\image\\demo_5.jpg");

    List<Mat> images = new List<Mat>() { image1, image2 };
    for (int begImgNo = 0; begImgNo < images.Count; begImgNo += BatchNum)
    {
        DateTime start = DateTime.Now;
        int endImgNo = Math.Min(images.Count, begImgNo + BatchNum);
        int batchNum = endImgNo - begImgNo;
        List<Mat> normImgBatch = new List<Mat>();
        int imageLen = 3 * 224 * 224;
        float[] inputData = new float[2 * imageLen];
        for (int ino = begImgNo; ino < endImgNo; ino++)
        {
            Mat input_mat = CvDnn.BlobFromImage(images[ino], 1.0 / 255.0, new OpenCvSharp.Size(224, 224), 0, true, false);
            float[] data = new float[imageLen];
            Marshal.Copy(input_mat.Ptr(0), data, 0, imageLen);
            Array.Copy(data, 0, inputData, ino * imageLen, imageLen);
        }
        predictor.LoadInferenceData("images", inputData);

        DateTime end = DateTime.Now;
        Console.WriteLine("[ INFO ] Input image data processing time: " + (end - start).TotalMilliseconds + " ms.");
        predictor.infer();
        start = DateTime.Now;
        predictor.infer();
        end = DateTime.Now;
        Console.WriteLine("[ INFO ] Model inference time: " + (end - start).TotalMilliseconds + " ms.");
        start = DateTime.Now;

        float[] outputData = predictor.GetInferenceResult("output0");
        for (int i = 0; i < batchNum; ++i)
        {
            Console.WriteLine(string.Format("\n[ INFO ] Classification Top {0} result : \n", 10));
            Console.WriteLine("[ INFO ] classid probability");
            Console.WriteLine("[ INFO ] ------- -----------");
            float[] data = new float[1000];
            Array.Copy(outputData, i * 1000, data, 0, 1000);
            List<int> sortResult = Argsort(new List<float>(data));
            for (int j = 0; j < 10; ++j)
            {
                string msg = "";
                msg += ("index: " + sortResult[j] + "\t");
                msg += ("score: " + data[sortResult[j]] + "\t");
                Console.WriteLine("[ INFO ] " + msg);
            }
        }
        end = DateTime.Now;
        Console.WriteLine("[ INFO ] Inference result processing time: " + (end - start).TotalMilliseconds + " ms.");
    }


}

public static List<int> Argsort(List<float> array)
{
    int arrayLen = array.Count;
    List<float[]> newArray = new List<float[]> { };
    for (int i = 0; i < arrayLen; i++)
    {
        newArray.Add(new float[] { array[i], i });
    }
    newArray.Sort((a, b) => b[0].CompareTo(a[0]));
    List<int> arrayIndex = new List<int>();
    foreach (float[] item in newArray)
    {
        arrayIndex.Add((int)item[1]);
    }
    return arrayIndex;
}

4.3 項目演示

  配置好項目並編寫好代碼後,運行該項目,項目輸出如下所示:

[03/31/2024-22:27:44] [I] [TRT] Loaded engine size: 15 MiB
[03/31/2024-22:27:44] [I] [TRT] [MemUsageChange] TensorRT-managed allocation in engine deserialization: CPU +0, GPU +12, now: CPU 0, GPU 12 (MiB)
[03/31/2024-22:27:44] [I] [TRT] [MemUsageChange] TensorRT-managed allocation in IExecutionContext creation: CPU +0, GPU +4, now: CPU 0, GPU 16 (MiB)
[03/31/2024-22:27:44] [W] [TRT] CUDA lazy loading is not enabled. Enabling it can significantly reduce device memory usage and speed up TensorRT initialization. See "Lazy Loading" section of CUDA documentation https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#lazy-loading
[ INFO ] Input image data processing time: 6.6193 ms.
[ INFO ] Model inference time: 1.1434 ms.

[ INFO ] Classification Top 10 result :

[ INFO ] classid probability
[ INFO ] ------- -----------
[ INFO ] index: 386     score: 0.87328124
[ INFO ] index: 385     score: 0.082506955
[ INFO ] index: 101     score: 0.04416279
[ INFO ] index: 51      score: 3.5818E-05
[ INFO ] index: 48      score: 4.2115275E-06
[ INFO ] index: 354     score: 3.5188648E-06
[ INFO ] index: 474     score: 5.789438E-07
[ INFO ] index: 490     score: 5.655325E-07
[ INFO ] index: 343     score: 5.1091644E-07
[ INFO ] index: 340     score: 4.837259E-07

[ INFO ] Classification Top 10 result :

[ INFO ] classid probability
[ INFO ] ------- -----------
[ INFO ] index: 293     score: 0.89423335
[ INFO ] index: 276     score: 0.052870292
[ INFO ] index: 288     score: 0.021361532
[ INFO ] index: 290     score: 0.009259541
[ INFO ] index: 275     score: 0.0066174944
[ INFO ] index: 355     score: 0.0025512716
[ INFO ] index: 287     score: 0.0024535337
[ INFO ] index: 210     score: 0.00083151844
[ INFO ] index: 184     score: 0.0006893527
[ INFO ] index: 272     score: 0.00054959994

  通過上面輸出可以看出,模型推理僅需1.1434ms,大大提升了模型的推理速度。

5. 總結

  在本項目中,我們開發了TensorRT C# API 2.0版本,重新封裝了推理介面。並結合分類模型部署流程向大家展示了TensorRT C# API 的使用方式,方便大家快速上手使用。

  為了方便各位開發者使用,此處開發了配套的演示項目,主要是基於Yolov8開發的目標檢測、目標分割、人體關鍵點識別、圖像分類以及旋轉目標識別,由於時間原因,還未開發配套的技術文檔,此處先行提供給大家項目源碼,大家可以根據自己需求使用:

  • Yolov8 Det 目標檢測項目源碼:
https://github.com/guojin-yan/TensorRT-CSharp-API-Samples/blob/master/model_samples/yolov8_custom/Yolov8Det.cs
  • Yolov8 Seg 目標分割項目源碼:
https://github.com/guojin-yan/TensorRT-CSharp-API-Samples/blob/master/model_samples/yolov8_custom/Yolov8Seg.cs
  • Yolov8 Pose 人體關鍵點識別項目源碼:
https://github.com/guojin-yan/TensorRT-CSharp-API-Samples/blob/master/model_samples/yolov8_custom/Yolov8Pose.cs
  • Yolov8 Cls 圖像分類項目源碼:
https://github.com/guojin-yan/TensorRT-CSharp-API-Samples/blob/master/model_samples/yolov8_custom/Yolov8Cls.cs
  • Yolov8 Obb 旋轉目標識別項目源碼:
https://github.com/guojin-yan/TensorRT-CSharp-API-Samples/blob/master/model_samples/yolov8_custom/Yolov8Obb.cs

  同時對本項目開發的案例進行了時間測試,以下時間只是程式運行一次的時間,測試環境為:

  • CPU:i7-165G7

  • CUDA型號:12.2

  • Cudnn:8.9.3

  • TensorRT:8.6.1.6

Model Batch 數據預處理 模型推理 結果後處理
Yolov8s-Det 2 25 ms 7 ms 20 ms
Yolov8s-Obb 2 49 ms 15 ms 32 ms
Yolov8s-Seg 2 23 ms 8 ms 128 ms
Yolov8s-Pose 2 27 ms 7 ms 20 ms
Yolov8s-Cls 2 16 ms 1 ms 3 ms

  最後如果各位開發者在使用中有任何問題,歡迎大家與我聯繫。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 關於JDK21控制台字元集編碼問題 前言: 某日嘗試JDK21,idea控制台字元集編碼一直亂碼,後將idea所有能配置UTF-8的配置都配了一遍,無果,後搜索JDK21字元集編碼相關後解決 1.配置項目字元集 點擊菜單 File - > settings -> appearence , 將字體設置 ...
  • 1. 四旋翼無人機飛行原理:欠驅動系統 通過4個電機的轉速,來控制飛行器X、Y、Z軸的加速度和角速度,實現懸停、垂直升降、俯仰、偏航、滾轉(這裡只對比較陌生的俯仰、偏偏行、滾轉做示意圖說明)。 組成 運動控制 運動控制主要參考:四旋翼無人機飛行原理及控制方法,你瞭解多少? 實物圖 2. 無人機設計思 ...
  • 大家好,我是 Java陳序員。 Redis 作為一款高性能的非關係型資料庫,可是深受開發者的喜愛,無論是什麼開發,都能看到 Redis 的身影。 今天,給大家介紹一款跨平臺的 Redis 客戶端連接工具,功能強大,界面美觀! 關註微信公眾號:【Java陳序員】,獲取開源項目分享、AI副業分享、超20 ...
  • 大家好,我是R哥。 最近做 Java 面試輔導,看了許多小伙伴的簡歷,有的人的簡歷一看就知道是包裝的,比如這位,他自己都承認了: 包裝過的簡歷,作為多年面試官,我一眼就能看出來,相信其他面試官也會有同樣的感覺,這也是為什麼很多人的簡歷都是已讀不回的狀態。 下麵我簡單說說包裝的簡歷的特點。 1、技術棧 ...
  • 寫在前面 在實際項目的開發過程中,我們程式往往需要在不同環境中運行。例如:開發環境、測試環境和生產環境。 每個環境中的配置參數可能都會有所不同,例如資料庫連接信息、文件伺服器等等。 Spring Boot 提供了非常方便的方式來管理這些不同環境的配置。 一、Spring Profile 介紹 Spr ...
  • 在 Python 2 中,str.format() 函數可以使用一些高級的格式化選項,下麵是一些常用的高級用法: 1. 格式化數字 可以使用格式化選項來控制數字的顯示方式,例如: # 將數字格式化為帶千位分隔符的字元串 n = 1234567 s = "{:,}".format(n) print(s ...
  • 概述 最近項目組在準備接入各種指標監控系統,筆者負責的部分剛好涉及到了 Druid,故記錄一下在過程中遇到的各種情況和坑。 1. 直接使用 Druid 直接使用 Druid 的監控功能,需要直接將它提供的 Servlet 配置到 Web 容器中。具體可以直接參照官方文檔。 配置信息採集:https: ...
  • 此系列文章逐個內容講解ET8.1的新特性。 ET8.1 發佈,帶來以下新特性: 1. 多線程多進程架構,架構更加靈活強大,多線程設計詳細內容請看多線程設計課程 2. 抽象出纖程(Fiber)的概念,類似erlang的進程,非常輕鬆的創建多個纖程,利用多核,仍然是單線程開發的體驗 3. 纖程調度: 主 ...
一周排行
    -Advertisement-
    Play Games
  • 最近做項目過程中,使用到了海康相機,官方只提供了C/C++的SDK,沒有搜尋到一個合適的封裝了的C#庫,故自己動手,簡單的封裝了一下,方便大家也方便自己使用和二次開發 ...
  • 前言 MediatR 是 .NET 下的一個實現消息傳遞的庫,輕量級、簡潔高效,用於實現進程內的消息傳遞機制。它基於中介者設計模式,支持請求/響應、命令、查詢、通知和事件等多種消息傳遞模式。通過泛型支持,MediatR 可以智能地調度不同類型的消息,非常適合用於領域事件處理。 在本文中,將通過一個簡 ...
  • 前言 今天給大家推薦一個超實用的開源項目《.NET 7 + Vue 許可權管理系統 小白快速上手》,DncZeus的願景就是做一個.NET 領域小白也能上手的簡易、通用的後臺許可權管理模板系統基礎框架。 不管你是技術小白還是技術大佬或者是不懂前端Vue 的新手,這個項目可以快速上手讓我們從0到1,搭建自 ...
  • 第1章:WPF概述 本章目標 瞭解Windows圖形演化 瞭解WPF高級API 瞭解解析度無關性概念 瞭解WPF體繫結構 瞭解WPF 4.5 WPF概述 ​ 歡迎使用 Windows Presentation Foundation (WPF) 桌面指南,這是一個與解析度無關的 UI 框架,使用基於矢 ...
  • 在日常開發中,並不是所有的功能都是用戶可見的,還在一些背後默默支持的程式,這些程式通常以服務的形式出現,統稱為輔助角色服務。今天以一個簡單的小例子,簡述基於.NET開發輔助角色服務的相關內容,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 第3章:佈局 本章目標 理解佈局的原則 理解佈局的過程 理解佈局的容器 掌握各類佈局容器的運用 理解 WPF 中的佈局 WPF 佈局原則 ​ WPF 視窗只能包含單個元素。為在WPF 視窗中放置多個元素並創建更貼近實用的用戶男面,需要在視窗上放置一個容器,然後在這個容器中添加其他元素。造成這一限制的 ...
  • 前言 在平時項目開發中,定時任務調度是一項重要的功能,廣泛應用於後臺作業、計劃任務和自動化腳本等模塊。 FreeScheduler 是一款輕量級且功能強大的定時任務調度庫,它支持臨時的延時任務和重覆迴圈任務(可持久化),能夠按秒、每天/每周/每月固定時間或自定義間隔執行(CRON 表達式)。 此外 ...
  • 目錄Blazor 組件基礎路由導航參數組件參數路由參數生命周期事件狀態更改組件事件 Blazor 組件 基礎 新建一個項目命名為 MyComponents ,項目模板的交互類型選 Auto ,其它保持預設選項: 客戶端組件 (Auto/WebAssembly): 最終解決方案裡面會有兩個項目:伺服器 ...
  • 先看一下效果吧: isChecked = false 的時候的效果 isChecked = true 的時候的效果 然後我們來實現一下這個效果吧 第一步:創建一個空的wpf項目; 第二步:在項目裡面添加一個checkbox <Grid> <CheckBox HorizontalAlignment=" ...
  • 在編寫上位機軟體時,需要經常處理命令拼接與其他設備進行通信,通常對不同的命令封裝成不同的方法,擴展稍許麻煩。 本次擬以特性方式實現,以兼顧維護性與擴展性。 思想: 一種命令對應一個類,其類中的各個屬性對應各個命令段,通過特性的方式,實現其在這包數據命令中的位置、大端或小端及其轉換為對應的目標類型; ...