WPF開發隨筆收錄-WriteableBitmap繪製高性能曲線圖

来源:https://www.cnblogs.com/cong2312/archive/2022/08/05/16553695.html
-Advertisement-
Play Games

一、前言 之前分享過一期關於DrawingVisual來繪製高性能曲線的博客,今天再分享一篇通過另一種方式來繪製高性能曲線的方法,也就是通過WriteableBitmap的方式;具體的一些細節這裡就不啰嗦了,同樣是局部繪製的思想,滾動條拖動到哪裡,就只繪製那一部分的曲線,直接貼代碼;(該程式在英特爾 ...


一、前言

之前分享過一期關於DrawingVisual來繪製高性能曲線的博客,今天再分享一篇通過另一種方式來繪製高性能曲線的方法,也就是通過WriteableBitmap的方式;具體的一些細節這裡就不啰嗦了,同樣是局部繪製的思想,滾動條拖動到哪裡,就只繪製那一部分的曲線,直接貼代碼;(該程式在英特爾11代CPU的電腦可能會遇到拖動滾動條曲線圖卡住不動的情況,這個是顯卡驅動的問題,官方已經修複了,遇到這問題的記得更新一下驅動)

二、正文

1、新建一個類,繼承FrameworkElement,然後在裡面實現一下繪圖的邏輯;

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Resources;
using _Font = System.Drawing.Font;
using GDI = System.Drawing;

namespace WriteableBitmapDemo.Controls
{
    public class CruveWriteableBitmap : FrameworkElement
    {
        private static PrivateFontCollection pfc = new PrivateFontCollection();
        private WriteableBitmap bitmap;

        private int bitmap_width = 0;
        private int bitmap_height = 0;

        private static _Font font = null;
        private static _Font time_font = null;

        private PointF[][] horizontals = null;
        private PointF[][] horizontals_thin = null;
        private PointF[][] verticals = null;
        private PointF[][] verticals_thin = null;

        private List<PointF> top_points1;
        private List<PointF> top_points2;
        private List<PointF> top_points3;
        private List<PointF> bottom_points;

        private List<PointF> labelPosition_up;
        private List<string> labelText_up;
        private List<PointF> labelPosition_down;
        private List<string> labelText_down;

        private List<PointF> timePosition;
        private List<string> timeText;

        private GDI.Pen blackPen = new GDI.Pen(GDI.Color.Black, 1.5f);
        private GDI.Pen grayPen = new GDI.Pen(GDI.Color.Gray, 1f);

        private GDI.Pen top_pen1 = new GDI.Pen(GDI.Color.Black, 2);
        private GDI.Pen top_pen2 = new GDI.Pen(GDI.Color.Orange, 2);
        private GDI.Pen top_pen3 = new GDI.Pen(GDI.Color.Purple, 2);public float scaleX { get; set; } = 1f;
        private float _ScaleY { get; set; } = 1f;
        public float ScaleY
        {
            get { return _ScaleY; }
            set
            {
                _ScaleY = value;
            }
        }

        static CruveWriteableBitmap()
        {
            var appRootDataDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "msyh.ttf");
            if (!File.Exists(appRootDataDir))
            {
                var key = $"/CurveChartDemo;component/Fonts/msyh.ttf";
                StreamResourceInfo info = Application.GetResourceStream(new Uri(key, UriKind.Relative));
                using (var stream = info.Stream)
                {
                    byte[] bytes = new byte[stream.Length];
                    int len = stream.Read(bytes, 0, bytes.Length);
                    File.WriteAllBytes(appRootDataDir, bytes);
                }
            }
            pfc.AddFontFile(appRootDataDir);
        }

        public CruveWriteableBitmap()
        {
            time_font = new _Font(pfc.Families[0], 10);
            font = new _Font(pfc.Families[0], 8);
        }

        public void DrawPoints()
        {
            //InitBitmap();
            if (this.bitmap == null)
            {
                return;
            }

            this.bitmap.Lock();
            using (Bitmap backBufferBitmap = new Bitmap(this.bitmap_width, this.bitmap_height,
                this.bitmap.BackBufferStride, GDI.Imaging.PixelFormat.Format24bppRgb,
                this.bitmap.BackBuffer))
            {
                using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap))
                {
                    backBufferGraphics.SmoothingMode = SmoothingMode.AntiAlias;
                    backBufferGraphics.CompositingQuality = CompositingQuality.HighSpeed;

                    backBufferGraphics.Clear(GDI.Color.White);

                    //粗橫線
                    if (this.horizontals != null)
                    {
                        foreach (var horizontal in this.horizontals)
                        {
                            backBufferGraphics.DrawLine(blackPen, horizontal[0], horizontal[1]);
                        }
                    }
                    //細橫線
                    if (this.horizontals_thin != null)
                    {
                        foreach (var horizontal in this.horizontals_thin)
                        {
                            backBufferGraphics.DrawLine(grayPen, horizontal[0], horizontal[1]);
                        }
                    }
                    //粗豎線
                    if (this.verticals != null)
                    {
                        foreach (var vertical in this.verticals)
                        {
                            backBufferGraphics.DrawLine(blackPen, vertical[0], vertical[1]);
                        }
                    }
                    //細豎線
                    if (this.verticals_thin != null)
                    {
                        foreach (var vertical in this.verticals_thin)
                        {
                            backBufferGraphics.DrawLine(grayPen, vertical[0], vertical[1]);
                        }
                    }
                    //上圖曲線1
                    if (this.top_points1 != null && this.top_points1.Count > 0)
                    {
                        backBufferGraphics.DrawLines(top_pen1, top_points1.ToArray());
                    }
                    //上圖曲線2
                    if (this.top_points2 != null && this.top_points2.Count > 0)
                    {
                        backBufferGraphics.DrawLines(top_pen2, this.top_points2.ToArray());
                    }
                    //上圖曲線3
                    if (this.top_points3 != null && this.top_points3.Count > 0)
                    {
                        backBufferGraphics.DrawLines(top_pen3, this.top_points3.ToArray());
                    }
                    //下圖曲線
                    if (this.bottom_points != null && this.bottom_points.Count > 0)
                    {
                        backBufferGraphics.DrawLines(top_pen1, this.bottom_points.ToArray());
                    }

                    //文本
                    if (labelPosition_up != null && labelPosition_up.Count > 0)
                    {
                        SizeF fontSize = backBufferGraphics.MeasureString(labelText_up[0], font);
                        for (int i = 0; i < labelPosition_up.Count; ++i)
                        {
                            backBufferGraphics.DrawString(labelText_up[i], font, GDI.Brushes.Black, labelPosition_up[i].X, labelPosition_up[i].Y - fontSize.Height);
                        }
                    }
                    if (labelPosition_down != null && labelPosition_down.Count > 0)
                    {
                        for (int i = 0; i < labelPosition_down.Count; ++i)
                        {
                            backBufferGraphics.DrawString(labelText_down[i], font, GDI.Brushes.Black, labelPosition_down[i].X, labelPosition_down[i].Y);
                        }
                    }
                    if (timePosition != null && timePosition.Count > 0)
                    {
                        for (int i = 0; i < timePosition.Count; ++i)
                        {
                            if (i == 0)
                                backBufferGraphics.DrawString(timeText[i], time_font, GDI.Brushes.Black, timePosition[i].X, timePosition[i].Y);
                            else
                            {
                                SizeF fontSize = backBufferGraphics.MeasureString(timeText[i], time_font);
                                backBufferGraphics.DrawString(timeText[i], time_font, GDI.Brushes.Black, timePosition[i].X - fontSize.Width / 2, timePosition[i].Y);
                            }

                        }
                    }

                    backBufferGraphics.Flush();
                }
            }
            this.bitmap.AddDirtyRect(new Int32Rect(0, 0, this.bitmap_width, this.bitmap_height));
            this.bitmap.Unlock();
        }public void UpdateTimeLabel(List<PointF> timePosition, List<string> timeText)
        {
            this.timePosition = timePosition;
            this.timeText = timeText;
        }
        public void UpdatePosition(List<PointF> fhr1_points, List<PointF> fhr2_points, List<PointF> fhr3_points, List<PointF> toco_points)
        {
            this.top_points1 = fhr1_points;
            this.top_points2 = fhr2_points;
            this.top_points3 = fhr3_points;
            this.bottom_points = toco_points;
        }

        public void UpdateLabelPosition(List<PointF> labelPosition_up, List<string> labelText_up, List<PointF> labelPosition_down, List<string> labelText_down)
        {
            this.labelPosition_up = labelPosition_up;
            this.labelText_up = labelText_up;
            this.labelPosition_down = labelPosition_down;
            this.labelText_down = labelText_down;
        }

        public void UpdateHorizontalLine(PointF[][] horizontals, PointF[][] horizontals_thin)
        {
            this.horizontals = horizontals;
            this.horizontals_thin = horizontals_thin;
        }

        public void UpdateVerticalLine(PointF[][] verticals, PointF[][] verticals_thin)
        {
            this.verticals = verticals;
            this.verticals_thin = verticals_thin;
        }

        protected override void OnRender(DrawingContext dc)
        {
            InitBitmap();
            if (this.bitmap != null)
            {
                dc.DrawImage(bitmap, new Rect(0, 0, RenderSize.Width, RenderSize.Height));
            }
            base.OnRender(dc);
        }

        private void InitBitmap()
        {
            if (bitmap == null || this.bitmap.Width != (int)this.ActualWidth || this.bitmap.Height != (int)this.ActualHeight)
            {
                if ((int)this.ActualWidth > 0 && (int)this.ActualHeight > 0)
                {
                    this.bitmap_width = (int)this.ActualWidth;
                    this.bitmap_height = (int)this.ActualHeight;
                    this.bitmap = new WriteableBitmap(bitmap_width, bitmap_height, 96, 96, PixelFormats.Bgr24, null);
                    this.bitmap.Lock();
                    using (Bitmap backBufferBitmap = new Bitmap(bitmap_width, bitmap_height,
                        this.bitmap.BackBufferStride, GDI.Imaging.PixelFormat.Format24bppRgb,
                        this.bitmap.BackBuffer))
                    {
                        using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap))
                        {
                            backBufferGraphics.SmoothingMode = SmoothingMode.HighSpeed;
                            backBufferGraphics.CompositingQuality = CompositingQuality.HighSpeed;
                            backBufferGraphics.Clear(GDI.Color.White);
                            backBufferGraphics.Flush();
                        }
                    }
                    this.bitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap_width, bitmap_height));
                    this.bitmap.Unlock();
                }
            }
        }
    }
}

