Browse Source

avalonia: created selectionview

Kenric Nugteren 1 tháng trước cách đây
mục cha
commit
8b8a3f94ff

+ 3 - 2
PRS.Avalonia/PRS.Avalonia.Desktop/PRS.Avalonia.Desktop.csproj

@@ -21,13 +21,14 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Avalonia.Desktop"/>
+        <PackageReference Include="Avalonia.Desktop" />
         <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
     </ItemGroup>
 
     <ItemGroup>
         <ProjectReference Include="..\..\..\inabox\InABox.Avalonia.Platform.Desktop\InABox.Avalonia.Platform.Desktop.csproj" />
-        <ProjectReference Include="..\PRS.Avalonia\PRS.Avalonia.csproj"/>
+        <ProjectReference Include="..\..\..\inabox\InABox.Avalonia\InABox.Avalonia.csproj" />
+        <ProjectReference Include="..\PRS.Avalonia\PRS.Avalonia.csproj" />
     </ItemGroup>
 
     <ItemGroup>

+ 43 - 0
PRS.Avalonia/PRS.Avalonia/Components/SelectionView/SelectionView.axaml

@@ -0,0 +1,43 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+             x:Class="PRS.Avalonia.Components.SelectionView"
+			 xmlns:components="using:InABox.Avalonia.Components"
+			 xmlns:prsComponents="using:PRS.Avalonia.Components"
+			 x:DataType="prsComponents:SelectionViewModel">
+	<Grid>
+		<Grid.RowDefinitions>
+			<RowDefinition Height="Auto"/>
+			<RowDefinition Height="*"/>
+			<RowDefinition Height="Auto"/>
+		</Grid.RowDefinitions>
+		<components:ButtonStrip Name="Filters" Grid.Row="0" Items="{Binding FilterButtons}" SelectionChanged="Filters_OnSelectionChanged"/>
+		<components:AvaloniaDataGrid Name="Grid" Grid.Row="1"
+									 ItemsSource="{Binding ItemsSource}"
+									 RefreshRequested="Grid_OnRefreshRequested"
+									 SelectionChanged="Grid_OnSelectionChanged"
+									 ShowRecordCount="{Binding CanRefresh}"
+									 CanSearch="{Binding CanSearch}"
+									 RefreshVisible="{Binding CanRefresh}"
+									 LastUpdated="{Binding LastUpdated}"/>
+		<ScrollViewer Grid.Row="2"
+					  HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
+			<ItemsControl ItemsSource="{Binding Buttons}">
+				<ItemsControl.ItemsPanel>
+					<ItemsPanelTemplate>
+						<UniformGrid Rows="1"/>
+					</ItemsPanelTemplate>
+				</ItemsControl.ItemsPanel>
+				<ItemsControl.ItemTemplate>
+					<DataTemplate DataType="prsComponents:SelectionViewButton">
+						<Button Classes="Standard"
+								Content="{Binding Text}"
+								Command="{Binding Command}"/>
+					</DataTemplate>
+				</ItemsControl.ItemTemplate>
+			</ItemsControl>
+		</ScrollViewer>
+	</Grid>
+</UserControl>

+ 69 - 0
PRS.Avalonia/PRS.Avalonia/Components/SelectionView/SelectionView.axaml.cs

@@ -0,0 +1,69 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using DynamicData.Binding;
+using InABox.Avalonia.Components;
+using SkiaSharp;
+using System;
+
+namespace PRS.Avalonia.Components;
+
+public class SelectionViewRefreshArgs(bool force, string filter) : EventArgs
+{
+    public bool Force { get; private set; } = force;
+
+    public string Filter { get; private set; } = filter;
+
+    public DateTime LastUpdated { get; set; }
+}
+
+public partial class SelectionView : UserControl
+{
+    private SelectionViewModel Model => (DataContext as SelectionViewModel)!;
+
+    public SelectionView()
+    {
+        InitializeComponent();
+
+        DataContextChanged += SelectionView_DataContextChanged;
+    }
+
+    private void SelectionView_DataContextChanged(object? sender, EventArgs e)
+    {
+        if (Model is null) return;
+
+        Model.Columns.Changed += Columns_Changed;
+        Columns_Changed(Model.Columns);
+        Model.WhenPropertyChanged(x => x.SelectionPageMode).Subscribe(value =>
+        {
+            Grid.SelectionMode = value.Value == SelectionPageMode.MultiSelect ? SelectionMode.Multiple : SelectionMode.Single;
+        });
+        Model.FilterButtons.CollectionChanged += FilterButtons_CollectionChanged;
+    }
+
+    private void FilterButtons_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+    {
+        Filters.IsVisible = Model.FilterButtons.Count > 1;
+    }
+
+    private void Columns_Changed(AvaloniaDataGridColumns columns)
+    {
+        Grid.Columns.BeginUpdate().Clear().AddRange(columns).EndUpdate();
+    }
+
+    private void Filters_OnSelectionChanged(object sender, EventArgs e)
+    {
+        var args = new SelectionViewRefreshArgs(true, Filters.SelectedItem?.Text ?? "");
+        Model.RefreshCommand.Execute(args);
+    }
+
+    private void Grid_OnRefreshRequested(object sender, AvaloniaDataGridRefreshRequestedEventArgs e)
+    {
+        var args = new SelectionViewRefreshArgs(true, Filters.SelectedItem?.Text ?? "");
+        Model.RefreshCommand.Execute(args);
+    }
+    private void Grid_OnSelectionChanged(object sender, AvaloniaDataGridSelectionChangedEventArgs e)
+    {
+        Model.SelectCommand.Execute(e.Selection);
+    }
+}

