PropertyGrid自定義控制項

来源:http://www.cnblogs.com/LinWorld/archive/2017/06/03/6876860.html
-Advertisement-
Play Games

PropertyGrid是一個很強大的控制項,使用該控制項做屬性設置面板的一個好處就是你只需要專註於代碼而無需關註UI的呈現,PropertyGrid會預設根據變數類型選擇合適的控制項顯示。但是這也帶來了一個問題,就是控制項的使用變得不是特別靈活,主要表現在你無法根據你的需求很好的選擇控制項,比如當你需要用S... ...


PropertyGrid是一個很強大的控制項,使用該控制項做屬性設置面板的一個好處就是你只需要專註於代碼而無需關註UI的呈現,PropertyGrid會預設根據變數類型選擇合適的控制項顯示。但是這也帶來了一個問題,就是控制項的使用變得不是特別靈活,主要表現在你無法根據你的需求很好的選擇控制項,比如當你需要用Slider控制項來設置int型變數時,PropertyGrid預設的模板選擇器是不支持的。網上找了許多資料基本都是介紹WinForm的實現方式,主要用到了IWindowFromService這個介面,並未找到合適的適合WPF的Demo,後來在參考了DEVExpress的官方Demo之後我做了一個基於WPF和DEV 16.2的PropertyGrid Demo,基本實現了上述功能。

為了實現這一點,需要自定義一個DataTemplateSeletor類,這也是本文的核心代碼。

1.創建一個CustomPropertyGrid自定義控制項:

 1 <UserControl
 2     x:Class="PropertyGridDemo.PropertyGridControl.CustomPropertyGrid"
 3     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 4     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 5     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 6     xmlns:dxprg="http://schemas.devexpress.com/winfx/2008/xaml/propertygrid"
 7     xmlns:local="clr-namespace:PropertyGridDemo.PropertyGridControl"
 8     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 9     d:DesignHeight="300"
10     d:DesignWidth="300"
11     mc:Ignorable="d">
12     <UserControl.Resources>
13         <ResourceDictionary>
14             <ResourceDictionary.MergedDictionaries>
15                 <!--  資源字典  -->
16                 <ResourceDictionary Source="../PropertyGridControl/DynamicallyAssignDataEditorsResources.xaml" />
17             </ResourceDictionary.MergedDictionaries>
18         </ResourceDictionary>
19     </UserControl.Resources>
20     <Grid>
21         <!--  PropertyDefinitionStyle:定義屬性描述的風格模板  -->
22         <!--  PropertyDefinitionTemplateSelector:定義一個模板選擇器,對應一個繼承自DataTemplateSelector的類  -->
23         <!--  PropertyDefinitionsSource:定義一個獲取數據屬性集合的類,對應一個自定義類(本Demo中對應DataEditorsViewModel)  -->
24         <dxprg:PropertyGridControl
25             x:Name="PropertyGridControl"
26             Margin="24"
27             DataContextChanged="PropertyGridControl_DataContextChanged"
28             ExpandCategoriesWhenSelectedObjectChanged="True"
29             PropertyDefinitionStyle="{StaticResource DynamicallyAssignDataEditorsPropertyDefinitionStyle}"
30             PropertyDefinitionTemplateSelector="{StaticResource DynamicallyAssignDataEditorsTemplateSelector}"
31             PropertyDefinitionsSource="{Binding Path=Properties, Source={StaticResource DemoDataProvider}}"
32             ShowCategories="True"
33             ShowDescriptionIn="Panel" />
34     </Grid>
35 </UserControl>
CustomPropertyGrid

 該控制項使用的資源字典如下:

 1 <ResourceDictionary
 2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5     xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
 6     xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
 7     xmlns:dxprg="http://schemas.devexpress.com/winfx/2008/xaml/propertygrid"
 8     xmlns:local="clr-namespace:PropertyGridDemo.PropertyGridControl"
 9     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
