diff --git a/Semi.Avalonia.sln b/Semi.Avalonia.sln index 41c8732..e41901a 100644 --- a/Semi.Avalonia.sln +++ b/Semi.Avalonia.sln @@ -39,8 +39,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semi.Avalonia.Demo.Drm", "d EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semi.Avalonia.TreeDataGrid", "src\Semi.Avalonia.TreeDataGrid\Semi.Avalonia.TreeDataGrid.csproj", "{398D2998-0835-41F5-99A3-608CAB8051E2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semi.Avalonia.TreeDataGrid.Demo", "demo\Semi.Avalonia.TreeDataGrid.Demo\Semi.Avalonia.TreeDataGrid.Demo.csproj", "{6178B545-4BB6-458C-A27C-EE11F3885D38}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -84,10 +82,6 @@ Global {398D2998-0835-41F5-99A3-608CAB8051E2}.Debug|Any CPU.Build.0 = Debug|Any CPU {398D2998-0835-41F5-99A3-608CAB8051E2}.Release|Any CPU.ActiveCfg = Release|Any CPU {398D2998-0835-41F5-99A3-608CAB8051E2}.Release|Any CPU.Build.0 = Release|Any CPU - {6178B545-4BB6-458C-A27C-EE11F3885D38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6178B545-4BB6-458C-A27C-EE11F3885D38}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6178B545-4BB6-458C-A27C-EE11F3885D38}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6178B545-4BB6-458C-A27C-EE11F3885D38}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -98,7 +92,6 @@ Global {D789AEDB-EBDF-4450-8E8E-B4A03FB257B0} = {43091528-9509-43CB-A003-9C5C11E96DD6} {0C81FC1C-5D2D-478A-9876-923A0C85EC2F} = {43091528-9509-43CB-A003-9C5C11E96DD6} {86D93406-412A-4429-93B2-92AAD0407784} = {43091528-9509-43CB-A003-9C5C11E96DD6} - {6178B545-4BB6-458C-A27C-EE11F3885D38} = {43091528-9509-43CB-A003-9C5C11E96DD6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7CA41ED3-2CED-40CC-AA21-28C3B42B1E86} diff --git a/demo/Semi.Avalonia.Demo/App.axaml b/demo/Semi.Avalonia.Demo/App.axaml index 6af3dda..3f18920 100644 --- a/demo/Semi.Avalonia.Demo/App.axaml +++ b/demo/Semi.Avalonia.Demo/App.axaml @@ -13,6 +13,7 @@ + diff --git a/demo/Semi.Avalonia.TreeDataGrid.Demo/Converters/FileIconConverter.cs b/demo/Semi.Avalonia.Demo/Converters/FileIconConverter.cs similarity index 93% rename from demo/Semi.Avalonia.TreeDataGrid.Demo/Converters/FileIconConverter.cs rename to demo/Semi.Avalonia.Demo/Converters/FileIconConverter.cs index 88acb9d..bdb170d 100644 --- a/demo/Semi.Avalonia.TreeDataGrid.Demo/Converters/FileIconConverter.cs +++ b/demo/Semi.Avalonia.Demo/Converters/FileIconConverter.cs @@ -5,7 +5,7 @@ using Avalonia; using Avalonia.Data.Converters; using Avalonia.Metadata; -namespace Semi.Avalonia.TreeDataGrid.Demo.Converters; +namespace Semi.Avalonia.Demo.Converters; public class FileIconConverter : IMultiValueConverter { diff --git a/demo/Semi.Avalonia.Demo/Pages/TreeDataGridDemo.axaml b/demo/Semi.Avalonia.Demo/Pages/TreeDataGridDemo.axaml new file mode 100644 index 0000000..d6453f4 --- /dev/null +++ b/demo/Semi.Avalonia.Demo/Pages/TreeDataGridDemo.axaml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/Semi.Avalonia.Demo/Pages/TreeDataGridDemo.axaml.cs b/demo/Semi.Avalonia.Demo/Pages/TreeDataGridDemo.axaml.cs new file mode 100644 index 0000000..9da4a82 --- /dev/null +++ b/demo/Semi.Avalonia.Demo/Pages/TreeDataGridDemo.axaml.cs @@ -0,0 +1,23 @@ +using Avalonia.Controls; +using Avalonia.Input; +using Semi.Avalonia.Demo.ViewModels; + +namespace Semi.Avalonia.Demo.Pages; + +public partial class TreeDataGridDemo : UserControl +{ + public TreeDataGridDemo() + { + InitializeComponent(); + this.DataContext = new TreeDataGridDemoViewModel(); + } + + private void SelectedPath_KeyDown(object? sender, KeyEventArgs e) + { + if (e.Key == Key.Enter) + { + var vm = DataContext as TreeDataGridDemoViewModel; + vm.FilesContext.SelectedPath = (sender as TextBox)!.Text; + } + } +} \ 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 6bea869..38a0043 100644 --- a/demo/Semi.Avalonia.Demo/Semi.Avalonia.Demo.csproj +++ b/demo/Semi.Avalonia.Demo/Semi.Avalonia.Demo.csproj @@ -1,6 +1,6 @@  - netstandard2.0 + net8.0 enable latest @@ -25,5 +25,6 @@ + diff --git a/demo/Semi.Avalonia.Demo/ViewModels/DataGridDemoViewModel.cs b/demo/Semi.Avalonia.Demo/ViewModels/DataGridDemoViewModel.cs index e1ec6a7..eae835b 100644 --- a/demo/Semi.Avalonia.Demo/ViewModels/DataGridDemoViewModel.cs +++ b/demo/Semi.Avalonia.Demo/ViewModels/DataGridDemoViewModel.cs @@ -59,6 +59,25 @@ public class Song 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), diff --git a/demo/Semi.Avalonia.TreeDataGrid.Demo/ViewModels/FilesPageViewModel.cs b/demo/Semi.Avalonia.Demo/ViewModels/TreeDataGridDemo/FilesPageViewModel.cs similarity index 78% rename from demo/Semi.Avalonia.TreeDataGrid.Demo/ViewModels/FilesPageViewModel.cs rename to demo/Semi.Avalonia.Demo/ViewModels/TreeDataGridDemo/FilesPageViewModel.cs index 3349363..c3b6ee7 100644 --- a/demo/Semi.Avalonia.TreeDataGrid.Demo/ViewModels/FilesPageViewModel.cs +++ b/demo/Semi.Avalonia.Demo/ViewModels/TreeDataGridDemo/FilesPageViewModel.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -11,43 +12,40 @@ using Avalonia.Controls.Selection; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; -namespace Semi.Avalonia.TreeDataGrid.Demo.ViewModels; +namespace Semi.Avalonia.Demo.ViewModels; -public class FilesPageViewModel: ObservableObject +public partial class FilesPageViewModel : ObservableObject { public IList Drives { get; } - private string _selectedDrive; - private string? _selectedPath; - private FileNodeViewModel? _root; - public string SelectedDrive + public HierarchicalTreeDataGridSource Source { get; } + [ObservableProperty] private string _selectedDrive; + [ObservableProperty] private string? _selectedPath; + [ObservableProperty] private FileNodeViewModel? _root; + + partial void OnSelectedDriveChanged(string value) { - get => _selectedDrive; - set + Root = new FileNodeViewModel(value, true, true); + if (Source is not null) { - SetProperty(ref _selectedDrive, value); - _root = new FileNodeViewModel(_selectedDrive, isDirectory: true, isRoot: true); - Source.Items = new[] { _root }; + Source.Items = [Root]; } } - public string? SelectedPath + partial void OnSelectedPathChanged(string? value) { - get => _selectedPath; - set => SetSelectedPath(value); + SetSelectedPath(value); } - - public HierarchicalTreeDataGridSource Source { get; } public FilesPageViewModel() { - Drives= DriveInfo.GetDrives().Select(x => x.Name).ToList(); + Drives = DriveInfo.GetDrives().Select(x => x.Name).ToList(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - _selectedDrive = "C:\\"; + SelectedDrive = @"C:\"; } else { - _selectedDrive = Drives.FirstOrDefault() ?? "/"; + SelectedDrive = Drives.FirstOrDefault() ?? "/"; } Source = new HierarchicalTreeDataGridSource(Array.Empty()) @@ -58,7 +56,7 @@ public class FilesPageViewModel: ObservableObject null, x => x.IsChecked, (o, v) => o.IsChecked = v, - options: new() + options: new CheckBoxColumnOptions { CanUserResizeColumn = false, }), @@ -68,20 +66,20 @@ public class FilesPageViewModel: ObservableObject "FileNameCell", "FileNameEditCell", new GridLength(1, GridUnitType.Star), - new() + new TemplateColumnOptions { - CompareAscending = FileNodeViewModel.SortAscending(x => x.Name), - CompareDescending = FileNodeViewModel.SortDescending(x => x.Name), + CompareAscending = FileNodeViewModel.SortAscending(vm => vm.Name), + CompareDescending = FileNodeViewModel.SortDescending(vm => vm.Name), IsTextSearchEnabled = true, - TextSearchValueSelector = x => x.Name + TextSearchValueSelector = vm => vm.Name }), - x => x.Children, - x => x.HasChildren, - x => x.IsExpanded), + vm => vm.Children, + vm => vm.HasChildren, + vm => vm.IsExpanded), new TextColumn( "Size", - x => x.Size, - options: new() + vm => vm.Size, + options: new TextColumnOptions { CompareAscending = FileNodeViewModel.SortAscending(x => x.Size), CompareDescending = FileNodeViewModel.SortDescending(x => x.Size), @@ -89,7 +87,7 @@ public class FilesPageViewModel: ObservableObject new TextColumn( "Modified", x => x.Modified, - options: new() + options: new TextColumnOptions { CompareAscending = FileNodeViewModel.SortAscending(x => x.Modified), CompareDescending = FileNodeViewModel.SortDescending(x => x.Modified), @@ -99,18 +97,18 @@ public class FilesPageViewModel: ObservableObject Source.RowSelection!.SingleSelect = false; Source.RowSelection.SelectionChanged += SelectionChanged; } - + private void SelectionChanged(object? sender, TreeSelectionModelSelectionChangedEventArgs e) { var selectedPath = Source.RowSelection?.SelectedItem?.Path; this.SetProperty(ref _selectedPath, selectedPath, nameof(SelectedPath)); foreach (var i in e.DeselectedItems) - System.Diagnostics.Trace.WriteLine($"Deselected '{i?.Path}'"); + Trace.WriteLine($"Deselected '{i?.Path}'"); foreach (var i in e.SelectedItems) - System.Diagnostics.Trace.WriteLine($"Selected '{i?.Path}'"); + Trace.WriteLine($"Selected '{i?.Path}'"); } - + private void SetSelectedPath(string? value) { if (string.IsNullOrEmpty(value)) @@ -164,27 +162,28 @@ public class FilesPageViewModel: ObservableObject } } + Source.Items = [Root]; Source.RowSelection!.SelectedIndex = index; } } -public class FileNodeViewModel: ObservableObject, IEditableObject +public partial class FileNodeViewModel : ObservableObject, IEditableObject { - private string _path; - private string _name; + [ObservableProperty] private string _path; + [ObservableProperty] private string _name; private string? _undoName; - private long? _size; - private DateTimeOffset? _modified; + [ObservableProperty] private long? _size; + [ObservableProperty] private DateTimeOffset? _modified; private FileSystemWatcher? _watcher; private ObservableCollection? _children; - private bool _hasChildren = true; - private bool _isExpanded; + [ObservableProperty] private bool _hasChildren = true; + [ObservableProperty] private bool _isExpanded; - public FileNodeViewModel( string path, bool isDirectory, bool isRoot = false) + public FileNodeViewModel(string path, bool isDirectory, bool isRoot = false) { - _path = path; - _name = isRoot ? path : System.IO.Path.GetFileName(Path); - _isExpanded = isRoot; + Path = path; + Name = isRoot ? path : System.IO.Path.GetFileName(Path); + IsExpanded = isRoot; IsDirectory = isDirectory; HasChildren = isDirectory; @@ -196,42 +195,6 @@ public class FileNodeViewModel: ObservableObject, IEditableObject } } - public string Path - { - get => _path; - private set => SetProperty(ref _path, value); - } - - public string Name - { - get => _name; - private set => SetProperty(ref _name, value); - } - - public long? Size - { - get => _size; - private set => SetProperty(ref _size, value); - } - - public DateTimeOffset? Modified - { - get => _modified; - private set => SetProperty(ref _modified, value); - } - - public bool HasChildren - { - get => _hasChildren; - private set => SetProperty(ref _hasChildren, value); - } - - public bool IsExpanded - { - get => _isExpanded; - set => SetProperty(ref _isExpanded, value); - } - public bool IsChecked { get; set; } public bool IsDirectory { get; } public IReadOnlyList Children => _children ??= LoadChildren(); @@ -332,6 +295,7 @@ public class FileNodeViewModel: ObservableObject, IEditableObject child.Size = info.Length; child.Modified = info.LastWriteTimeUtc; } + break; } } @@ -359,7 +323,7 @@ public class FileNodeViewModel: ObservableObject, IEditableObject if (_children[i].Path == e.FullPath) { _children.RemoveAt(i); - System.Diagnostics.Debug.WriteLine($"Removed {e.FullPath}"); + Debug.WriteLine($"Removed {e.FullPath}"); break; } } @@ -394,6 +358,7 @@ internal static class ListExtensions return i; i++; } + return -1; } } \ No newline at end of file diff --git a/demo/Semi.Avalonia.Demo/ViewModels/TreeDataGridDemo/SongsPageViewModel.cs b/demo/Semi.Avalonia.Demo/ViewModels/TreeDataGridDemo/SongsPageViewModel.cs new file mode 100644 index 0000000..5c0ba5e --- /dev/null +++ b/demo/Semi.Avalonia.Demo/ViewModels/TreeDataGridDemo/SongsPageViewModel.cs @@ -0,0 +1,54 @@ +using System.Collections.ObjectModel; +using System.Linq; +using Avalonia.Controls; +using Avalonia.Controls.Models.TreeDataGrid; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Semi.Avalonia.Demo.ViewModels; + +public class SongsPageViewModel : ObservableObject +{ + public FlatTreeDataGridSource Songs { get; } + + public SongsPageViewModel() + { + var songs = new ObservableCollection(Song.Songs.Select(a => new SongViewModel() + { + Title = a.Title, + Artist = a.Artist, + Album = a.Album, + CountOfComment = a.CountOfComment, + IsSelected = false + })); + + Songs = new FlatTreeDataGridSource(songs) + { + Columns = + { + new CheckBoxColumn( + "IsSelected", + a => a.IsSelected, + (model, b) => { model.IsSelected = b; }, + new GridLength(108, GridUnitType.Pixel)), + new TextColumn( + "Title", + a => a.Title, + (o, a) => o.Title = a, + new GridLength(6, GridUnitType.Star)), + new TextColumn("Artist", + a => a.Artist, + (o, a) => o.Artist = a, + new GridLength(6, GridUnitType.Star)), + new TemplateColumn("Album", + "AlbumCell", + "AlbumEditCell", + new GridLength(6, GridUnitType.Star)), + new TemplateColumn( + "Comments", + "CommentsCell", + "CommentsEditCell", + new GridLength(6, GridUnitType.Star)), + } + }; + } +} \ No newline at end of file diff --git a/demo/Semi.Avalonia.TreeDataGrid.Demo/ViewModels/MainViewModel.cs b/demo/Semi.Avalonia.Demo/ViewModels/TreeDataGridDemo/TreeDataGridDemoViewModel.cs similarity index 63% rename from demo/Semi.Avalonia.TreeDataGrid.Demo/ViewModels/MainViewModel.cs rename to demo/Semi.Avalonia.Demo/ViewModels/TreeDataGridDemo/TreeDataGridDemoViewModel.cs index 066bcb4..791d14b 100644 --- a/demo/Semi.Avalonia.TreeDataGrid.Demo/ViewModels/MainViewModel.cs +++ b/demo/Semi.Avalonia.Demo/ViewModels/TreeDataGridDemo/TreeDataGridDemoViewModel.cs @@ -1,8 +1,8 @@ using CommunityToolkit.Mvvm.ComponentModel; -namespace Semi.Avalonia.TreeDataGrid.Demo.ViewModels; +namespace Semi.Avalonia.Demo.ViewModels; -public class MainViewModel: ObservableObject +public class TreeDataGridDemoViewModel: ObservableObject { public SongsPageViewModel SongsContext { get; } = new(); public FilesPageViewModel FilesContext { get; } = new(); diff --git a/demo/Semi.Avalonia.Demo/Views/MainView.axaml b/demo/Semi.Avalonia.Demo/Views/MainView.axaml index 2f79f72..bc1422b 100644 --- a/demo/Semi.Avalonia.Demo/Views/MainView.axaml +++ b/demo/Semi.Avalonia.Demo/Views/MainView.axaml @@ -135,6 +135,9 @@ + + + diff --git a/demo/Semi.Avalonia.TreeDataGrid.Demo/App.axaml b/demo/Semi.Avalonia.TreeDataGrid.Demo/App.axaml deleted file mode 100644 index 35166a6..0000000 --- a/demo/Semi.Avalonia.TreeDataGrid.Demo/App.axaml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/demo/Semi.Avalonia.TreeDataGrid.Demo/App.axaml.cs b/demo/Semi.Avalonia.TreeDataGrid.Demo/App.axaml.cs deleted file mode 100644 index c4d36b3..0000000 --- a/demo/Semi.Avalonia.TreeDataGrid.Demo/App.axaml.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Markup.Xaml; - -namespace Semi.Avalonia.TreeDataGrid.Demo; - -public partial class App : Application -{ - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - } - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.MainWindow = new MainWindow(); - } - - base.OnFrameworkInitializationCompleted(); - } -} \ No newline at end of file diff --git a/demo/Semi.Avalonia.TreeDataGrid.Demo/MainWindow.axaml b/demo/Semi.Avalonia.TreeDataGrid.Demo/MainWindow.axaml deleted file mode 100644 index 076f2bd..0000000 --- a/demo/Semi.Avalonia.TreeDataGrid.Demo/MainWindow.axaml +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - -