Browse Source

avalonia: Made the backing collection of ButtonStrip into an ItemsSource to avoid threading issues with accessing UI objects.

Kenric Nugteren 4 weeks ago
parent
commit
3ca94aaf41

+ 67 - 5
InABox.Avalonia/Components/ButtonStrip/ButtonStrip.cs

@@ -29,6 +29,10 @@ public partial class ButtonStrip : TemplatedControl
         nameof(ItemSpacing));
     public static StyledProperty<ObservableCollection<ButtonStripItem>> ItemsProperty =
         AvaloniaProperty.Register<ButtonStripItem, ObservableCollection<ButtonStripItem>>(nameof(Items));
+    public static StyledProperty<IEnumerable?> ItemsSourceProperty =
+        AvaloniaProperty.Register<ButtonStripItem, IEnumerable?>(nameof(ItemsSource));
+    public static StyledProperty<ICommand> SelectedCommandProperty = AvaloniaProperty.Register<ButtonStrip, ICommand>(nameof(SelectedCommand));
+    public static StyledProperty<object?> SelectedItemProperty = AvaloniaProperty.Register<ButtonStrip, object?>(nameof(SelectedItem));
 
     public IBrush? SelectedBackground
     {
@@ -45,6 +49,17 @@ public partial class ButtonStrip : TemplatedControl
         get => GetValue(ItemSpacingProperty);
         set => SetValue(ItemSpacingProperty, value);
     }
+    public ICommand SelectedCommand
+    {
+        get => GetValue(SelectedCommandProperty);
+        set => SetValue(SelectedCommandProperty, value);
+    }
+
+    public IEnumerable? ItemsSource
+    {
+        get => GetValue(ItemsSourceProperty);
+        set => SetValue(ItemsSourceProperty, value);
+    }
 
     [Content]
     public ObservableCollection<ButtonStripItem> Items
@@ -53,7 +68,7 @@ public partial class ButtonStrip : TemplatedControl
         set => SetValue(ItemsProperty, value);
     }
 
-    public ButtonStripItem? SelectedItem
+    private ButtonStripItem? SelectedButton
     {
         get => Items.FirstOrDefault(x => x.Selected);
         set
@@ -62,14 +77,50 @@ public partial class ButtonStrip : TemplatedControl
             {
                 item.Selected = item == value;
             }
+            SelectedItem = value?.Tag;
         }
     }
 
+    public object? SelectedItem
+    {
+        get => GetValue(SelectedItemProperty);
+        set => SetValue(SelectedItemProperty, value);
+    }
+
     public event EventHandler? SelectionChanged;
 
     static ButtonStrip()
     {
         ItemsProperty.Changed.AddClassHandler<ButtonStrip>(Items_Changed);
+        ItemsSourceProperty.Changed.AddClassHandler<ButtonStrip>(ItemsSource_Changed);
+    }
+
+    private static void ItemsSource_Changed(ButtonStrip strip, AvaloniaPropertyChangedEventArgs args)
+    {
+        if(strip.ItemsSource is INotifyCollectionChanged notify)
+        {
+            notify.CollectionChanged += (sender, e) => strip.RebuildItems();
+        }
+        strip.RebuildItems();
+    }
+
+    private void RebuildItems()
+    {
+        Items.Clear();
+        if(ItemsSource is not null)
+        {
+            foreach(var item in ItemsSource)
+            {
+                if(item is ButtonStripItem btn)
+                {
+                    Items.Add(btn);
+                }
+                else
+                {
+                    Items.Add(new(item?.ToString() ?? "") { Tag = item });
+                }
+            }
+        }
     }
 
     private static void Items_Changed(ButtonStrip strip, AvaloniaPropertyChangedEventArgs args)
@@ -77,7 +128,7 @@ public partial class ButtonStrip : TemplatedControl
         strip.LogicalChildren.Clear();
         if (strip.Items is not null)
         {
-            strip.SelectedItem = strip.Items.FirstOrDefault();
+            strip.SelectedButton = strip.Items.FirstOrDefault();
 
             strip.LogicalChildren.AddRange(strip.Items);
             foreach(var item in strip.Items)
@@ -95,7 +146,7 @@ public partial class ButtonStrip : TemplatedControl
 
     private void Items_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
     {
-        SelectedItem ??= Items.FirstOrDefault();
+        SelectedButton ??= Items.FirstOrDefault();
         switch (e.Action)
         {
             case NotifyCollectionChangedAction.Add:
@@ -152,14 +203,16 @@ public partial class ButtonStrip : TemplatedControl
     private void ItemSelected(ButtonStripItem item)
     {
         var children = this.GetLogicalChildren().ToArray();
-        SelectedItem = item;
+        SelectedButton = item;
+        SelectionChanged?.Invoke(this, new());
+        SelectedCommand?.Execute(item.Tag);
     }
 
     public void AddRange(IEnumerable<string> items)
     {
         foreach(var item in items)
         {
-            Items.Add(new() { Text = item });
+            Items.Add(new(item));
         }
     }
 }
@@ -193,6 +246,15 @@ public class ButtonStripItem : TemplatedControl
         set => SetValue(CommandProperty, value);
     }
 
+    public ButtonStripItem()
+    {
+    }
+
+    public ButtonStripItem(string text)
+    {
+        Text = text;
+    }
+
     static ButtonStripItem()
     {
         SelectedProperty.Changed.AddClassHandler<ButtonStripItem>(Selected_Changed);

+ 2 - 1
InABox.Avalonia/DataModels/CoreRepository.cs

@@ -70,6 +70,7 @@ namespace InABox.Avalonia
         [ObservableProperty]
         private bool _selected;
 
+        public override string ToString() => Name ?? "";
     }
     
     public abstract class CoreRepository<TParent, TItem, TEntity> : CoreRepository, ICoreRepository, IEnumerable<TItem>
@@ -403,7 +404,7 @@ namespace InABox.Avalonia
             return this;
         }
         
-        public ICoreRepository Search(Func<TItem, bool> searchpredicate)
+        public ICoreRepository Search(Func<TItem, bool>? searchpredicate)
         {
             SearchPredicate = searchpredicate;
             Search();

+ 8 - 3
InABox.Avalonia/Theme/Classes/ButtonStrip.axaml

@@ -27,12 +27,17 @@
 	<Style Selector="components|ButtonStrip components|ButtonStripItem">
 		<Setter Property="Template">
 			<ControlTemplate>
-				<Button Classes="NoHover"
+				<Button Classes="NoHover Standard"
 						CommandParameter="{TemplateBinding}"
 						Command="{TemplateBinding Command}"
-						Background="{TemplateBinding Background}"
-						Foreground="{TemplateBinding Foreground}"
 						Content="{TemplateBinding Text}">
+					<Button.Styles>
+						<Style Selector="Button.Standard">
+							<Setter Property="Background" Value="{TemplateBinding Background}"/>
+							<Setter Property="Foreground" Value="{TemplateBinding Foreground}"/>
+							<Setter Property="FontSize" Value="{DynamicResource PrsFontSizeExtraSmall}"/>
+						</Style>
+					</Button.Styles>
 				</Button>
 			</ControlTemplate>
 		</Setter>