2、主視窗添加該控制項,並添加滾動條那些

<Window
    x:Class="WriteableBitmapDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ct="clr-namespace:WriteableBitmapDemo.Controls"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WriteableBitmapDemo"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="1500"
    Height="450"
    Loaded="Window_Loaded"
    mc:Ignorable="d">
    <Grid>
        <ct:CruveWriteableBitmap x:Name="curve" Margin="0,0,0,20" />
        <ScrollViewer
            Name="scroll"
            HorizontalScrollBarVisibility="Auto"
            ScrollChanged="ScrollViewer_ScrollChanged"
            VerticalScrollBarVisibility="Disabled">
            <Canvas x:Name="canvas" Height="1" />
        </ScrollViewer>
        <Canvas
            x:Name="CanvasPanel"
            Margin="0,0,0,20"
            Background="Transparent" />
    </Grid>
</Window>

3、主視窗後臺添加曲線數值生成方法和更新視圖數據方法

using System.Collections.Generic;
using System.Drawing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WriteableBitmapDemo
{
    /// <summary>
    /// MainWindow.xaml 的交互邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        private bool isAdd = true;

        private Dictionary<int, int> dicTopPoints = new Dictionary<int, int>();
        private Dictionary<int, int> dicBottomPoints = new Dictionary<int, int>();

        private float y_scale;

        private static int Top_Val_Max = 240;
        private static int Top_Val_Min = 30;
        private static int Top_X_Sex = 20;
        private static int Bottom = 100;
        private static int Center = 25;
        private static int BottomOffset = 0;

        private double offset = -1;

        public MainWindow()
        {
            InitializeComponent();

            CanvasPanel.MouseMove += delegate (object sender, MouseEventArgs e)
            {
                if (e.LeftButton == MouseButtonState.Pressed)
                {
                    if (Mouse.Captured == null) Mouse.Capture(CanvasPanel);

                    if (offset >= 0 && offset <= CanvasPanel.ActualWidth)
                    {
                        scroll.ScrollToHorizontalOffset(scroll.HorizontalOffset - (e.GetPosition(this).X - offset));
                    }
                    offset = e.GetPosition(this).X;
                }
                else
                {
                    offset = -1;
                    Mouse.Capture(null); // 釋放滑鼠捕獲
                }
            };
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            //生成曲線數據
            int temp = 50;
            for (int i = 0; i < 24 * 60 * 60 * 4; i++)
            {
                if (isAdd)
                {
                    dicTopPoints.Add(i, temp);
                    temp += 2;
                }
                else
                {
                    dicTopPoints.Add(i, temp);
                    temp -= 2;
                }

                if (temp == 210) isAdd = false;
                if (temp == 50) isAdd = true;
            }
            temp = 0;
            for (int i = 0; i < 24 * 60 * 60 * 4; i++)
            {
                if (isAdd)
                {
                    dicBottomPoints.Add(i, temp);
                    temp += 2;
                }
                else
                {
                    dicBottomPoints.Add(i, temp);
                    temp -= 2;
                }

                if (temp == 100) isAdd = false;
                if (temp == 0) isAdd = true;
            }
            //初始化滾動條和觸發曲線繪製
            canvas.Width = dicTopPoints.Count;
            scroll.ScrollToLeftEnd();
        }

        private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            InitChartData((float)scroll.HorizontalOffset);
        }

        /// <summary>
        /// 根據滾動條偏移量更新需要繪製的數據
        /// </summary>
        /// <param name="offset"></param>
        private void InitChartData(float offset)
        {
            y_scale = (float)((curve.ActualHeight - Center) / (Top_Val_Max - Top_Val_Min + Bottom));

            //上圖橫線
            List<PointF[]> horizontalList = new List<PointF[]>();
            List<PointF[]> horizontalList_thin = new List<PointF[]>();
            for (int y = 0; y <= Top_Val_Max - Top_Val_Min; y += 10)
            {
                float currentHeight = (float)(curve.ActualHeight - (y + Bottom) * y_scale - Center);
                PointF point1 = new PointF(0, currentHeight);
                PointF point2 = new PointF((float)curve.ActualWidth, currentHeight);
                if (y % 30 == 0)
                    horizontalList.Add(new PointF[] { point1, point2 });
                else
                    horizontalList_thin.Add(new PointF[] { point1, point2 });
            }
            for (int y = 0; y <= Bottom; y += 10)
            {
                float currentHeight = (float)(curve.ActualHeight - y * y_scale - BottomOffset);
                PointF point1 = new PointF(0, currentHeight);
                PointF point2 = new PointF((float)curve.ActualWidth, currentHeight);
                if (y % 20 == 0)
                    horizontalList.Add(new PointF[] { point1, point2 });
                else
                    horizontalList_thin.Add(new PointF[] { point1, point2 });
            }

            //豎線與文字
            List<PointF[]> verticals = new List<PointF[]>();
            List<PointF[]> verticals_thin = new List<PointF[]>();

            List<PointF> timePosition = new List<PointF>();
            List<string> timeText = new List<string>();

            List<PointF> labelPosition_up = new List<PointF>();
            List<string> labelText_up = new List<string>();

            List<PointF> labelPosition_down = new List<PointF>();
            List<string> labelText_down = new List<string>();

            for (int i = 0; i < offset + curve.ActualWidth; i += Top_X_Sex * 2)
            {
                if (i < offset) continue;
                //下豎線
                PointF point1 = new PointF(i - offset, (float)(curve.ActualHeight - BottomOffset));
                PointF point2 = new PointF(i - offset, (float)(curve.ActualHeight - Bottom * y_scale - BottomOffset));
                //上豎線
                PointF point3 = new PointF(i - offset, 0);
                PointF point4 = new PointF(i - offset, (float)(curve.ActualHeight - Bottom * y_scale - Center));

                if ((i + (60 * 2)) % (60 * 2) == 0)
                {
                    verticals.Add(new PointF[] { point1, point2 });
                    verticals.Add(new PointF[] { point3, point4 });
                }
                else
                {
                    verticals_thin.Add(new PointF[] { point1, point2 });
                    verticals_thin.Add(new PointF[] { point3, point4 });
                }

                if (i % 240 == 0)
                {
                    timeText.Add(i + "");
                    timePosition.Add(new PointF(i - offset, (float)(curve.ActualHeight - Bottom * y_scale - Center)));
                }

                if ((i + (60 * 2)) % (120 * 2) == 0)
                {
                    for (int y = Top_Val_Min; y <= Top_Val_Max; y += 10)
                    {
                        if (y % 30 == 0)
                        {
                            labelText_up.Add(y + "");
                            labelPosition_up.Add(new PointF(i - offset, (float)(curve.ActualHeight - (Bottom + y - Top_Val_Min) * y_scale - Center)));
                        }
                    }
                    for (int y = 20; y <= 100; y += 10)
                    {
                        if (y % 20 == 0)
                        {
                            labelText_down.Add(y + "");
                            labelPosition_down.Add(new PointF(i - offset, (float)(curve.ActualHeight - y * y_scale)));
                        }
                    }
                }
            }

            List<PointF> top_points1 = new List<PointF>();
            for (int i = (int)offset, j = 0; i < dicTopPoints.Count && j < curve.ActualWidth; i++, j++)
            {
                top_points1.Add(new PointF(j, (float)(curve.ActualHeight - (dicTopPoints[i] + 100 - Top_Val_Min) * y_scale) - Center));
            }

            List<PointF> top_points2 = new List<PointF>();
            for (int i = (int)offset, j = 0; i < dicTopPoints.Count && j < curve.ActualWidth; i++, j++)
            {
                top_points2.Add(new PointF(j, (float)(curve.ActualHeight - (dicTopPoints[i] + 20 + 100 - Top_Val_Min) * y_scale) - Center));
            }

            List<PointF> top_points3 = new List<PointF>();
            for (int i = (int)offset, j = 0; i < dicTopPoints.Count && j < curve.ActualWidth; i++, j++)
            {
                top_points3.Add(new PointF(j, (float)(curve.ActualHeight - (dicTopPoints[i] - 20 + 100 - Top_Val_Min) * y_scale) - Center));
            }

            List<PointF> bottom_points = new List<PointF>();
            for (int i = (int)offset, j = 0; i < dicBottomPoints.Count && j < curve.ActualWidth; i++, j++)
            {
                bottom_points.Add(new PointF(j, (float)(curve.ActualHeight - dicBottomPoints[i] * y_scale - BottomOffset)));
            }

            curve.UpdateHorizontalLine(horizontalList.ToArray(), horizontalList_thin.ToArray());
            curve.UpdateVerticalLine(verticals.ToArray(), verticals_thin.ToArray());
            curve.UpdatePosition(top_points1, top_points2, top_points3, bottom_points);
            curve.UpdateTimeLabel(timePosition, timeText);
            curve.UpdateLabelPosition(labelPosition_up, labelText_up, labelPosition_down, labelText_down);
            curve.DrawPoints();
        }
    }
}

