diff --git a/AvaloniaBehaviors.sln b/AvaloniaBehaviors.sln index 0b7c9b4b..cf4f72f9 100644 --- a/AvaloniaBehaviors.sln +++ b/AvaloniaBehaviors.sln @@ -79,12 +79,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Xaml.Interactions. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Xaml.Interactions.DragAndDrop", "src\Avalonia.Xaml.Interactions.DragAndDrop\Avalonia.Xaml.Interactions.DragAndDrop.csproj", "{EF243B1A-FF29-41E9-B44B-2E55A73F6CCE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Xaml.Interactions.Reactive", "src\Avalonia.Xaml.Interactions.Reactive\Avalonia.Xaml.Interactions.Reactive.csproj", "{D647E09E-BBF2-4432-A4E0-80FBB921C1EE}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Xaml.Interactions.Custom", "src\Avalonia.Xaml.Interactions.Custom\Avalonia.Xaml.Interactions.Custom.csproj", "{D647E09E-BBF2-4432-A4E0-80FBB921C1EE}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Xaml.Interactions.Events", "src\Avalonia.Xaml.Interactions.Events\Avalonia.Xaml.Interactions.Events.csproj", "{648FFF6A-A8F5-491C-AC1C-19FC2061D6B4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Xaml.Interactions.Custom", "src\Avalonia.Xaml.Interactions.Custom\Avalonia.Xaml.Interactions.Custom.csproj", "{0BB3669E-45E4-41E8-B765-7F4A5201C4E0}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -147,10 +145,6 @@ Global {648FFF6A-A8F5-491C-AC1C-19FC2061D6B4}.Debug|Any CPU.Build.0 = Debug|Any CPU {648FFF6A-A8F5-491C-AC1C-19FC2061D6B4}.Release|Any CPU.ActiveCfg = Release|Any CPU {648FFF6A-A8F5-491C-AC1C-19FC2061D6B4}.Release|Any CPU.Build.0 = Release|Any CPU - {0BB3669E-45E4-41E8-B765-7F4A5201C4E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0BB3669E-45E4-41E8-B765-7F4A5201C4E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0BB3669E-45E4-41E8-B765-7F4A5201C4E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0BB3669E-45E4-41E8-B765-7F4A5201C4E0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -174,7 +168,6 @@ Global {EF243B1A-FF29-41E9-B44B-2E55A73F6CCE} = {84224365-32B6-46AF-85A2-45640E6D7EEB} {D647E09E-BBF2-4432-A4E0-80FBB921C1EE} = {84224365-32B6-46AF-85A2-45640E6D7EEB} {648FFF6A-A8F5-491C-AC1C-19FC2061D6B4} = {84224365-32B6-46AF-85A2-45640E6D7EEB} - {0BB3669E-45E4-41E8-B765-7F4A5201C4E0} = {84224365-32B6-46AF-85A2-45640E6D7EEB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E64CD420-15B1-487C-9806-41EBE6DC15A4} diff --git a/Directory.Build.props b/Directory.Build.props index 91799241..52fc3feb 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,15 +1,16 @@  - 11.0.5 + 11.0.10.2 Wiesław Šoltés Wiesław Šoltés - Copyright © Wiesław Šoltés 2023 + Copyright © Wiesław Šoltés 2024 MIT https://github.com/wieslawsoltes/AvaloniaBehaviors latest latest + true diff --git a/Directory.Packages.props b/Directory.Packages.props index 6892fa9c..84e57556 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -14,8 +14,7 @@ - - + diff --git a/samples/BehaviorsTestApplication/BehaviorsTestApplication.csproj b/samples/BehaviorsTestApplication/BehaviorsTestApplication.csproj index 5a40a07d..00f0a20e 100644 --- a/samples/BehaviorsTestApplication/BehaviorsTestApplication.csproj +++ b/samples/BehaviorsTestApplication/BehaviorsTestApplication.csproj @@ -15,7 +15,6 @@ - @@ -26,7 +25,6 @@ - diff --git a/samples/BehaviorsTestApplication/ViewModels/ItemViewModel.cs b/samples/BehaviorsTestApplication/ViewModels/ItemViewModel.cs index 4e19002b..a4336efa 100644 --- a/samples/BehaviorsTestApplication/ViewModels/ItemViewModel.cs +++ b/samples/BehaviorsTestApplication/ViewModels/ItemViewModel.cs @@ -4,15 +4,14 @@ namespace BehaviorsTestApplication.ViewModels; -public class ItemViewModel : ViewModelBase +public class ItemViewModel(string name) : ViewModelBase { - private string _name; private ObservableCollection? _items; public string Name { - get => _name; - set => this.RaiseAndSetIfChanged(ref _name, value); + get => name; + set => this.RaiseAndSetIfChanged(ref name, value); } public ObservableCollection? Items @@ -21,10 +20,5 @@ public ObservableCollection? Items set => this.RaiseAndSetIfChanged(ref _items, value); } - public ItemViewModel(string name) - { - _name = name; - } - - public override string ToString() => _name; + public override string ToString() => name; } \ No newline at end of file diff --git a/samples/Directory.Packages.props b/samples/Directory.Packages.props index 26c7d182..15e650f6 100644 --- a/samples/Directory.Packages.props +++ b/samples/Directory.Packages.props @@ -1,7 +1,7 @@  true - 11.0.5 + 11.0.10 @@ -15,7 +15,6 @@ - - + diff --git a/samples/DragAndDropSample/Behaviors/NodesTreeViewDropHandler.cs b/samples/DragAndDropSample/Behaviors/NodesTreeViewDropHandler.cs index 25f20c76..e2598014 100644 --- a/samples/DragAndDropSample/Behaviors/NodesTreeViewDropHandler.cs +++ b/samples/DragAndDropSample/Behaviors/NodesTreeViewDropHandler.cs @@ -20,10 +20,10 @@ private bool Validate(TreeView treeView, DragEventArgs e, object? sourceConte var sourceParent = sourceNode.Parent; var targetParent = targetNode.Parent; - var sourceNodes = sourceParent is { } ? sourceParent.Nodes : vm.Nodes; - var targetNodes = targetParent is { } ? targetParent.Nodes : vm.Nodes; + var sourceNodes = sourceParent is not null ? sourceParent.Nodes : vm.Nodes; + var targetNodes = targetParent is not null ? targetParent.Nodes : vm.Nodes; - if (sourceNodes is { } && targetNodes is { }) + if (sourceNodes is not null && targetNodes is not null) { var sourceIndex = sourceNodes.IndexOf(sourceNode); var targetIndex = targetNodes.IndexOf(targetNode); diff --git a/samples/DragAndDropSample/DragAndDropSample.csproj b/samples/DragAndDropSample/DragAndDropSample.csproj index 224d9204..ae9dd2e0 100644 --- a/samples/DragAndDropSample/DragAndDropSample.csproj +++ b/samples/DragAndDropSample/DragAndDropSample.csproj @@ -20,7 +20,6 @@ - diff --git a/samples/DraggableDemo/DraggableDemo.csproj b/samples/DraggableDemo/DraggableDemo.csproj index 5d87ce40..449be893 100644 --- a/samples/DraggableDemo/DraggableDemo.csproj +++ b/samples/DraggableDemo/DraggableDemo.csproj @@ -19,7 +19,6 @@ - diff --git a/src/Avalonia.Xaml.Behaviors/Avalonia.Xaml.Behaviors.csproj b/src/Avalonia.Xaml.Behaviors/Avalonia.Xaml.Behaviors.csproj index 8aedd2f8..1d620f86 100644 --- a/src/Avalonia.Xaml.Behaviors/Avalonia.Xaml.Behaviors.csproj +++ b/src/Avalonia.Xaml.Behaviors/Avalonia.Xaml.Behaviors.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1;net461;net6.0;net8.0 + netstandard2.0;net6.0;net8.0 False enable $(NoWarn);NU5128 @@ -15,7 +15,6 @@ - @@ -28,7 +27,6 @@ - diff --git a/src/Avalonia.Xaml.Interactions.Custom/AddClassAction.cs b/src/Avalonia.Xaml.Interactions.Custom/AddClassAction.cs index ac533cdc..46145093 100644 --- a/src/Avalonia.Xaml.Interactions.Custom/AddClassAction.cs +++ b/src/Avalonia.Xaml.Interactions.Custom/AddClassAction.cs @@ -62,7 +62,7 @@ public bool RemoveIfExists /// True if the class is successfully added; else false. public object Execute(object? sender, object? parameter) { - var target = GetValue(StyledElementProperty) is { } ? StyledElement : sender as StyledElement; + var target = GetValue(StyledElementProperty) is not null ? StyledElement : sender as StyledElement; if (target is null || string.IsNullOrEmpty(ClassName)) { return false; diff --git a/src/Avalonia.Xaml.Interactions.Reactive/AttachedToVisualTreeBehavior.cs b/src/Avalonia.Xaml.Interactions.Custom/AttachedToVisualTreeBehavior.cs similarity index 100% rename from src/Avalonia.Xaml.Interactions.Reactive/AttachedToVisualTreeBehavior.cs rename to src/Avalonia.Xaml.Interactions.Custom/AttachedToVisualTreeBehavior.cs diff --git a/src/Avalonia.Xaml.Interactions.Custom/Avalonia.Xaml.Interactions.Custom.csproj b/src/Avalonia.Xaml.Interactions.Custom/Avalonia.Xaml.Interactions.Custom.csproj index 27e1cb73..7bc10841 100644 --- a/src/Avalonia.Xaml.Interactions.Custom/Avalonia.Xaml.Interactions.Custom.csproj +++ b/src/Avalonia.Xaml.Interactions.Custom/Avalonia.Xaml.Interactions.Custom.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netstandard2.1;net461;net6.0;net8.0 + netstandard2.0;net6.0;net8.0 Library bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml enable @@ -15,7 +15,7 @@ - + diff --git a/src/Avalonia.Xaml.Interactions.Custom/BindTagToVisualRootDataContextBehavior.cs b/src/Avalonia.Xaml.Interactions.Custom/BindTagToVisualRootDataContextBehavior.cs index f4389a86..095bcdc8 100644 --- a/src/Avalonia.Xaml.Interactions.Custom/BindTagToVisualRootDataContextBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Custom/BindTagToVisualRootDataContextBehavior.cs @@ -16,7 +16,7 @@ public class BindTagToVisualRootDataContextBehavior : Behavior protected override void OnAttachedToVisualTree() { var visualRoot = (Control?)AssociatedObject?.GetVisualRoot(); - if (visualRoot is { }) + if (visualRoot is not null) { _disposable = BindDataContextToTag(visualRoot, AssociatedObject); } @@ -37,6 +37,6 @@ protected override void OnDetachedFromVisualTree() throw new ArgumentNullException(nameof(target)); var data = source.GetObservable(StyledElement.DataContextProperty); - return data is { } ? target.Bind(Control.TagProperty, data) : null; + return data is not null ? target.Bind(Control.TagProperty, data) : null; } } diff --git a/src/Avalonia.Xaml.Interactions.Custom/BoundsObserverBehavior.cs b/src/Avalonia.Xaml.Interactions.Custom/BoundsObserverBehavior.cs new file mode 100644 index 00000000..7dab95d4 --- /dev/null +++ b/src/Avalonia.Xaml.Interactions.Custom/BoundsObserverBehavior.cs @@ -0,0 +1,76 @@ +using System.Reactive; +using System.Reactive.Disposables; +using Avalonia.Controls; +using Avalonia.Data; + +namespace Avalonia.Xaml.Interactions.Custom; + +/// +/// Observes the bounds of an associated and updates its Width and Height properties. +/// +public class BoundsObserverBehavior : DisposingBehavior +{ + /// + /// Defines the property. + /// + public static readonly StyledProperty BoundsProperty = + AvaloniaProperty.Register(nameof(Bounds), defaultBindingMode: BindingMode.OneWay); + + /// + /// Defines the property. + /// + public static readonly StyledProperty WidthProperty = + AvaloniaProperty.Register(nameof(Width), + defaultBindingMode: BindingMode.TwoWay); + + /// + /// Defines the property. + /// + public static readonly StyledProperty HeightProperty = + AvaloniaProperty.Register(nameof(Height), + defaultBindingMode: BindingMode.TwoWay); + + /// + /// Gets or sets the bounds of the associated control. This is a styled Avalonia property. + /// + public Rect Bounds + { + get => GetValue(BoundsProperty); + set => SetValue(BoundsProperty, value); + } + + /// + /// Gets or sets the width of the associated control. This is a two-way bound Avalonia property. + /// + public double Width + { + get => GetValue(WidthProperty); + set => SetValue(WidthProperty, value); + } + + /// + /// Gets or sets the height of the associated control. This is a two-way bound Avalonia property. + /// + public double Height + { + get => GetValue(HeightProperty); + set => SetValue(HeightProperty, value); + } + + /// + /// Attaches the behavior to the associated control and starts observing its bounds to update the Width and Height properties accordingly. + /// + /// A composite disposable used to manage the lifecycle of subscriptions and other disposables. + protected override void OnAttached(CompositeDisposable disposables) + { + if (AssociatedObject is not null) + { + disposables.Add(this.GetObservable(BoundsProperty) + .Subscribe(new AnonymousObserver(bounds => + { + Width = bounds.Width; + Height = bounds.Height; + }))); + } + } +} diff --git a/src/Avalonia.Xaml.Interactions.Custom/ButtonClickEventTriggerBehavior.cs b/src/Avalonia.Xaml.Interactions.Custom/ButtonClickEventTriggerBehavior.cs index 157f0837..e30a1368 100644 --- a/src/Avalonia.Xaml.Interactions.Custom/ButtonClickEventTriggerBehavior.cs +++ b/src/Avalonia.Xaml.Interactions.Custom/ButtonClickEventTriggerBehavior.cs @@ -30,7 +30,7 @@ public KeyModifiers KeyModifiers /// protected override void OnAttachedToVisualTree() { - if (AssociatedObject is { }) + if (AssociatedObject is not null) { AssociatedObject.Click += AssociatedObject_OnClick; AssociatedObject.AddHandler(InputElement.KeyDownEvent, Button_OnKeyDown, RoutingStrategies.Tunnel); @@ -41,7 +41,7 @@ protected override void OnAttachedToVisualTree() /// protected override void OnDetachedFromVisualTree() { - if (AssociatedObject is { }) + if (AssociatedObject is not null) { AssociatedObject.Click -= AssociatedObject_OnClick; AssociatedObject.RemoveHandler(InputElement.KeyDownEvent, Button_OnKeyDown); @@ -51,7 +51,7 @@ protected override void OnDetachedFromVisualTree() private void AssociatedObject_OnClick(object? sender, RoutedEventArgs e) { - if (AssociatedObject is { } && KeyModifiers == _savedKeyModifiers) + if (AssociatedObject is not null && KeyModifiers == _savedKeyModifiers) { Interaction.ExecuteActions(AssociatedObject, Actions, e); } diff --git a/src/Avalonia.Xaml.Interactions.Custom/ButtonExecuteCommandOnKeyDownBehavior.cs b/src/Avalonia.Xaml.Interactions.Custom/ButtonExecuteCommandOnKeyDownBehavior.cs new file mode 100644 index 00000000..3b1d161a --- /dev/null +++ b/src/Avalonia.Xaml.Interactions.Custom/ButtonExecuteCommandOnKeyDownBehavior.cs @@ -0,0 +1,80 @@ +using System.Reactive.Disposables; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.VisualTree; + +namespace Avalonia.Xaml.Interactions.Custom; + +/// +/// +/// +public class ButtonExecuteCommandOnKeyDownBehavior : AttachedToVisualTreeBehavior