Merged
This commit is contained in:
@@ -2,11 +2,12 @@
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class AttachedBehavior : Trigger
|
||||
public class AttachedBehaviour :
|
||||
Trigger
|
||||
{
|
||||
protected override void OnAttachedToVisualTree()
|
||||
{
|
||||
Interaction.ExecuteActions(AssociatedObject, Actions, null);
|
||||
base.OnAttachedToVisualTree();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Xaml.Interactivity;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class AttachedEventTriggerBehaviour :
|
||||
Trigger
|
||||
{
|
||||
public static readonly StyledProperty<RoutedEvent> RoutedEventProperty =
|
||||
AvaloniaProperty.Register<AttachedEventTriggerBehaviour, RoutedEvent>(nameof(RoutedEvent));
|
||||
|
||||
public RoutedEvent RoutedEvent
|
||||
{
|
||||
get => GetValue(RoutedEventProperty);
|
||||
set => SetValue(RoutedEventProperty, value);
|
||||
}
|
||||
|
||||
protected override void OnAttached()
|
||||
{
|
||||
if (RoutedEvent is not null)
|
||||
{
|
||||
if (AssociatedObject is Interactive interactive)
|
||||
{
|
||||
interactive.AddHandler(RoutedEvent, Handle);
|
||||
}
|
||||
}
|
||||
|
||||
base.OnAttached();
|
||||
}
|
||||
|
||||
protected override void OnDetaching()
|
||||
{
|
||||
if (RoutedEvent is not null)
|
||||
{
|
||||
if (AssociatedObject is Interactive interactive)
|
||||
{
|
||||
interactive.RemoveHandler(RoutedEvent, Handle);
|
||||
}
|
||||
}
|
||||
|
||||
base.OnDetaching();
|
||||
}
|
||||
|
||||
private void Handle(object sender, RoutedEventArgs args) =>
|
||||
Interaction.ExecuteActions(AssociatedObject, Actions, null);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class BooleanToPasswordCharConverter :
|
||||
MarkupExtension,
|
||||
IValueConverter
|
||||
{
|
||||
public override object ProvideValue(IServiceProvider serviceProvider) =>
|
||||
this;
|
||||
|
||||
public char PasswordChar { get; set; }
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
|
||||
value is bool boolValue ? boolValue ? '\0' : PasswordChar : (object)PasswordChar;
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -2,8 +2,9 @@
|
||||
using Avalonia.Xaml.Interactivity;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
public class ComparisonCondition :
|
||||
AvaloniaObject,
|
||||
|
||||
public class ComparisonCondition :
|
||||
AvaloniaObject,
|
||||
ICondition
|
||||
{
|
||||
public static readonly StyledProperty<object> LeftOperandProperty =
|
||||
@@ -35,4 +36,4 @@ public class ComparisonCondition :
|
||||
|
||||
public bool Evaluate() => ComparisonLogic.Evaluate(LeftOperand,
|
||||
Operator, RightOperand);
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ namespace Toolkit.UI.Avalonia;
|
||||
|
||||
internal static class ComparisonLogic
|
||||
{
|
||||
internal static bool Evaluate(object leftOperand,
|
||||
ComparisonConditionType operatorType,
|
||||
internal static bool Evaluate(object leftOperand,
|
||||
ComparisonConditionType operatorType,
|
||||
object? rightOperand)
|
||||
{
|
||||
bool result = false;
|
||||
@@ -17,14 +17,13 @@ internal static class ComparisonLogic
|
||||
Type leftType = leftOperand.GetType();
|
||||
|
||||
if (rightOperand != null)
|
||||
{
|
||||
{
|
||||
TypeConverter typeConverter = TypeDescriptor.GetConverter(leftType);
|
||||
rightOperand = typeConverter.ConvertFrom(rightOperand);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (leftOperand is IComparable leftComparableOperand &&
|
||||
if (leftOperand is IComparable leftComparableOperand &&
|
||||
rightOperand is IComparable rightComparableOperand)
|
||||
{
|
||||
return EvaluateComparable(leftComparableOperand, operatorType, rightComparableOperand);
|
||||
@@ -35,6 +34,7 @@ internal static class ComparisonLogic
|
||||
case ComparisonConditionType.Equal:
|
||||
result = Equals(leftOperand, rightOperand);
|
||||
break;
|
||||
|
||||
case ComparisonConditionType.NotEqual:
|
||||
result = !Equals(leftOperand, rightOperand);
|
||||
break;
|
||||
@@ -43,7 +43,7 @@ internal static class ComparisonLogic
|
||||
}
|
||||
|
||||
private static bool EvaluateComparable(IComparable leftOperand,
|
||||
ComparisonConditionType operatorType,
|
||||
ComparisonConditionType operatorType,
|
||||
IComparable rightOperand)
|
||||
{
|
||||
object? convertedOperand = null;
|
||||
@@ -54,11 +54,9 @@ internal static class ComparisonLogic
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if (convertedOperand == null)
|
||||
@@ -74,18 +72,23 @@ internal static class ComparisonLogic
|
||||
case ComparisonConditionType.Equal:
|
||||
result = comparison == 0;
|
||||
break;
|
||||
|
||||
case ComparisonConditionType.GreaterThan:
|
||||
result = comparison > 0;
|
||||
break;
|
||||
|
||||
case ComparisonConditionType.GreaterThanOrEqual:
|
||||
result = comparison >= 0;
|
||||
break;
|
||||
|
||||
case ComparisonConditionType.LessThan:
|
||||
result = comparison < 0;
|
||||
break;
|
||||
|
||||
case ComparisonConditionType.LessThanOrEqual:
|
||||
result = comparison <= 0;
|
||||
break;
|
||||
|
||||
case ComparisonConditionType.NotEqual:
|
||||
result = comparison != 0;
|
||||
break;
|
||||
@@ -93,4 +96,4 @@ internal static class ComparisonLogic
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,12 @@ using Avalonia.Xaml.Interactivity;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class ConditionAction :
|
||||
public class ConditionAction :
|
||||
AvaloniaObject,
|
||||
IAction
|
||||
{
|
||||
public static readonly DirectProperty<ConditionAction, ActionCollection> ActionsProperty =
|
||||
AvaloniaProperty.RegisterDirect<ConditionAction, ActionCollection>(nameof(Actions),
|
||||
x => x.Actions);
|
||||
AvaloniaProperty.RegisterDirect<ConditionAction, ActionCollection>(nameof(Actions), x => x.Actions);
|
||||
|
||||
public static readonly StyledProperty<ICondition> ConditionProperty =
|
||||
AvaloniaProperty.Register<ConditionAction, ICondition>(nameof(Condition));
|
||||
|
||||
@@ -5,4 +5,4 @@ namespace Toolkit.UI.Avalonia;
|
||||
public class ConditionCollection :
|
||||
ObservableCollection<ComparisonCondition>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Metadata;
|
||||
using Toolkit.UI.Avalonia;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class ConditionalExpression :
|
||||
public class ConditionalExpression :
|
||||
AvaloniaObject,
|
||||
ICondition
|
||||
{
|
||||
@@ -18,7 +17,7 @@ public class ConditionalExpression :
|
||||
SetValue(ConditionsProperty, []);
|
||||
|
||||
[Content]
|
||||
public ConditionCollection Conditions =>
|
||||
public ConditionCollection Conditions =>
|
||||
GetValue(ConditionsProperty);
|
||||
|
||||
public ForwardChaining ForwardChaining
|
||||
@@ -47,4 +46,4 @@ public class ConditionalExpression :
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Xaml.Interactivity;
|
||||
using System.Reactive;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class EventListenerBehaviour :
|
||||
Trigger
|
||||
{
|
||||
public static readonly StyledProperty<string> EventNameProperty =
|
||||
AvaloniaProperty.Register<EventListenerBehaviour, string>(nameof(EventName));
|
||||
|
||||
public static readonly StyledProperty<object> SourceProperty =
|
||||
AvaloniaProperty.Register<EventListenerBehaviour, object>(nameof(Source));
|
||||
|
||||
private readonly Delegate? eventHandler;
|
||||
private object? resolvedSource;
|
||||
|
||||
static EventListenerBehaviour()
|
||||
{
|
||||
EventNameProperty.Changed.Subscribe(new AnonymousObserver<AvaloniaPropertyChangedEventArgs<string>>(EventNamePropertyChanged));
|
||||
SourceProperty.Changed.Subscribe(new AnonymousObserver<AvaloniaPropertyChangedEventArgs<object>>(SourcePropertyChanged));
|
||||
}
|
||||
|
||||
public string EventName
|
||||
{
|
||||
get => GetValue(EventNameProperty);
|
||||
set => SetValue(EventNameProperty, value);
|
||||
}
|
||||
|
||||
public object Source
|
||||
{
|
||||
get => GetValue(SourceProperty);
|
||||
set => SetValue(SourceProperty, value);
|
||||
}
|
||||
|
||||
private static void EventNamePropertyChanged(AvaloniaPropertyChangedEventArgs<string> args)
|
||||
{
|
||||
if (args.Sender is EventListenerBehaviour behaviour)
|
||||
{
|
||||
if (args.OldValue.GetValueOrDefault() is string oldValue)
|
||||
{
|
||||
behaviour.UnregisterEvent(oldValue);
|
||||
}
|
||||
|
||||
if (args.NewValue.GetValueOrDefault() is string newValue)
|
||||
{
|
||||
behaviour.RegisterEvent(newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SourcePropertyChanged(AvaloniaPropertyChangedEventArgs<object> args)
|
||||
{
|
||||
if (args.Sender is EventListenerBehaviour behaviour)
|
||||
{
|
||||
behaviour.SetResolvedSource(args.GetNewValue<object>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnEventRaised(object? sender, EventArgs args)
|
||||
{
|
||||
if (IsEnabled)
|
||||
{
|
||||
Interaction.ExecuteActions(AssociatedObject, Actions, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterEvent(string eventName)
|
||||
{
|
||||
if (eventName is { Length: 0 })
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (resolvedSource is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Type sourceType = resolvedSource.GetType();
|
||||
if (sourceType.GetEvent(EventName) is EventInfo eventInfo)
|
||||
{
|
||||
eventInfo.AddEventHandler(resolvedSource, new EventHandler(OnEventRaised));
|
||||
}
|
||||
}
|
||||
|
||||
private void SetResolvedSource(object? newSource)
|
||||
{
|
||||
if (resolvedSource == newSource)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (resolvedSource is not null)
|
||||
{
|
||||
UnregisterEvent(EventName);
|
||||
}
|
||||
|
||||
resolvedSource = newSource;
|
||||
|
||||
if (resolvedSource is not null)
|
||||
{
|
||||
RegisterEvent(EventName);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterEvent(string eventName)
|
||||
{
|
||||
if (eventHandler is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventName is { Length: 0 })
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (resolvedSource is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Type sourceType = resolvedSource.GetType();
|
||||
if (sourceType.GetEvent(EventName) is EventInfo eventInfo)
|
||||
{
|
||||
eventInfo.RemoveEventHandler(resolvedSource, new EventHandler(OnEventRaised));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class FileSizeNameConverter :
|
||||
MarkupExtension,
|
||||
IValueConverter
|
||||
{
|
||||
public override object ProvideValue(IServiceProvider serviceProvider) =>
|
||||
this;
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is long fileSize)
|
||||
{
|
||||
string[] sizeSuffixes = ["bytes", "KB", "MB", "GB", "TB", "PB"];
|
||||
|
||||
int order = 0;
|
||||
double adjustedSize = fileSize;
|
||||
|
||||
while (adjustedSize >= 1024 && order < sizeSuffixes.Length - 1)
|
||||
{
|
||||
order++;
|
||||
adjustedSize /= 1024;
|
||||
}
|
||||
|
||||
return $"{adjustedSize:0.##} {sizeSuffixes[order]}";
|
||||
}
|
||||
|
||||
return "0 bytes";
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -4,4 +4,4 @@ public enum ForwardChaining
|
||||
{
|
||||
And,
|
||||
Or
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,4 @@
|
||||
public interface ICondition
|
||||
{
|
||||
bool Evaluate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.Xaml.Interactivity;
|
||||
using System.Collections;
|
||||
using Toolkit.UI.Controls.Avalonia;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class InvokeNavigationViewItemAction :
|
||||
AvaloniaObject,
|
||||
IAction
|
||||
{
|
||||
public static readonly StyledProperty<int> SelectedIndexProperty =
|
||||
AvaloniaProperty.Register<InvokeNavigationViewItemAction, int>(nameof(SelectedIndex), 0);
|
||||
|
||||
public static readonly StyledProperty<object> TargetProperty =
|
||||
AvaloniaProperty.Register<InvokeNavigationViewItemAction, object>(nameof(Target));
|
||||
|
||||
public object Target
|
||||
{
|
||||
get => GetValue(TargetProperty);
|
||||
set => SetValue(TargetProperty, value);
|
||||
}
|
||||
|
||||
public int SelectedIndex
|
||||
{
|
||||
get => GetValue(SelectedIndexProperty);
|
||||
set => SetValue(SelectedIndexProperty, value);
|
||||
}
|
||||
|
||||
public object? Execute(object? sender, object? parameter)
|
||||
{
|
||||
if ((Target ?? sender) is NavigationViewItem navigationViewItem)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
if (navigationViewItem.MenuItemsSource is IList collection)
|
||||
{
|
||||
if (collection is { Count: > 0 })
|
||||
{
|
||||
navigationViewItem.SetValue(NavigationView.SelectedItemProperty, collection[SelectedIndex]);
|
||||
}
|
||||
}
|
||||
}, DispatcherPriority.ContextIdle);
|
||||
}
|
||||
|
||||
if ((Target ?? sender) is NavigationView navigationView)
|
||||
{
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
if (navigationView.MenuItemsSource is IList collection)
|
||||
{
|
||||
if (collection is { Count: > 0 })
|
||||
{
|
||||
navigationView.SetValue(NavigationView.SelectedItemProperty, collection[SelectedIndex]);
|
||||
}
|
||||
else
|
||||
{
|
||||
navigationView.SetValue(NavigationView.SelectedItemProperty, null);
|
||||
}
|
||||
}
|
||||
}, DispatcherPriority.ContextIdle);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using Avalonia.Interactivity;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class ItemInvokedEventArgs :
|
||||
RoutedEventArgs;
|
||||
@@ -0,0 +1,48 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Xaml.Interactivity;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class KeyBindingTriggerBehaviour :
|
||||
Trigger<InputElement>,
|
||||
ICommand
|
||||
{
|
||||
public static readonly StyledProperty<KeyGesture> GestureProperty =
|
||||
AvaloniaProperty.Register<KeyBindingTriggerBehaviour, KeyGesture>(nameof(Gesture));
|
||||
|
||||
public KeyGesture Gesture
|
||||
{
|
||||
get => GetValue(GestureProperty);
|
||||
set => SetValue(GestureProperty, value);
|
||||
}
|
||||
|
||||
public event EventHandler? CanExecuteChanged;
|
||||
|
||||
protected override void OnAttached()
|
||||
{
|
||||
if (Gesture is not null)
|
||||
{
|
||||
KeyBinding keyBinding = new()
|
||||
{
|
||||
Gesture = Gesture,
|
||||
Command = this
|
||||
};
|
||||
|
||||
AssociatedObject?.KeyBindings.Add(keyBinding);
|
||||
}
|
||||
|
||||
base.OnAttached();
|
||||
}
|
||||
|
||||
public bool CanExecute(object? parameter) => true;
|
||||
|
||||
public void Execute(object? parameter)
|
||||
{
|
||||
if (IsEnabled)
|
||||
{
|
||||
Interaction.ExecuteActions(AssociatedObject, Actions, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.LogicalTree;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class ListBoxExtension
|
||||
{
|
||||
public static readonly AttachedProperty<bool> IsItemInvokedEnabledProperty =
|
||||
AvaloniaProperty.RegisterAttached<ListBoxItem, bool>("IsItemInvokedEnabled",
|
||||
typeof(ListBoxExtension), false);
|
||||
|
||||
public static readonly RoutedEvent<ItemInvokedEventArgs> ItemInvokedEvent =
|
||||
RoutedEvent.Register<ItemInvokedEventArgs>("ItemInvoked",
|
||||
RoutingStrategies.Bubble, typeof(ListBoxExtension));
|
||||
|
||||
static ListBoxExtension()
|
||||
{
|
||||
IsItemInvokedEnabledProperty.Changed.AddClassHandler<ListBoxItem>(OnIsItemClickEnabledPropertyChanged);
|
||||
}
|
||||
|
||||
private static void OnIsItemClickEnabledPropertyChanged(ListBoxItem sender,
|
||||
AvaloniaPropertyChangedEventArgs args)
|
||||
{
|
||||
bool TrySetupListBox()
|
||||
{
|
||||
if (sender.GetLogicalAncestors().OfType<ListBox>().FirstOrDefault() is ListBox listBox)
|
||||
{
|
||||
void OnItemInvoked(object? _, SelectionChangedEventArgs args)
|
||||
{
|
||||
if (args.AddedItems is { Count: > 0 })
|
||||
{
|
||||
if (sender.DataContext == listBox.SelectedItem)
|
||||
{
|
||||
sender.RaiseEvent(new ItemInvokedEventArgs { RoutedEvent = ItemInvokedEvent });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sender.DataContext == listBox.SelectedItem)
|
||||
{
|
||||
sender.RaiseEvent(new ItemInvokedEventArgs { RoutedEvent = ItemInvokedEvent });
|
||||
}
|
||||
|
||||
void HandleUnloaded(object? _, RoutedEventArgs __)
|
||||
{
|
||||
listBox.SelectionChanged -= OnItemInvoked;
|
||||
listBox.Unloaded -= HandleUnloaded;
|
||||
}
|
||||
|
||||
listBox.SelectionChanged += OnItemInvoked;
|
||||
listBox.Unloaded += HandleUnloaded;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TrySetupListBox())
|
||||
{
|
||||
void HandleLoaded(object? _, RoutedEventArgs __)
|
||||
{
|
||||
TrySetupListBox();
|
||||
}
|
||||
|
||||
sender.Loaded += HandleLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetIsItemInvokedEnabled(ListBoxItem element) =>
|
||||
element.GetValue(IsItemInvokedEnabledProperty);
|
||||
|
||||
public static void SetIsItemInvokedEnabled(ListBoxItem element, bool value) =>
|
||||
element.SetValue(IsItemInvokedEnabledProperty, value);
|
||||
|
||||
public static void AddItemInvokedHandler(ListBoxItem element, EventHandler<ItemInvokedEventArgs> handler) =>
|
||||
element.AddHandler(ItemInvokedEvent, handler);
|
||||
|
||||
public static void RemoveItemInvokedHandler(ListBoxItem element, EventHandler<ItemInvokedEventArgs> handler) =>
|
||||
element.RemoveHandler(ItemInvokedEvent, handler);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class NamedTypeConverter :
|
||||
MarkupExtension,
|
||||
IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
|
||||
value is not null ? value.GetType().Name : (object?)null;
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider) => this;
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Metadata;
|
||||
using Avalonia.Xaml.Interactivity;
|
||||
using System.Collections.Immutable;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
@@ -10,15 +11,12 @@ public class NavigateAction :
|
||||
AvaloniaObject,
|
||||
IAction
|
||||
{
|
||||
public static readonly StyledProperty<object> ContextProperty =
|
||||
AvaloniaProperty.Register<NavigateAction, object>(nameof(Context));
|
||||
public static readonly DirectProperty<NavigateAction, ParameterCollection> ParametersProperty =
|
||||
AvaloniaProperty.RegisterDirect<NavigateAction, ParameterCollection>(nameof(Parameters),
|
||||
x => x.Parameters);
|
||||
|
||||
public static readonly DirectProperty<NavigateAction, ParameterBindingCollection> ParameterBindingsProperty =
|
||||
AvaloniaProperty.RegisterDirect<NavigateAction, ParameterBindingCollection>(nameof(ParameterBindings),
|
||||
x => x.ParameterBindings);
|
||||
|
||||
public static readonly StyledProperty<object[]?> ParametersProperty =
|
||||
AvaloniaProperty.Register<NavigateAction, object[]?>(nameof(Parameters));
|
||||
public static readonly StyledProperty<object> RegionProperty =
|
||||
AvaloniaProperty.Register<NavigateAction, object>(nameof(Region));
|
||||
|
||||
public static readonly StyledProperty<string> RouteProperty =
|
||||
AvaloniaProperty.Register<NavigateAction, string>(nameof(Route));
|
||||
@@ -26,31 +24,26 @@ public class NavigateAction :
|
||||
public static readonly StyledProperty<string> ScopeProperty =
|
||||
AvaloniaProperty.Register<NavigateAction, string>(nameof(Scope));
|
||||
|
||||
private ParameterBindingCollection parameterCollection = [];
|
||||
private ParameterCollection parameterCollection = [];
|
||||
|
||||
public event EventHandler? Navigated;
|
||||
|
||||
public object Context
|
||||
public object Region
|
||||
{
|
||||
get => GetValue(ContextProperty);
|
||||
set => SetValue(ContextProperty, value);
|
||||
get => GetValue(RegionProperty);
|
||||
set => SetValue(RegionProperty, value);
|
||||
}
|
||||
|
||||
[Content]
|
||||
public ParameterBindingCollection ParameterBindings =>
|
||||
public ParameterCollection Parameters =>
|
||||
parameterCollection ??= [];
|
||||
|
||||
public object[]? Parameters
|
||||
{
|
||||
get => GetValue(ParametersProperty);
|
||||
set => SetValue(ParametersProperty, value);
|
||||
}
|
||||
|
||||
public string Route
|
||||
{
|
||||
get => GetValue(RouteProperty);
|
||||
set => SetValue(RouteProperty, value);
|
||||
}
|
||||
|
||||
public string Scope
|
||||
{
|
||||
get => GetValue(ScopeProperty);
|
||||
@@ -60,23 +53,21 @@ public class NavigateAction :
|
||||
public object Execute(object? sender,
|
||||
object? parameter)
|
||||
{
|
||||
if (sender is TemplatedControl control)
|
||||
if (sender is Control content)
|
||||
{
|
||||
Dictionary<string, object> arguments =
|
||||
Dictionary<string, object> arguments =
|
||||
new(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
if (control.DataContext is IObservableViewModel observableViewModel)
|
||||
if (content.DataContext is IObservableViewModel observableViewModel)
|
||||
{
|
||||
object[] parameters = [.. Parameters ?? Enumerable.Empty<object?>(), ..
|
||||
ParameterBindings is { Count: > 0 } ?
|
||||
ParameterBindings.Select(binding => new KeyValuePair<string, object>(binding.Key, binding.Value)).ToArray() :
|
||||
Enumerable.Empty<KeyValuePair<string, object>>()];
|
||||
ImmutableDictionary<string, object>? parameters = Parameters is { Count: > 0 } ? Parameters.ToImmutableDictionary(x => x.Key, x => x.Value) :
|
||||
ImmutableDictionary<string, object>.Empty;
|
||||
|
||||
observableViewModel.Publisher.Publish(new Navigate(Route, Context
|
||||
?? null, Scope ?? null, control.DataContext, Navigated, parameters));
|
||||
observableViewModel.Publisher.Publish(new NavigateEventArgs(Route, Region == this ? content : Region, Scope ?? null,
|
||||
content.DataContext, Navigated, parameters));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,16 @@ public class NavigateBackAction :
|
||||
AvaloniaObject,
|
||||
IAction
|
||||
{
|
||||
public static readonly StyledProperty<string> ContextProperty =
|
||||
AvaloniaProperty.Register<NavigateBackAction, string>(nameof(Context));
|
||||
public static readonly StyledProperty<string> RegionProperty =
|
||||
AvaloniaProperty.Register<NavigateBackAction, string>(nameof(Region));
|
||||
|
||||
public static readonly StyledProperty<string> ScopeProperty =
|
||||
AvaloniaProperty.Register<NavigateBackAction, string>(nameof(Scope));
|
||||
|
||||
public string Context
|
||||
public string Region
|
||||
{
|
||||
get => GetValue(ContextProperty);
|
||||
set => SetValue(ContextProperty, value);
|
||||
get => GetValue(RegionProperty);
|
||||
set => SetValue(RegionProperty, value);
|
||||
}
|
||||
|
||||
public string Scope
|
||||
@@ -34,11 +34,11 @@ public class NavigateBackAction :
|
||||
{
|
||||
if (control.DataContext is IObservableViewModel observableViewModel)
|
||||
{
|
||||
observableViewModel.Publisher.Publish(new NavigateBack(Context
|
||||
?? null, Scope ?? null)).GetAwaiter().GetResult();
|
||||
observableViewModel.Publisher.Publish(new NavigateBackEventArgs(Region
|
||||
?? null, Scope ?? null));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Metadata;
|
||||
using Avalonia.Xaml.Interactivity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class NavigateRegionAction :
|
||||
AvaloniaObject,
|
||||
IAction
|
||||
{
|
||||
public static readonly DirectProperty<NavigateRegionAction, ActionCollection> ActionsProperty =
|
||||
AvaloniaProperty.RegisterDirect<NavigateRegionAction, ActionCollection>(nameof(Actions), x => x.Actions);
|
||||
|
||||
public static readonly StyledProperty<string> NameProperty =
|
||||
AvaloniaProperty.Register<NavigateRegionAction, string>(nameof(Name));
|
||||
|
||||
private ActionCollection? actions;
|
||||
|
||||
[Content]
|
||||
public ActionCollection Actions => actions ??= [];
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => GetValue(NameProperty);
|
||||
set => SetValue(NameProperty, value);
|
||||
}
|
||||
|
||||
public object? Execute(object? sender,
|
||||
object? parameter)
|
||||
{
|
||||
if (sender is Control control)
|
||||
{
|
||||
if (control.DataContext is IObservableViewModel observableViewModel)
|
||||
{
|
||||
if (observableViewModel.Provider.GetRequiredService<INavigationRegion>() is INavigationRegion navigationRegion)
|
||||
{
|
||||
navigationRegion.Register(Name, sender);
|
||||
Interaction.ExecuteActions(sender, Actions, parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.LogicalTree;
|
||||
using Toolkit.UI.Controls.Avalonia;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class NavigationViewExtension
|
||||
{
|
||||
public static readonly AttachedProperty<bool> IsItemInvokedEnabledProperty =
|
||||
AvaloniaProperty.RegisterAttached<NavigationViewItem, bool>("IsItemInvokedEnabled",
|
||||
typeof(NavigationViewExtension), false);
|
||||
|
||||
public static readonly RoutedEvent<ItemInvokedEventArgs> ItemInvokedEvent =
|
||||
RoutedEvent.Register<ItemInvokedEventArgs>("ItemInvoked",
|
||||
RoutingStrategies.Bubble, typeof(NavigationViewExtension));
|
||||
|
||||
static NavigationViewExtension()
|
||||
{
|
||||
IsItemInvokedEnabledProperty.Changed.AddClassHandler<NavigationViewItem>(OnIsItemInvokedEnabledPropertyChanged);
|
||||
}
|
||||
|
||||
private static void OnIsItemInvokedEnabledPropertyChanged(NavigationViewItem sender,
|
||||
AvaloniaPropertyChangedEventArgs args)
|
||||
{
|
||||
bool TrySetupNavigationView()
|
||||
{
|
||||
if (sender.GetLogicalAncestors().OfType<NavigationView>().FirstOrDefault() is NavigationView navigationView)
|
||||
{
|
||||
sender.GetObservable(NavigationViewItem.IsSelectedProperty).Subscribe(args =>
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
sender.RaiseEvent(new ItemInvokedEventArgs { RoutedEvent = ItemInvokedEvent });
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TrySetupNavigationView())
|
||||
{
|
||||
void HandleLoaded(object? _, RoutedEventArgs __)
|
||||
{
|
||||
TrySetupNavigationView();
|
||||
}
|
||||
|
||||
sender.Loaded += HandleLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetIsItemInvokedEnabled(NavigationViewItem element) =>
|
||||
element.GetValue(IsItemInvokedEnabledProperty);
|
||||
|
||||
public static void SetIsItemInvokedEnabled(NavigationViewItem element, bool value) =>
|
||||
element.SetValue(IsItemInvokedEnabledProperty, value);
|
||||
|
||||
public static void AddItemInvokedHandler(NavigationViewItem element, EventHandler<ItemInvokedEventArgs> handler) =>
|
||||
element.AddHandler(ItemInvokedEvent, handler);
|
||||
|
||||
public static void RemoveItemInvokedHandler(NavigationViewItem element, EventHandler<ItemInvokedEventArgs> handler) =>
|
||||
element.RemoveHandler(ItemInvokedEvent, handler);
|
||||
}
|
||||
@@ -2,23 +2,24 @@
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class ParameterBinding :
|
||||
public class Parameter :
|
||||
AvaloniaObject
|
||||
{
|
||||
public static readonly StyledProperty<string> KeyProperty =
|
||||
AvaloniaProperty.Register<ParameterBinding, string>(nameof(Key));
|
||||
AvaloniaProperty.Register<Parameter, string>(nameof(Key));
|
||||
|
||||
public static readonly StyledProperty<object> ValueProperty =
|
||||
AvaloniaProperty.Register<ParameterBinding, object>(nameof(Value));
|
||||
AvaloniaProperty.Register<Parameter, object>(nameof(Value));
|
||||
|
||||
public string Key
|
||||
{
|
||||
get => GetValue(KeyProperty);
|
||||
set => SetValue(KeyProperty, value);
|
||||
}
|
||||
|
||||
public object Value
|
||||
{
|
||||
get => GetValue(ValueProperty);
|
||||
set => SetValue(ValueProperty, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class ParameterBindingCollection :
|
||||
ObservableCollection<ParameterBinding>;
|
||||
@@ -0,0 +1,6 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class ParameterCollection :
|
||||
ObservableCollection<Parameter>;
|
||||
@@ -0,0 +1,3 @@
|
||||
using Avalonia.Metadata;
|
||||
|
||||
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Toolkit.UI.Avalonia")]
|
||||
@@ -0,0 +1,84 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.LogicalTree;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class TabStripExtension
|
||||
{
|
||||
public static readonly AttachedProperty<bool> IsItemInvokedEnabledProperty =
|
||||
AvaloniaProperty.RegisterAttached<TabStripItem, bool>("IsItemInvokedEnabled",
|
||||
typeof(TabStripExtension), false);
|
||||
|
||||
public static readonly RoutedEvent<ItemInvokedEventArgs> ItemInvokedEvent =
|
||||
RoutedEvent.Register<ItemInvokedEventArgs>("ItemInvoked",
|
||||
RoutingStrategies.Bubble, typeof(TabStripExtension));
|
||||
|
||||
static TabStripExtension()
|
||||
{
|
||||
IsItemInvokedEnabledProperty.Changed.AddClassHandler<TabStripItem>(OnIsItemClickEnabledPropertyChanged);
|
||||
}
|
||||
|
||||
private static void OnIsItemClickEnabledPropertyChanged(TabStripItem sender,
|
||||
AvaloniaPropertyChangedEventArgs args)
|
||||
{
|
||||
bool TrySetupTabStrip()
|
||||
{
|
||||
if (sender.GetLogicalAncestors().OfType<TabStrip>().FirstOrDefault() is TabStrip tabStrip)
|
||||
{
|
||||
void OnItemInvoked(object? _, SelectionChangedEventArgs args)
|
||||
{
|
||||
if (args.AddedItems is { Count: > 0 })
|
||||
{
|
||||
if (sender.DataContext == tabStrip.SelectedItem)
|
||||
{
|
||||
sender.RaiseEvent(new ItemInvokedEventArgs { RoutedEvent = ItemInvokedEvent });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sender.DataContext == tabStrip.SelectedItem)
|
||||
{
|
||||
sender.RaiseEvent(new ItemInvokedEventArgs { RoutedEvent = ItemInvokedEvent });
|
||||
}
|
||||
|
||||
void HandleUnloaded(object? _, RoutedEventArgs __)
|
||||
{
|
||||
tabStrip.SelectionChanged -= OnItemInvoked;
|
||||
tabStrip.Unloaded -= HandleUnloaded;
|
||||
}
|
||||
|
||||
tabStrip.SelectionChanged += OnItemInvoked;
|
||||
tabStrip.Unloaded += HandleUnloaded;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TrySetupTabStrip())
|
||||
{
|
||||
void HandleLoaded(object? _, RoutedEventArgs __)
|
||||
{
|
||||
TrySetupTabStrip();
|
||||
}
|
||||
|
||||
sender.Loaded += HandleLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetIsItemInvokedEnabled(TabStripItem element) =>
|
||||
element.GetValue(IsItemInvokedEnabledProperty);
|
||||
|
||||
public static void SetIsItemInvokedEnabled(TabStripItem element, bool value) =>
|
||||
element.SetValue(IsItemInvokedEnabledProperty, value);
|
||||
|
||||
public static void AddItemInvokedHandler(TabStripItem element, EventHandler<ItemInvokedEventArgs> handler) =>
|
||||
element.AddHandler(ItemInvokedEvent, handler);
|
||||
|
||||
public static void RemoveItemInvokedHandler(TabStripItem element, EventHandler<ItemInvokedEventArgs> handler) =>
|
||||
element.RemoveHandler(ItemInvokedEvent, handler);
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Platforms>AnyCPU;x64;x86</Platforms>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.1.0-beta1" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.0.9.2" />
|
||||
<PackageReference Include="Avalonia" Version="11.2.0-rc1" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.1.0.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Toolkit.Foundation\Toolkit.Foundation.csproj" />
|
||||
<ProjectReference Include="..\Toolkit.UI.Controls.Avalonia\Toolkit.UI.Controls.Avalonia.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user