【.NET 深呼吸】全代碼編寫WPF程式

来源:https://www.cnblogs.com/tcjiaan/archive/2023/06/15/17482371.html
-Advertisement-
Play Games

學習 Code 總有這樣一個過程:入門時候比較依賴設計器、標記語言等輔助工具;等到玩熟練了就會發現純代碼寫 UI 其實更高效。而且,純代碼編寫也是最靈活的。Windows Forms 項目是肯定可以全代碼編寫的,哪怕你使用了設計器,它最後也是生成代碼文件;而 WPF 就值得探索一下了。咱們知道,WP ...


學習 Code 總有這樣一個過程:入門時候比較依賴設計器、標記語言等輔助工具;等到玩熟練了就會發現純代碼寫 UI 其實更高效。而且,純代碼編寫也是最靈活的。Windows Forms 項目是肯定可以全代碼編寫的,哪怕你使用了設計器,它最後也是生成代碼文件;而 WPF 就值得探索一下了。咱們知道,WPF 使用 XAML 標記來構建 UI 部分。由於 XAML 擴展了許多功能,用起來自然比 HTML 舒服。但是,老周向來不喜歡標記語言,這也是我向來不喜歡搞前端的原因。儘管某些前端框架模仿 WPF 也搞出數據綁定、MVVM、數據模板之類的名堂,也很難說用得特舒服。

有很多中小型項目都會把 Web 前端部分外包出去,尤其是給私人做——比如一兩個人或兩三個人做,也不外給其他公司做。有些人總以為前端很火(這裡頭媒體造勢的功勞不小),可往深層一挖,那可不一定了。Web 其實只做了一套 UI 罷了,後端許多是通用模型,既可以和 Web 前端對接,也可以和桌面前端對接,B/S、C/S 通殺的項目也不少。

很多行業軟體,如工業醫療,甚至財務、進銷存等,還是用成熟的技術好,尤其是桌面技術體驗更佳。當然有些行業軟體也有 Web UI,純輔助,一般就是看看報表看看大圖查查訂單而已。生產力悠關的東西,你還得相信桌面的魅力,娛樂相關的就隨便。當然也有用 Web 技術開發桌面UI的框架,這些東西能用但效果不算好,尤其是性能。老周這裡說的性能只是要求較寬的性能,而不是苛刻要求下的性能。啥意思呢,就是說用 Web 技術做桌面程式,存在性能問題不需要專用工具測,肉眼就能感覺到嚴重的性能問題了——吃記憶體特大,占CPU有億點高(雖說占得不算恐怖)。這裡所說的性能問題要排除 VS Code,因為這貨是個奇葩,性能表現挺好。

許多人容易被錶面現象迷惑,比如認為招聘信息多的就以為很吃香。那可不一定,有些技術,招聘少並不代表用的人少。老在招聘的頂多說明這些崗位流動性大,這個公司的員工熱愛跳槽罷了。近年來 Python 被“利益攜帶體”們炒得可熱了,甚至一些新手以為 Python 是剛出來的新語言。你想多了,就算沒有 C 語言早(1972),那也是 80 年代末的東東了。我在學 Python 的時候,估計某些小菜鳥還沒出生呢。不要聽那些培訓班胡說八道,它們的目的是你的錢包,而不是你的碼農生涯,它們說話從來不需要負責的。如果你除了 Python 什麼都不會的話,那除了會寫點“腳毛”外,你什麼也幹不成。不管你想玩人工智障、視覺神經還是別的東西,你得掌握C語言,特別是想搞更底層的。只會 Py 沒準連工作都難找,更別說年薪 500W 越南幣了。

在你不知道的領域,你可曾想象,VB6、易語言、Delphi、MFC 還有不少人在用呢。告訴你個秘密,學好彙編可能更吃香,以後會這個的人更少了。信不信由你。當然,積極學習新東西是沒錯的,這也老周一向主張。不過,你同時得清楚,許多技術之間並不存在相互替代的關係,只不過是你做什麼樣的程式,就用什麼樣的技術罷了。比如這桌面程式,你不用糾結,很簡單:考慮跨平臺的,首選 Qt;僅考慮 Windows 的,那多了去,隨便,當然,微軟自家自然是最合適的。

可是,一些腦子太靈活的人又糾結了,我選了 Qt,那我用 Widgets 做還是用 QML 做?我選了.NET,那我用 Windows Forms 還是 WPF?還是 MAUI ?對於這種問題,老周送你一句:“像你們這種人是沒法改變的,只有滾出碼農界”。

