我的開發助手之時間線控制項

来源:https://www.cnblogs.com/wxdongtt2007/archive/2019/08/27/11421494.html
-Advertisement-
Play Games

數年前因為某個原因,開始編寫的我的開發助手,一路艱辛,一路堅持,目前仍不斷完善之中,項目地址:https://gitee.com/sqlorm/DevelopAssistant 歡迎大家點贊和支持。 今天想和大家分享一下其中的時間線控制項,這是一個通過GDI繪製和對原有事件重寫來實現的用戶自定義控制項, ...


    數年前因為某個原因,開始編寫的我的開發助手,一路艱辛,一路堅持,目前仍不斷完善之中,項目地址:https://gitee.com/sqlorm/DevelopAssistant 歡迎大家點贊和支持。

    今天想和大家分享一下其中的時間線控制項,這是一個通過GDI繪製和對原有事件重寫來實現的用戶自定義控制項,界面還算美觀,操作也很簡捷,喜歡的同學不妨收下。

    控制項是這樣子的:    

沒有內容時界面

 

管理界面帶編輯功能界面

    下麵就來介紹一下關於這個控制項的開發:

    第一步、我們創建一個類繼承 UserControl

    控制項主要對 OnPaint ,OnMouseClick ,OnMouseMove,OnSizeChanged,OnMouseWheel 方法進行重寫,其中 OnPaint 方法用戶界面元素的繪製,併在該方法裡面計算控制項元素的繪製區域,以便在OnMouseClick 重寫方法里實現元素的點擊事件,OnMouseClick 方法就是實現控制項元素的點擊事件,OnMouseMove 主要實體一些滑鼠特效,例如滑動滑鼠改變背景色,改變控制項預設游標形狀等,OnSizeChanged 方法主要實現當改變控制項大小時控制項控制項滾動條相關屬性的計算和觸發控制項元素重繪及事件區域範圍Rectangle的計算,通過判斷滑鼠點擊的位置屬於哪個元素的區域範圍來確定觸發哪個元素的相關事件,OnMouseWheel  滑鼠滾輪滾動時發生。其實winform下自定義控制項特別是通過GDI繪製來實現的一類基本上都是實現上述幾個事件方法來實現,可以用一張圖來概括:

    第二步、定義控制項的內部元素

    控制項主要涉及到 月份對象元素: MonthItem ,日期對象元素:DateItem ,時間對象元素:DateTimeItem 他們都繼承自公共對象元素:TimelineItem 他們都有公同的屬性Id (與資料庫表主鍵做關聯),Name 名稱,Tag 其它數據相關綁定的標簽。其次MonthItem和 DateItem 都有 Bound 屬於,用戶保存該元素在控制項中的繪製區域。下麵貼出這三個元素實體類的代碼:                   

    MonthItem:

    [Serializable]
    public class MonthItem : TimelineItem
    {
        public DateTime Date { get; set; }
        public string DateLabel { get; set; }
        public List<DateItem> List { get; set; }

        internal Size Size { get; set; }
        internal Rectangle Bound { get; set; }
    }

     DateItem:   

    [Serializable]
    public class DateItem : TimelineItem
    {
        public DateTime Date { get; set; }      
        public List<DateTimeItem> List { get; set; }

        internal Size Size { get; set; }
        internal Rectangle Bound { get; set; }
        internal Rectangle AddRect { get; set; }
        internal Rectangle ClickRect { get; set; }
        public bool Selected { get; set; }

        private string _tag;
        public override string Tag
        {
            get
            {
                if (string.IsNullOrEmpty(_tag))
                {
                    _tag = Date.ToString("yyyyMMdd");
                }
                return _tag;
            }
        }

    }

    DateTimeItem :  

    [Serializable]
    public class DateTimeItem : TimelineItem
    {       
        public Image Icon { get; set; }
        public string Title { get; set; } 
        public string Summary { get; set; }
        public string Description { get; set; }
        public string ToolTip { get; set; }
        public string PersonName { get; set; }
        public DateTime DateTime { get; set; }
        public ImportantLevel Level { get; set; }
        public Timeliness Timeliness { get; set; }
        public string ResponsiblePerson { get; set; }

        internal Rectangle EditRect { get; set; }
        internal Rectangle DeleteRect { get; set; }
        /// <summary>
        /// 0 :預設 1:修改  2:刪除
        /// </summary>
        internal int ButtonState { get; set; }
        public Rectangle ClickRect { get; set; }
        public bool Selected { get; set; }

        private string _tag;
        public override string Tag
        {
            get
            {
                if (string.IsNullOrEmpty(_tag))
                {
                    _tag = DateTime.ToString("yyyyMMddHHmmss");
                }
                return _tag;
            }
        }

    }

    第三步、繪製控制項內部的元素

    繪製控制項內部的元素主要分為繪製 TimelineItem 一類(包括 MonthItem ,DateItem 和 DateTimeItem)和 控制項的滾動條,一般來講winform 控制項自帶的滾動條都由系統繪製,往往和操作系統息息相關,這裡我們的時間線控制項要適合開發助手相關的主題,所以我們採用在內部自己繪製滾動條,通過主要對OnMouseMove,OnMouseWheel兩相事件方法進行重寫來實現滾動條的控制項。

   這裡對TimelineItem 一類的元素繪製主要貼出以下代碼:   