10     mc:Ignorable="d">
11 
12     <local:DynamicallyAssignDataEditorsTemplateSelector x:Key="DynamicallyAssignDataEditorsTemplateSelector" />
13     <local:DataEditorsViewModel x:Key="DemoDataProvider" />
14 
15     <DataTemplate x:Key="DescriptionTemplate">
16         <RichTextBox
17             x:Name="descriptionRichTextBox"
18             MinWidth="150"
19             HorizontalContentAlignment="Stretch"
20             Background="Transparent"
21             BorderThickness="0"
22             Foreground="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource TemplatedParent}}"
23             IsReadOnly="True"
24             IsTabStop="False" />
25     </DataTemplate>
26     <DataTemplate x:Key="descriptionTemplate">
27         <RichTextBox
28             x:Name="descriptionRichTextBox"
29             MinWidth="150"
30             HorizontalContentAlignment="Stretch"
31             Background="Transparent"
32             BorderThickness="0"
33             Foreground="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource TemplatedParent}}"
34             IsReadOnly="True"
35             IsTabStop="False" />
36     </DataTemplate>
37 
38     <!--  設置控制項的全局樣式和數據綁定  -->
39     <Style x:Key="DynamicallyAssignDataEditorsPropertyDefinitionStyle" TargetType="dxprg:PropertyDefinition">
40         <Setter Property="Path" Value="{Binding Name}" />
41         <!--<Setter Property="Header" Value="{Binding Converter={StaticResource PropertyDescriptorToDisplayNameConverter}}"/>-->
42         <Setter Property="Description" Value="{Binding}" />
43         <Setter Property="DescriptionTemplate" Value="{StaticResource descriptionTemplate}" />
44     </Style>
45     <Style x:Key="DescriptionContainerStyle" TargetType="dxprg:PropertyDescriptionPresenterControl">
46         <Setter Property="ShowSelectedRowHeader" Value="False" />
47         <Setter Property="MinHeight" Value="70" />
48     </Style>
49 
50     <Style TargetType="Slider">
51         <Setter Property="Margin" Value="2" />
52     </Style>
53     <Style TargetType="dxe:ComboBoxEdit">
54         <Setter Property="IsTextEditable" Value="False" />
55         <Setter Property="ApplyItemTemplateToSelectedItem" Value="True" />
56         <Setter Property="Margin" Value="2" />
57     </Style>
58 
59     <!--  測試直接從DataTemplate獲取控制項  -->
60     <DataTemplate x:Key="SliderTemplate" DataType="local:SliderExtend">
61         <!--<dxprg:PropertyDefinition>
62             <dxprg:PropertyDefinition.CellTemplate>-->
63         <!--<DataTemplate>-->
64         <StackPanel x:Name="Root">
65             <Slider
66                 Maximum="{Binding Path=Max}"
67                 Minimum="{Binding Path=Min}"
68                 Value="{Binding Path=Value}" />
69             <TextBlock Text="{Binding Path=Value}" />
70         </StackPanel>
71         <!--</DataTemplate>-->
72         <!--</dxprg:PropertyDefinition.CellTemplate>
73         </dxprg:PropertyDefinition>-->
74     </DataTemplate>
75 
76     <DataTemplate x:Key="ComboBoxEditItemTemplate" DataType="Tuple">
77         <TextBlock
78             Height="20"
79             Margin="5,3,0,0"
80             VerticalAlignment="Center"
81             Text="{Binding Item1}" />
82     </DataTemplate>
83 </ResourceDictionary>
ResourceDictionary