4、運行效果如下,歡迎各位大佬指點

 


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

-Advertisement-
Play Games
更多相關文章
  • 繼 Tabby、Warp 後,今天再來給大家推薦一款終端神器——WindTerm,完全開源,在 GitHub 上已經收穫 6.6k 的 star。 https://github.com/kingToolbox/WindTerm 作者還拿 WindTerm 和 Putty、xterm、Windows ...
  • 一、前言 我們在實際開發中肯定會遇到後端的時間傳到前端是這個樣子的:2022-08-02T15:43:50 這個時候前後端就開始踢皮球了,!! 後端說:前端來做就可! 前端說:後端來做就可! 作為一名有責任感的後端,這種事情怎麼能讓前端來搞呢! 還有就是Long類型的返回到前端可能會損失精度,這個情 ...
  • 在上一遍文章中已經介紹了PixelShaderEffect 用hlsl(著色器) 可以實現各種自定義濾鏡效果了,本文將用 "ThresholdEffect" 來講解如何編寫,編譯hlsl,然後使用PixelShaderEffect製作自定義濾鏡。 效果圖: 一.hlsl幫助程式介紹 在寫hlsl 代 ...
  • 簡介 FTP是FileTransferProtocol(文件傳輸協議)的英文簡稱,而中文簡稱為“文傳協議”。用於Internet上的控制文件的雙向傳輸。同時,它也是一個應用程式(Application)。基於不同的操作系統有不同的FTP應用程式,而所有這些應用程式都遵守同一種協議以傳輸文件。 FTP ...
  • 在繼承中,派生類可以拿到基類的方法,若是派生類很多,且有時某部分派生類的部分實現邏輯是一樣的,但其他的派生類又用不到,這個時候這些邏輯若是全部寫到派生類中,就會導致產生很多的重覆邏輯,但是若是寫到基類中就會導致其他用不到當前邏輯的派生類也能調用,這樣就會導致代碼維護出現了問題。由此產生了介面。 在C ...
  • WPF 截圖控制項之移除控制項(九)「仿微信」 WPF 截圖控制項之移除控制項(九)「仿微信」 作者:WPFDevelopersOrg 原文鏈接: https://github.com/WPFDevelopersOrg/WPFDevelopers 框架使用大於等於.NET40; Visual Studio ...
  • 之前寫的DBHelper,名稱確實太Low,就改了個名,叫LiteSql,本來想叫SqlShuttle(SQL一把梭),奈何單詞太長。 有兩個版本,一個是LiteSql,一個是Dapper.LiteSql,LiteSql底層用的是ADO.NET,Dapper.LiteSql底層用的是Dapper,提 ...
  • 從零開始搭建基於 ABP Framework 分層架構解決方案,快速集成框架內置應用模塊。探索基於 ABP Framework 極速開發的最佳路徑,構建一個模塊完備、可開發、可調試、可發佈和部署的分層架構解決方案。 ...
一周排行
    -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=" ...
  • 在編寫上位機軟體時,需要經常處理命令拼接與其他設備進行通信,通常對不同的命令封裝成不同的方法,擴展稍許麻煩。 本次擬以特性方式實現,以兼顧維護性與擴展性。 思想: 一種命令對應一個類,其類中的各個屬性對應各個命令段,通過特性的方式,實現其在這包數據命令中的位置、大端或小端及其轉換為對應的目標類型; ...