/// <summary>
        /// 計算 TimelineItem 繪製區域 通過對 MonthItem 子元素遞規迴圈計算 整個 MonthItem 元素的繪製區域
        /// </summary>
        /// <param name="g"></param>
        /// <param name="index"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        private Rectangle MeasureItemBound(Graphics g, int index, MonthItem item)
        {
            int itemHeight = 46;
            if (item.List != null)
            {
                foreach (DateItem subItem in item.List)
                {
                    if (subItem.List != null)
                    {
                        foreach(DateTimeItem subsubItem in subItem.List)
                        {
                            itemHeight = itemHeight + 32;
                            if (!string.IsNullOrEmpty(subsubItem.Summary))
                            {
                                itemHeight = itemHeight + 26;
                            }
                        }
                    }
                    itemHeight = itemHeight + 32;
                }               
            }
            Rectangle rect = new Rectangle(drawPositionOffset.X + padding.Left, drawPositionOffset.Y + position, this.Width - padding.Left - padding.Right - (scrollerBarVisable ? 0 : scrollerBarWidth), itemHeight);
            position = position + itemHeight;
            return rect;
        }

/// <summary>
        /// 繪製 MonthItem 元素,包括下麵的  DateItem 和  DateTimeItem 子元素
        /// </summary>
        /// <param name="g"></param>
        /// <param name="index"></param>
        /// <param name="item"></param>
        private void DrawTimelineItem(Graphics g, int index, MonthItem item)
        {
            StringFormat sf = new StringFormat();
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Center;

            // margin
            Rectangle bound = item.Bound;
            
            //g.DrawRectangle(new Pen(SystemColors.ControlDark), bound);
            g.DrawLine(new Pen(SystemColors.ControlLight) { DashStyle = System.Drawing.Drawing2D.DashStyle.Dot }, 5, bound.Top + 23, bound.Width - 10, bound.Top + 23);

            Point start = new Point(5 + 18, bound.Top + (index > 0 ? 0 : 5));
            Point end = new Point(5 + 18, bound.Bottom - (index < this.DataList.Count - 1 ? 0 : 5));
            g.DrawLine(new Pen(SystemColors.ControlLight), start, end);

            Rectangle iconRect = new Rectangle(5, bound.Top + 5, 36, 36);
            g.FillEllipse(Brushes.Orange, iconRect);
            g.DrawString(item.DateLabel, this.Font, Brushes.White, iconRect, sf);

            if (item.List != null)
            {
                StringFormat subSf = new StringFormat();
                subSf.LineAlignment = StringAlignment.Center;
                Font subTitleFont = new Font("仿宋", 12, FontStyle.Bold | FontStyle.Italic) { };

                int top = bound.Top + 15;
                for (int i = 0; i < item.List.Count; i++)
                {
                    top = top + 32;
                    DateItem subItem = item.List[i];

                    Rectangle subIconRect = new Rectangle(5 + 12, top + 9, 12, 12);
                    g.FillEllipse(Brushes.Orange, subIconRect);
                    //g.DrawEllipse(new Pen(Color.Orange) { Width=2.0f }, subIconRect);
                    //g.DrawString((i + 1).ToString(), this.Font, Brushes.White, subIconRect, sf);
                    subIconRect.Inflate(-2, -2);
                    g.FillEllipse(Brushes.White, subIconRect);

                    Rectangle subRect = new Rectangle(56, top, bound.Width - 64, 32);
                    if (subItem.Selected)
                    {
                        using (var roundedRectanglePath = CreateRoundedRectanglePath(subRect, 2))
                        {
                            g.FillPath(new SolidBrush(Color.FromArgb(240, 245, 249)), roundedRectanglePath);
                        }
                    }

                    Rectangle subTitleRect = new Rectangle(56, top, bound.Width - 64 - 30, 32);
                    //g.DrawRectangle(new Pen(Color.Orange), subTitleRect);
                    //g.DrawString((i + 1) + "、" + subItem.Title, this.Font, Brushes.Red, subTitleRect, subSf);

                    Brush subTitleBrush = Brushes.Black;                   
                    g.DrawString(subItem.Date.ToString("yyyy-MM-dd"), subTitleFont, subTitleBrush, subTitleRect, subSf);
                    //g.FillRectangle(Brushes.Red, subTitleRect);

                    Rectangle subOptionRect = new Rectangle(bound.X + bound.Width - 34 + 4, top + 8, 16, 16);
                    //g.FillRectangle(Brushes.Yellow, subOptionRect);
                    g.DrawImage(this.TimeLineIcons.Images[2], subOptionRect);
                    subItem.AddRect = subOptionRect;
                    subItem.ClickRect = subRect;

                    if (subItem.List != null)
                    {
                        for (int j = 0; j < subItem.List.Count; j++)
                        {
                            top = top + 32;
                            DateTimeItem subsubItem = subItem.List[j];

                            //Rectangle subsubIconRect = new Rectangle(5 + 14, top + 10, 8, 8);
                            //g.FillEllipse(Brushes.Orange, subsubIconRect);
                            //subsubIconRect.Inflate(-2, -2);
                            //g.FillEllipse(Brushes.White, subsubIconRect);

                            Rectangle DateTimeItemClickRect = new Rectangle(56, top + 2, bound.Width - 64, 28);

                            if (!string.IsNullOrEmpty(subsubItem.Summary))
                                DateTimeItemClickRect = new Rectangle(DateTimeItemClickRect.X, DateTimeItemClickRect.Y, DateTimeItemClickRect.Width, DateTimeItemClickRect.Height + 32);

                            if (subsubItem.Selected)
                            {
                                using (var roundedRectanglePath = CreateRoundedRectanglePath(DateTimeItemClickRect, 2))
                                {
                                    g.FillPath(new SolidBrush(Color.FromArgb(240, 245, 249)), roundedRectanglePath);
                                }
                            }

                            Brush drawTitleBrush = Brushes.Black;
                            if (subsubItem.Selected)
                                drawTitleBrush = Brushes.Blue;

                            Color drawTitleColor = Color.Black;
                            if (subsubItem.Selected)
                                drawTitleColor = Color.Blue;

                            if (!subsubItem.Selected)
                            {
                                switch (subsubItem.Level)
                                {
                                    case ImportantLevel.Important:
                                        drawTitleColor = Color.Orange;
                                        break;
                                    case ImportantLevel.MoreImportant:
                                        drawTitleColor = Color.Brown;
                                        break;
                                    case ImportantLevel.MostImportant:
                                        drawTitleColor = Color.Red;
                                        break;
                                }
                            }

                            //Rectangle subsubImgRect = new Rectangle(56 + 0, top + 7, 16, 16);
                            ////g.FillEllipse(Brushes.Red, subsubImgRect);
                            //g.DrawImage(this.TimeLineIcons.Images[2], subsubImgRect);

                            Brush itemIconBrush = Brushes.Red;
                            switch (subsubItem.Timeliness)
                            {
                                case Timeliness.Normal:
                                    itemIconBrush = Brushes.Green;
                                    break;
                                case Timeliness.Yellow:
                                    itemIconBrush = Brushes.Yellow;
                                    break;
                                case Timeliness.Orange:
                                    itemIconBrush = Brushes.Orange;
                                    break;
                                case Timeliness.Red:
                                    itemIconBrush = Brushes.Red;
                                    break;
                                case Timeliness.Dark:
                                    itemIconBrush = Brushes.Gray;
                                    break;
                                case Timeliness.Black:
                                    itemIconBrush = Brushes.Black;
                                    break;
                            }

                            int m = 20;
                            Rectangle subsubImgRect = Rectangle.Empty;
                            if (subsubItem.Icon != null)
                            {
                                if (subsubItem.Icon.Height == 16)
                                {
                                    m = 20;
                                    subsubImgRect = new Rectangle(56 + 0, top + 3, 16, 16);
                                }
                                if (subsubItem.Icon.Height == 24)
                                {
                                    m = 28;
                                    subsubImgRect = new Rectangle(56 + 0, top + 3, 24, 24);
                                } 
                                else
                                {
                                    throw new Exception("只支持16*16、24*24大小的圖標");
                                }
                                g.DrawImage(subsubItem.Icon, subsubImgRect);
                            }
                            else
                            {
                                if(!string.IsNullOrEmpty(subsubItem.PersonName))
                                {
                                    m = 28;
                                    subsubImgRect = new Rectangle(56 + 0, top + 3, 24, 24);
                                }
                                else
                                {
                                    m = 20;
                                    subsubImgRect = new Rectangle(56 + 0, top + 7, 16, 16);
                                }                               
                                g.FillEllipse(itemIconBrush, subsubImgRect);

                                if (!string.IsNullOrEmpty(subsubItem.PersonName))
                                {
                                    Brush showNameBrush = Brushes.White;
                                    Font showNameFont = new Font("微軟雅黑", 8, FontStyle.Bold);

                                    if (itemIconBrush == Brushes.Red ||
                                        itemIconBrush == Brushes.Yellow)
                                    {
                                        showNameBrush = Brushes.Black;
                                    }

                                    g.DrawString(subsubItem.PersonName, showNameFont, showNameBrush, subsubImgRect, sf);
                                }

                            }

                            //Rectangle subsubTitleRect = new Rectangle(56 + 20, top, bound.Width - 84 - 60, 32);
                            Rectangle subsubTitleRect = new Rectangle(56 + m, top, bound.Width - 84 - 60, 32);
                            //g.DrawRectangle(new Pen(Color.Orange), subsubTitleRect);
                            //g.DrawString((i + 1) + "、" + subItem.Title, this.Font, Brushes.Red, subTitleRect, subSf);
                            //g.DrawString(subsubItem.Title, this.Font, drawTitleBrush, subsubTitleRect, subSf);
                            TextRenderer.DrawText(g, subsubItem.Title, this.Font, subsubTitleRect, drawTitleColor, TextFormatFlags.Left | TextFormatFlags.WordEllipsis | TextFormatFlags.VerticalCenter);

                            if (_isEditModel && subsubItem.Selected)
                            {
                                //繪製刪除按扭
                                Size subsubTitleSize = TextRenderer.MeasureText(g, subsubItem.Title, this.Font);
                                subsubItem.EditRect = new Rectangle(subsubTitleRect.X + subsubTitleSize.Width + 2, top + 8, 16, 16);
                                subsubItem.DeleteRect = new Rectangle(subsubTitleRect.X + subsubTitleSize.Width + 2 + 16 + 4, top + 8, 16, 16);
                            }

                            Rectangle subsubTimeRect = new Rectangle(bound.Width - 64, top, 56, 32);
                            //g.FillRectangle(Brushes.Green, subsubTimeRect);
                            //g.DrawString((i + 1) + "、" + subItem.Title, this.Font, Brushes.Red, subTitleRect, subSf);
                            g.DrawString(subsubItem.DateTime.ToString("HH:mm:ss"), this.Font, drawTitleBrush, subsubTimeRect, subSf);

                            if (!string.IsNullOrEmpty(subsubItem.Summary))
                            {
                                Font drawSummaryFont = this.Font;
                                Brush drawSummaryBrush = Brushes.Gray;
                                Color drawSummaryColor = Color.Gray;

                                if (subsubItem.Selected)
                                {
                                    drawSummaryBrush = new SolidBrush(SystemColors.ControlDark);
                                    //drawSummaryFont = new Font(this.Font, FontStyle.Italic);
                                    drawSummaryColor = SystemColors.ControlDark;
                                }

                                top = top + 32;
                                Rectangle subsubSummaryRect = new Rectangle(56, top, bound.Width - 64, 26);
                                //g.DrawRectangle(Pens.Red, subsubSummaryRect);
                                //g.DrawString(subsubItem.Summary, drawSummaryFont, drawSummaryBrush, subsubSummaryRect, subSf);
                                TextRenderer.DrawText(g, subsubItem.Summary, drawSummaryFont, subsubSummaryRect, drawSummaryColor, TextFormatFlags.Left | TextFormatFlags.WordEllipsis | TextFormatFlags.VerticalCenter);

                            }

                            subsubItem.ClickRect = DateTimeItemClickRect;

                            g.DrawLine(new Pen(SystemColors.ControlLight) { DashStyle = System.Drawing.Drawing2D.DashStyle.Dot }, 56, top + 32, bound.Width - 10, top + 32);

                            if (_isEditModel && subsubItem.Selected)
                            {
                                //switch (subsubItem.ButtonState)
                                //{
                                //    case 1:
                                //        g.FillRectangle(new SolidBrush(Color.Orange), subsubItem.EditRect);
                                //        g.FillRectangle(new SolidBrush(Color.FromArgb(240, 245, 249)), subsubItem.DeleteRect);
                                //        break;
                                //    case 2:
                                //        g.FillRectangle(new SolidBrush(Color.FromArgb(240, 245, 249)), subsubItem.EditRect);
                                //        g.FillRectangle(new SolidBrush(Color.Orange), subsubItem.DeleteRect);
                                //        break;
                                //    default:
                                //        g.FillRectangle(new SolidBrush(Color.FromArgb(240, 245, 249)), subsubItem.EditRect);
                                //        g.FillRectangle(new SolidBrush(Color.FromArgb(240, 245, 249)), subsubItem.DeleteRect);
                                //        break;
                                //}

                                g.FillRectangle(new SolidBrush(Color.FromArgb(240, 245, 249)), subsubItem.EditRect);
                                g.FillRectangle(new SolidBrush(Color.FromArgb(240, 245, 249)), subsubItem.DeleteRect);

                                //繪製編輯按扭
                                g.DrawImage(this.TimeLineIcons.Images[0], subsubItem.EditRect);
                                //繪製刪除按扭
                                g.DrawImage(this.TimeLineIcons.Images[1], subsubItem.DeleteRect);
                            }                            

                        }
                    }                   

                }

            }

            StringFormat sf2 = new StringFormat();
            sf2.LineAlignment = StringAlignment.Center;
            Rectangle itemTitleRect = new Rectangle(56, bound.Top + 5, bound.Width - 64, 36);
            //g.DrawRectangle(new Pen(Color.Orange), itemTitleRect);
            //g.DrawString("共計 " + (item.List == null ? 0 : item.List.Count()) + " 個事項", this.Font, Brushes.Black, itemTitleRect, sf2);

        }

  繪製滾動條這裡先貼一張不帶上下箭頭的滾動條截圖  

 主要涉及到的計算如下: 

 /// <summary>
        /// 計算 Thumb 的高
        /// </summary>
        /// <returns></returns>
        private int GetThumbHeight()
        {
            int disHeight = this.BorderStyle == NBorderStyle.None ? this.Height : this.Height - 2;
            if (MaxnumHeight == 0 || MaxnumHeight <= disHeight) return disHeight;
            int thumbHeight = (int)(disHeight * 1.0d / MaxnumHeight * disHeight);
            if (thumbHeight < 20) thumbHeight = 20;
            largeChange = DisplayRectangle.Height - thumbHeight;
            return thumbHeight;
        }

        /// <summary>
        /// 繪製 Thumb 及計算 Thumb 的位置
        /// </summary>
        /// <param name="g"></param>
        private void DrawScrollThumb(Graphics g)
        {
            int thumbOffsetY = (int)(scrollerBarValue * 1.0 / scrollerBarMaxnum * (scrollerRect.Height - thumbRect.Height));
            thumbRect = new Rectangle(scrollerRect.X, scrollerRect.Y + thumbOffsetY, scrollerRect.Width, scrollerThumbHeight);
            g.FillRectangle(Brushes.Gray, thumbRect);
        }

        /// <summary>
        /// 計算所以元素 累加起來總的高度(包話不可見部分)
        /// </summary>
        private int MaxnumHeight
        {
            get
            {
                int maxnum = 0;
                Graphics g = null;
                if (this.DataList != null)
                {
                    for (int i = 0; i < this.DataList.Count; i++)
                    {
                        var item = this.DataList[i];
                        maxnum += MeasureItemBound(g, i, item).Height;
                    }
                }
                return maxnum;
            }
        }

  第四步、處理控制項中的事件

  首先我們重寫一個Click事件對外開放,usercontrol 自控制項本身就有一個click事件,這裡我們在定義事件的屬性前面添加 new 關鍵字表達用新的事件屬性來替換掉原有的click事件。代碼如下:  

 private static readonly object itemEventObject = new object();      
        /// <summary>
        /// 重寫一個Click事件對外開放
        /// </summary>
        public new event EventHandler<TimeLineEventArgs> Click
        {
            add { Events.AddHandler(itemEventObject, value); }
            remove { Events.RemoveHandler(itemEventObject, value); }
        }

  這裡可以看到我們定義了一個 TimeLineEventArgs 實體類,裡面主要有 Command 命令 Data 數據兩個屬性,在控制項內類我們通過判斷點擊滑鼠的位置來判斷觸發哪個元素,哪種操

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

