Bläddra i källkod

avalonia: added lookup fields (there is still a bug though)

Kenric Nugteren 1 månad sedan
förälder
incheckning
fe388ef7de

+ 212 - 0
PRS.Avalonia/PRS.Avalonia/Components/FormsEditor/Fields/DFLookupFieldControl.cs

@@ -0,0 +1,212 @@
+using Avalonia.Controls;
+using Avalonia.Media;
+using CommunityToolkit.Mvvm.Input;
+using DynamicData;
+using InABox.Avalonia;
+using InABox.Avalonia.Components;
+using InABox.Clients;
+using InABox.Core;
+using PRS.Avalonia.Components;
+using ReactiveUI;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ComboItemType = System.Tuple<string, System.Collections.Generic.Dictionary<string, object?>>;
+
+namespace PRS.Avalonia.DigitalForms;
+
+partial class DFLookupFieldControl : DigitalFormFieldControl<DFLayoutLookupField, DFLayoutLookupFieldProperties, Guid, DFLayoutLookupValue>
+{
+    private Button Button = null!;
+    private DFLayoutLookupValue? _value;
+
+    protected override Control Create()
+    {
+        Button = new Button();
+        Button.Classes.Add("Standard");
+        Button.Content = "Select Value";
+        Button.Command = ClickedCommand;
+        return Button;
+    }
+
+    [RelayCommand]
+    private async Task Clicked()
+    {
+        if (!CoreUtils.TryGetEntity(Field.Properties.LookupType, out var type)) return;
+
+        var filter = GetLookupFilter();
+
+        var visibleColumns = Columns.None(type);
+        var hiddenColumns = Columns.None(type).Add("ID");
+        foreach(var column in LookupFactory.DefineColumns(type))
+        {
+            if(column.PropertyDefinition.Editor.Visible != Visible.Default)
+            {
+                visibleColumns.Add(column);
+            }
+            else
+            {
+                hiddenColumns.Add(column);
+            }
+        }
+        foreach(var column in Field.Properties.AdditionalPropertiesList)
+        {
+            hiddenColumns.Add(column);
+        }
+        hiddenColumns.Add(Field.Properties.Property);
+
+        var sort = LookupFactory.DefineSort(type);
+
+        var selection = await SelectionViewModel.ExecutePopup<KeyValuePair<Guid, ComboItemType>>(model =>
+        {
+            model.Columns.BeginUpdate();
+            model.Columns.Clear();
+            var column = new AvaloniaDataGridTextColumn<KeyValuePair<Guid, ComboItemType>>();
+            column.ColumnName = "Value.Item1";
+            column.Caption = type.Name;
+            column.Width = GridLength.Star;
+            model.Columns.Add(column);
+            model.Columns.EndUpdate();
+        }, args =>
+        {
+            var columns = Columns.None(type);
+            foreach(var column in visibleColumns)
+            {
+                columns.Add(column);
+            }
+            foreach(var column in hiddenColumns)
+            {
+                columns.Add(column);
+            }
+
+            var table = Client.Create(type).Query(filter, columns, sort);
+            var lookups = new Dictionary<Guid, ComboItemType>()
+            {
+                { Guid.Empty, new("(None)", table.NewRow().ToDictionary(new[] { "ID" })) }
+            };
+            foreach (var row in table.Rows)
+            {
+                var id = row.Get<Guid>("ID");
+                var values = row.ToDictionary(new[] { "ID" });
+                var display = LookupFactory.FormatLookup(type, values, Array.Empty<string>());
+                lookups[id] = new(display, values);
+            }
+            return lookups;
+        });
+
+        var value = selection?.FirstOrDefault();
+        if(value is null)
+        {
+            if(_value is null)
+            {
+                return;
+            }
+            _value = null;
+        }
+        else
+        {
+            _value ??= new();
+            _value.ID = value.Value.Key;
+            _value.Text = value.Value.Value.Item1;
+            _value.Values = value.Value.Value.Item2;
+        }
+        Button.Content = _value?.Text ?? "Select Value";
+        
+        ChangeField();
+    }
+
+    private IFilter? GetLookupFilter()
+    {
+        if (!CoreUtils.TryGetEntity(Field.Properties.LookupType, out var type)) return null;
+
+        IFilter? filter;
+        if (!Field.Properties.Filter.IsNullOrWhiteSpace())
+        {
+            filter = Serialization.Deserialize(typeof(Filter<>).MakeGenericType(type), Field.Properties.Filter) as IFilter;
+            filter?.ConvertCustomValues((_, value) =>
+            {
+                var property = System.Text.Encoding.UTF8.GetString(value.Data);
+                try
+                {
+                    return CoreUtils.GetPropertyValue(FormViewer.Entity, property);
+                }
+                catch
+                {
+                    return null;
+                }
+            });
+        }
+        else
+        {
+            filter = LookupFactory.DefineFilter(type);
+        }
+        return filter;
+    }
+
+    private void DoLookup(Guid id)
+    {
+        if (!CoreUtils.TryGetEntity(Field.Properties.LookupType, out var type)) return;
+
+        var client = ClientFactory.CreateClient(type);
+
+        var filter = Filter.Create(type, "ID").IsEqualTo(id);
+        var columns = LookupFactory.DefineColumns(type);
+        foreach(var property in Field.Properties.AdditionalPropertiesList)
+        {
+            columns.Add(property);
+        }
+
+        var row = client.Query(filter, columns).Rows.FirstOrDefault();
+        if(row is null)
+        {
+            if(_value is null)
+            {
+                return;
+            }
+            _value = null;
+        }
+        else
+        {
+            _value ??= new();
+            _value.ID = id;
+            _value.Text = LookupFactory.FormatLookup(type, row.ToDictionary(), new[] { "ID" });
+            _value.Values = row.ToDictionary();
+        }
+        Button.Content = _value?.Text ?? "Select Value";
+        
+        ChangeField();
+    }
+
+    public override void SetSerializedValue(DFLayoutLookupValue value)
+    {
+        DoLookup(value.ID);
+    }
+
+    public override DFLayoutLookupValue GetSerializedValue()
+    {
+        return _value ?? new();
+    }
+
+    public override object? GetData(string dataField)
+    {
+        return _value?.Values.GetValueOrDefault(dataField);
+    }
+
+    public override Guid GetValue()
+    {
+        return _value?.ID ?? Guid.Empty;
+    }
+    public override void SetValue(Guid value)
+    {
+        DoLookup(value);
+    }
+
+    protected override bool IsEmpty() => _value is null || _value.ID == Guid.Empty;
+
+    public override void SetBackground(IBrush brush, bool defaultColour)
+    {
+        Button.Background = brush;
+    }
+}