+ 130 - 0
PRS.Avalonia/PRS.Avalonia/Components/SelectionView/SelectionViewModel.cs

@@ -0,0 +1,130 @@
+using Avalonia.Media;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using InABox.Avalonia;
+using InABox.Avalonia.Components;
+using InABox.Core;
+using PRS.Avalonia.Modules;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace PRS.Avalonia.Components;
+
+public partial class SelectionViewButton : ObservableObject
+{
+    [ObservableProperty]
+    private string _text = "";
+
+    [ObservableProperty]
+    private ICommand? _command;
+}
+
+public enum SelectionPageMode
+{
+    MultiSelect,
+    Confirm,
+    Immediate
+}
+
+public partial class SelectionViewModel : PopupViewModel<object?[]?>, IModuleViewModel, IDualFunctionViewModel
+{
+    public string Title => SelectionTitle;
+
+    [ObservableProperty]
+    private string _selectionTitle = "";
+
+    [ObservableProperty]
+    private SelectionPageMode _selectionPageMode = SelectionPageMode.Immediate;
+
+    public ObservableCollection<ButtonStripItem> FilterButtons { get; } = new();
+
+    public ObservableCollection<SelectionViewButton> Buttons { get; } = new();
+
+    public AvaloniaDataGridColumns Columns { get; } = new();
+    public bool IsPopup { get; set; }
+
+    [ObservableProperty]
+    private IEnumerable? _itemsSource;
+
+    [ObservableProperty]
+    private bool _canSearch = true;
+
+    [ObservableProperty]
+    private bool _canRefresh = true;
+
+    [ObservableProperty]
+    private DateTime _lastUpdated;
+
+    [ObservableProperty]
+    private Func<SelectionViewRefreshArgs, IEnumerable>? _refresh;
+
+    [ObservableProperty]
+    private Action<object?[]>? _selected;
+
+    private object?[] _selectedItems = [];
+
+    public SelectionViewModel()
+    {
+        PrimaryMenu.Add(new(Images.tick, Tick_Click));
+    }
+
+    private Task<bool> Tick_Click()
+    {
+        Selected?.Invoke(_selectedItems);
+        Navigation.Back();
+        return Task.FromResult(true);
+    }
+
+    protected override Task OnActivated()
+    {
+        return Task.Run(() =>
+        {
+            var result = Refresh?.Invoke(new(false, FilterButtons.FirstOrDefault(x => x.Selected)?.Text ?? ""));
+            if (result is not null)
+            {
+                if (result is ICoreRepository repository)
+                {
+                    LastUpdated = repository.LastUpdated;
+                }
+                ItemsSource = result;
+            }
+        });
+    }
+
+    [RelayCommand]
+    private void OnRefresh(SelectionViewRefreshArgs args)
+    {
+        var result = Refresh?.Invoke(args);
+        if (result is null) return;
+
+        if(result is ICoreRepository repository)
+        {
+            LastUpdated = repository.LastUpdated;
+        }
+        ItemsSource = result;
+    }
+
+    [RelayCommand]
+    private void Select(object?[] items)
+    {
+        _selectedItems = items;
+        if(IsPopup)
+        {
+            Close(items);
+        }
+        else
+        {
+            if(SelectionPageMode == SelectionPageMode.Immediate)
+            {
+                Selected?.Invoke(items);
+                Navigation.Back();
+            }
+        }
+    }
+}

+ 1 - 1
PRS.Avalonia/PRS.Avalonia/MainViewModel.cs

@@ -30,7 +30,7 @@ public partial class MainViewModel : ViewModelBase
                 Content.Deactivate();
             IsTransitioning = true;
             Content = viewModel;
-            Title = viewModel is ModuleViewModel module
+            Title = viewModel is IModuleViewModel module
                 ? module.Title
                 : Repositories.Me?.Name ?? "PRS Avalonia";
             PrimaryMenu = viewModel.PrimaryMenu;

+ 6 - 1
PRS.Avalonia/PRS.Avalonia/Modules/ModuleViewModel.cs

@@ -2,7 +2,12 @@
 
 namespace PRS.Avalonia.Modules;
 
-public abstract class ModuleViewModel : ViewModelBase
+public interface IModuleViewModel
+{
+    string Title { get; }
+}
+
+public abstract class ModuleViewModel : ViewModelBase, IModuleViewModel
 {
     public abstract string Title { get; }
 }

+ 3 - 0
PRS.Avalonia/PRS.Avalonia/PRS.Avalonia.csproj

@@ -340,6 +340,9 @@
         <Compile Update="Components\FormsList\FormsList.axaml.cs">
           <DependentUpon>FormsList.axaml</DependentUpon>
         </Compile>
+        <Compile Update="Components\SelectionView\SelectionView.axaml.cs">
+          <DependentUpon>SelectionView.axaml</DependentUpon>
+        </Compile>
         <Compile Update="Modules\Assignments\AssignmentsView.axaml.cs">
             <DependentUpon>AssignmentsView.axaml</DependentUpon>
             <SubType>Code</SubType>