好了,上面扯了幾段“廢腑”之話,回歸正題,咱們討論 WPF,老周這裡說的是完全用代碼寫,指的是一行 XAML 都沒有。當然,大伙伴們肯定說那沒問題的。構建常規界面絕對行得通,但遇到像數據模板、控制項模板、資源字典這些,就得費一點點代碼。雖然網上能找到幾位同道中人寫的小作文,但要麼版本太舊,要麼過於粗糙。於是老周逮住了這個機會,可以瞎扯蛋一回了。

前文多次強調,咱們就純代碼寫 WPF 的,無一行 XAML。所以,預設的 WPF 項目模板咱們就不用了。咱們用控制台應用的模板就行了。來,動手練習一下。

首先,創建一個控制台項目。

dotnet new console -n MyApp -o .

dotnet new 命令知道乎?嗯,用來創建項目的,然後是項目模板的名稱,console 表示控制台應用程式。模板名字我記不住喲。記它幹嗎,執行一下下麵這一句就能看各種模板了:

dotnet new list

這裡你可別理解歪了,它不是說用名叫 list 的模板創建項目啊,list 是列出可用的項目模板。然後,你能得到這個表:

模板名                                   短名稱               語言        標記
---------------------------------------  -------------------  ----------  --------------------------------
ASP.NET Core gRPC 服務                   grpc                 [C#]        Web/gRPC
ASP.NET Core Web API                     webapi               [C#],F#     Web/WebAPI
ASP.NET Core Web 應用                    webapp,razor         [C#]        Web/MVC/Razor Pages
ASP.NET Core Web 應用(模型-視圖-控制器)  mvc                  [C#],F#     Web/MVC
ASP.NET Core 與 Angular                  angular              [C#]        Web/MVC/SPA
ASP.NET Core 與 React.js                 react                [C#]        Web/MVC/SPA
ASP.NET Core 空                          web                  [C#],F#     Web/Empty
Blazor Server 應用                       blazorserver         [C#]        Web/Blazor
Blazor Server 應用空                     blazorserver-empty   [C#]        Web/Blazor/Empty
Blazor WebAssembly 應用                  blazorwasm           [C#]        Web/Blazor/WebAssembly/PWA
Blazor WebAssembly 應用空                blazorwasm-empty     [C#]        Web/Blazor/WebAssembly/PWA/Empty
dotnet gitignore 文件                    gitignore                        Config
Dotnet 本地工具清單文件                  tool-manifest                    Config
EditorConfig 文件                        editorconfig                     Config
global.json file                         globaljson                       Config
MSBuild Directory.Build.props 文件       buildprops                       MSBuild/props
MSBuild Directory.Build.targets 文件     buildtargets                     MSBuild/props
MSTest Test Project                      mstest               [C#],F#,VB  Test/MSTest
MVC ViewImports                          viewimports          [C#]        Web/ASP.NET
MVC ViewStart                            viewstart            [C#]        Web/ASP.NET
NuGet 配置                               nugetconfig                      Config
NUnit 3 Test Item                        nunit-test           [C#],F#,VB  Test/NUnit
NUnit 3 Test Project                     nunit                [C#],F#,VB  Test/NUnit
Razor 類庫                               razorclasslib        [C#]        Web/Razor/Library
Razor 組件                               razorcomponent       [C#]        Web/ASP.NET
Razor 頁面                               page                 [C#]        Web/ASP.NET
Web 配置                                 webconfig                        Config
Windows 窗體應用                         winforms             [C#],VB     Common/WinForms
Windows 窗體控制項庫                       winformscontrollib   [C#],VB     Common/WinForms
Windows 窗體類庫                         winformslib          [C#],VB     Common/WinForms
WPF 應用程式                             wpf                  [C#],VB     Common/WPF
WPF 用戶控制項庫                           wpfusercontrollib    [C#],VB     Common/WPF
WPF 類庫                                 wpflib               [C#],VB     Common/WPF
WPF 自定義控制項庫                         wpfcustomcontrollib  [C#],VB     Common/WPF
xUnit Test Project                       xunit                [C#],F#,VB  Test/xUnit
協議緩衝區文件                           proto                            Web/gRPC
介面                                     interface            [C#],VB     Common
控制台應用                               console              [C#],F#,VB  Common/Console
枚舉                                     enum                 [C#],VB     Common
類                                       class                [C#],VB     Common
類庫                                     classlib             [C#],F#,VB  Common/Library
結構                                     struct,structure     [C#],VB     Common
解決方案文件                             sln,solution                     Solution
記錄                                     record               [C#]        Common
輔助角色服務                             worker               [C#],F#     Common/Worker/Web

咱們平常用得多的都是前幾那幾個,比如 mvc、web、wpf、classlib 等。我們在命令中引用的就是項目模板的短名稱即可。比如控制台就是 console。

-n 參數指定項目的名稱,我這裡是“MyApp”,-o 參數指定項目存放目錄,“.” 表示當前目錄。

接下來要改一下項目文件(*.csproj)。

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0-windows</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

</Project>

1、添加 MSBuild 屬性 UseWPF,且設置為 true。有了這個你才能在項目中引用 WPF 有關的程式集。同理,如果要使用 Windows Forms,就將 UseWindowsForms 屬性設置為 true。

2、TargetFramework 要在.NET版本後加上“-windows”,表示這是 Windows 平臺特定的,Linux 上不可用的。類似的如 net7.0-android 等。

至於 OutputType 屬性要不要改為 WinExe,.NET 5 以上是不需要的,它會自動判斷啟不啟動控制台視窗。

好了,保存,關閉項目文件。可以寫代碼了。

在寫代碼前,咱們先理清楚一些核心對象的關係。你才會知道怎麼寫。Application 類是 WPF 程式啟動的核心對象,通常表示該應用程式相關的初始化。所以,在 Main 方法中記得 new 一個。你可別太聰明,千萬不要直接從 Application.Current 靜態屬性來獲取。因為這時應用程式還沒初始化呢,Current 屬性還是 null。Current 屬性適合在項目的其他代碼中方便訪問 Application 對象而使用的。

如果你沒別的東西初始化,那就調用 Application 對象的 Run 方法。應用程式正式啟動,並且主線程會卡在(也不是真的卡)這裡,直到程式要退出了才從 Run 方法返回。其間,調度器會不斷調度/處理各線程上的消息,直到消息迴圈終止。

視窗應用程式當然要一個主視窗。表示視窗的基類是 Window,可以直接用它,也可以派生出自己的類,然後初始化要在視窗上顯示的控制項。

public class MyWindow:Window
{
    public MyWindow()
    {
        InitUI();
    }

    private void InitUI()
    {
        // 本視窗的屬性
        this.Title = "鼠爺快樂園";
        this.Height = 225;
        this.Width = 315;
        // 啟動時視窗在屏幕中央
        WindowStartupLocation = WindowStartupLocation.CenterScreen;
        // 整點控制項
        // 兩個block
        TextBlock tb1 = new()
        {
            Text = "每天斃一鼠",
            TextAlignment = TextAlignment.Center,
            // 文本顏色
            Foreground = new SolidColorBrush(Color.FromRgb(12, 50, 208))
        };
        TextBlock tb2 = new()
        {
            Text = "添壽又增福",
            TextAlignment = TextAlignment.Center
        };
        // 再加一個按鈕
        Button btn = new()
        {
            Content = "行動起來",
            Margin = new Thickness(0d, 15d, 0d, 2d)
        };
        // 單擊事件
        btn.Click += OnClick;
        // 佈局控制項
        StackPanel panel = new();
        // 垂直方向
        panel.Orientation = Orientation.Vertical;
        // 添加子元素
        panel.Children.Add(tb1);
        panel.Children.Add(tb2);
        panel.Children.Add(btn);
        // 作為視窗的內容
        this.Content = panel;
    }

    private void OnClick(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("自在其間樂");
    }
}

Windows 屬於內容控制項,公開 Content 屬性,用來設置單個對象引用。上述代碼先創建兩個 TextBlock 實例和一個 Button 實例,然後把它們塞進 StackPanel 中,再把 StackPanel 實例賦值給視窗的 Content 屬性。

視窗類寫好後,在 Main 方法中,調用 Run 方法時把視窗實例傳進去。

    [STAThread]
    static void Main(string[] args)
    {
        Application app = new();
        app.Run(new MyWindow());
    }

這個程式已經可以運行了。

 

************************************************************************************************

咱們繼續探索。如果要用到數據綁定呢。在 XAML 中是用 {Binding} 擴展標記的,而在代碼中對應的是 Binding 類,位於 System.Windows.Data 命名空間。

Binding 類的構造函數可以傳遞一個字元串常量,對應 {Binding Path=... } 中的 Path,即要綁定的對象路徑。數據源則由 Source 屬性設置。

public Binding(string path);

關聯綁定用的是 BindingOperations 類的靜態方法 SetBinding,要獲取已關聯的 Binding 對象就調用 GetBinding 方法。

public static Binding GetBinding(DependencyObject target, DependencyProperty dp);
public static BindingExpressionBase SetBinding(DependencyObject target, DependencyProperty dp, BindingBase binding);

BindingOperations 類本身是靜態類,所以它的成員自然也是靜態的。target 參數是綁定目標,即 WPF 對象,dp 是要綁定的依賴屬性,binding 就是Binding對象。

咱們舉個例子。

假設用以下類作為數據源。

public class Student
{
    public int ID { get; set; }
    public string? Name { get; set; }
    public int Age { get; set; }
}

視窗的結構:內容根為 Grid 對象,它包含三行兩列,用來放六個 TextBlock 控制項。

        Grid layout = new();
        // 設置為視窗內容
        this.Content = layout;
        // 設置邊距
        layout.Margin = new Thickness(13.5d);
        // 三行兩列
        layout.ColumnDefinitions.Add(new ColumnDefinition()
        {
            Width = GridLength.Auto
        });
        layout.ColumnDefinitions.Add(new ColumnDefinition(){
            // 星號,即 1*
            Width = new GridLength(1.0d, GridUnitType.Star)
        });
        layout.RowDefinitions.Add(new()
        {
            Height = GridLength.Auto
        });
        layout.RowDefinitions.Add(new()
        {
            Height = GridLength.Auto
        });
        layout.RowDefinitions.Add(new()
        {
            Height = GridLength.Auto
        });

ColumnDefinitions 用來定義列。上述代碼中,第一列的寬度為 Auto,第二列的寬度為 *。

RowDefinitions 集合用來定義行。上述代碼中,三行的高度都是 Auto。

然後 new 出六個 TextBlock,三個用來顯示欄位標簽,三個用於數據綁定,顯示屬性值。

        // 六個block
        var tbIDf = new TextBlock(){
            Text = "學號:"
        };
        var tbNamef = new TextBlock()
        {
            Text = "姓名:"
        };
        var tbAgef = new TextBlock()
        {
            Text = "年齡:"
        };

        TextBlock tbIDv = new();
        var tbNamev = new TextBlock();
        var tbAgev = new TextBlock();

把六個 TextBlock 控制項添加到 Grid 的子級中。

        layout.Children.Add(tbIDf);
        layout.Children.Add(tbIDv);
        layout.Children.Add(tbNamef);
        layout.Children.Add(tbNamev);
        layout.Children.Add(tbAgef);
        layout.Children.Add(tbAgev);

設置子元素所在行、列的 Grid.Row 和 Grid.Column 是附加屬性,以 Grid.SetXXX 方法調用。

    layout.Children.Add(tbIDf);
    layout.Children.Add(tbIDv);
    layout.Children.Add(tbNamef);
    layout.Children.Add(tbNamev);
    layout.Children.Add(tbAgef);
    layout.Children.Add(tbAgev);
    // Row、Column 附加屬性
    // 第一行第一列
    Grid.SetRow(tbIDf, 0);
    Grid.SetColumn(tbIDf, 0);
    // 第一行第二列
    Grid.SetRow(tbIDv, 0);
    Grid.SetColumn(tbIDv, 1);
    // 第二行第一列
    Grid.SetRow(tbNamef, 1);
    Grid.SetColumn(tbNamef, 0);
    // 第二行第二列
    Grid.SetRow(tbNamev, 1);
    Grid.SetColumn(tbNamev, 1);
    // 第三行第一列
    Grid.SetRow(tbAgef, 2);
    Grid.SetColumn(tbAgef,0);
    // 第三行第二列
    Grid.SetRow(tbAgev, 2);
    Grid.SetColumn(tbAgev, 1);

最後是創建三個 Binding ,為 Student 類的三個屬性做綁定。

    /* ID */
    Binding bindID = new(nameof(Student.ID))
    {
        Source = this.stu
    };
    BindingOperations.SetBinding(tbIDv, TextBlock.TextProperty, bindID);
    /* Name */
    Binding bindName = new(nameof(Student.Name))
    {
        Source = stu
    };
    BindingOperations.SetBinding(tbNamev, TextBlock.TextProperty, 
bindName);
    /* Age */
    Binding bindAge = new(nameof(Student.Age))
    {
        Source = stu
    };
    BindingOperations.SetBinding(tbAgev, TextBlock.TextProperty, bindAge);

完整的初始化方法代碼如下:

private void InitUI()
{
    Title = "數據綁定";
    Width = 240;
    Height = 185;
    // 創建Grid
    Grid layout = new();
    // 設置為視窗內容
    this.Content = layout;
    // 設置邊距
    layout.Margin = new Thickness(13.5d);
    // 三行兩列
    layout.ColumnDefinitions.Add(new ColumnDefinition()
    {
        Width = GridLength.Auto
    });
    layout.ColumnDefinitions.Add(new ColumnDefinition(){
        // 星號,即 1*
        Width = new GridLength(1.0d, GridUnitType.Star)
    });
    layout.RowDefinitions.Add(new()
    {
        Height = GridLength.Auto
    });
    layout.RowDefinitions.Add(new()
    {
        Height = GridLength.Auto
    });
    layout.RowDefinitions.Add(new()
    {
        Height = GridLength.Auto
    });
    
    // 六個block
    var tbIDf = new TextBlock(){
        Text = "學號:"
    };
    var tbNamef = new TextBlock()
    {
        Text = "姓名:"
    };
    var tbAgef = new TextBlock()
    {
        Text = "年齡:"
    };
    TextBlock tbIDv = new();
    var tbNamev = new TextBlock();
    var tbAgev = new TextBlock();
    // 把六個block放到 grid 上
    layout.Children.Add(tbIDf);
    layout.Children.Add(tbIDv);
    layout.Children.Add(tbNamef);
    layout.Children.Add(tbNamev);
    layout.Children.Add(tbAgef);
    layout.Children.Add(tbAgev);
    // Row、Column 附加屬性
    // 第一行第一列
    Grid.SetRow(tbIDf, 0);
    Grid.SetColumn(tbIDf, 0);
    // 第一行第二列
    Grid.SetRow(tbIDv, 0);
    Grid.SetColumn(tbIDv, 1);
    // 第二行第一列
    Grid.SetRow(tbNamef, 1);
    Grid.SetColumn(tbNamef, 0);
    // 第二行第二列
    Grid.SetRow(tbNamev, 1);
    Grid.SetColumn(tbNamev, 1);
    // 第三行第一列
    Grid.SetRow(tbAgef, 2);
    Grid.SetColumn(tbAgef,0);
    // 第三行第二列
    Grid.SetRow(tbAgev, 2);
    Grid.SetColumn(tbAgev, 1);
    // 數據綁定
    /* ID */
    Binding bindID = new(nameof(Student.ID))
    {
        Source = this.stu
    };
    BindingOperations.SetBinding(tbIDv, TextBlock.TextProperty, bindID);
    /* Name */
    Binding bindName = new(nameof(Student.Name))
    {
        Source = stu
    };
    BindingOperations.SetBinding(tbNamev, TextBlock.TextProperty, 
bindName);
    /* Age */
    Binding bindAge = new(nameof(Student.Age))
    {
        Source = stu
    };
    BindingOperations.SetBinding(tbAgev, TextBlock.TextProperty, bindAge);
}

運行效果如下:

 

這時候,估計你也想到了一件事—— WPF 元素之間的綁定咋弄?對應的 XAML 擴展標記 {Binding ElementName=..., Path=... }。這個用代碼寫起來也不難,Binding 類有 ElementName 屬性,可以引用已命名的對象。但是,在代碼裡面,咱們是直接用變數名來引用對象的,並沒有分配對象名稱。雖然 FrameworkElement 類的子類都繼承了 Name 屬性,但,設置這個 Name 屬性 Binding.ElementName 是找不到的,必須要在 NameScope 對象里註冊到XAML名稱空間後才能被 ElementName 引用。

NameScope 類其實是個 Key=String, Value = Object 的字典,維護當前名稱空間範圍內的對象列表。對應的是 XAML 中的 x:Name = ...。 NameScope 類定義了 NameScope 附加屬性,允許將 NameScope 實例設置到目標對象上。XAML 語法是
<NameScope.NameScope>
    <NameScope />
</NameScope.NameScope>

但我們在寫 XAML 時是不需要,都是自動添加的,用 x:Name 就行了。

在代碼中用 SetNameScope 方法設置。

 

看看下麵的例子。

void InitUI()
{
    Title = "元素之間綁定";
    // 根據內容自動調整視窗大小
    SizeToContent = SizeToContent.WidthAndHeight;
    StackPanel panel = new(){
        Orientation = Orientation.Vertical,
        Margin = new Thickness(15.0d)
    };
    this.Content = panel;   // 佈局
    // 文本輸入控制項
    TextBox txt = new TextBox();
    txt.Margin = new Thickness(3.0d, 5.0d, 3.0d, 8.5d);
    // 給它分配一個名字,綁定時用到
    NameScope myScope = new();
    NameScope.SetNameScope(this, myScope);
    myScope.RegisterName("txtInput", txt);
    // 文本塊
    TextBlock tb = new TextBlock();
    tb.Margin = new Thickness(5.0d, 0d, 5.0d, 0d);
    // 綁定
    Binding bind = new();
    bind.ElementName = "txtInput";
    bind.Path = new PropertyPath(TextBox.TextProperty);
    bind.Mode = BindingMode.OneWay;
    // 在文本框更改時更新數據
    bind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
    BindingOperations.SetBinding(tb, TextBlock.TextProperty, bind);
    // 添加到佈局
    panel.Children.Add(txt);
    panel.Children.Add(tb);
}

這個方法也是寫在 Window 的派生類中,SizeToContent = SizeToContent.WidthAndHeight 表示本視窗的寬度和高度會根據它要顯示的內容自動調整。

由於 TextBlock 控制項的文本來源於 TextBox,因此,要為 TextBox 註冊一個名字“txtInput”。由於 FrameworkElement 類有 RegisterName 方法,所以,註冊名稱的代碼也可以這樣寫:

NameScope.SetNameScope(this, new NameScope());
this.RegisterName("txtInput", txt);

設置 bind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged 允許在輸入的內容更改後就馬上更新綁定數據,能做到實時顯示輸入的內容。

效果如下:

好了,今天咱們先說到這兒,剩下的如模板、樣式、動畫、3D 什麼的,咱們下次再探討。

 


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

-Advertisement-
Play Games
更多相關文章
  • # 網格線分級 一般情況下,我們設置網格線都不會太在意密度和精度的問題,導致下圖這種看的有些眼花,橫坐標在比較密集的時候很容易看竄行。 ![image-20230615191231845](https://img2023.cnblogs.com/blog/2862884/202306/2862884 ...
  • 分析 Switch 相較於 if 的優點 1、switch 執行效率 高於 if 的執行效率 分析: switch是在編譯階段將子函數的地址和判斷條件綁定了,只要直接將a的直接映射到子函數地址去執行就可以了, if處理起來首先要把a的值放到CPU的寄存器中,然後要把比較的值放到CPU的另一個寄存器中 ...
  • ## 概述 RMI 是 Java 提供的一個完善的簡單易用的遠程方法調用框架,採用客戶/伺服器通信方式,在伺服器上部署了提供各種服務的遠程對象,客戶端請求訪問伺服器上遠程對象的方法,它要求客戶端與伺服器端都是 Java 程式 RMI 框架採用代理來負責客戶與遠程對象之間通過 Socket 進行通信的 ...
  • 基於java的智能停車場管理系統設計與實現,可適用於java車輛管理,java停車場信息管理平臺,小區停車管理平臺,小區停車,物業停車管理,智慧停車場管理系統,智慧小區停車場平臺,車輛AI識別,車輛識別。 ...
  • 在調用超類型構造函數之前無法引用“XxxClass.xxx” -----在一個類的構造器方法還未執行的時候,我們無法使用這個類的成員屬性或成員方法。 ...
  • 基於java的校園教務系統設計與實現,可適用於學生教務系統,教務管理系統,學生管理系統,學生選課系統,學生評教,評價教師系統,教務系統設計,java教務管理系統,學生成績管理系統,學生信息管理系統。 ...
  • 在某些時候我們的系統中會出現一些無法被正常刪除的文件,如果想要強制刪除則需要在驅動層面對其進行解鎖後才可刪掉,而所謂的解鎖其實就是釋放掉文件描述符(句柄表)占用,文件解鎖的核心原理是通過調用`ObSetHandleAttributes`函數將特定句柄設置為可關閉狀態,然後在調用`ZwClose`將其... ...
  • 本文將通過閱讀源碼方式分析SpringBoot應用的啟動流程,不涉及Spring啟動部分(有相應的文章介紹)。 本文不會對各個流程做展開分析,後續會有文章介紹詳細流程。 # SpringApplication類 ## 應用啟動入口 使用以下方式啟動一個SpringBoot應用: ```java @S ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...