diff --git a/AvaloniaBehaviors.sln b/AvaloniaBehaviors.sln index cf4f72f9..77e1fd26 100644 --- a/AvaloniaBehaviors.sln +++ b/AvaloniaBehaviors.sln @@ -43,6 +43,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "props", "props", "{40A43022 build\SignAssembly.props = build\SignAssembly.props build\SourceLink.props = build\SourceLink.props build\XUnit.props = build\XUnit.props + build\TrimmingEnable.targets = build\TrimmingEnable.targets EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{721A55B7-C5B0-44E2-803A-56E291C672FE}" diff --git a/Directory.Build.props b/Directory.Build.props index cf8ff149..370f948f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,12 +1,13 @@  - 11.0.10.8 - + 11.1.0.5 + beta.2 Wiesław Šoltés Wiesław Šoltés Copyright © Wiesław Šoltés 2024 MIT https://github.com/wieslawsoltes/AvaloniaBehaviors + true latest diff --git a/Directory.Packages.props b/Directory.Packages.props index 84e57556..baae8de2 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@  true - 11.0.0 + 11.1.0 @@ -14,7 +14,7 @@ - + diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 01acfc57..449c144d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -25,13 +25,13 @@ jobs: - template: Test-Bash.yml@templates parameters: name: 'Test_Linux' - vmImage: 'ubuntu-20.04' + vmImage: 'ubuntu-22.04' BuildConfiguration: ${{ variables.BuildConfiguration }} - template: Test-Bash.yml@templates parameters: name: 'Test_macOS' - vmImage: 'macOS-11' + vmImage: 'macOS-14' BuildConfiguration: ${{ variables.BuildConfiguration }} - template: Pack-MyGet.yml@templates @@ -58,7 +58,7 @@ jobs: - template: Publish-Bash.yml@templates parameters: name: 'Publish_Linux' - vmImage: 'ubuntu-20.04' + vmImage: 'ubuntu-22.04' BuildConfiguration: ${{ variables.BuildConfiguration }} PublishFramework: ${{ variables.PublishFramework }} PublishProject: ${{ variables.PublishProject }} @@ -67,7 +67,7 @@ jobs: - template: Publish-Bash.yml@templates parameters: name: 'Publish_macOS_x64' - vmImage: 'macOS-11' + vmImage: 'macOS-14' BuildConfiguration: ${{ variables.BuildConfiguration }} PublishFramework: ${{ variables.PublishFramework }} PublishProject: ${{ variables.PublishProject }} @@ -76,7 +76,7 @@ jobs: - template: Publish-Bash.yml@templates parameters: name: 'Publish_macOS_arm64' - vmImage: 'macOS-11' + vmImage: 'macOS-14' BuildConfiguration: ${{ variables.BuildConfiguration }} PublishFramework: ${{ variables.PublishFramework }} PublishProject: ${{ variables.PublishProject }} diff --git a/build/TrimmingEnable.targets b/build/TrimmingEnable.targets new file mode 100644 index 00000000..c74452a4 --- /dev/null +++ b/build/TrimmingEnable.targets @@ -0,0 +1,29 @@ + + + + false + true + false + true + + + + true + + + + true + + $(WarningsAsErrors);IL2000;IL2001;IL2002;IL2003;IL2004;IL2005;IL2006;IL2007;IL2008;IL2009;IL2010;IL2011;IL2012;IL2013;IL2014;IL2015;IL2016;IL2017;IL2018;IL2019;IL2020;IL2021;IL2022;IL2023;IL2024;IL2025;IL2026;IL2027;IL2028;IL2029;IL2030;IL2031;IL2032;IL2033;IL2034;IL2035;IL2036;IL2037;IL2038;IL2039;IL2040;IL2041;IL2042;IL2043;IL2044;IL2045;IL2046;IL2047;IL2048;IL2049;IL2050;IL2051;IL2052;IL2053;IL2054;IL2055;IL2056;IL2057;IL2058;IL2059;IL2060;IL2061;IL2062;IL2063;IL2064;IL2065;IL2066;IL2067;IL2068;IL2069;IL2070;IL2071;IL2072;IL2073;IL2074;IL2075;IL2076;IL2077;IL2078;IL2079;IL2080;IL2081;IL2082;IL2083;IL2084;IL2085;IL2086;IL2087;IL2088;IL2089;IL2090;IL2091;IL2092;IL2093;IL2094;IL2095;IL2096;IL2097;IL2098;IL2099;IL2100;IL2101;IL2102;IL2103;IL2104;IL2105;IL2106;IL2107;IL2108;IL2109;IL2110;IL2111;IL2112;IL2113;IL2114;IL2115;IL2116;IL2117;IL2118;IL2119;IL2120;IL2121;IL2122;IL2123;IL2124;IL2125;IL2126;IL2127;IL2128;IL2129;IL2130;IL2131;IL2132;IL2133;IL2134;IL2135;IL2136;IL2137;IL2138;IL2139;IL2140;IL2141;IL2142;IL2143;IL2144;IL2145;IL2146;IL2147;IL2148;IL2149;IL2150;IL2151;IL2152;IL2153;IL2154;IL2155;IL2156;IL2157 + + $(WarningsAsErrors);IL3050;IL3051;IL3052;IL3053;IL3054;IL3055;IL3056 + + + + $(WarningsAsErrors);CA1420;CA1421 + + + + + + diff --git a/samples/BehaviorsTestApplication/BehaviorsTestApplication.csproj b/samples/BehaviorsTestApplication/BehaviorsTestApplication.csproj index 00f0a20e..0b778bfa 100644 --- a/samples/BehaviorsTestApplication/BehaviorsTestApplication.csproj +++ b/samples/BehaviorsTestApplication/BehaviorsTestApplication.csproj @@ -6,17 +6,25 @@ False False enable + true - + + + + + + + + diff --git a/samples/BehaviorsTestApplication/Views/ItemView.axaml b/samples/BehaviorsTestApplication/Controls/EditableItem.axaml similarity index 50% rename from samples/BehaviorsTestApplication/Views/ItemView.axaml rename to samples/BehaviorsTestApplication/Controls/EditableItem.axaml index 916cc3d4..6d852f25 100644 --- a/samples/BehaviorsTestApplication/Views/ItemView.axaml +++ b/samples/BehaviorsTestApplication/Controls/EditableItem.axaml @@ -1,18 +1,15 @@  + x:Class="BehaviorsTestApplication.Controls.EditableItem" + Name="EditableItemUserControl"> - - - - + + + + - - - - - - + Text="{Binding #EditableItemUserControl.Text, Mode=TwoWay}"> + + + + + + diff --git a/samples/BehaviorsTestApplication/Controls/EditableItem.axaml.cs b/samples/BehaviorsTestApplication/Controls/EditableItem.axaml.cs new file mode 100644 index 00000000..e76c79d8 --- /dev/null +++ b/samples/BehaviorsTestApplication/Controls/EditableItem.axaml.cs @@ -0,0 +1,29 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Markup.Xaml; + +namespace BehaviorsTestApplication.Controls; + +public partial class EditableItem : UserControl +{ + public static readonly StyledProperty TextProperty = + TextBlock.TextProperty.AddOwner(new( + defaultBindingMode: BindingMode.TwoWay)); + + public string? Text + { + get => GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public EditableItem() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } +} diff --git a/samples/BehaviorsTestApplication/Program.cs b/samples/BehaviorsTestApplication/Program.cs index 3259a67b..41a27396 100644 --- a/samples/BehaviorsTestApplication/Program.cs +++ b/samples/BehaviorsTestApplication/Program.cs @@ -1,7 +1,6 @@ using System; using Avalonia; using Avalonia.ReactiveUI; -using Avalonia.Xaml.Interactions.Core; using Avalonia.Xaml.Interactivity; namespace BehaviorsTestApplication; @@ -19,6 +18,7 @@ public static AppBuilder BuildAvaloniaApp() GC.KeepAlive(typeof(Interaction).Assembly); GC.KeepAlive(typeof(ComparisonConditionType).Assembly); return AppBuilder.Configure() + .WithInterFont() .UsePlatformDetect() .UseReactiveUI() .LogToTrace(); diff --git a/samples/BehaviorsTestApplication/ViewModels/ItemViewModel.cs b/samples/BehaviorsTestApplication/ViewModels/ItemViewModel.cs index a4336efa..3bd29f7e 100644 --- a/samples/BehaviorsTestApplication/ViewModels/ItemViewModel.cs +++ b/samples/BehaviorsTestApplication/ViewModels/ItemViewModel.cs @@ -1,17 +1,17 @@ using System.Collections.ObjectModel; -using BehaviorsTestApplication.ViewModels.Core; using ReactiveUI; namespace BehaviorsTestApplication.ViewModels; -public class ItemViewModel(string name) : ViewModelBase +public class ItemViewModel(string value) : ViewModelBase { private ObservableCollection? _items; + private string? _value = value; - public string Name + public string? Value { - get => name; - set => this.RaiseAndSetIfChanged(ref name, value); + get => _value; + set => this.RaiseAndSetIfChanged(ref _value, value); } public ObservableCollection? Items @@ -20,5 +20,5 @@ public ObservableCollection? Items set => this.RaiseAndSetIfChanged(ref _items, value); } - public override string ToString() => name; -} \ No newline at end of file + public override string ToString() => _value ?? string.Empty; +} diff --git a/samples/BehaviorsTestApplication/ViewModels/MainWindowViewModel.cs b/samples/BehaviorsTestApplication/ViewModels/MainWindowViewModel.cs index 599439d3..20bde54f 100644 --- a/samples/BehaviorsTestApplication/ViewModels/MainWindowViewModel.cs +++ b/samples/BehaviorsTestApplication/ViewModels/MainWindowViewModel.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.Reactive.Linq; using System.Windows.Input; -using BehaviorsTestApplication.ViewModels.Core; using ReactiveUI; namespace BehaviorsTestApplication.ViewModels; @@ -48,63 +46,57 @@ public MainWindowViewModel() MoveLeftCommand = ReactiveCommand.Create(() => Position -= 5.0); MoveRightCommand = ReactiveCommand.Create(() => Position += 5.0); ResetMoveCommand = ReactiveCommand.Create(() => Position = 100.0); - Items = new ObservableCollection() - { + Items = + [ new("First Item") { - Items = new ObservableCollection() - { - new("First Item Sub Item 1"), - new("First Item Sub Item 2"), - new("First Item Sub Item 3"), - } + Items = + [ + new("First Item Sub Item 1"), new("First Item Sub Item 2"), new("First Item Sub Item 3") + ] }, + new("Second Item") { - Items = new ObservableCollection() - { - new("Second Item Sub Item 1"), - new("Second Item Sub Item 2"), - new("Second Item Sub Item 3"), - } + Items = + [ + new("Second Item Sub Item 1"), new("Second Item Sub Item 2"), new("Second Item Sub Item 3") + ] }, + new("Third Item") { - Items = new ObservableCollection() - { - new("Third Item Sub Item 1"), - new("Third Item Sub Item 2"), - new("Third Item Sub Item 3"), - } + Items = + [ + new("Third Item Sub Item 1"), new("Third Item Sub Item 2"), new("Third Item Sub Item 3") + ] }, + new("Fourth Item") { - Items = new ObservableCollection() - { - new("Fourth Item Sub Item 1"), - new("Fourth Item Sub Item 2"), - new("Fourth Item Sub Item 3"), - } + Items = + [ + new("Fourth Item Sub Item 1"), new("Fourth Item Sub Item 2"), new("Fourth Item Sub Item 3") + ] }, + new("Fifth Item") { - Items = new ObservableCollection() - { - new("Fifth Item Sub Item 1"), - new("Fifth Item Sub Item 2"), - new("Fifth Item Sub Item 3"), - } + Items = + [ + new("Fifth Item Sub Item 1"), new("Fifth Item Sub Item 2"), new("Fifth Item Sub Item 3") + ] }, + new("Sixth Item") { - Items = new ObservableCollection() - { - new("Sixth Item Sub Item 1"), - new("Sixth Item Sub Item 2"), - new("Sixth Item Sub Item 3"), - } - }, - }; + Items = + [ + new("Sixth Item Sub Item 1"), new("Sixth Item Sub Item 2"), new("Sixth Item Sub Item 3") + ] + } + + ]; Values = Observable.Interval(TimeSpan.FromSeconds(1)).Select(_ => _value++); } diff --git a/samples/BehaviorsTestApplication/ViewModels/Core/ViewModelBase.cs b/samples/BehaviorsTestApplication/ViewModels/ViewModelBase.cs similarity index 59% rename from samples/BehaviorsTestApplication/ViewModels/Core/ViewModelBase.cs rename to samples/BehaviorsTestApplication/ViewModels/ViewModelBase.cs index d41a80eb..667f7b58 100644 --- a/samples/BehaviorsTestApplication/ViewModels/Core/ViewModelBase.cs +++ b/samples/BehaviorsTestApplication/ViewModels/ViewModelBase.cs @@ -1,7 +1,7 @@ using ReactiveUI; -namespace BehaviorsTestApplication.ViewModels.Core; +namespace BehaviorsTestApplication.ViewModels; public abstract class ViewModelBase : ReactiveObject { -} \ No newline at end of file +} diff --git a/samples/BehaviorsTestApplication/Views/ItemView.axaml.cs b/samples/BehaviorsTestApplication/Views/ItemView.axaml.cs deleted file mode 100644 index 364f18fd..00000000 --- a/samples/BehaviorsTestApplication/Views/ItemView.axaml.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; - -namespace BehaviorsTestApplication.Views; - -public partial class ItemView : UserControl -{ - public ItemView() - { - InitializeComponent(); - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } -} diff --git a/samples/BehaviorsTestApplication/Views/MainView.axaml b/samples/BehaviorsTestApplication/Views/MainView.axaml index 8ee4e0de..3856c46a 100644 --- a/samples/BehaviorsTestApplication/Views/MainView.axaml +++ b/samples/BehaviorsTestApplication/Views/MainView.axaml @@ -5,9 +5,8 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="using:BehaviorsTestApplication.ViewModels" - x:CompileBindings="True" x:DataType="vm:MainWindowViewModel" - mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="700" - FontFamily="avares://Avalonia.Fonts.Inter/Assets#Inter"> + x:DataType="vm:MainWindowViewModel" + mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="700"> - - + - - - + - - - + + diff --git a/samples/BehaviorsTestApplication/Views/Pages/AdvancedView.axaml b/samples/BehaviorsTestApplication/Views/Pages/AdvancedView.axaml index cd99fd60..71bc5c75 100644 --- a/samples/BehaviorsTestApplication/Views/Pages/AdvancedView.axaml +++ b/samples/BehaviorsTestApplication/Views/Pages/AdvancedView.axaml @@ -1,12 +1,10 @@  @@ -17,30 +15,30 @@ Width="100" Height="50" Grid.Row="1" Grid.Column="0" Margin="5,0,0,5"> - - + - - - - - + - - - - - + + + xmlns:vm="clr-namespace:BehaviorsTestApplication.ViewModels" + mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="450" + x:DataType="vm:MainWindowViewModel"> - - - - - - - - - - - + + + + + + + + + + + diff --git a/samples/BehaviorsTestApplication/Views/Pages/CallMethodActionView.axaml b/samples/BehaviorsTestApplication/Views/Pages/CallMethodActionView.axaml index 0b8c1265..442b1e14 100644 --- a/samples/BehaviorsTestApplication/Views/Pages/CallMethodActionView.axaml +++ b/samples/BehaviorsTestApplication/Views/Pages/CallMethodActionView.axaml @@ -1,12 +1,10 @@  @@ -24,25 +22,25 @@ Foreground="{DynamicResource YellowBrush}" /> diff --git a/samples/BehaviorsTestApplication/Views/Pages/ChangeAvaloniaPropertyActionView.axaml b/samples/BehaviorsTestApplication/Views/Pages/ChangeAvaloniaPropertyActionView.axaml index d697f08b..dec8b612 100644 --- a/samples/BehaviorsTestApplication/Views/Pages/ChangeAvaloniaPropertyActionView.axaml +++ b/samples/BehaviorsTestApplication/Views/Pages/ChangeAvaloniaPropertyActionView.axaml @@ -1,13 +1,10 @@  @@ -20,26 +17,26 @@ Stroke="{DynamicResource GrayBrush}" StrokeThickness="5" /> diff --git a/samples/BehaviorsTestApplication/Views/Pages/ChangePropertyActionView.axaml b/samples/BehaviorsTestApplication/Views/Pages/ChangePropertyActionView.axaml index b961046e..980bab67 100644 --- a/samples/BehaviorsTestApplication/Views/Pages/ChangePropertyActionView.axaml +++ b/samples/BehaviorsTestApplication/Views/Pages/ChangePropertyActionView.axaml @@ -1,12 +1,10 @@  @@ -19,22 +17,22 @@ Stroke="{DynamicResource GrayBrush}" StrokeThickness="5" /> diff --git a/samples/BehaviorsTestApplication/Views/Pages/CustomActionView.axaml b/samples/BehaviorsTestApplication/Views/Pages/CustomActionView.axaml index 8b04832b..4972ccbb 100644 --- a/samples/BehaviorsTestApplication/Views/Pages/CustomActionView.axaml +++ b/samples/BehaviorsTestApplication/Views/Pages/CustomActionView.axaml @@ -1,13 +1,10 @@  @@ -29,10 +26,10 @@ Background="{DynamicResource GrayBrush}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> - - - - + + + + - - - + + + diff --git a/samples/BehaviorsTestApplication/Views/Pages/CustomBehaviorView.axaml b/samples/BehaviorsTestApplication/Views/Pages/CustomBehaviorView.axaml index a5ad2be4..8844986f 100644 --- a/samples/BehaviorsTestApplication/Views/Pages/CustomBehaviorView.axaml +++ b/samples/BehaviorsTestApplication/Views/Pages/CustomBehaviorView.axaml @@ -1,24 +1,22 @@  @@ -35,18 +33,18 @@ Height="125" Width="125" StrokeThickness="0" HorizontalAlignment="Left"> - - - + + + - - - + + + diff --git a/samples/BehaviorsTestApplication/Views/Pages/DataTriggerBehaviorView.axaml b/samples/BehaviorsTestApplication/Views/Pages/DataTriggerBehaviorView.axaml index 1758335f..8bd9f134 100644 --- a/samples/BehaviorsTestApplication/Views/Pages/DataTriggerBehaviorView.axaml +++ b/samples/BehaviorsTestApplication/Views/Pages/DataTriggerBehaviorView.axaml @@ -1,12 +1,10 @@  @@ -17,22 +15,22 @@ Fill="{DynamicResource BlueBrush}" Stroke="{DynamicResource GrayBrush}" StrokeThickness="5"> - - + - - - + - - - + + @@ -20,7 +20,7 @@ - + diff --git a/samples/BehaviorsTestApplication/Views/Pages/EditableTreeViewView.axaml b/samples/BehaviorsTestApplication/Views/Pages/EditableTreeViewView.axaml index 17ef8a1e..9a11edbc 100644 --- a/samples/BehaviorsTestApplication/Views/Pages/EditableTreeViewView.axaml +++ b/samples/BehaviorsTestApplication/Views/Pages/EditableTreeViewView.axaml @@ -4,7 +4,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="using:BehaviorsTestApplication.ViewModels" - xmlns:views="clr-namespace:BehaviorsTestApplication.Views" + xmlns:controls="clr-namespace:BehaviorsTestApplication.Controls" x:CompileBindings="True" x:DataType="vm:MainWindowViewModel" mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="450"> @@ -19,9 +19,9 @@ - - + diff --git a/samples/BehaviorsTestApplication/Views/Pages/EventTriggerBehaviorView.axaml b/samples/BehaviorsTestApplication/Views/Pages/EventTriggerBehaviorView.axaml index 2222397b..6ae54483 100644 --- a/samples/BehaviorsTestApplication/Views/Pages/EventTriggerBehaviorView.axaml +++ b/samples/BehaviorsTestApplication/Views/Pages/EventTriggerBehaviorView.axaml @@ -1,12 +1,10 @@  @@ -14,11 +12,11 @@ diff --git a/samples/BehaviorsTestApplication/Views/Pages/InvokeCommandActionView.axaml b/samples/BehaviorsTestApplication/Views/Pages/InvokeCommandActionView.axaml index 69345c2d..9f2c6c78 100644 --- a/samples/BehaviorsTestApplication/Views/Pages/InvokeCommandActionView.axaml +++ b/samples/BehaviorsTestApplication/Views/Pages/InvokeCommandActionView.axaml @@ -1,13 +1,11 @@  @@ -22,26 +20,26 @@ Height="100" Width="100" /> diff --git a/samples/BehaviorsTestApplication/Views/Pages/RoutedEventTriggerBehaviorView.axaml b/samples/BehaviorsTestApplication/Views/Pages/RoutedEventTriggerBehaviorView.axaml index d3dae44f..26c76630 100644 --- a/samples/BehaviorsTestApplication/Views/Pages/RoutedEventTriggerBehaviorView.axaml +++ b/samples/BehaviorsTestApplication/Views/Pages/RoutedEventTriggerBehaviorView.axaml @@ -1,13 +1,10 @@  @@ -15,33 +12,33 @@ - - + - - - + - - - + + diff --git a/samples/BehaviorsTestApplication/Views/Pages/SlidingAnimationView.axaml b/samples/BehaviorsTestApplication/Views/Pages/SlidingAnimationView.axaml new file mode 100644 index 00000000..b5eddb54 --- /dev/null +++ b/samples/BehaviorsTestApplication/Views/Pages/SlidingAnimationView.axaml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/BehaviorsTestApplication/Views/Pages/SlidingAnimationView.axaml.cs b/samples/BehaviorsTestApplication/Views/Pages/SlidingAnimationView.axaml.cs new file mode 100644 index 00000000..81d59b52 --- /dev/null +++ b/samples/BehaviorsTestApplication/Views/Pages/SlidingAnimationView.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace BehaviorsTestApplication.Views.Pages; + +public partial class SlidingAnimationView : UserControl +{ + public SlidingAnimationView() + { + InitializeComponent(); + } +} diff --git a/samples/BehaviorsTestApplication/Views/Pages/ValueChangedTriggerBehaviorView.axaml b/samples/BehaviorsTestApplication/Views/Pages/ValueChangedTriggerBehaviorView.axaml index fe45397f..933481ac 100644 --- a/samples/BehaviorsTestApplication/Views/Pages/ValueChangedTriggerBehaviorView.axaml +++ b/samples/BehaviorsTestApplication/Views/Pages/ValueChangedTriggerBehaviorView.axaml @@ -1,26 +1,23 @@  - - - + + - - + + diff --git a/samples/Directory.Packages.props b/samples/Directory.Packages.props index 15e650f6..ad36907f 100644 --- a/samples/Directory.Packages.props +++ b/samples/Directory.Packages.props @@ -1,7 +1,7 @@  true - 11.0.10 + 11.1.4 diff --git a/samples/DragAndDropSample/App.axaml b/samples/DragAndDropSample/App.axaml index 197d3404..1295c48e 100644 --- a/samples/DragAndDropSample/App.axaml +++ b/samples/DragAndDropSample/App.axaml @@ -1,13 +1,8 @@ - - - - diff --git a/samples/DragAndDropSample/Behaviors/ContextDragWithDirectionBehavior.cs b/samples/DragAndDropSample/Behaviors/ContextDragWithDirectionBehavior.cs index 57ce999a..fb1ac523 100644 --- a/samples/DragAndDropSample/Behaviors/ContextDragWithDirectionBehavior.cs +++ b/samples/DragAndDropSample/Behaviors/ContextDragWithDirectionBehavior.cs @@ -12,7 +12,7 @@ namespace DragAndDropSample.Behaviors; /// /// /// -public sealed class ContextDragWithDirectionBehavior : Behavior +public sealed class ContextDragWithDirectionBehavior : StyledElementBehavior { private Point _dragStartPoint; private PointerEventArgs? _triggerEvent; diff --git a/samples/DragAndDropSample/DragAndDropSample.csproj b/samples/DragAndDropSample/DragAndDropSample.csproj index ae9dd2e0..097c89df 100644 --- a/samples/DragAndDropSample/DragAndDropSample.csproj +++ b/samples/DragAndDropSample/DragAndDropSample.csproj @@ -6,6 +6,7 @@ False False enable + true @@ -16,7 +17,7 @@ - + diff --git a/samples/DragAndDropSample/Program.cs b/samples/DragAndDropSample/Program.cs index ea7ece78..bd74d31f 100644 --- a/samples/DragAndDropSample/Program.cs +++ b/samples/DragAndDropSample/Program.cs @@ -1,8 +1,6 @@ using System; using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; using Avalonia.ReactiveUI; -using Avalonia.Xaml.Interactions.Core; using Avalonia.Xaml.Interactivity; namespace DragAndDropSample; diff --git a/samples/DragAndDropSample/ViewLocator.cs b/samples/DragAndDropSample/ViewLocator.cs deleted file mode 100644 index e9f17949..00000000 --- a/samples/DragAndDropSample/ViewLocator.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using Avalonia.Controls; -using Avalonia.Controls.Templates; -using DragAndDropSample.ViewModels; - -namespace DragAndDropSample; - -public class ViewLocator : IDataTemplate -{ - public Control Build(object? data) - { - var name = data?.GetType().FullName?.Replace("ViewModel", "View"); - var type = name is null ? null : Type.GetType(name); - - if (type != null) - { - return (Control)Activator.CreateInstance(type)!; - } - else - { - return new TextBlock {Text = "Not Found: " + name}; - } - } - - public bool Match(object? data) - { - return data is ViewModelBase; - } -} diff --git a/samples/DragAndDropSample/ViewModels/MainWindowViewModel.cs b/samples/DragAndDropSample/ViewModels/MainWindowViewModel.cs index eab18594..2cb10554 100644 --- a/samples/DragAndDropSample/ViewModels/MainWindowViewModel.cs +++ b/samples/DragAndDropSample/ViewModels/MainWindowViewModel.cs @@ -22,53 +22,53 @@ public ObservableCollection Nodes public MainWindowViewModel() { - _items = new ObservableCollection() - { + _items = + [ new() { Title = "Item0" }, new() { Title = "Item1" }, new() { Title = "Item2" }, new() { Title = "Item3" }, new() { Title = "Item4" } - }; + ]; var node0 = new NodeViewModel() { Title = "Node0" }; - node0.Nodes = new ObservableCollection() - { - new() { Title = "Node0-0", Parent = node0}, - new() { Title = "Node0-1", Parent = node0}, - new() { Title = "Node0-2", Parent = node0}, - }; + node0.Nodes = + [ + new() { Title = "Node0-0", Parent = node0 }, + new() { Title = "Node0-1", Parent = node0 }, + new() { Title = "Node0-2", Parent = node0 } + ]; var node1 = new NodeViewModel() { Title = "Node1" }; - node1.Nodes = new ObservableCollection() - { - new() { Title = "Node1-0", Parent = node1}, - new() { Title = "Node1-1", Parent = node1}, - new() { Title = "Node1-2", Parent = node1}, - }; + node1.Nodes = + [ + new() { Title = "Node1-0", Parent = node1 }, + new() { Title = "Node1-1", Parent = node1 }, + new() { Title = "Node1-2", Parent = node1 } + ]; var node2 = new NodeViewModel() { Title = "Node2" }; - node2.Nodes = new ObservableCollection() - { - new() { Title = "Node2-0", Parent = node2}, - new() { Title = "Node2-1", Parent = node2}, - new() { Title = "Node2-2", Parent = node2}, - }; + node2.Nodes = + [ + new() { Title = "Node2-0", Parent = node2 }, + new() { Title = "Node2-1", Parent = node2 }, + new() { Title = "Node2-2", Parent = node2 } + ]; - _nodes = new ObservableCollection() - { + _nodes = + [ node0, node1, node2 - }; + ]; } } \ No newline at end of file diff --git a/samples/DragAndDropSample/Views/MainWindow.axaml b/samples/DragAndDropSample/Views/MainWindow.axaml index e6bfcbb7..74c0e786 100644 --- a/samples/DragAndDropSample/Views/MainWindow.axaml +++ b/samples/DragAndDropSample/Views/MainWindow.axaml @@ -2,15 +2,14 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="using:DragAndDropSample.ViewModels" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity" - xmlns:idd="clr-namespace:Avalonia.Xaml.Interactions.DragAndDrop;assembly=Avalonia.Xaml.Interactions.DragAndDrop" xmlns:b="using:DragAndDropSample.Behaviors" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="650" d:DesignHeight="450" x:Class="DragAndDropSample.Views.MainWindow" Icon="/Assets/avalonia-logo.ico" Width="650" WindowStartupLocation="CenterScreen" - Title="DragAndDropSample"> + Title="DragAndDropSample" + x:DataType="vm:MainWindowViewModel"> @@ -28,23 +27,23 @@ - - - - - - + + + + + + @@ -52,23 +51,23 @@ - - - - - - + + + + + + @@ -76,23 +75,23 @@ - - - - - - + + + + + + @@ -103,12 +102,12 @@ - - - - - - + + + + + + @@ -131,12 +130,12 @@ diff --git a/samples/DraggableDemo/DraggableDemo.csproj b/samples/DraggableDemo/DraggableDemo.csproj index 449be893..341ac4fb 100644 --- a/samples/DraggableDemo/DraggableDemo.csproj +++ b/samples/DraggableDemo/DraggableDemo.csproj @@ -6,6 +6,7 @@ False False enable + true @@ -15,7 +16,7 @@ - + diff --git a/samples/DraggableDemo/MainWindow.axaml b/samples/DraggableDemo/MainWindow.axaml index 22027d4e..f90cabec 100644 --- a/samples/DraggableDemo/MainWindow.axaml +++ b/samples/DraggableDemo/MainWindow.axaml @@ -2,13 +2,11 @@ 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" - xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity" - xmlns:id="clr-namespace:Avalonia.Xaml.Interactions.Draggable;assembly=Avalonia.Xaml.Interactions.Draggable" xmlns:local="clr-namespace:DraggableDemo" xmlns:models="clr-namespace:DraggableDemo.Models" mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="550" x:Class="DraggableDemo.MainWindow" - x:CompileBindings="True" x:DataType="local:MainWindow" + x:DataType="local:MainWindow" WindowState="Normal" WindowStartupLocation="CenterScreen" Width="1200" Height="550" Title="Draggable Controls Demo"> @@ -172,12 +170,12 @@ @@ -40,12 +39,12 @@ @@ -67,12 +66,12 @@ @@ -94,12 +93,12 @@ @@ -110,36 +109,36 @@ diff --git a/src/Avalonia.Xaml.Interactions.Events/Avalonia.Xaml.Interactions.Events.csproj b/src/Avalonia.Xaml.Interactions.Events/Avalonia.Xaml.Interactions.Events.csproj index e9ffffce..297eca68 100644 --- a/src/Avalonia.Xaml.Interactions.Events/Avalonia.Xaml.Interactions.Events.csproj +++ b/src/Avalonia.Xaml.Interactions.Events/Avalonia.Xaml.Interactions.Events.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Avalonia.Xaml.Interactions.Events/Core/InteractiveBehaviorBase.cs b/src/Avalonia.Xaml.Interactions.Events/Core/InteractiveBehaviorBase.cs new file mode 100644 index 00000000..729ed848 --- /dev/null +++ b/src/Avalonia.Xaml.Interactions.Events/Core/InteractiveBehaviorBase.cs @@ -0,0 +1,27 @@ +using Avalonia.Interactivity; +using Avalonia.Xaml.Interactivity; + +namespace Avalonia.Xaml.Interactions.Events; + +/// +/// +/// +public abstract class InteractiveBehaviorBase : StyledElementBehavior +{ + /// + /// + /// + public static readonly StyledProperty RoutingStrategiesProperty = + AvaloniaProperty.Register( + nameof(RoutingStrategies), + RoutingStrategies.Bubble); + + /// + /// + /// + public RoutingStrategies RoutingStrategies + { + get => GetValue(RoutingStrategiesProperty); + set => SetValue(RoutingStrategiesProperty, value); + } +} diff --git a/src/Avalonia.Xaml.Interactions.Events/DoubleTappedEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/DoubleTappedEventBehavior.cs index da29295f..ab107a67 100644 --- a/src/Avalonia.Xaml.Interactions.Events/DoubleTappedEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/DoubleTappedEventBehavior.cs @@ -1,32 +1,13 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class DoubleTappedEventBehavior : Behavior +public abstract class DoubleTappedEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies - { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); - } - - /// protected override void OnAttachedToVisualTree() { diff --git a/src/Avalonia.Xaml.Interactions.Events/GotFocusEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/GotFocusEventBehavior.cs index f7014515..238f98f1 100644 --- a/src/Avalonia.Xaml.Interactions.Events/GotFocusEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/GotFocusEventBehavior.cs @@ -1,32 +1,12 @@ using Avalonia.Input; -using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class GotFocusEventBehavior : Behavior +public abstract class GotFocusEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Bubble); - - - /// - /// - /// - public RoutingStrategies RoutingStrategies - { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); - } - /// protected override void OnAttachedToVisualTree() { diff --git a/src/Avalonia.Xaml.Interactions.Events/KeyDownEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/KeyDownEventBehavior.cs index ae00e605..3c11c4ee 100644 --- a/src/Avalonia.Xaml.Interactions.Events/KeyDownEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/KeyDownEventBehavior.cs @@ -1,29 +1,18 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class KeyDownEventBehavior : Behavior +public abstract class KeyDownEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Tunnel | RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies + static KeyDownEventBehavior() { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); + RoutingStrategiesProperty.OverrideMetadata( + new StyledPropertyMetadata( + defaultValue: RoutingStrategies.Tunnel | RoutingStrategies.Bubble)); } /// diff --git a/src/Avalonia.Xaml.Interactions.Events/KeyUpEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/KeyUpEventBehavior.cs index dbb85919..dd4d7e25 100644 --- a/src/Avalonia.Xaml.Interactions.Events/KeyUpEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/KeyUpEventBehavior.cs @@ -1,29 +1,18 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class KeyUpEventBehavior : Behavior +public abstract class KeyUpEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Tunnel | RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies + static KeyUpEventBehavior() { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); + RoutingStrategiesProperty.OverrideMetadata( + new StyledPropertyMetadata( + defaultValue: RoutingStrategies.Tunnel | RoutingStrategies.Bubble)); } /// diff --git a/src/Avalonia.Xaml.Interactions.Events/LostFocusEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/LostFocusEventBehavior.cs index c93da4cc..27ca9d8d 100644 --- a/src/Avalonia.Xaml.Interactions.Events/LostFocusEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/LostFocusEventBehavior.cs @@ -1,31 +1,13 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class LostFocusEventBehavior : Behavior +public abstract class LostFocusEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies - { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); - } - /// protected override void OnAttachedToVisualTree() { diff --git a/src/Avalonia.Xaml.Interactions.Events/PointerCaptureLostEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/PointerCaptureLostEventBehavior.cs index fde7fdbd..3c9330a5 100644 --- a/src/Avalonia.Xaml.Interactions.Events/PointerCaptureLostEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/PointerCaptureLostEventBehavior.cs @@ -1,31 +1,20 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class PointerCaptureLostEventBehavior : Behavior +public abstract class PointerCaptureLostEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Direct); - - /// - /// - /// - public RoutingStrategies RoutingStrategies + static PointerCaptureLostEventBehavior() { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); + RoutingStrategiesProperty.OverrideMetadata( + new StyledPropertyMetadata( + defaultValue: RoutingStrategies.Direct)); } - + /// protected override void OnAttachedToVisualTree() { diff --git a/src/Avalonia.Xaml.Interactions.Events/PointerEnteredEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/PointerEnteredEventBehavior.cs index 0d3376a9..b2666a27 100644 --- a/src/Avalonia.Xaml.Interactions.Events/PointerEnteredEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/PointerEnteredEventBehavior.cs @@ -1,29 +1,18 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class PointerEnteredEventBehavior : Behavior +public abstract class PointerEnteredEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Direct); - - /// - /// - /// - public RoutingStrategies RoutingStrategies + static PointerEnteredEventBehavior() { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); + RoutingStrategiesProperty.OverrideMetadata( + new StyledPropertyMetadata( + defaultValue: RoutingStrategies.Direct)); } /// diff --git a/src/Avalonia.Xaml.Interactions.Events/PointerEventsBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/PointerEventsBehavior.cs index 39532aab..a0ae6c86 100644 --- a/src/Avalonia.Xaml.Interactions.Events/PointerEventsBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/PointerEventsBehavior.cs @@ -1,29 +1,18 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class PointerEventsBehavior : Behavior +public abstract class PointerEventsBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Tunnel | RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies + static PointerEventsBehavior() { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); + RoutingStrategiesProperty.OverrideMetadata( + new StyledPropertyMetadata( + defaultValue: RoutingStrategies.Tunnel | RoutingStrategies.Bubble)); } /// diff --git a/src/Avalonia.Xaml.Interactions.Events/PointerExitedEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/PointerExitedEventBehavior.cs index 0f88a74c..42eb285e 100644 --- a/src/Avalonia.Xaml.Interactions.Events/PointerExitedEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/PointerExitedEventBehavior.cs @@ -1,31 +1,21 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class PointerExitedEventBehavior : Behavior +public abstract class PointerExitedEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Direct); - - /// - /// - /// - public RoutingStrategies RoutingStrategies + static PointerExitedEventBehavior() { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); + RoutingStrategiesProperty.OverrideMetadata( + new StyledPropertyMetadata( + defaultValue: RoutingStrategies.Direct)); } + /// protected override void OnAttachedToVisualTree() { diff --git a/src/Avalonia.Xaml.Interactions.Events/PointerMovedEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/PointerMovedEventBehavior.cs index 1d21d205..faa4fd5d 100644 --- a/src/Avalonia.Xaml.Interactions.Events/PointerMovedEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/PointerMovedEventBehavior.cs @@ -1,29 +1,18 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class PointerMovedEventBehavior : Behavior +public abstract class PointerMovedEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Tunnel | RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies + static PointerMovedEventBehavior() { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); + RoutingStrategiesProperty.OverrideMetadata( + new StyledPropertyMetadata( + defaultValue: RoutingStrategies.Tunnel | RoutingStrategies.Bubble)); } /// diff --git a/src/Avalonia.Xaml.Interactions.Events/PointerPressedEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/PointerPressedEventBehavior.cs index 693c91cb..37862230 100644 --- a/src/Avalonia.Xaml.Interactions.Events/PointerPressedEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/PointerPressedEventBehavior.cs @@ -1,29 +1,18 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class PointerPressedEventBehavior : Behavior +public abstract class PointerPressedEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Tunnel | RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies + static PointerPressedEventBehavior() { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); + RoutingStrategiesProperty.OverrideMetadata( + new StyledPropertyMetadata( + defaultValue: RoutingStrategies.Tunnel | RoutingStrategies.Bubble)); } /// diff --git a/src/Avalonia.Xaml.Interactions.Events/PointerReleasedEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/PointerReleasedEventBehavior.cs index c452f730..af8e0763 100644 --- a/src/Avalonia.Xaml.Interactions.Events/PointerReleasedEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/PointerReleasedEventBehavior.cs @@ -1,29 +1,18 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class PointerReleasedEventBehavior : Behavior +public abstract class PointerReleasedEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Tunnel | RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies + static PointerReleasedEventBehavior() { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); + RoutingStrategiesProperty.OverrideMetadata( + new StyledPropertyMetadata( + defaultValue: RoutingStrategies.Tunnel | RoutingStrategies.Bubble)); } /// diff --git a/src/Avalonia.Xaml.Interactions.Events/PointerWheelChangedEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/PointerWheelChangedEventBehavior.cs index 71a338b1..f07b9509 100644 --- a/src/Avalonia.Xaml.Interactions.Events/PointerWheelChangedEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/PointerWheelChangedEventBehavior.cs @@ -1,29 +1,18 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class PointerWheelChangedEventBehavior : Behavior +public abstract class PointerWheelChangedEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Tunnel | RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies + static PointerWheelChangedEventBehavior() { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); + RoutingStrategiesProperty.OverrideMetadata( + new StyledPropertyMetadata( + defaultValue: RoutingStrategies.Tunnel | RoutingStrategies.Bubble)); } /// diff --git a/src/Avalonia.Xaml.Interactions.Events/RightTappedEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/RightTappedEventBehavior.cs index 5e2fd61f..3292a75e 100644 --- a/src/Avalonia.Xaml.Interactions.Events/RightTappedEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/RightTappedEventBehavior.cs @@ -1,31 +1,13 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class RightTappedEventBehavior : Behavior +public abstract class RightTappedEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies - { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); - } - /// protected override void OnAttachedToVisualTree() { diff --git a/src/Avalonia.Xaml.Interactions.Events/ScrollGestureEndedEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/ScrollGestureEndedEventBehavior.cs index a211c109..8212e59e 100644 --- a/src/Avalonia.Xaml.Interactions.Events/ScrollGestureEndedEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/ScrollGestureEndedEventBehavior.cs @@ -1,31 +1,12 @@ using Avalonia.Input; -using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class ScrollGestureEndedEventBehavior : Behavior +public abstract class ScrollGestureEndedEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies - { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); - } - /// protected override void OnAttachedToVisualTree() { diff --git a/src/Avalonia.Xaml.Interactions.Events/ScrollGestureEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/ScrollGestureEventBehavior.cs index 48ad94f1..1f069885 100644 --- a/src/Avalonia.Xaml.Interactions.Events/ScrollGestureEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/ScrollGestureEventBehavior.cs @@ -1,31 +1,12 @@ using Avalonia.Input; -using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class ScrollGestureEventBehavior : Behavior +public abstract class ScrollGestureEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies - { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); - } - /// protected override void OnAttachedToVisualTree() { diff --git a/src/Avalonia.Xaml.Interactions.Events/TappedEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/TappedEventBehavior.cs index 9741ba8d..039a89fe 100644 --- a/src/Avalonia.Xaml.Interactions.Events/TappedEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/TappedEventBehavior.cs @@ -1,31 +1,13 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class TappedEventBehavior : Behavior +public abstract class TappedEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies - { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); - } - /// protected override void OnAttachedToVisualTree() { diff --git a/src/Avalonia.Xaml.Interactions.Events/TextInputEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/TextInputEventBehavior.cs index fdc67a4f..04d00367 100644 --- a/src/Avalonia.Xaml.Interactions.Events/TextInputEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/TextInputEventBehavior.cs @@ -1,29 +1,18 @@ using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class TextInputEventBehavior : Behavior +public abstract class TextInputEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Tunnel | RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies + static TextInputEventBehavior() { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); + RoutingStrategiesProperty.OverrideMetadata( + new StyledPropertyMetadata( + defaultValue: RoutingStrategies.Tunnel | RoutingStrategies.Bubble)); } /// diff --git a/src/Avalonia.Xaml.Interactions.Events/TextInputMethodClientRequestedEventBehavior.cs b/src/Avalonia.Xaml.Interactions.Events/TextInputMethodClientRequestedEventBehavior.cs index 11d9f85b..d13c388f 100644 --- a/src/Avalonia.Xaml.Interactions.Events/TextInputMethodClientRequestedEventBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Events/TextInputMethodClientRequestedEventBehavior.cs @@ -1,30 +1,19 @@ using Avalonia.Input; using Avalonia.Input.TextInput; using Avalonia.Interactivity; -using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Events; /// /// /// -public abstract class TextInputMethodClientRequestedEventBehavior : Behavior +public abstract class TextInputMethodClientRequestedEventBehavior : InteractiveBehaviorBase { - /// - /// - /// - public static readonly StyledProperty RoutingStrategiesProperty = - AvaloniaProperty.Register( - nameof(RoutingStrategies), - RoutingStrategies.Tunnel | RoutingStrategies.Bubble); - - /// - /// - /// - public RoutingStrategies RoutingStrategies + static TextInputMethodClientRequestedEventBehavior() { - get => GetValue(RoutingStrategiesProperty); - set => SetValue(RoutingStrategiesProperty, value); + RoutingStrategiesProperty.OverrideMetadata( + new StyledPropertyMetadata( + defaultValue: RoutingStrategies.Tunnel | RoutingStrategies.Bubble)); } /// diff --git a/src/Avalonia.Xaml.Interactions.Responsive/AdaptiveBehavior.cs b/src/Avalonia.Xaml.Interactions.Responsive/AdaptiveBehavior.cs index 6a8a7dcd..a8fabeb0 100644 --- a/src/Avalonia.Xaml.Interactions.Responsive/AdaptiveBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Responsive/AdaptiveBehavior.cs @@ -8,9 +8,9 @@ namespace Avalonia.Xaml.Interactions.Responsive; /// -/// Observes control or control property changes and if triggered sets or removes style classes when conditions from are met. +/// Observes control or control property changes and if triggered sets or removes style classes when conditions from are met. /// -public class AdaptiveBehavior : Behavior +public class AdaptiveBehavior : StyledElementBehavior { private IDisposable? _disposable; private AvaloniaList? _setters; @@ -34,7 +34,7 @@ public class AdaptiveBehavior : Behavior AvaloniaProperty.RegisterDirect>(nameof(Setters), t => t.Setters); /// - /// Gets or sets the the source control that property are observed from, if not set is used. This is a avalonia property. + /// Gets or sets the the source control that property are observed from, if not set is used. This is a avalonia property. /// [ResolveByName] public Control? SourceControl @@ -44,7 +44,7 @@ public Control? SourceControl } /// - /// Gets or sets the target control that class name that should be added or removed when triggered, if not set is used or from . This is a avalonia property. + /// Gets or sets the target control that class name that should be added or removed when triggered, if not set is used or from . This is a avalonia property. /// [ResolveByName] public Control? TargetControl @@ -57,7 +57,7 @@ public Control? TargetControl /// Gets adaptive class setters collection. This is a avalonia property. /// [Content] - public AvaloniaList Setters => _setters ??= new AvaloniaList(); + public AvaloniaList Setters => _setters ??= []; /// protected override void OnAttachedToVisualTree() diff --git a/src/Avalonia.Xaml.Interactions.Responsive/AdaptiveClassSetter.cs b/src/Avalonia.Xaml.Interactions.Responsive/AdaptiveClassSetter.cs index 65987908..d387a590 100644 --- a/src/Avalonia.Xaml.Interactions.Responsive/AdaptiveClassSetter.cs +++ b/src/Avalonia.Xaml.Interactions.Responsive/AdaptiveClassSetter.cs @@ -167,7 +167,7 @@ public bool IsPseudoClass } /// - /// Gets or sets the target control that class name that should be added or removed when triggered, if not set is used or from . This is a avalonia property. + /// Gets or sets the target control that class name that should be added or removed when triggered, if not set is used or from . This is a avalonia property. /// [ResolveByName] public Control? TargetControl diff --git a/src/Avalonia.Xaml.Interactions.Responsive/Avalonia.Xaml.Interactions.Responsive.csproj b/src/Avalonia.Xaml.Interactions.Responsive/Avalonia.Xaml.Interactions.Responsive.csproj index de98e9a7..3229a4c8 100644 --- a/src/Avalonia.Xaml.Interactions.Responsive/Avalonia.Xaml.Interactions.Responsive.csproj +++ b/src/Avalonia.Xaml.Interactions.Responsive/Avalonia.Xaml.Interactions.Responsive.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Avalonia.Xaml.Interactions/Avalonia.Xaml.Interactions.csproj b/src/Avalonia.Xaml.Interactions/Avalonia.Xaml.Interactions.csproj index b1fef9c4..8f96ba71 100644 --- a/src/Avalonia.Xaml.Interactions/Avalonia.Xaml.Interactions.csproj +++ b/src/Avalonia.Xaml.Interactions/Avalonia.Xaml.Interactions.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Avalonia.Xaml.Interactions/Core/CallMethodAction.cs b/src/Avalonia.Xaml.Interactions/Core/CallMethodAction.cs index 898865de..5c8474b3 100644 --- a/src/Avalonia.Xaml.Interactions/Core/CallMethodAction.cs +++ b/src/Avalonia.Xaml.Interactions/Core/CallMethodAction.cs @@ -14,23 +14,18 @@ namespace Avalonia.Xaml.Interactions.Core; /// /// An action that calls a method on a specified object when invoked. /// -public class CallMethodAction : AvaloniaObject, IAction +[RequiresUnreferencedCode("This functionality is not compatible with trimming.")] +public class CallMethodAction : Avalonia.Xaml.Interactivity.Action { private Type? _targetObjectType; - private readonly List _methodDescriptors = new(); + private readonly List _methodDescriptors = []; private MethodDescriptor? _cachedMethodDescriptor; - /// - /// Identifies the avalonia property. - /// - public static readonly StyledProperty IsEnabledProperty = - AvaloniaProperty.Register(nameof(IsEnabled), defaultValue: true); - /// /// Identifies the avalonia property. /// - public static readonly StyledProperty MethodNameProperty = - AvaloniaProperty.Register(nameof(MethodName)); + public static readonly StyledProperty MethodNameProperty = + AvaloniaProperty.Register(nameof(MethodName)); /// /// Identifies the avalonia property. @@ -38,20 +33,10 @@ public class CallMethodAction : AvaloniaObject, IAction public static readonly StyledProperty TargetObjectProperty = AvaloniaProperty.Register(nameof(TargetObject)); - /// - /// Gets or sets a value indicating whether this instance is enabled. - /// - /// true if this instance is enabled; otherwise, false. - public bool IsEnabled - { - get => GetValue(IsEnabledProperty); - set => SetValue(IsEnabledProperty, value); - } - /// /// Gets or sets the name of the method to invoke. This is a avalonia property. /// - public string MethodName + public string? MethodName { get => GetValue(MethodNameProperty); set => SetValue(MethodNameProperty, value); @@ -108,8 +93,7 @@ private static void TargetObjectChanged(AvaloniaPropertyChangedEventArgsThe that is passed to the action by the behavior. Generally this is or a target object. /// The value of this parameter is determined by the caller. /// True if the method is called; else false. - [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] - public virtual object Execute(object? sender, object? parameter) + public override object Execute(object? sender, object? parameter) { if (!IsEnabled) { @@ -146,7 +130,7 @@ public virtual object Execute(object? sender, object? parameter) methodDescriptor.MethodInfo.Invoke(target, null); return true; case 2: - methodDescriptor.MethodInfo.Invoke(target, new[] { target, parameter! }); + methodDescriptor.MethodInfo.Invoke(target, [target, parameter!]); return true; default: return false; diff --git a/src/Avalonia.Xaml.Interactions/Core/ChangePropertyAction.cs b/src/Avalonia.Xaml.Interactions/Core/ChangePropertyAction.cs index 0c009366..9412dfe4 100644 --- a/src/Avalonia.Xaml.Interactions/Core/ChangePropertyAction.cs +++ b/src/Avalonia.Xaml.Interactions/Core/ChangePropertyAction.cs @@ -11,10 +11,11 @@ namespace Avalonia.Xaml.Interactions.Core; /// /// An action that will change a specified property to a specified value when invoked. /// -public class ChangePropertyAction : AvaloniaObject, IAction +[RequiresUnreferencedCode("This functionality is not compatible with trimming.")] +public class ChangePropertyAction : Avalonia.Xaml.Interactivity.Action { - private static readonly char[] s_trimChars = { '(', ')' }; - private static readonly char[] s_separator = { '.' }; + private static readonly char[] s_trimChars = ['(', ')']; + private static readonly char[] s_separator = ['.']; [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] private static Type? GetTypeByName(string name) @@ -74,8 +75,8 @@ public class ChangePropertyAction : AvaloniaObject, IAction /// /// Identifies the avalonia property. /// - public static readonly StyledProperty PropertyNameProperty = - AvaloniaProperty.Register(nameof(PropertyName)); + public static readonly StyledProperty PropertyNameProperty = + AvaloniaProperty.Register(nameof(PropertyName)); /// /// Identifies the avalonia property. @@ -92,7 +93,7 @@ public class ChangePropertyAction : AvaloniaObject, IAction /// /// Gets or sets the name of the property to change. This is a avalonia property. /// - public string PropertyName + public string? PropertyName { get => GetValue(PropertyNameProperty); set => SetValue(PropertyNameProperty, value); @@ -124,8 +125,13 @@ public object? TargetObject /// The that is passed to the action by the behavior. Generally this is or a target object. /// The value of this parameter is determined by the caller. /// True if updating the property value succeeds; else false. - public virtual object Execute(object? sender, object? parameter) + public override object Execute(object? sender, object? parameter) { + if (!IsEnabled) + { + return false; + } + object? targetObject; if (GetValue(TargetObjectProperty) is not null) { @@ -141,11 +147,17 @@ public virtual object Execute(object? sender, object? parameter) return false; } + var propertyName = PropertyName; + if (propertyName is null) + { + return false; + } + if (targetObject is AvaloniaObject avaloniaObject) { - if (PropertyName.Contains('.')) + if (propertyName.Contains('.')) { - var avaloniaProperty = FindAttachedProperty(targetObject, PropertyName); + var avaloniaProperty = FindAttachedProperty(targetObject, propertyName); if (avaloniaProperty is not null) { UpdateAvaloniaPropertyValue(avaloniaObject, avaloniaProperty); @@ -156,7 +168,7 @@ public virtual object Execute(object? sender, object? parameter) } else { - var avaloniaProperty = AvaloniaPropertyRegistry.Instance.FindRegistered(avaloniaObject, PropertyName); + var avaloniaProperty = AvaloniaPropertyRegistry.Instance.FindRegistered(avaloniaObject, propertyName); if (avaloniaProperty is not null) { UpdateAvaloniaPropertyValue(avaloniaObject, avaloniaProperty); @@ -172,16 +184,22 @@ public virtual object Execute(object? sender, object? parameter) [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] private void UpdatePropertyValue(object targetObject) { + var propertyName = PropertyName; + if (propertyName is null) + { + return; + } + var targetType = targetObject.GetType(); var targetTypeName = targetType.Name; - var propertyInfo = targetType.GetRuntimeProperty(PropertyName); + var propertyInfo = targetType.GetRuntimeProperty(propertyName); if (propertyInfo is null) { throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, "Cannot find a property named {0} on type {1}.", - PropertyName, + propertyName, targetTypeName)); } else if (!propertyInfo.CanWrite) @@ -189,7 +207,7 @@ private void UpdatePropertyValue(object targetObject) throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, "Cannot find a property named {0} on type {1}.", - PropertyName, + propertyName, targetTypeName)); } @@ -218,7 +236,7 @@ private void UpdatePropertyValue(object targetObject) } } - propertyInfo.SetValue(targetObject, result, Array.Empty()); + propertyInfo.SetValue(targetObject, result, []); } catch (FormatException e) { @@ -235,7 +253,7 @@ private void UpdatePropertyValue(object targetObject) CultureInfo.CurrentCulture, "Cannot assign value of type {0} to property {1} of type {2}. The {1} property can be assigned only values of type {2}.", Value?.GetType().Name ?? "null", - PropertyName, + propertyName, propertyInfo.PropertyType.Name), innerException); } diff --git a/src/Avalonia.Xaml.Interactions/Core/DataTriggerBehavior.cs b/src/Avalonia.Xaml.Interactions/Core/DataTriggerBehavior.cs index 6f26fd3e..eee6855c 100644 --- a/src/Avalonia.Xaml.Interactions/Core/DataTriggerBehavior.cs +++ b/src/Avalonia.Xaml.Interactions/Core/DataTriggerBehavior.cs @@ -9,7 +9,8 @@ namespace Avalonia.Xaml.Interactions.Core; /// /// A behavior that performs actions when the bound data meets a specified condition. /// -public class DataTriggerBehavior : Trigger +[RequiresUnreferencedCode("This functionality is not compatible with trimming.")] +public class DataTriggerBehavior : StyledElementTrigger { /// /// Identifies the avalonia property. @@ -68,7 +69,6 @@ static DataTriggerBehavior() new AnonymousObserver>(OnValueChanged)); } - [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] private static bool Compare(object? leftOperand, ComparisonConditionType operatorType, object? rightOperand) { if (leftOperand is not null && rightOperand is not null) @@ -124,7 +124,6 @@ private static bool Compare(object? leftOperand, ComparisonConditionType operato /// /// Evaluates both operands that implement the IComparable interface. /// - [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] private static bool EvaluateComparable(IComparable leftOperand, ComparisonConditionType operatorType, IComparable rightOperand) { object? convertedOperand = null; @@ -166,6 +165,11 @@ private static void OnValueChanged(AvaloniaPropertyChangedEventArgs args) return; } + if (!behavior.IsEnabled) + { + return; + } + // NOTE: In UWP version binding null check is not present but Avalonia throws exception as Bindings are null when first initialized. var binding = behavior.Binding; if (binding is not null) diff --git a/src/Avalonia.Xaml.Interactions/Core/EventTriggerBehavior.cs b/src/Avalonia.Xaml.Interactions/Core/EventTriggerBehavior.cs index 9999a988..28fbf871 100644 --- a/src/Avalonia.Xaml.Interactions/Core/EventTriggerBehavior.cs +++ b/src/Avalonia.Xaml.Interactions/Core/EventTriggerBehavior.cs @@ -11,15 +11,16 @@ namespace Avalonia.Xaml.Interactions.Core; /// /// A behavior that listens for a specified event on its source and executes its actions when that event is fired. /// -public class EventTriggerBehavior : Trigger +[RequiresUnreferencedCode("This functionality is not compatible with trimming.")] +public class EventTriggerBehavior : StyledElementTrigger { private const string EventNameDefaultValue = "AttachedToVisualTree"; /// /// Identifies the avalonia property. /// - public static readonly StyledProperty EventNameProperty = - AvaloniaProperty.Register(nameof(EventName), EventNameDefaultValue); + public static readonly StyledProperty EventNameProperty = + AvaloniaProperty.Register(nameof(EventName), EventNameDefaultValue); /// /// Identifies the avalonia property. @@ -34,7 +35,7 @@ public class EventTriggerBehavior : Trigger /// /// Gets or sets the name of the event to listen for. This is a avalonia property. /// - public string EventName + public string? EventName { get => GetValue(EventNameProperty); set => SetValue(EventNameProperty, value); @@ -42,7 +43,7 @@ public string EventName /// /// Gets or sets the source object from which this behavior listens for events. - /// If is not set, the source will default to . This is a avalonia property. + /// If is not set, the source will default to . This is a avalonia property. /// [ResolveByName] public object? SourceObject @@ -96,7 +97,7 @@ private static void SourceObjectChanged(AvaloniaPropertyChangedEventArgs - /// Called after the behavior is attached to the . + /// Called after the behavior is attached to the . /// protected override void OnAttached() { @@ -105,7 +106,7 @@ protected override void OnAttached() } /// - /// Called when the behavior is being detached from its . + /// Called when the behavior is being detached from its . /// protected override void OnDetaching() { @@ -159,6 +160,11 @@ private void RegisterEvent(string eventName) { return; } + + if (EventName is null) + { + return; + } var sourceObjectType = _resolvedSource.GetType(); var eventInfo = sourceObjectType.GetRuntimeEvent(EventName); @@ -234,6 +240,11 @@ private void UnregisterEvent(string eventName) /// The event args. protected virtual void AttachedToVisualTree(object? sender, object eventArgs) { + if (!IsEnabled) + { + return; + } + Interaction.ExecuteActions(_resolvedSource, Actions, eventArgs); } diff --git a/src/Avalonia.Xaml.Interactions/Core/InvokeCommandAction.cs b/src/Avalonia.Xaml.Interactions/Core/InvokeCommandAction.cs index c5038331..96724103 100644 --- a/src/Avalonia.Xaml.Interactions/Core/InvokeCommandAction.cs +++ b/src/Avalonia.Xaml.Interactions/Core/InvokeCommandAction.cs @@ -7,14 +7,8 @@ namespace Avalonia.Xaml.Interactions.Core; /// /// Executes a specified when invoked. /// -public class InvokeCommandAction : AvaloniaObject, IAction +public class InvokeCommandAction : Avalonia.Xaml.Interactivity.Action { - /// - /// Identifies the avalonia property. - /// - public static readonly StyledProperty IsEnabledProperty = - AvaloniaProperty.Register(nameof(IsEnabled), defaultValue: true); - /// /// Identifies the avalonia property. /// @@ -46,16 +40,6 @@ public class InvokeCommandAction : AvaloniaObject, IAction public static readonly StyledProperty InputConverterLanguageProperty = AvaloniaProperty.Register(nameof(InputConverterLanguage), string.Empty); - /// - /// Gets or sets a value indicating whether this instance is enabled. - /// - /// true if this instance is enabled; otherwise, false. - public bool IsEnabled - { - get => GetValue(IsEnabledProperty); - set => SetValue(IsEnabledProperty, value); - } - /// /// Gets or sets the command this action should invoke. This is a avalonia property. /// @@ -119,7 +103,7 @@ public string? InputConverterLanguage /// The that is passed to the action by the behavior. Generally this is or a target object. /// The value of this parameter is determined by the caller. /// True if the command is successfully executed; else false. - public virtual object Execute(object? sender, object? parameter) + public override object Execute(object? sender, object? parameter) { if (IsEnabled != true || Command is null) { diff --git a/src/Avalonia.Xaml.Interactivity/Action.cs b/src/Avalonia.Xaml.Interactivity/Action.cs new file mode 100644 index 00000000..97574c51 --- /dev/null +++ b/src/Avalonia.Xaml.Interactivity/Action.cs @@ -0,0 +1,32 @@ +namespace Avalonia.Xaml.Interactivity; + +/// +/// A base class for action that calls a method on a specified object when invoked. +/// +public abstract class Action : AvaloniaObject, IAction +{ + /// + /// Identifies the avalonia property. + /// + public static readonly StyledProperty IsEnabledProperty = + AvaloniaProperty.Register(nameof(IsEnabled), defaultValue: true); + + /// + /// Gets or sets a value indicating whether this instance is enabled. + /// + /// true if this instance is enabled; otherwise, false. + public bool IsEnabled + { + get => GetValue(IsEnabledProperty); + set => SetValue(IsEnabledProperty, value); + } + + /// + /// Executes the action. + /// + /// The that is passed to the action by the behavior. Generally this is or a target object. + /// The value of this parameter is determined by the caller. + /// An example of parameter usage is EventTriggerBehavior, which passes the EventArgs as a parameter to its actions. + /// Returns the result of the action. + public abstract object? Execute(object? sender, object? parameter); +} diff --git a/src/Avalonia.Xaml.Interactivity/ActionCollection.cs b/src/Avalonia.Xaml.Interactivity/ActionCollection.cs index 4d4ec2c9..a995fdeb 100644 --- a/src/Avalonia.Xaml.Interactivity/ActionCollection.cs +++ b/src/Avalonia.Xaml.Interactivity/ActionCollection.cs @@ -28,8 +28,7 @@ private void ActionCollection_CollectionChanged(object? sender, NotifyCollection VerifyType(item); } } - else if (collectionChangedAction == NotifyCollectionChangedAction.Add - || collectionChangedAction == NotifyCollectionChangedAction.Replace) + else if (collectionChangedAction is NotifyCollectionChangedAction.Add or NotifyCollectionChangedAction.Replace) { var changedItem = eventArgs.NewItems?[0] as AvaloniaObject; VerifyType(changedItem); diff --git a/src/Avalonia.Xaml.Interactivity/Avalonia.Xaml.Interactivity.csproj b/src/Avalonia.Xaml.Interactivity/Avalonia.Xaml.Interactivity.csproj index 2d160795..a13ef81b 100644 --- a/src/Avalonia.Xaml.Interactivity/Avalonia.Xaml.Interactivity.csproj +++ b/src/Avalonia.Xaml.Interactivity/Avalonia.Xaml.Interactivity.csproj @@ -19,5 +19,6 @@ + diff --git a/src/Avalonia.Xaml.Interactivity/Behavior.cs b/src/Avalonia.Xaml.Interactivity/Behavior.cs index 08852ee7..7dbd6e26 100644 --- a/src/Avalonia.Xaml.Interactivity/Behavior.cs +++ b/src/Avalonia.Xaml.Interactivity/Behavior.cs @@ -8,13 +8,29 @@ namespace Avalonia.Xaml.Interactivity; /// /// A base class for behaviors, implementing the basic plumbing of . /// -public abstract class Behavior : AvaloniaObject, IBehavior +public abstract class Behavior : AvaloniaObject, IBehavior, IInternalBehavior { + /// + /// Identifies the avalonia property. + /// + public static readonly StyledProperty IsEnabledProperty = + AvaloniaProperty.Register(nameof(IsEnabled), defaultValue: true); + /// /// Gets the to which the behavior is attached. /// public AvaloniaObject? AssociatedObject { get; private set; } + /// + /// Gets or sets a value indicating whether this instance is enabled. + /// + /// true if this instance is enabled; otherwise, false. + public bool IsEnabled + { + get => GetValue(IsEnabledProperty); + set => SetValue(IsEnabledProperty, value); + } + /// /// Attaches the behavior to the specified . /// @@ -69,15 +85,17 @@ protected virtual void OnDetaching() { } - internal void AttachedToVisualTree() - { - OnAttachedToVisualTree(); - } + void IInternalBehavior.AttachedToVisualTreeImpl() => OnAttachedToVisualTree(); - internal void DetachedFromVisualTree() - { - OnDetachedFromVisualTree(); - } + void IInternalBehavior.DetachedFromVisualTreeImpl() => OnDetachedFromVisualTree(); + + void IInternalBehavior.AttachedToLogicalTreeImpl() => OnAttachedToLogicalTree(); + + void IInternalBehavior.DetachedFromLogicalTreeImpl() => OnDetachedFromLogicalTree(); + + void IInternalBehavior.LoadedImpl() => OnLoaded(); + + void IInternalBehavior.UnloadedImpl() => OnUnloaded(); /// /// Called after the is attached to the visual tree. @@ -98,4 +116,44 @@ protected virtual void OnAttachedToVisualTree() protected virtual void OnDetachedFromVisualTree() { } + + /// + /// Called after the is attached to the logical tree. + /// + /// + /// Invoked only when the is of type . + /// + protected virtual void OnAttachedToLogicalTree() + { + } + + /// + /// Called when the is being detached from the logical tree. + /// + /// + /// Invoked only when the is of type . + /// + protected virtual void OnDetachedFromLogicalTree() + { + } + + /// + /// Called after the is loaded. + /// + /// + /// Invoked only when the is of type . + /// + protected virtual void OnLoaded() + { + } + + /// + /// Called when the is unloaded. + /// + /// + /// Invoked only when the is of type . + /// + protected virtual void OnUnloaded() + { + } } diff --git a/src/Avalonia.Xaml.Interactivity/BehaviorCollection.cs b/src/Avalonia.Xaml.Interactivity/BehaviorCollection.cs index 42912fb9..42947d27 100644 --- a/src/Avalonia.Xaml.Interactivity/BehaviorCollection.cs +++ b/src/Avalonia.Xaml.Interactivity/BehaviorCollection.cs @@ -13,7 +13,7 @@ public class BehaviorCollection : AvaloniaList { // After a VectorChanged event we need to compare the current state of the collection // with the old collection so that we can call Detach on all removed items. - private readonly List _oldCollection = new(); + private readonly List _oldCollection = []; /// /// Initializes a new instance of the class. @@ -81,9 +81,9 @@ internal void AttachedToVisualTree() { foreach (var item in this) { - if (item is Behavior behavior) + if (item is IInternalBehavior behavior) { - behavior.AttachedToVisualTree(); + behavior.AttachedToVisualTreeImpl(); } } } @@ -92,9 +92,53 @@ internal void DetachedFromVisualTree() { foreach (var item in this) { - if (item is Behavior { AssociatedObject: not null} behavior) + if (item is IInternalBehavior behavior and IBehavior { AssociatedObject: not null}) { - behavior.DetachedFromVisualTree(); + behavior.DetachedFromVisualTreeImpl(); + } + } + } + + internal void AttachedToLogicalTree() + { + foreach (var item in this) + { + if (item is IInternalBehavior behavior) + { + behavior.AttachedToLogicalTreeImpl(); + } + } + } + + internal void DetachedFromLogicalTree() + { + foreach (var item in this) + { + if (item is IInternalBehavior behavior and IBehavior { AssociatedObject: not null}) + { + behavior.DetachedFromLogicalTreeImpl(); + } + } + } + + internal void Loaded() + { + foreach (var item in this) + { + if (item is IInternalBehavior behavior) + { + behavior.LoadedImpl(); + } + } + } + + internal void Unloaded() + { + foreach (var item in this) + { + if (item is IInternalBehavior behavior and IBehavior { AssociatedObject: not null}) + { + behavior.UnloadedImpl(); } } } diff --git a/src/Avalonia.Xaml.Interactivity/BehaviorCollectionTemplate.cs b/src/Avalonia.Xaml.Interactivity/BehaviorCollectionTemplate.cs index f9fb2b98..635d5e01 100644 --- a/src/Avalonia.Xaml.Interactivity/BehaviorCollectionTemplate.cs +++ b/src/Avalonia.Xaml.Interactivity/BehaviorCollectionTemplate.cs @@ -5,7 +5,7 @@ namespace Avalonia.Xaml.Interactivity; /// -/// A template for creating a collection of objects. +/// A template for creating a collection of objects. /// public class BehaviorCollectionTemplate : ITemplate { diff --git a/src/Avalonia.Xaml.Interactivity/BehaviorOfT.cs b/src/Avalonia.Xaml.Interactivity/BehaviorOfT.cs index 1d709947..b46ccef1 100644 --- a/src/Avalonia.Xaml.Interactivity/BehaviorOfT.cs +++ b/src/Avalonia.Xaml.Interactivity/BehaviorOfT.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics.CodeAnalysis; namespace Avalonia.Xaml.Interactivity; @@ -16,12 +15,11 @@ public abstract class Behavior : Behavior where T : AvaloniaObject public new T? AssociatedObject => base.AssociatedObject as T; /// - /// Called after the behavior is attached to the . + /// Called after the behavior is attached to the . /// /// - /// Override this to hook up functionality to the + /// Override this to hook up functionality to the /// - [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] protected override void OnAttached() { base.OnAttached(); diff --git a/src/Avalonia.Xaml.Interactivity/IInternalBehavior.cs b/src/Avalonia.Xaml.Interactivity/IInternalBehavior.cs new file mode 100644 index 00000000..9399380c --- /dev/null +++ b/src/Avalonia.Xaml.Interactivity/IInternalBehavior.cs @@ -0,0 +1,16 @@ +namespace Avalonia.Xaml.Interactivity; + +internal interface IInternalBehavior +{ + void AttachedToVisualTreeImpl(); + + void DetachedFromVisualTreeImpl(); + + void AttachedToLogicalTreeImpl(); + + void DetachedFromLogicalTreeImpl(); + + void LoadedImpl(); + + void UnloadedImpl(); +} diff --git a/src/Avalonia.Xaml.Interactivity/Interaction.cs b/src/Avalonia.Xaml.Interactivity/Interaction.cs index 453cdeb6..48df9b8a 100644 --- a/src/Avalonia.Xaml.Interactivity/Interaction.cs +++ b/src/Avalonia.Xaml.Interactivity/Interaction.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; -using System.Linq; using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.LogicalTree; using Avalonia.Reactive; namespace Avalonia.Xaml.Interactivity; @@ -38,7 +39,7 @@ public static BehaviorCollection GetBehaviors(AvaloniaObject obj) var behaviorCollection = obj.GetValue(BehaviorsProperty); if (behaviorCollection is null) { - behaviorCollection = new BehaviorCollection(); + behaviorCollection = []; obj.SetValue(BehaviorsProperty, behaviorCollection); SetVisualTreeEventHandlersInitial(obj); } @@ -71,7 +72,7 @@ public static IEnumerable ExecuteActions(object? sender, ActionCollectio { if (actions is null) { - return Enumerable.Empty(); + return []; } var results = new List(); @@ -122,14 +123,32 @@ private static void SetVisualTreeEventHandlersInitial(AvaloniaObject obj) return; } + // AttachedToVisualTree / DetachedFromVisualTree + control.AttachedToVisualTree -= Control_AttachedToVisualTreeRuntime; control.DetachedFromVisualTree -= Control_DetachedFromVisualTreeRuntime; - control.AttachedToVisualTree -= Control_AttachedToVisualTreeInitial; control.AttachedToVisualTree += Control_AttachedToVisualTreeInitial; - control.DetachedFromVisualTree -= Control_DetachedFromVisualTreeInitial; control.DetachedFromVisualTree += Control_DetachedFromVisualTreeInitial; + + // AttachedToLogicalTree / DetachedFromLogicalTree + + control.AttachedToLogicalTree -= Control_AttachedToLogicalTreeRuntime; + control.DetachedFromLogicalTree -= Control_DetachedFromLogicalTreeRuntime; + control.AttachedToLogicalTree -= Control_AttachedToLogicalTreeInitial; + control.AttachedToLogicalTree += Control_AttachedToLogicalTreeInitial; + control.DetachedFromLogicalTree -= Control_DetachedFromLogicalTreeInitial; + control.DetachedFromLogicalTree += Control_DetachedFromLogicalTreeInitial; + + // Loaded / Unloaded + + control.Loaded -= Control_LoadedRuntime; + control.Unloaded -= Control_UnloadedRuntime; + control.Loaded -= Control_LoadedInitial; + control.Loaded += Control_LoadedInitial; + control.Unloaded -= Control_UnloadedInitial; + control.Unloaded += Control_UnloadedInitial; } private static void SetVisualTreeEventHandlersRuntime(AvaloniaObject obj) @@ -139,47 +158,159 @@ private static void SetVisualTreeEventHandlersRuntime(AvaloniaObject obj) return; } + // AttachedToVisualTree / DetachedFromVisualTree + control.AttachedToVisualTree -= Control_AttachedToVisualTreeInitial; control.DetachedFromVisualTree -= Control_DetachedFromVisualTreeInitial; - control.AttachedToVisualTree -= Control_AttachedToVisualTreeRuntime; control.AttachedToVisualTree += Control_AttachedToVisualTreeRuntime; - control.DetachedFromVisualTree -= Control_DetachedFromVisualTreeRuntime; control.DetachedFromVisualTree += Control_DetachedFromVisualTreeRuntime; + + // AttachedToLogicalTree / DetachedFromLogicalTree + + control.AttachedToLogicalTree -= Control_AttachedToLogicalTreeInitial; + control.DetachedFromLogicalTree -= Control_DetachedFromLogicalTreeInitial; + control.AttachedToLogicalTree -= Control_AttachedToLogicalTreeRuntime; + control.AttachedToLogicalTree += Control_AttachedToLogicalTreeRuntime; + control.DetachedFromLogicalTree -= Control_DetachedFromLogicalTreeRuntime; + control.DetachedFromLogicalTree += Control_DetachedFromLogicalTreeRuntime; + + // Loaded / Unloaded + + control.Loaded -= Control_LoadedInitial; + control.Unloaded -= Control_UnloadedInitial; + control.Loaded -= Control_LoadedRuntime; + control.Loaded += Control_LoadedRuntime; + control.Unloaded -= Control_UnloadedRuntime; + control.Unloaded += Control_UnloadedRuntime; } + // AttachedToVisualTree / DetachedFromVisualTree + private static void Control_AttachedToVisualTreeInitial(object? sender, VisualTreeAttachmentEventArgs e) { - if (sender is AvaloniaObject d) + if (sender is not AvaloniaObject d) { - GetBehaviors(d).Attach(d); - GetBehaviors(d).AttachedToVisualTree(); + return; } + + GetBehaviors(d).Attach(d); + GetBehaviors(d).AttachedToVisualTree(); } private static void Control_DetachedFromVisualTreeInitial(object? sender, VisualTreeAttachmentEventArgs e) { - if (sender is AvaloniaObject d) + if (sender is not AvaloniaObject d) { - GetBehaviors(d).DetachedFromVisualTree(); - GetBehaviors(d).Detach(); + return; } + + GetBehaviors(d).DetachedFromVisualTree(); + GetBehaviors(d).Detach(); } private static void Control_AttachedToVisualTreeRuntime(object? sender, VisualTreeAttachmentEventArgs e) { - if (sender is AvaloniaObject d) + if (sender is not AvaloniaObject d) { - GetBehaviors(d).AttachedToVisualTree(); + return; } + + GetBehaviors(d).AttachedToVisualTree(); } private static void Control_DetachedFromVisualTreeRuntime(object? sender, VisualTreeAttachmentEventArgs e) { - if (sender is AvaloniaObject d) + if (sender is not AvaloniaObject d) { - GetBehaviors(d).DetachedFromVisualTree(); + return; + } + + GetBehaviors(d).DetachedFromVisualTree(); + } + + // AttachedToLogicalTree / DetachedFromLogicalTree + + private static void Control_AttachedToLogicalTreeInitial(object? sender, LogicalTreeAttachmentEventArgs e) + { + if (sender is not AvaloniaObject d) + { + return; + } + + GetBehaviors(d).AttachedToLogicalTree(); + } + + private static void Control_DetachedFromLogicalTreeInitial(object? sender, LogicalTreeAttachmentEventArgs e) + { + if (sender is not AvaloniaObject d) + { + return; } + + GetBehaviors(d).DetachedFromLogicalTree(); + } + + private static void Control_AttachedToLogicalTreeRuntime(object? sender, LogicalTreeAttachmentEventArgs e) + { + if (sender is not AvaloniaObject d) + { + return; + } + + GetBehaviors(d).AttachedToLogicalTree(); + } + + private static void Control_DetachedFromLogicalTreeRuntime(object? sender, LogicalTreeAttachmentEventArgs e) + { + if (sender is not AvaloniaObject d) + { + return; + } + + GetBehaviors(d).DetachedFromLogicalTree(); + } + + // Loaded / Unloaded + + private static void Control_LoadedInitial(object? sender, RoutedEventArgs e) + { + if (sender is not AvaloniaObject d) + { + return; + } + + GetBehaviors(d).Loaded(); + } + + private static void Control_UnloadedInitial(object? sender, RoutedEventArgs e) + { + if (sender is not AvaloniaObject d) + { + return; + } + + GetBehaviors(d).Unloaded(); + } + + private static void Control_LoadedRuntime(object? sender, RoutedEventArgs e) + { + if (sender is not AvaloniaObject d) + { + return; + } + + GetBehaviors(d).Loaded(); + } + + private static void Control_UnloadedRuntime(object? sender, RoutedEventArgs e) + { + if (sender is not AvaloniaObject d) + { + return; + } + + GetBehaviors(d).Unloaded(); } } diff --git a/src/Avalonia.Xaml.Interactivity/StyledElementBehavior.cs b/src/Avalonia.Xaml.Interactivity/StyledElementBehavior.cs new file mode 100644 index 00000000..d41970f5 --- /dev/null +++ b/src/Avalonia.Xaml.Interactivity/StyledElementBehavior.cs @@ -0,0 +1,208 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using Avalonia.Controls; +using Avalonia.Reactive; + +namespace Avalonia.Xaml.Interactivity; + +/// +/// A base class for behaviors, implementing the basic plumbing of . +/// +public abstract class StyledElementBehavior : StyledElement, IBehavior, IInternalBehavior +{ + private IDisposable? _dataContextDisposable; + + /// + /// Identifies the avalonia property. + /// + public static readonly StyledProperty IsEnabledProperty = + AvaloniaProperty.Register(nameof(IsEnabled), defaultValue: true); + + /// + /// Gets the to which the behavior is attached. + /// + public AvaloniaObject? AssociatedObject { get; private set; } + + /// + /// Gets the to which this behavior is attached. + /// + public StyledElement? AssociatedStyledElement => AssociatedObject as StyledElement; + + /// + /// Gets or sets a value indicating whether this instance is enabled. + /// + /// true if this instance is enabled; otherwise, false. + public bool IsEnabled + { + get => GetValue(IsEnabledProperty); + set => SetValue(IsEnabledProperty, value); + } + + /// + /// Attaches the behavior to the specified . + /// + /// The to which to attach. + /// is null. + public void Attach(AvaloniaObject? associatedObject) + { + if (Equals(associatedObject, AssociatedObject)) + { + return; + } + + if (AssociatedObject is not null) + { + throw new InvalidOperationException(string.Format( + CultureInfo.CurrentCulture, + "An instance of a behavior cannot be attached to more than one object at a time.")); + } + + Debug.Assert(associatedObject is not null, "Cannot attach the behavior to a null object."); + AssociatedObject = associatedObject ?? throw new ArgumentNullException(nameof(associatedObject)); + _dataContextDisposable = SynchronizeDataContext(associatedObject); + + OnAttached(); + } + + /// + /// Detaches the behaviors from the . + /// + public void Detach() + { + OnDetaching(); + _dataContextDisposable?.Dispose(); + AssociatedObject = null; + } + + /// + /// Called after the behavior is attached to the . + /// + /// + /// Override this to hook up functionality to the + /// + protected virtual void OnAttached() + { + } + + /// + /// Called when the behavior is being detached from its . + /// + /// + /// Override this to unhook functionality from the + /// + protected virtual void OnDetaching() + { + } + + void IInternalBehavior.AttachedToVisualTreeImpl() => OnAttachedToVisualTree(); + + void IInternalBehavior.DetachedFromVisualTreeImpl() => OnDetachedFromVisualTree(); + + void IInternalBehavior.AttachedToLogicalTreeImpl() + { + AttachToLogicalTree(); + OnAttachedToLogicalTree(); + } + + void IInternalBehavior.DetachedFromLogicalTreeImpl() + { + DetachFromLogicalTree(); + OnDetachedFromLogicalTree(); + } + + void IInternalBehavior.LoadedImpl() => OnLoaded(); + + void IInternalBehavior.UnloadedImpl() => OnUnloaded(); + + /// + /// Called after the is attached to the visual tree. + /// + /// + /// Invoked only when the is of type . + /// + protected virtual void OnAttachedToVisualTree() + { + } + + /// + /// Called when the is being detached from the visual tree. + /// + /// + /// Invoked only when the is of type . + /// + protected virtual void OnDetachedFromVisualTree() + { + } + + /// + /// Called after the is attached to the logical tree. + /// + /// + /// Invoked only when the is of type . + /// + protected virtual void OnAttachedToLogicalTree() + { + } + + /// + /// Called when the is being detached from the logical tree. + /// + /// + /// Invoked only when the is of type . + /// + protected virtual void OnDetachedFromLogicalTree() + { + } + + /// + /// Called after the is loaded. + /// + /// + /// Invoked only when the is of type . + /// + protected virtual void OnLoaded() + { + } + + /// + /// Called when the is unloaded. + /// + /// + /// Invoked only when the is of type . + /// + protected virtual void OnUnloaded() + { + } + + private void AttachToLogicalTree() + { + if (AssociatedObject is not StyledElement styledElement) + { + return; + } + + ((ISetLogicalParent)this).SetParent(null); + ((ISetLogicalParent)this).SetParent(styledElement); + } + + private void DetachFromLogicalTree() + { + ((ISetLogicalParent)this).SetParent(null); + } + + private IDisposable? SynchronizeDataContext(AvaloniaObject associatedObject) + { + if (associatedObject is StyledElement styledElement) + { + return styledElement + .GetObservable(DataContextProperty) + .Subscribe(new AnonymousObserver(x => + { + SetCurrentValue(DataContextProperty, x); + })); + } + + return default; + } +} diff --git a/src/Avalonia.Xaml.Interactivity/StyledElementBehaviorOfT.cs b/src/Avalonia.Xaml.Interactivity/StyledElementBehaviorOfT.cs new file mode 100644 index 00000000..bbd83009 --- /dev/null +++ b/src/Avalonia.Xaml.Interactivity/StyledElementBehaviorOfT.cs @@ -0,0 +1,36 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Avalonia.Xaml.Interactivity; + +/// +/// A base class for behaviors making them code compatible with older frameworks, +/// and allow for typed associated objects. +/// +/// The object type to attach to +public abstract class StyledElementBehavior : StyledElementBehavior where T : AvaloniaObject +{ + /// + /// Gets the object to which this behavior is attached. + /// + public new T? AssociatedObject => base.AssociatedObject as T; + + /// + /// Called after the behavior is attached to the . + /// + /// + /// Override this to hook up functionality to the + /// + protected override void OnAttached() + { + base.OnAttached(); + + if (AssociatedObject is null && base.AssociatedObject is not null) + { + var actualType = base.AssociatedObject?.GetType().FullName; + var expectedType = typeof(T).FullName; + var message = $"AssociatedObject is of type {actualType} but should be of type {expectedType}."; + throw new InvalidOperationException(message); + } + } +} diff --git a/src/Avalonia.Xaml.Interactivity/StyledElementTrigger.cs b/src/Avalonia.Xaml.Interactivity/StyledElementTrigger.cs new file mode 100644 index 00000000..bf91a756 --- /dev/null +++ b/src/Avalonia.Xaml.Interactivity/StyledElementTrigger.cs @@ -0,0 +1,23 @@ +using Avalonia.Metadata; + +namespace Avalonia.Xaml.Interactivity; + +/// +/// A base class for behaviors, implementing the basic plumbing of . +/// +public abstract class StyledElementTrigger : StyledElementBehavior, ITrigger +{ + /// + /// Identifies the avalonia property. + /// + public static readonly DirectProperty ActionsProperty = + AvaloniaProperty.RegisterDirect(nameof(Actions), t => t.Actions); + + private ActionCollection? _actions; + + /// + /// Gets the collection of actions associated with the behavior. This is a avalonia property. + /// + [Content] + public ActionCollection Actions => _actions ??= []; +} diff --git a/src/Avalonia.Xaml.Interactivity/StyledElementTriggerOfT.cs b/src/Avalonia.Xaml.Interactivity/StyledElementTriggerOfT.cs new file mode 100644 index 00000000..329d0d89 --- /dev/null +++ b/src/Avalonia.Xaml.Interactivity/StyledElementTriggerOfT.cs @@ -0,0 +1,37 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Avalonia.Metadata; + +namespace Avalonia.Xaml.Interactivity; + +/// +/// A base class for behaviors, implementing the basic plumbing of . +/// +/// The object type to attach to +public abstract class StyledElementTrigger : StyledElementTrigger where T : AvaloniaObject +{ + /// + /// Gets the object to which this behavior is attached. + /// + public new T? AssociatedObject => base.AssociatedObject as T; + + /// + /// Called after the behavior is attached to the . + /// + /// + /// Override this to hook up functionality to the + /// + /// + protected override void OnAttached() + { + base.OnAttached(); + + if (AssociatedObject is null && base.AssociatedObject is not null) + { + var actualType = base.AssociatedObject?.GetType().FullName; + var expectedType = typeof(T).FullName; + var message = $"AssociatedObject is of type {actualType} but should be of type {expectedType}."; + throw new InvalidOperationException(message); + } + } +} diff --git a/src/Avalonia.Xaml.Interactivity/Trigger.cs b/src/Avalonia.Xaml.Interactivity/Trigger.cs index 0d76bda8..2edae93c 100644 --- a/src/Avalonia.Xaml.Interactivity/Trigger.cs +++ b/src/Avalonia.Xaml.Interactivity/Trigger.cs @@ -19,5 +19,5 @@ public abstract class Trigger : Behavior, ITrigger /// Gets the collection of actions associated with the behavior. This is a avalonia property. /// [Content] - public ActionCollection Actions => _actions ??= new ActionCollection(); + public ActionCollection Actions => _actions ??= []; } diff --git a/src/Avalonia.Xaml.Interactivity/TriggerOfT.cs b/src/Avalonia.Xaml.Interactivity/TriggerOfT.cs index 0113721d..b8d91f31 100644 --- a/src/Avalonia.Xaml.Interactivity/TriggerOfT.cs +++ b/src/Avalonia.Xaml.Interactivity/TriggerOfT.cs @@ -15,13 +15,11 @@ public abstract class Trigger : Trigger where T : AvaloniaObject public new T? AssociatedObject => base.AssociatedObject as T; /// - /// Called after the behavior is attached to the . + /// Called after the behavior is attached to the . /// /// - /// Override this to hook up functionality to the + /// Override this to hook up functionality to the /// - /// - [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] protected override void OnAttached() { base.OnAttached(); diff --git a/src/Avalonia.Xaml.Interactivity/TypeConverterHelper.cs b/src/Avalonia.Xaml.Interactivity/TypeConverterHelper.cs index 1e366a8a..dec3e5f8 100644 --- a/src/Avalonia.Xaml.Interactivity/TypeConverterHelper.cs +++ b/src/Avalonia.Xaml.Interactivity/TypeConverterHelper.cs @@ -9,6 +9,7 @@ namespace Avalonia.Xaml.Interactivity; /// /// A helper class that enables converting values specified in markup (strings) to their object representation. /// +[RequiresUnreferencedCode("This functionality is not compatible with trimming.")] internal static class TypeConverterHelper { /// @@ -18,8 +19,8 @@ internal static class TypeConverterHelper /// The destination type. /// Object representation of the string value. /// destinationType cannot be null. - [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] - public static object? Convert(string value, Type destinationType) + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "DynamicallyAccessedMembers handles most of the problems.")] + public static object? Convert(string value, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type destinationType) { if (destinationType is null) { diff --git a/tests/Avalonia.Xaml.Interactions.UnitTests/Core/CallMethodActionTests.cs b/tests/Avalonia.Xaml.Interactions.UnitTests/Core/CallMethodActionTests.cs index d66fe3cb..b7433d84 100644 --- a/tests/Avalonia.Xaml.Interactions.UnitTests/Core/CallMethodActionTests.cs +++ b/tests/Avalonia.Xaml.Interactions.UnitTests/Core/CallMethodActionTests.cs @@ -3,7 +3,6 @@ using Avalonia.Headless.XUnit; using Avalonia.Input; using Avalonia.Interactivity; -using VerifyTests; using VerifyXunit; using Xunit; diff --git a/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction001.axaml b/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction001.axaml index 96d6491b..545eb090 100644 --- a/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction001.axaml +++ b/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction001.axaml @@ -1,7 +1,9 @@  + Title="InvokeCommandAction001" + x:DataType="core:InvokeCommandAction001"> diff --git a/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction002.axaml b/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction002.axaml index bcf7c2eb..b9fb6439 100644 --- a/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction002.axaml +++ b/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction002.axaml @@ -1,7 +1,9 @@  + Title="InvokeCommandAction002" + x:DataType="core:InvokeCommandAction002"> diff --git a/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction003.axaml b/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction003.axaml index 46afc540..c87a3bcf 100644 --- a/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction003.axaml +++ b/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction003.axaml @@ -2,7 +2,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="Avalonia.Xaml.Interactions.UnitTests.Core.InvokeCommandAction003" xmlns:local="clr-namespace:Avalonia.Xaml.Interactions.UnitTests.Core" - Title="InvokeCommandAction003"> + Title="InvokeCommandAction003" + x:DataType="local:InvokeCommandAction003"> diff --git a/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction004.axaml b/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction004.axaml index 8f9f662d..92f48bf0 100644 --- a/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction004.axaml +++ b/tests/Avalonia.Xaml.Interactions.UnitTests/Core/InvokeCommandAction004.axaml @@ -1,7 +1,9 @@  + Title="InvokeCommandAction004" + x:DataType="core:InvokeCommandAction004"> diff --git a/tests/Avalonia.Xaml.Interactivity.UnitTests/BehaviorCollectionTemplateTests.cs b/tests/Avalonia.Xaml.Interactivity.UnitTests/BehaviorCollectionTemplateTests.cs index 8877414f..72d725b2 100644 --- a/tests/Avalonia.Xaml.Interactivity.UnitTests/BehaviorCollectionTemplateTests.cs +++ b/tests/Avalonia.Xaml.Interactivity.UnitTests/BehaviorCollectionTemplateTests.cs @@ -4,7 +4,6 @@ using Avalonia.Headless.XUnit; using Avalonia.Input; using Avalonia.Media; -using Avalonia.Media.Immutable; using Xunit; namespace Avalonia.Xaml.Interactivity.UnitTests; diff --git a/tests/Avalonia.Xaml.Interactivity.UnitTests/BehaviorCollectionTest.cs b/tests/Avalonia.Xaml.Interactivity.UnitTests/BehaviorCollectionTest.cs index eb60fed7..4c3703a3 100644 --- a/tests/Avalonia.Xaml.Interactivity.UnitTests/BehaviorCollectionTest.cs +++ b/tests/Avalonia.Xaml.Interactivity.UnitTests/BehaviorCollectionTest.cs @@ -108,7 +108,7 @@ public void VectorChanged_RemoveWhileAttached_Detached() [AvaloniaFact] public void VectorChanged_ResetWhileNotAttached_DetachNotCalled() { - StubBehavior[] behaviorArray = { new StubBehavior(), new StubBehavior(), new StubBehavior() }; + StubBehavior[] behaviorArray = [new StubBehavior(), new StubBehavior(), new StubBehavior()]; var behaviorCollection = new BehaviorCollection(); foreach (var behavior in behaviorArray) @@ -127,7 +127,7 @@ public void VectorChanged_ResetWhileNotAttached_DetachNotCalled() [AvaloniaFact] public void VectorChanged_ResetWhileAttached_AllDetached() { - StubBehavior[] behaviorArray = { new StubBehavior(), new StubBehavior(), new StubBehavior() }; + StubBehavior[] behaviorArray = [new StubBehavior(), new StubBehavior(), new StubBehavior()]; var behaviorCollection = new BehaviorCollection(); behaviorCollection.Attach(new Button()); diff --git a/tests/Avalonia.Xaml.Interactivity.UnitTests/InteractionTest.cs b/tests/Avalonia.Xaml.Interactivity.UnitTests/InteractionTest.cs index bd716cc9..f2ade9fc 100644 --- a/tests/Avalonia.Xaml.Interactivity.UnitTests/InteractionTest.cs +++ b/tests/Avalonia.Xaml.Interactivity.UnitTests/InteractionTest.cs @@ -133,7 +133,7 @@ public void ExecuteActions_MultipleActions_AllActionsExecuted() [AvaloniaFact] public void ExecuteActions_ActionsWithResults_ResultsInActionOrder() { - string[] expectedReturnValues = { "A", "B", "C" }; + string[] expectedReturnValues = ["A", "B", "C"]; var actions = new ActionCollection(); diff --git a/tests/Avalonia.Xaml.Interactivity.UnitTests/StubAction.cs b/tests/Avalonia.Xaml.Interactivity.UnitTests/StubAction.cs index d7cd857f..edeccc4e 100644 --- a/tests/Avalonia.Xaml.Interactivity.UnitTests/StubAction.cs +++ b/tests/Avalonia.Xaml.Interactivity.UnitTests/StubAction.cs @@ -1,6 +1,6 @@ namespace Avalonia.Xaml.Interactivity.UnitTests; -public class StubAction(object? returnValue) : AvaloniaObject, IAction +public class StubAction(object? returnValue) : Avalonia.Xaml.Interactivity.Action { public StubAction() : this(null) { @@ -24,11 +24,11 @@ public int ExecuteCount private set; } - public object? Execute(object? sender, object? parameter) + public override object? Execute(object? sender, object? parameter) { ExecuteCount++; Sender = sender; Parameter = parameter; return returnValue; } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Xaml.Interactivity.UnitTests/StubBehavior.cs b/tests/Avalonia.Xaml.Interactivity.UnitTests/StubBehavior.cs index 89cafe31..e7723f1d 100644 --- a/tests/Avalonia.Xaml.Interactivity.UnitTests/StubBehavior.cs +++ b/tests/Avalonia.Xaml.Interactivity.UnitTests/StubBehavior.cs @@ -16,7 +16,7 @@ public int DetachCount private set; } - public ActionCollection Actions { get; private set; } = new(); + public ActionCollection Actions { get; private set; } = []; public AvaloniaObject? AssociatedObject { diff --git a/tests/Avalonia.Xaml.Interactivity.UnitTests/TestUitilties.cs b/tests/Avalonia.Xaml.Interactivity.UnitTests/TestUitilties.cs index 283a7ff7..91c0ee77 100644 --- a/tests/Avalonia.Xaml.Interactivity.UnitTests/TestUitilties.cs +++ b/tests/Avalonia.Xaml.Interactivity.UnitTests/TestUitilties.cs @@ -8,12 +8,12 @@ public static class TestUtilities /// /// Handles the difference between InvalidOperationException in managed and native. /// - public static void AssertThrowsInvalidOperationException(Action action) + public static void AssertThrowsInvalidOperationException(System.Action action) { Assert.Throws(action); } - public static void AssertThrowsArgumentException(Action action) + public static void AssertThrowsArgumentException(System.Action action) { Assert.Throws(action); } diff --git a/tests/Directory.Packages.props b/tests/Directory.Packages.props index 39976547..9bca952f 100644 --- a/tests/Directory.Packages.props +++ b/tests/Directory.Packages.props @@ -1,7 +1,7 @@  true - 11.0.10 + 11.1.4