+ 4 - 4
PRS.Avalonia/PRS.Avalonia/Components/SelectionView/SelectionView.axaml.cs

@@ -8,11 +8,11 @@ using System;
 
 namespace PRS.Avalonia.Components;
 
-public class SelectionViewRefreshArgs(bool force, string filter) : EventArgs
+public class SelectionViewRefreshArgs(bool force, string? filter) : EventArgs
 {
     public bool Force { get; private set; } = force;
 
-    public string Filter { get; private set; } = filter;
+    public string? Filter { get; private set; } = filter;
 
     public DateTime LastUpdated { get; set; }
 }
@@ -53,13 +53,13 @@ public partial class SelectionView : UserControl
 
     private void Filters_OnSelectionChanged(object sender, EventArgs e)
     {
-        var args = new SelectionViewRefreshArgs(true, Filters.SelectedItem?.Text ?? "");
+        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 ?? "");
+        var args = new SelectionViewRefreshArgs(true, Filters.SelectedItem?.Text);
         Model.RefreshCommand.Execute(args);
     }
     private void Grid_OnSelectionChanged(object sender, AvaloniaDataGridSelectionChangedEventArgs e)

+ 30 - 1
PRS.Avalonia/PRS.Avalonia/Components/SelectionView/SelectionViewModel.cs

@@ -32,7 +32,7 @@ public enum SelectionPageMode
     Immediate
 }
 
-public partial class SelectionViewModel : PopupViewModel<object?[]?>, IModuleViewModel, IDualFunctionViewModel
+public partial class SelectionViewModel : PopupViewModel<object?[]>, IModuleViewModel, IDualFunctionViewModel
 {
     public string Title => SelectionTitle;
 
@@ -127,4 +127,33 @@ public partial class SelectionViewModel : PopupViewModel<object?[]?>, IModuleVie
             }
         }
     }
+
+    public static async Task<T[]?> ExecutePopup<T>(
+        Action<SelectionViewModel> configure,
+        Func<SelectionViewRefreshArgs, IEnumerable> refresh,
+        bool canTapAway = true)
+    {
+        var result = await Navigation.Popup<SelectionViewModel, object?[]>(model =>
+        {
+            configure(model);
+            model.Refresh = refresh;
+        }, canTapAway: canTapAway);
+        if(result is null) return null;
+        return result.Cast<T>().Where(x => x != null).ToArray();
+    }
+    public static void Execute<T>(
+        Action<SelectionViewModel> configure,
+        Func<SelectionViewRefreshArgs, IEnumerable> refresh,
+        Action<T[]> selected)
+    {
+        Navigation.Navigate<SelectionViewModel>(model =>
+        {
+            configure(model);
+            model.Refresh = refresh;
+            model.Selected = items =>
+            {
+                selected(items.Cast<T>().Where(x => x != null).ToArray());
+            };
+        });
+    }
 }