2.編寫對應的模板選擇類 DynamicallyAssignDataEditorsTemplateSelector:

  1 using DevExpress.Xpf.Editors;
  2 using DevExpress.Xpf.PropertyGrid;
  3 using System.ComponentModel;
  4 using System.Reflection;
  5 using System.Windows;
  6 using System.Windows.Controls;
  7 using System.Windows.Data;
  8 
  9 namespace PropertyGridDemo.PropertyGridControl
 10 {
 11     public class DynamicallyAssignDataEditorsTemplateSelector : DataTemplateSelector
 12     {
 13         private PropertyDescriptor _property = null;
 14         private RootPropertyDefinition _element = null;
 15         private PropertyDataContext _propertyDataContext => App.PropertyGridDataContext;
 16 
 17         /// <summary>
 18         /// 當重寫在派生類中,返回根據自定義邏輯的 <see cref="T:System.Windows.DataTemplate" /> 19         /// </summary>
 20         /// <param name="item">數據對象可以選擇模板。</param>
 21         /// <param name="container">數據對象。</param>
 22         /// <returns>
 23         /// 返回 <see cref="T:System.Windows.DataTemplate" /> 或 null。預設值為 null。
 24         /// </returns>
 25         public override DataTemplate SelectTemplate(object item, DependencyObject container)
 26         {
 27             _element = (RootPropertyDefinition)container;
 28             DataTemplate resource = TryCreateResource(item);
 29             return resource ?? base.SelectTemplate(item, container);
 30         }
 31 
 32         /// <summary>
 33         /// Tries the create resource.
 34         /// </summary>
 35         /// <param name="item">The item.</param>
 36         /// <returns></returns>
 37         private DataTemplate TryCreateResource(object item)
 38         {
 39             if (!(item is PropertyDescriptor)) return null;
 40             PropertyDescriptor pd = (PropertyDescriptor)item;
 41             _property = pd;
 42             var customUIAttribute = (CustomUIAttribute)pd.Attributes[typeof(CustomUIAttribute)];
 43             if (customUIAttribute == null) return null;
 44             var customUIType = customUIAttribute.CustomUI;
 45             return CreatePropertyDefinitionTemplate(customUIAttribute);
 46         }
 47 
 48         /// <summary>
 49         /// Gets the data context.
 50         /// </summary>
 51         /// <param name="dataContextPropertyName">Name of the data context property.</param>
 52         /// <returns></returns>
 53         private object GetDataContext(string dataContextPropertyName)
 54         {
 55             PropertyInfo property = _propertyDataContext?.GetType().GetProperty(dataContextPropertyName);
 56             if (property == null) return null;
 57             return property.GetValue(_propertyDataContext, null);
 58         }
 59 
 60         /// <summary>
 61         /// Creates the slider data template.
 62         /// </summary>
 63         /// <param name="customUIAttribute">The custom UI attribute.</param>
 64         /// <returns></returns>
 65         private DataTemplate CreateSliderDataTemplate(CustomUIAttribute customUIAttribute)
 66         {
 67             DataTemplate ct = new DataTemplate();
 68             ct.VisualTree = new FrameworkElementFactory(typeof(StackPanel));
 69             ct.VisualTree.SetValue(StackPanel.DataContextProperty, GetDataContext(customUIAttribute.DataContextPropertyName));
 70 
 71             FrameworkElementFactory sliderFactory = new FrameworkElementFactory(typeof(Slider));
 72             sliderFactory.SetBinding(Slider.MaximumProperty, new Binding(nameof(SliderUIDataContext.Max)));
 73             sliderFactory.SetBinding(Slider.MinimumProperty, new Binding(nameof(SliderUIDataContext.Min)));
 74             sliderFactory.SetBinding(Slider.SmallChangeProperty, new Binding(nameof(SliderUIDataContext.SmallChange)));
 75             sliderFactory.SetBinding(Slider.LargeChangeProperty, new Binding(nameof(SliderUIDataContext.LargeChange)));
 76             sliderFactory.SetBinding(Slider.ValueProperty, new Binding(nameof(SliderUIDataContext.Value)));
 77             ct.VisualTree.AppendChild(sliderFactory);
 78 
 79             FrameworkElementFactory textFacotry = new FrameworkElementFactory(typeof(TextBlock), "TextBlock");
 80             textFacotry.SetValue(TextBlock.TextProperty, new Binding(nameof(SliderUIDataContext.Value)));
 81             //textBoxFactory.AddHandler(TextBox.IsVisibleChanged, new DependencyPropertyChangedEventHandler(SearchBoxVisibleChanged));
 82             ct.VisualTree.AppendChild(textFacotry);
 83             ct.Seal();
 84             return ct;
 85         }
 86 
 87         /// <summary>
 88         /// Creates the ComboBox edit template.
 89         /// </summary>
 90         /// <param name="customUIAttribute">The custom UI attribute.</param>
 91         /// <returns></returns>
 92         private DataTemplate CreateComboBoxEditTemplate(CustomUIAttribute customUIAttribute)
 93         {
 94             DataTemplate template = new DataTemplate();
 95             template.VisualTree = new FrameworkElementFactory(typeof(DockPanel));
 96             template.VisualTree.SetValue(DockPanel.DataContextProperty, GetDataContext(customUIAttribute.DataContextPropertyName));
 97 
 98             FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBlock)) ;
 99             textFactory.SetValue(TextBlock.TextProperty, new Binding(nameof(ComboBoxEditDataContext.Name)));
100             template.VisualTree.AppendChild(textFactory);
101 
102             FrameworkElementFactory comboBoxEditFactory = new FrameworkElementFactory(typeof(ComboBoxEdit));
103             comboBoxEditFactory.SetBinding(ComboBoxEdit.ItemsSourceProperty, new Binding(nameof(ComboBoxEditDataContext.ItemSource)));
104             comboBoxEditFactory.SetBinding(ComboBoxEdit.EditValueProperty, new Binding(nameof(ComboBoxEditDataContext.EditValue)));
105             comboBoxEditFactory.SetBinding(ComboBoxEdit.SelectedIndexProperty, new Binding(nameof(ComboBoxEditDataContext.SelectedIndex)));
106             comboBoxEditFactory.SetValue(ComboBoxEdit.ItemTemplateProperty, (DataTemplate)_element.TryFindResource("ComboBoxEditItemTemplate"));
107             template.VisualTree.AppendChild(comboBoxEditFactory);
108             template.Seal();
109             return template;
110         }
111 
112         /// <summary>
113         /// Creates the property definition template.
114         /// </summary>
115         /// <param name="customUIAttribute">The custom UI attribute.</param>
116         /// <returns></returns>
117         private DataTemplate CreatePropertyDefinitionTemplate(CustomUIAttribute customUIAttribute)
118         {
119             DataTemplate dataTemplate = new DataTemplate();
120             DataTemplate cellTemplate = null;//單元格模板
121             FrameworkElementFactory factory = new FrameworkElementFactory(typeof(PropertyDefinition));
122             dataTemplate.VisualTree = factory;
123             switch (customUIAttribute.CustomUI)
124             {
125                 case CustomUITypes.Slider:
126                     cellTemplate = CreateSliderDataTemplate(customUIAttribute); break;
127                     //cellTemplate = (DataTemplate)_element.TryFindResource("SliderTemplate");break;
128                 case CustomUITypes.ComboBoxEit:
129                     cellTemplate = CreateComboBoxEditTemplate(customUIAttribute);break;
130                 
131             }
132 
133             if (cellTemplate != null)
134             {
135                 factory.SetValue(PropertyDefinition.CellTemplateProperty, cellTemplate);
136                 dataTemplate.Seal();
137 
138             }
139             else
140             {
141                 return null;
142             }
143             return dataTemplate;
144         }
145     }
146 }
DynamicallyAssignDataEditorsTemplateSelector
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace PropertyGridDemo.PropertyGridControl
{
    /// <summary>
    ///初始化所有屬性並調用模板選擇器進行匹配
    /// </summary>
    public class DataEditorsViewModel
    {
        public IEnumerable<PropertyDescriptor> Properties { get { return TypeDescriptor.GetProperties(typeof(TestPropertyGrid)).Cast<PropertyDescriptor>(); } }
    }
}
DataEditorsViewModel

