From 01e213d741ce2afbe8430f4b64e6634d2e3f7afa Mon Sep 17 00:00:00 2001
From: Dong Bin <14807942+rabbitism@users.noreply.github.com>
Date: Thu, 19 Mar 2026 13:28:13 +0800
Subject: [PATCH] Implement DataGrid V12 (#770)
* feat: bring datagrid demo back.
* feat: add FunctionalColorGroupControl and ShadowGroupControl with data binding
* Add 10 TFM
Co-authored-by: Zhang Dian <54255897+zdpcdt@users.noreply.github.com>
* feat: Use new property
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: fix changelog.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: fix sort member path.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* chore: remove useless property.
---------
Co-authored-by: Zhang Dian <54255897+zdpcdt@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---
demo/Directory.Packages.props | 2 +-
demo/Semi.Avalonia.Demo/App.axaml | 2 +-
.../Pages/DataGridDemo.axaml | 179 ++++++++++
.../Pages/DataGridDemo.axaml.cs | 15 +
.../Pages/HighContrastDemo.axaml | 327 ++++++++++++++++++
.../Pages/HighContrastDemo.axaml.cs | 25 ++
.../Pages/VariablesDemo.axaml | 103 ++++++
.../Pages/VariablesDemo.axaml.cs | 25 ++
.../Semi.Avalonia.Demo.csproj | 8 +-
.../Themes/FunctionalColorGroupControl.axaml | 152 ++++++++
.../Themes/ShadowGroupControl.axaml | 110 ++++++
demo/Semi.Avalonia.Demo/Themes/_index.axaml | 2 +
.../ViewModels/DataGridDemoViewModel.cs | 154 +++++++++
.../ViewModels/VariablesDemoViewModel.cs | 151 ++++++++
demo/Semi.Avalonia.Demo/Views/MainView.axaml | 100 ++----
src/Directory.Packages.props | 2 +-
.../Semi.Avalonia.DataGrid.csproj | 6 +-
17 files changed, 1289 insertions(+), 74 deletions(-)
create mode 100644 demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml
create mode 100644 demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml.cs
create mode 100644 demo/Semi.Avalonia.Demo/Pages/HighContrastDemo.axaml
create mode 100644 demo/Semi.Avalonia.Demo/Pages/HighContrastDemo.axaml.cs
create mode 100644 demo/Semi.Avalonia.Demo/Pages/VariablesDemo.axaml
create mode 100644 demo/Semi.Avalonia.Demo/Pages/VariablesDemo.axaml.cs
create mode 100644 demo/Semi.Avalonia.Demo/Themes/FunctionalColorGroupControl.axaml
create mode 100644 demo/Semi.Avalonia.Demo/Themes/ShadowGroupControl.axaml
create mode 100644 demo/Semi.Avalonia.Demo/ViewModels/DataGridDemoViewModel.cs
create mode 100644 demo/Semi.Avalonia.Demo/ViewModels/VariablesDemoViewModel.cs
diff --git a/demo/Directory.Packages.props b/demo/Directory.Packages.props
index 79a3188..7703940 100644
--- a/demo/Directory.Packages.props
+++ b/demo/Directory.Packages.props
@@ -2,7 +2,7 @@
true
12.0.0-preview2
- 11.3.10
+ 12.0.0-preview2-2
3.119.1
diff --git a/demo/Semi.Avalonia.Demo/App.axaml b/demo/Semi.Avalonia.Demo/App.axaml
index 5bb5dcb..76a705d 100644
--- a/demo/Semi.Avalonia.Demo/App.axaml
+++ b/demo/Semi.Avalonia.Demo/App.axaml
@@ -10,7 +10,7 @@
-
+
diff --git a/demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml b/demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml
new file mode 100644
index 0000000..8fb13a9
--- /dev/null
+++ b/demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml.cs b/demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml.cs
new file mode 100644
index 0000000..1de46c8
--- /dev/null
+++ b/demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml.cs
@@ -0,0 +1,15 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Semi.Avalonia.Demo.ViewModels;
+
+namespace Semi.Avalonia.Demo.Pages;
+
+public partial class DataGridDemo : UserControl
+{
+ public DataGridDemo()
+ {
+ InitializeComponent();
+ DataContext = new DataGridDemoViewModel();
+ }
+}
\ No newline at end of file
diff --git a/demo/Semi.Avalonia.Demo/Pages/HighContrastDemo.axaml b/demo/Semi.Avalonia.Demo/Pages/HighContrastDemo.axaml
new file mode 100644
index 0000000..fd7b76c
--- /dev/null
+++ b/demo/Semi.Avalonia.Demo/Pages/HighContrastDemo.axaml
@@ -0,0 +1,327 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/Semi.Avalonia.Demo/Pages/HighContrastDemo.axaml.cs b/demo/Semi.Avalonia.Demo/Pages/HighContrastDemo.axaml.cs
new file mode 100644
index 0000000..fb3a50a
--- /dev/null
+++ b/demo/Semi.Avalonia.Demo/Pages/HighContrastDemo.axaml.cs
@@ -0,0 +1,25 @@
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using Avalonia.Input.Platform;
+using Semi.Avalonia.Demo.ViewModels;
+
+namespace Semi.Avalonia.Demo.Pages;
+
+public partial class HighContrastDemo : UserControl
+{
+ public HighContrastDemo()
+ {
+ InitializeComponent();
+ this.DataContext = new HighContrastDemoViewModel();
+ }
+
+ public async Task Copy(object? o)
+ {
+ if (o is null) return;
+ var toplevel = TopLevel.GetTopLevel(this);
+ if (toplevel?.Clipboard is { } c)
+ {
+ await c.SetTextAsync(o.ToString());
+ }
+ }
+}
\ No newline at end of file
diff --git a/demo/Semi.Avalonia.Demo/Pages/VariablesDemo.axaml b/demo/Semi.Avalonia.Demo/Pages/VariablesDemo.axaml
new file mode 100644
index 0000000..fe9bdaa
--- /dev/null
+++ b/demo/Semi.Avalonia.Demo/Pages/VariablesDemo.axaml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/Semi.Avalonia.Demo/Pages/VariablesDemo.axaml.cs b/demo/Semi.Avalonia.Demo/Pages/VariablesDemo.axaml.cs
new file mode 100644
index 0000000..78e5db4
--- /dev/null
+++ b/demo/Semi.Avalonia.Demo/Pages/VariablesDemo.axaml.cs
@@ -0,0 +1,25 @@
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using Avalonia.Input.Platform;
+using Semi.Avalonia.Demo.ViewModels;
+
+namespace Semi.Avalonia.Demo.Pages;
+
+public partial class VariablesDemo : UserControl
+{
+ public VariablesDemo()
+ {
+ InitializeComponent();
+ this.DataContext = new VariablesDemoViewModel();
+ }
+
+ public async Task Copy(object? o)
+ {
+ if (o is null) return;
+ var toplevel = TopLevel.GetTopLevel(this);
+ if (toplevel?.Clipboard is { } c)
+ {
+ await c.SetTextAsync(o.ToString());
+ }
+ }
+}
\ No newline at end of file
diff --git a/demo/Semi.Avalonia.Demo/Semi.Avalonia.Demo.csproj b/demo/Semi.Avalonia.Demo/Semi.Avalonia.Demo.csproj
index 30203ee..2b85fe5 100644
--- a/demo/Semi.Avalonia.Demo/Semi.Avalonia.Demo.csproj
+++ b/demo/Semi.Avalonia.Demo/Semi.Avalonia.Demo.csproj
@@ -13,7 +13,7 @@
-
+
None
@@ -25,11 +25,7 @@
-
+
-
-
-
-
diff --git a/demo/Semi.Avalonia.Demo/Themes/FunctionalColorGroupControl.axaml b/demo/Semi.Avalonia.Demo/Themes/FunctionalColorGroupControl.axaml
new file mode 100644
index 0000000..1b65098
--- /dev/null
+++ b/demo/Semi.Avalonia.Demo/Themes/FunctionalColorGroupControl.axaml
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/Semi.Avalonia.Demo/Themes/ShadowGroupControl.axaml b/demo/Semi.Avalonia.Demo/Themes/ShadowGroupControl.axaml
new file mode 100644
index 0000000..5028edf
--- /dev/null
+++ b/demo/Semi.Avalonia.Demo/Themes/ShadowGroupControl.axaml
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/Semi.Avalonia.Demo/Themes/_index.axaml b/demo/Semi.Avalonia.Demo/Themes/_index.axaml
index c006588..cfccca3 100644
--- a/demo/Semi.Avalonia.Demo/Themes/_index.axaml
+++ b/demo/Semi.Avalonia.Demo/Themes/_index.axaml
@@ -2,6 +2,8 @@
+
+
\ No newline at end of file
diff --git a/demo/Semi.Avalonia.Demo/ViewModels/DataGridDemoViewModel.cs b/demo/Semi.Avalonia.Demo/ViewModels/DataGridDemoViewModel.cs
new file mode 100644
index 0000000..eae835b
--- /dev/null
+++ b/demo/Semi.Avalonia.Demo/ViewModels/DataGridDemoViewModel.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using Avalonia.Collections;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+
+namespace Semi.Avalonia.Demo.ViewModels;
+
+public class DataGridDemoViewModel : ObservableObject
+{
+ public ObservableCollection GridData1 { get; set; }
+
+ public DataGridCollectionView GridData2 { get; set; }
+
+ public ObservableCollection GridData3 { get; set; }
+
+ public RelayCommand AddCommand { get; set; }
+
+ public DataGridDemoViewModel()
+ {
+ GridData1 = new ObservableCollection(Song.Songs);
+ GridData2 = new DataGridCollectionView(Song.Songs);
+ GridData2.GroupDescriptions.Add(new DataGridPathGroupDescription("Album"));
+ GridData3 = new ObservableCollection(Song.Songs.Take(10).Select(a => new SongViewModel()
+ {
+ Title = a.Title,
+ Artist = a.Artist,
+ Album = a.Album,
+ CountOfComment = a.CountOfComment,
+ IsSelected = false
+ }));
+ AddCommand = new RelayCommand(Add);
+ }
+
+ private void Add()
+ {
+ GridData3.Add(new SongViewModel());
+ }
+}
+
+public class Song
+{
+ public string? Title { get; set; }
+ public string? Artist { get; set; }
+ public TimeSpan? Duration { get; set; }
+ public string? Album { get; set; }
+ public int CountOfComment { get; set; }
+ public string Url { get; set; }
+
+ public Song(string title, string artist, int m, int s, string album, int countOfComment, int netEaseId)
+ {
+ Title = title;
+ Artist = artist;
+ Duration = new TimeSpan(0, m, s);
+ Album = album;
+ CountOfComment = countOfComment;
+ Url = $"https://music.163.com/song?id={netEaseId}";
+ }
+
+ public static List Albums =>
+ [
+ "A.S.I.A",
+ "饕餮人间",
+ "七步咙咚呛",
+ "大惊小怪",
+ "The ONE",
+ "以梦为马 (壮志骄阳版)",
+ "emo了",
+ "一眼万年",
+ "冲刺吧",
+ "爱的赏味期限",
+ "COSMIC ANTHEM / 手紙",
+ "世界晚安",
+ "明年也要好好长大",
+ "320万年前",
+ "W.O.R.L.D."
+ ];
+
+ public static List Songs =>
+ [
+ new("好肚有肚(feat.李玲玉)", "熊猫堂ProducePandas", 2, 50, "A.S.I.A", 730, 1487039339),
+ new("荒诞秀", "熊猫堂ProducePandas", 3, 15, "A.S.I.A", 639, 1487037601),
+ new("长大", "熊猫堂ProducePandas", 4, 6, "A.S.I.A", 1114, 1487037690),
+ new("招财猫(feat.纪粹希(G-Tracy))", "熊猫堂ProducePandas", 3, 37, "A.S.I.A", 361, 1487039632),
+ new("千转", "熊猫堂ProducePandas", 4, 0, "A.S.I.A", 1115, 1477312398),
+ new("辣辣辣", "熊猫堂ProducePandas", 3, 24, "A.S.I.A", 1873, 1465043716),
+ new("碎碎念", "熊猫堂ProducePandas", 3, 25, "A.S.I.A", 676, 1474142064),
+ new("盘他", "熊猫堂ProducePandas", 2, 16, "A.S.I.A", 365, 1481652786),
+ new("Na Na Na", "熊猫堂ProducePandas", 3, 26, "A.S.I.A", 312, 1469022662),
+ new("Indigo", "熊猫堂ProducePandas", 3, 15, "A.S.I.A", 137, 1487039517),
+ new("饕餮人间", "熊猫堂ProducePandas", 3, 20, "饕餮人间", 1295, 1499584605),
+ new("七步咙咚呛", "熊猫堂ProducePandas", 3, 10, "七步咙咚呛", 175, 1809095152),
+ new("大惊小怪", "熊猫堂ProducePandas", 3, 32, "大惊小怪", 10420, 1847477425),
+ new("工具人", "熊猫堂ProducePandas", 2, 46, "大惊小怪", 1135, 1847476499),
+ new("以梦为马", "熊猫堂ProducePandas", 4, 19, "大惊小怪", 18361, 1836034373),
+ new("以梦为马(Piano Version)", "熊猫堂ProducePandas", 3, 4, "大惊小怪", 570, 1847477423),
+ new("The ONE", "熊猫堂ProducePandas", 2, 58, "The ONE", 1508, 1864329424),
+ new("The ONE(日文版)", "熊猫堂ProducePandas", 2, 57, "The ONE", 385, 1864329429),
+ new("以梦为马 (壮志骄阳版)", "熊猫堂ProducePandas", 4, 19, "以梦为马 (壮志骄阳版)", 161, 1865138896),
+ new("New Horse", "熊猫堂ProducePandas", 2, 30, "emo了", 643, 1887021307),
+ new("不例外", "熊猫堂ProducePandas", 3, 31, "emo了", 1818, 1887022665),
+ new("满意", "熊猫堂ProducePandas", 4, 32, "emo了", 1081, 1882433472),
+ new("就算与全世界为敌也要跟你在一起", "熊猫堂ProducePandas", 3, 32, "emo了", 2119, 1881759960),
+ new("The ONE", "熊猫堂ProducePandas", 2, 58, "emo了", 67, 1887022648),
+ new("口香糖", "熊猫堂ProducePandas", 3, 10, "emo了", 2181, 1885502254),
+ new("Suuuuuuper Mario", "熊猫堂ProducePandas", 3, 32, "emo了", 1010, 1887021318),
+ new("饕餮人间", "熊猫堂ProducePandas", 3, 22, "emo了", 109, 1887021320),
+ new("以梦为马 (壮志骄阳版)", "熊猫堂ProducePandas", 4, 21, "emo了", 34, 1887022666),
+ new("The ONE(日文版)", "熊猫堂ProducePandas", 2, 57, "emo了", 27, 1887022646),
+ new("满意(DJheap九天版)", "熊猫堂ProducePandas", 4, 31, "emo了", 31, 1901605941),
+ new("一眼万年", "熊猫堂ProducePandas", 3, 54, "一眼万年", 20, 1922599361),
+ new("冲刺", "熊猫堂ProducePandas", 3, 49, "冲刺吧", 1006, 1932878194),
+ new("滴答滴", "熊猫堂ProducePandas", 2, 30, "爱的赏味期限", 86, 1957515790),
+ new("热带季风", "熊猫堂ProducePandas", 2, 45, "爱的赏味期限", 212, 1957514964),
+ new("渣", "熊猫堂ProducePandas", 3, 28, "爱的赏味期限", 22, 1957514965),
+ new("独特", "熊猫堂ProducePandas", 3, 33, "爱的赏味期限", 62, 1957514966),
+ new("雨后", "熊猫堂ProducePandas", 4, 15, "爱的赏味期限", 23, 1957514967),
+ new("然后然后", "熊猫堂ProducePandas", 3, 50, "爱的赏味期限", 108, 1957514968),
+ new("丢", "熊猫堂ProducePandas", 3, 26, "爱的赏味期限", 30, 1957515792),
+ new("热带疾风(FACEVOID桃心连哥 Remix)", "熊猫堂ProducePandas", 3, 23, "爱的赏味期限", 55, 1957515793),
+ new("COSMIC ANTHEM -Japanese Ver.-", "熊猫堂ProducePandas", 3, 11, "COSMIC ANTHEM / 手紙", 0, 1977171493),
+ new("手紙 (「長大-You Raise Me Up-」-Japanese Ver.-)", "熊猫堂ProducePandas", 4, 11, "COSMIC ANTHEM / 手紙", 0,
+ 1977171494),
+ new("COSMIC ANTHEM -Chinese Ver.-", "熊猫堂ProducePandas", 3, 31, "COSMIC ANTHEM / 手紙", 0, 1977172202),
+ new("世界晚安", "熊猫堂ProducePandas", 2, 59, "世界晚安", 652, 1985063377),
+ new("世界晚安(泰文版)", "熊猫堂ProducePandas", 2, 59, "世界晚安", 134, 1987842504),
+ new("世界晚安(钢琴版)", "熊猫堂ProducePandas", 3, 2, "世界晚安", 76, 1990475933),
+ new("世界晚安(泰文钢琴版)", "熊猫堂ProducePandas", 3, 2, "世界晚安", 29, 1990475934),
+ new("世界晚安(DJ沈念版)", "熊猫堂ProducePandas", 3, 9, "世界晚安", 34, 2014263184),
+ new("世界晚安(钢琴配乐)", "熊猫堂ProducePandas", 2, 59, "世界晚安", 11, 2014263185),
+ new("明年也要好好长大", "熊猫堂ProducePandas", 3, 12, "明年也要好好长大", 0, 2010515162),
+ new("320万年前(DJ沈念版)", "熊猫堂ProducePandas", 3, 21, "320万年前", 8, 2055888636),
+ new("320万年前", "熊猫堂ProducePandas", 3, 7, "W.O.R.L.D.", 329, 2049770469),
+ new("隐德来希", "熊猫堂ProducePandas", 3, 3, "W.O.R.L.D.", 594, 2061317924),
+ new("孔明", "熊猫堂ProducePandas", 3, 59, "W.O.R.L.D.", 91, 2063175274),
+ new("锦鲤卟噜噜", "熊猫堂ProducePandas", 3, 5, "W.O.R.L.D.", 67, 2059208262),
+ new("指鹿为马", "熊猫堂ProducePandas", 3, 12, "W.O.R.L.D.", 74, 2063175272),
+ new("热带季风Remix", "熊猫堂ProducePandas", 3, 22, "W.O.R.L.D.", 23, 2063173319),
+ new("加州梦境", "熊猫堂ProducePandas", 2, 56, "W.O.R.L.D.", 1662, 2063173324),
+ new("渐近自由", "熊猫堂ProducePandas", 4, 19, "W.O.R.L.D.", 124, 2063173321),
+ new("世界所有的烂漫", "熊猫堂ProducePandas", 3, 30, "W.O.R.L.D.", 335, 2053388775)
+ ];
+}
+
+public partial class SongViewModel : ObservableObject
+{
+ [ObservableProperty] private string? _title;
+ [ObservableProperty] private string? _artist;
+ [ObservableProperty] private string? _album;
+ [ObservableProperty] private int _countOfComment;
+ [ObservableProperty] private bool? _isSelected;
+}
\ No newline at end of file
diff --git a/demo/Semi.Avalonia.Demo/ViewModels/VariablesDemoViewModel.cs b/demo/Semi.Avalonia.Demo/ViewModels/VariablesDemoViewModel.cs
new file mode 100644
index 0000000..cea2ab4
--- /dev/null
+++ b/demo/Semi.Avalonia.Demo/ViewModels/VariablesDemoViewModel.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using Avalonia;
+using Avalonia.Collections;
+using Avalonia.Controls;
+using Avalonia.Media;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Semi.Avalonia.Tokens;
+
+namespace Semi.Avalonia.Demo.ViewModels;
+
+public partial class VariablesDemoViewModel : ObservableObject
+{
+ public DataGridCollectionView GridData { get; set; }
+ [ObservableProperty] private string _searchText = string.Empty;
+
+ public VariablesDemoViewModel()
+ {
+ IResourceDictionary dictionary = new Variables();
+ foreach (var token in Tokens)
+ {
+ if (token.ResourceKey is not null && dictionary.TryGetValue(token.ResourceKey, out var value))
+ {
+ token.Type = value?.GetType();
+ token.Value = GetValueString(value);
+ }
+ }
+
+ GridData = new DataGridCollectionView(Tokens);
+ GridData.GroupDescriptions.Add(new DataGridPathGroupDescription(nameof(VariableItem.Category)));
+ }
+
+ private static string? GetValueString(object? value)
+ {
+ if (value is null) return string.Empty;
+
+ return value switch
+ {
+ double d => d.ToString(CultureInfo.InvariantCulture),
+ CornerRadius c => c.IsUniform ? $"{c.TopLeft}" : c.ToString(),
+ Thickness t => t.IsUniform ? $"{t.Left}" : t.ToString(),
+ FontWeight fontWeight => Convert.ToInt32(fontWeight).ToString(),
+ FontFamily fontFamily => fontFamily.FamilyNames.ToString(),
+ _ => value.ToString()
+ };
+ }
+
+ partial void OnSearchTextChanged(string value)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ GridData.Filter = _ => true;
+ GridData.Refresh();
+ return;
+ }
+
+ var search = value.Trim();
+ GridData.Filter = item =>
+ {
+ if (item is not VariableItem variableItem) return false;
+ return (variableItem.Category?.Contains(search, StringComparison.InvariantCultureIgnoreCase) ?? false) ||
+ (variableItem.ResourceKey?.Contains(search, StringComparison.InvariantCultureIgnoreCase) ?? false) ||
+ (variableItem.Value?.Contains(search, StringComparison.InvariantCultureIgnoreCase) ?? false) ||
+ (variableItem.Type?.Name.Contains(search, StringComparison.InvariantCultureIgnoreCase) ?? false) ||
+ (variableItem.Description?.Contains(search, StringComparison.InvariantCultureIgnoreCase) ?? false);
+ };
+ GridData.Refresh();
+ }
+
+ private static List Tokens { get; set; } =
+ [
+ new("Height", "SemiHeightControlSmall"),
+ new("Height", "SemiHeightControlDefault"),
+ new("Height", "SemiHeightControlLarge"),
+ new("Icon Size", "SemiWidthIconExtraSmall"),
+ new("Icon Size", "SemiWidthIconSmall"),
+ new("Icon Size", "SemiWidthIconMedium"),
+ new("Icon Size", "SemiWidthIconLarge"),
+ new("Icon Size", "SemiWidthIconExtraLarge"),
+ new("Border CornerRadius Spacing", "SemiBorderRadiusSpacingExtraSmall"),
+ new("Border CornerRadius Spacing", "SemiBorderRadiusSpacingSmall"),
+ new("Border CornerRadius Spacing", "SemiBorderRadiusSpacingMedium"),
+ new("Border CornerRadius Spacing", "SemiBorderRadiusSpacingLarge"),
+ new("Border CornerRadius Spacing", "SemiBorderRadiusSpacingFull"),
+ new("Border CornerRadius", "SemiBorderRadiusExtraSmall"),
+ new("Border CornerRadius", "SemiBorderRadiusSmall"),
+ new("Border CornerRadius", "SemiBorderRadiusMedium"),
+ new("Border CornerRadius", "SemiBorderRadiusLarge"),
+ new("Border CornerRadius", "SemiBorderRadiusFull"),
+ new("Border Spacing", "SemiBorderSpacing"),
+ new("Border Spacing", "SemiBorderSpacingControl"),
+ new("Border Spacing", "SemiBorderSpacingControlFocus"),
+ new("Border Thickness", "SemiBorderThickness"),
+ new("Border Thickness", "SemiBorderThicknessControl"),
+ new("Border Thickness", "SemiBorderThicknessControlFocus"),
+ new("Spacing", "SemiSpacingNone"),
+ new("Spacing", "SemiSpacingSuperTight"),
+ new("Spacing", "SemiSpacingExtraTight"),
+ new("Spacing", "SemiSpacingTight"),
+ new("Spacing", "SemiSpacingBaseTight"),
+ new("Spacing", "SemiSpacingBase"),
+ new("Spacing", "SemiSpacingBaseLoose"),
+ new("Spacing", "SemiSpacingLoose"),
+ new("Spacing", "SemiSpacingExtraLoose"),
+ new("Spacing", "SemiSpacingSuperLoose"),
+ new("Thickness", "SemiThicknessNone"),
+ new("Thickness", "SemiThicknessSuperTight"),
+ new("Thickness", "SemiThicknessExtraTight"),
+ new("Thickness", "SemiThicknessTight"),
+ new("Thickness", "SemiThicknessBaseTight"),
+ new("Thickness", "SemiThicknessBase"),
+ new("Thickness", "SemiThicknessBaseLoose"),
+ new("Thickness", "SemiThicknessLoose"),
+ new("Thickness", "SemiThicknessExtraLoose"),
+ new("Thickness", "SemiThicknessSuperLoose"),
+ new("FontSize", "SemiFontSizeSmall"),
+ new("FontSize", "SemiFontSizeRegular"),
+ new("FontSize", "SemiFontSizeHeader6"),
+ new("FontSize", "SemiFontSizeHeader5"),
+ new("FontSize", "SemiFontSizeHeader4"),
+ new("FontSize", "SemiFontSizeHeader3"),
+ new("FontSize", "SemiFontSizeHeader2"),
+ new("FontSize", "SemiFontSizeHeader1"),
+ new("FontWeight", "SemiFontWeightLight"),
+ new("FontWeight", "SemiFontWeightRegular"),
+ new("FontWeight", "SemiFontWeightBold"),
+ new("FontFamily", "SemiFontFamilyRegular"),
+ ];
+}
+
+public class VariableItem()
+{
+ public string? Category { get; set; }
+ public string? ResourceKey { get; set; }
+ public Type? Type { get; set; }
+ public string? Value { get; set; }
+ public string? Description { get; set; }
+
+ public VariableItem(string category, string resourceKey, string description = "") : this()
+ {
+ Category = category;
+ ResourceKey = resourceKey;
+ Description = description;
+ }
+
+ public string CopyText =>
+ $"""
+
+ """;
+}
\ No newline at end of file
diff --git a/demo/Semi.Avalonia.Demo/Views/MainView.axaml b/demo/Semi.Avalonia.Demo/Views/MainView.axaml
index f6304d0..0a6d806 100644
--- a/demo/Semi.Avalonia.Demo/Views/MainView.axaml
+++ b/demo/Semi.Avalonia.Demo/Views/MainView.axaml
@@ -16,9 +16,9 @@
@@ -37,48 +37,44 @@
+ Content="{StaticResource SemiIconSidebar}"
+ Theme="{DynamicResource IconBorderlessToggleSwitch}" />
-
+
-
+
+ Content="{StaticResource SemiIconGlobe}"
+ Theme="{DynamicResource IconBorderlessButton}" />
+ Content="{StaticResource SemiIconGithubLogo}"
+ Theme="{DynamicResource IconBorderlessButton}" />
+ Theme="{DynamicResource IconBorderlessToggleSwitch}" />
-