-Advertisement-
Play Games
更多相關文章
  • hashlib模塊 用於加密相關的操作,3.x里代替了md5模塊和sha模塊 加密功能 運行結果(將hello轉換成一個密文): ...
  • SQLite是一款輕型的資料庫,是遵守ACID的關係型資料庫管理系統。 不像常見的客戶-伺服器範例,SQLite引擎不是個程式與之通信的獨立進程,而是連接到程式中成為它的一個主要部分。所以主要的通信協議是在編程語言內的直接API調用。 Python標準庫包含一個SQLite包裝器:使用模塊sql... ...
  • 最近培訓新人,最後練習使用Struts2框架練習,但是聯繫中碰到了畫面List對象傳後臺無法傳遞的問題。網上看了一圈沒有找到對應的解決辦法。最後自己找到了一種,寫下來後面可以再看。 註:方法千千萬,有其他能解決的,或者我寫的有不對的地方歡迎指正! 上圖表格裡面的就是我想上傳到後臺的內容,因為下麵的更 ...
  • 電腦與程式設計 1.根據一系列指令對數據進行處理的工具或機器 (編程語言) 2.特征: 2.1可以進行數據計算 2.2 根據指令執行任務 3.組成 3.1運算器+控制器(CPU) 3.2存儲器(記憶體及硬碟)——>記憶體:臨時;硬碟:永久 3.3輸入設備和輸出設備 4.工作過程(IPO) 4.1輸入( ...
  • ACM-Jesus Is Here[遞推]2015沈陽online ...
  • 一、分治策略 “分而治之”,大問題能夠拆成相似的小問題,記住這些小問題需要具有相似性。而後將小問題的每個解合成為大問題的解。所以說大問題如何拆,小問題如何合併才是這個演算法最主要的一個思想。實際上很多演算法如貪心演算法,動態規劃等等都是要求把大問題拆成小問題。而分治演算法的重要一點就是要適用於能夠重新把小問 ...
  • 課時一、python運行方式 1、windows命令提示符打開python的方法:首先windows+r輸入cmd打開命令提示符,在cmd輸入python或者py 2、腳本方式:步驟1:編寫腳本代碼,保存為 *.py 步驟2:命令提示符 python *.py 課時二、Python變數、數據類型及存 ...
  • .NET Core CSharp 中級篇2 7 本節內容為文件操作 簡介 文件操作在我們C 里還是比較常見的,例如我們讀取Excel、Txt文件的內容,在程式中,這些文件都是以流的方式讀取進入我們記憶體中,本節內容會詳細的講解有關文件的操作的方法。 我們從最大的驅動器開始往文件這個最小的開始講解 驅動 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...