From d737cd21994243f16ddebac5e425dfba872ff8e1 Mon Sep 17 00:00:00 2001 From: Mbucari Date: Tue, 4 Apr 2023 12:10:23 -0600 Subject: [PATCH] Improve LinkLabel control --- Source/LibationAvalonia/App.axaml | 5 ++ Source/LibationAvalonia/App.axaml.cs | 2 - .../LibationAvalonia/Controls/LinkLabel.axaml | 4 +- .../Controls/LinkLabel.axaml.cs | 89 ++++++++++++++++++- .../Dialogs/AboutDialog.axaml | 2 +- 5 files changed, 94 insertions(+), 8 deletions(-) diff --git a/Source/LibationAvalonia/App.axaml b/Source/LibationAvalonia/App.axaml index 9c4b6240..1342b7cb 100644 --- a/Source/LibationAvalonia/App.axaml +++ b/Source/LibationAvalonia/App.axaml @@ -1,6 +1,7 @@  @@ -69,6 +70,10 @@ + diff --git a/Source/LibationAvalonia/App.axaml.cs b/Source/LibationAvalonia/App.axaml.cs index 9d1c5159..939e9513 100644 --- a/Source/LibationAvalonia/App.axaml.cs +++ b/Source/LibationAvalonia/App.axaml.cs @@ -27,7 +27,6 @@ namespace LibationAvalonia public static IBrush ProcessQueueBookCancelledBrush { get; private set; } public static IBrush ProcessQueueBookDefaultBrush { get; private set; } public static IBrush SeriesEntryGridBackgroundBrush { get; private set; } - public static IBrush HyperlinkVisited { get; private set; } public static IAssetLoader AssetLoader { get; private set; } @@ -230,7 +229,6 @@ namespace LibationAvalonia ProcessQueueBookCancelledBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookCancelledBrush)); SeriesEntryGridBackgroundBrush = AvaloniaUtils.GetBrushFromResources(nameof(SeriesEntryGridBackgroundBrush)); ProcessQueueBookDefaultBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookDefaultBrush)); - HyperlinkVisited = AvaloniaUtils.GetBrushFromResources(nameof(HyperlinkVisited)); } } } diff --git a/Source/LibationAvalonia/Controls/LinkLabel.axaml b/Source/LibationAvalonia/Controls/LinkLabel.axaml index f15725b0..b5786403 100644 --- a/Source/LibationAvalonia/Controls/LinkLabel.axaml +++ b/Source/LibationAvalonia/Controls/LinkLabel.axaml @@ -3,10 +3,10 @@ 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" + xmlns:controls="using:LibationAvalonia.Controls" x:Class="LibationAvalonia.Controls.LinkLabel"> - diff --git a/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs b/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs index 978a1e7a..097416ff 100644 --- a/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs +++ b/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs @@ -1,14 +1,57 @@ +using Avalonia; using Avalonia.Controls; +using Avalonia.Data; using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Media; using Avalonia.Styling; using System; +using System.Windows.Input; namespace LibationAvalonia.Controls { - public partial class LinkLabel : TextBlock, IStyleable + public partial class LinkLabel : TextBlock, IStyleable, ICommandSource { - Type IStyleable.StyleKey => typeof(TextBlock); + Type IStyleable.StyleKey => typeof(LinkLabel); + + public static readonly StyledProperty CommandProperty = + AvaloniaProperty.Register(nameof(Command), enableDataValidation: true); + + public static readonly StyledProperty CommandParameterProperty = + AvaloniaProperty.Register(nameof(CommandParameter)); + + public static readonly StyledProperty ForegroundVisitedProperty = + AvaloniaProperty.Register(nameof(ForegroundVisited)); + + public static readonly RoutedEvent ClickEvent = + RoutedEvent.Register(nameof(Click), RoutingStrategies.Bubble); + + public ICommand Command + { + get => GetValue(CommandProperty); + set => SetValue(CommandProperty, value); + } + + public object CommandParameter + { + get => GetValue(CommandParameterProperty); + set => SetValue(CommandParameterProperty, value); + } + + public IBrush ForegroundVisited + { + get => GetValue(ForegroundVisitedProperty); + set => SetValue(ForegroundVisitedProperty, value); + } + + public event EventHandler Click + { + add => AddHandler(ClickEvent, value); + remove => RemoveHandler(ClickEvent, value); + } + private static readonly Cursor HandCursor = new Cursor(StandardCursorType.Hand); + private bool _commandCanExecute = true; public LinkLabel() { InitializeComponent(); @@ -17,7 +60,19 @@ namespace LibationAvalonia.Controls private void LinkLabel_Tapped(object sender, TappedEventArgs e) { - Foreground = App.HyperlinkVisited; + Foreground = ForegroundVisited; + if (IsEffectivelyEnabled) + { + + var args = new RoutedEventArgs(ClickEvent); + RaiseEvent(args); + + if (!args.Handled && Command?.CanExecute(CommandParameter) == true) + { + Command.Execute(CommandParameter); + args.Handled = true; + } + } } protected override void OnPointerEntered(PointerEventArgs e) @@ -30,5 +85,33 @@ namespace LibationAvalonia.Controls this.Cursor = Cursor.Default; base.OnPointerExited(e); } + protected override bool IsEnabledCore => base.IsEnabledCore && _commandCanExecute; + + protected override void UpdateDataValidation(AvaloniaProperty property, BindingValueType state, Exception error) + { + base.UpdateDataValidation(property, state, error); + if (property == CommandProperty) + { + if (state == BindingValueType.BindingError) + { + if (_commandCanExecute) + { + _commandCanExecute = false; + UpdateIsEffectivelyEnabled(); + } + } + } + } + + public void CanExecuteChanged(object sender, EventArgs e) + { + var canExecute = Command == null || Command.CanExecute(CommandParameter); + + if (canExecute != _commandCanExecute) + { + _commandCanExecute = canExecute; + UpdateIsEffectivelyEnabled(); + } + } } } diff --git a/Source/LibationAvalonia/Dialogs/AboutDialog.axaml b/Source/LibationAvalonia/Dialogs/AboutDialog.axaml index 56d71595..3d529453 100644 --- a/Source/LibationAvalonia/Dialogs/AboutDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/AboutDialog.axaml @@ -54,7 +54,7 @@ -