3.編寫一個可用於構建模板的屬性 CustomUIType:

using System;

namespace PropertyGridDemo.PropertyGridControl
{
    public class CustomUIType
    {

    }

    public enum CustomUITypes
    {
        Slider,
        ComboBoxEit,
        SpinEdit,
        CheckBoxEdit
    }

    [AttributeUsage(AttributeTargets.Property)]
    internal class CustomUIAttribute : Attribute
    {
        public string DataContextPropertyName { get; set; }
        public CustomUITypes CustomUI { get; set; }
        /// <summary>
        /// 自定義控制項屬性構造函數
        /// </summary>
        /// <param name="uiTypes">The UI types.</param>
        /// <param name="dataContextPropertyName">Name of the data context property.</param>
        internal CustomUIAttribute(CustomUITypes uiTypes, string dataContextPropertyName)
        {
            CustomUI = uiTypes;
            DataContextPropertyName = dataContextPropertyName;
        }
    }

}
CustomUIType

4.編寫對應的DataContext類 TestPropertyGrid:

  1 using DevExpress.Mvvm.DataAnnotations;
  2 using System;
  3 using System.ComponentModel;
  4 using System.ComponentModel.DataAnnotations;
  5 using System.Timers;
  6 using System.Windows;
  7 
  8 namespace PropertyGridDemo.PropertyGridControl
  9 {
 10     [MetadataType(typeof(DynamicallyAssignDataEditorsMetadata))]
 11     public class TestPropertyGrid : PropertyDataContext
 12     {
 13         private double _count = 0;
 14         private SliderUIDataContext _countSource = null;
 15         private ComboBoxEditDataContext _comboSource = null;
 16         private double _value=1;
 17 
 18         public TestPropertyGrid()
 19         {
 20             Password = "1111111";
 21             Notes = "Hello";
 22             Text = "Hello hi";
 23         }
 24 
 25         [Browsable(false)]
 26         public SliderUIDataContext CountSource
 27         {
 28             get
 29             {
 30                 if (_countSource != null)
 31                 {
 32 
 33                     return _countSource;
 34                 }
 35                 else
 36                 {
 37                     _countSource = new SliderUIDataContext(0, 100, Count, 0.1, 1);
 38                     _countSource.PropertyChanged += (object o, PropertyChangedEventArgs e) =>
 39                     {
 40                         this.Count = _countSource.Value;
 41                     };
 42                     return _countSource;
 43                 }
 44             }
 45         }
 46 
 47         [Browsable(false)]
 48         public ComboBoxEditDataContext ComboSource
 49         {
 50             get
 51             {
 52                 if(_comboSource==null)
 53                 {
 54                     _comboSource =new ComboBoxEditDataContext(ComboBoxEditItemSource.TestItemSource,Value);
 55                     _comboSource.PropertyChanged += (object o, PropertyChangedEventArgs e) =>
 56                       {
 57                           this.Value =Convert.ToDouble(_comboSource.EditValue.Item2); 
 58                       };
 59                    
 60                 }
 61                 return _comboSource;
 62             }
 63         }
 64 
 65         [Display(Name = "SliderEdit", GroupName = "CustomUI")]
 66         [CustomUI(CustomUITypes.Slider, nameof(CountSource))]
 67         public double Count
 68         {
 69             get => _count;
 70             set
 71             {
 72                 _count = value;
 73                 CountSource.Value = value; 
 74                 RaisePropertyChanged(nameof(Count));
 75 

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

-Advertisement-
Play Games
更多相關文章
  • Linux常用基礎命令 1.ls(list 列表) ls:列出當前目錄下的所有文件(不包含隱藏文件) ls-a:列出當前目錄下的所有文件(包含隱藏文件) ls-l:列出當前目錄下的所有文件並顯示詳細信息 ls-a-l ls-l-a ls-al ls-la:以上四種均表示列出當前目錄下的所有文件並顯示 ...
  • 應用進程是:小明 內核:小王 1 阻塞式I/O模型: 小明向小王借大米做飯,小王暫時沒有,但是小明這時候其它啥事情也不做了(例如這時候他還可以洗菜),就在哪裡等的,直到小王有了大米,然後小明把大米搬運到自己家裡,開始做飯。 2 非阻塞式I/O模型: 小明向小王借大米做飯,小王暫時沒有,但是小明這時候 ...
  • Spring.Net包括控制反轉(IoC) 和麵向切麵(AOP),這篇文章主要說下IoC方面的入門。 一、首先建立一個MVC項目名稱叫SpringDemo,然後用NuGet下載spring(我用的是Spring.Net NHibernate 4 support) 二、類設計,在Models文件夾下麵 ...
  • 官方教程 http://www.kuitao8.com/demo/20140224/1/bootstrap-multiselect-master/index.html 使用方法: 第一步引用樣式以及相關JS 第二步 構造Selection(註意 需要設置multiple屬性,否則依然是單選模式,mu ...
  • 因為參加一個小項目,需要對繼電器進行串口控制,所以這兩天學習了基本的串口編程。同事那邊有JAVA的串口通信包,不過是從網上下載的,比較零亂,難以準確掌握串口通信的流程和內含。因此,個人通過學習網上大牛的方法,利用C#實現了基本的串口通信編程。下麵對學習成果進行總結歸納,希望對大家有所幫助。 一、串口 ...
  • 如果在一個類中定義了虛屬性或者虛方法,又在構造函數中訪問了這個虛屬性或方法,那麼很可能會埋下一個安全隱患。 ...
  • 鏈表的相關知識整理 什麼是鏈表 鏈表是一種物理存儲單元上非連續、非順序的存儲結構,數據元素的邏輯順序是通過鏈表中的指針鏈接次序實現的。鏈表由一系列結點(鏈表中每一個元素稱為結點)組成,結點可以在運行時動態生成。每個結點包括兩個部分:一個是存儲數據元素的數據域,另一個是存儲下一個結點地址的指針域。 鏈 ...
  • 背景:二次開發 用的asp.net 三層 伺服器控制項 今天在開發的時候 發現這個系統裡面 很多東西都封裝了 同時也發現了一個問題 Dopostback 這個方法 怎麼使用的 因為我發現 一些html 元素 既沒有給它綁定交互的方法 又不是伺服器控制項 偏偏你點擊了 卻可以交互數據 仔細研究了下 決定於 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...