Repository: AvaloniaUI/Avalonia.Xaml.Behaviors Branch: master Commit: 4f21f265b14b Files: 337 Total size: 525.0 KB Directory structure: gitextract_xihcw05z/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── .github/ │ │ └── stale.yml │ ├── FUNDING.yml │ ├── stale.yml │ └── workflows/ │ └── build.yml ├── .gitignore ├── .nuke/ │ ├── build.schema.json │ └── parameters.json ├── AvaloniaBehaviors.sln ├── Directory.Build.props ├── Directory.Packages.props ├── LICENSE.TXT ├── NuGet.Config ├── README.md ├── _config.yml ├── azure-pipelines.yml ├── build/ │ ├── SignAssembly.props │ ├── SourceLink.props │ ├── XUnit.props │ ├── behaviors.public.snk │ └── build/ │ ├── Build.cs │ └── _build.csproj ├── build.cmd ├── build.ps1 ├── build.sh ├── global.json ├── samples/ │ ├── BehaviorsTestApplication/ │ │ ├── App.axaml │ │ ├── App.axaml.cs │ │ ├── BehaviorsTestApplication.csproj │ │ ├── Converters/ │ │ │ └── ClassesToStringConverter.cs │ │ ├── Program.cs │ │ ├── SideBar.axaml │ │ ├── ViewModels/ │ │ │ ├── Core/ │ │ │ │ └── ViewModelBase.cs │ │ │ ├── ItemViewModel.cs │ │ │ └── MainWindowViewModel.cs │ │ └── Views/ │ │ ├── ItemView.axaml │ │ ├── ItemView.axaml.cs │ │ ├── MainView.axaml │ │ ├── MainView.axaml.cs │ │ ├── MainWindow.axaml │ │ ├── MainWindow.axaml.cs │ │ └── Pages/ │ │ ├── AdaptiveBehaviorView.axaml │ │ ├── AdaptiveBehaviorView.axaml.cs │ │ ├── AddRemoveClassActionView.axaml │ │ ├── AddRemoveClassActionView.axaml.cs │ │ ├── AdvancedView.axaml │ │ ├── AdvancedView.axaml.cs │ │ ├── ButtonClickEventTriggerBehaviorView.axaml │ │ ├── ButtonClickEventTriggerBehaviorView.axaml.cs │ │ ├── CallMethodActionView.axaml │ │ ├── CallMethodActionView.axaml.cs │ │ ├── ChangeAvaloniaPropertyActionView.axaml │ │ ├── ChangeAvaloniaPropertyActionView.axaml.cs │ │ ├── ChangePropertyActionView.axaml │ │ ├── ChangePropertyActionView.axaml.cs │ │ ├── CustomActionView.axaml │ │ ├── CustomActionView.axaml.cs │ │ ├── CustomBehaviorView.axaml │ │ ├── CustomBehaviorView.axaml.cs │ │ ├── DataTriggerBehaviorView.axaml │ │ ├── DataTriggerBehaviorView.axaml.cs │ │ ├── EditableListBoxView.axaml │ │ ├── EditableListBoxView.axaml.cs │ │ ├── EditableTreeViewView.axaml │ │ ├── EditableTreeViewView.axaml.cs │ │ ├── EventTriggerBehaviorView.axaml │ │ ├── EventTriggerBehaviorView.axaml.cs │ │ ├── InvokeCommandActionView.axaml │ │ ├── InvokeCommandActionView.axaml.cs │ │ ├── RoutedEventTriggerBehaviorView.axaml │ │ ├── RoutedEventTriggerBehaviorView.axaml.cs │ │ ├── ValueChangedTriggerBehaviorView.axaml │ │ └── ValueChangedTriggerBehaviorView.axaml.cs │ ├── Directory.Packages.props │ ├── DragAndDropSample/ │ │ ├── App.axaml │ │ ├── App.axaml.cs │ │ ├── Behaviors/ │ │ │ ├── BaseDataGridDropHandler.cs │ │ │ ├── ContextDragWithDirectionBehavior.cs │ │ │ ├── ItemsDataGridDropHandler.cs │ │ │ ├── ItemsListBoxDropHandler.cs │ │ │ ├── NodesListBoxDropHandler.cs │ │ │ └── NodesTreeViewDropHandler.cs │ │ ├── DragAndDropSample.csproj │ │ ├── Program.cs │ │ ├── ViewLocator.cs │ │ ├── ViewModels/ │ │ │ ├── ItemViewModel.cs │ │ │ ├── MainWindowViewModel.cs │ │ │ ├── NodeViewModel.cs │ │ │ └── ViewModelBase.cs │ │ └── Views/ │ │ ├── MainWindow.axaml │ │ └── MainWindow.axaml.cs │ └── DraggableDemo/ │ ├── App.axaml │ ├── App.axaml.cs │ ├── DraggableDemo.csproj │ ├── MainWindow.axaml │ ├── MainWindow.axaml.cs │ ├── Models/ │ │ ├── Item.cs │ │ └── Tile.cs │ ├── Program.cs │ └── Styles/ │ └── Custom.axaml ├── src/ │ ├── Avalonia.Xaml.Behaviors/ │ │ └── Avalonia.Xaml.Behaviors.csproj │ ├── Avalonia.Xaml.Interactions/ │ │ ├── Avalonia.Xaml.Interactions.csproj │ │ ├── Core/ │ │ │ ├── CallMethodAction.cs │ │ │ ├── ChangePropertyAction.cs │ │ │ ├── DataTriggerBehavior.cs │ │ │ ├── EventTriggerBehavior.cs │ │ │ └── InvokeCommandAction.cs │ │ └── Properties/ │ │ └── AssemblyInfo.cs │ ├── Avalonia.Xaml.Interactions.Custom/ │ │ ├── AddClassAction.cs │ │ ├── AttachedToVisualTreeBehavior.cs │ │ ├── Avalonia.Xaml.Interactions.Custom.csproj │ │ ├── BindPointerOverBehavior.cs │ │ ├── BindTagToVisualRootDataContextBehavior.cs │ │ ├── BindingBehavior.cs │ │ ├── BoundsObserverBehavior.cs │ │ ├── ButtonClickEventTriggerBehavior.cs │ │ ├── ButtonExecuteCommandOnKeyDownBehavior.cs │ │ ├── ChangeAvaloniaPropertyAction.cs │ │ ├── DisposingBehavior.cs │ │ ├── DisposingTrigger.cs │ │ ├── DragControlBehavior.cs │ │ ├── ExecuteCommandBehaviorBase.cs │ │ ├── ExecuteCommandOnActivatedBehavior.cs │ │ ├── ExecuteCommandOnDoubleTappedBehavior.cs │ │ ├── ExecuteCommandOnGotFocusBehavior.cs │ │ ├── ExecuteCommandOnHoldingBehavior.cs │ │ ├── ExecuteCommandOnKeyBehaviorBase.cs │ │ ├── ExecuteCommandOnKeyDownBehavior.cs │ │ ├── ExecuteCommandOnKeyUpBehavior.cs │ │ ├── ExecuteCommandOnLostFocusBehavior.cs │ │ ├── ExecuteCommandOnPinchBehavior.cs │ │ ├── ExecuteCommandOnPinchEndedBehavior.cs │ │ ├── ExecuteCommandOnPointerCaptureLostBehavior.cs │ │ ├── ExecuteCommandOnPointerEnteredBehavior.cs │ │ ├── ExecuteCommandOnPointerExitedBehavior.cs │ │ ├── ExecuteCommandOnPointerMovedBehavior.cs │ │ ├── ExecuteCommandOnPointerPressedBehavior.cs │ │ ├── ExecuteCommandOnPointerReleasedBehavior.cs │ │ ├── ExecuteCommandOnPointerTouchPadGestureMagnifyBehavior.cs │ │ ├── ExecuteCommandOnPointerTouchPadGestureRotateBehavior.cs │ │ ├── ExecuteCommandOnPointerTouchPadGestureSwipeBehavior.cs │ │ ├── ExecuteCommandOnPointerWheelChangedBehavior.cs │ │ ├── ExecuteCommandOnPullGestureBehavior.cs │ │ ├── ExecuteCommandOnPullGestureEndedBehavior.cs │ │ ├── ExecuteCommandOnRightTappedBehavior.cs │ │ ├── ExecuteCommandOnScrollGestureBehavior.cs │ │ ├── ExecuteCommandOnScrollGestureEndedBehavior.cs │ │ ├── ExecuteCommandOnScrollGestureInertiaStartingBehavior.cs │ │ ├── ExecuteCommandOnTappedBehavior.cs │ │ ├── ExecuteCommandOnTextInputBehavior.cs │ │ ├── ExecuteCommandOnTextInputMethodClientRequestedBehavior.cs │ │ ├── ExecuteCommandRoutedEventBehaviorBase.cs │ │ ├── FadeInBehavior.cs │ │ ├── FocusBehavior.cs │ │ ├── FocusBehaviorBase.cs │ │ ├── FocusControlAction.cs │ │ ├── FocusOnAttachedBehavior.cs │ │ ├── FocusOnAttachedToVisualTreeBehavior.cs │ │ ├── FocusOnPointerMovedBehavior.cs │ │ ├── FocusOnPointerPressedBehavior.cs │ │ ├── FocusSelectedItemBehavior.cs │ │ ├── HideFlyoutOnClickBehavior.cs │ │ ├── HideOnKeyPressedBehavior.cs │ │ ├── HideOnLostFocusBehavior.cs │ │ ├── HorizontalScrollViewerBehavior.cs │ │ ├── KeyDownTrigger.cs │ │ ├── PopupAction.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── RemoveClassAction.cs │ │ ├── RoutedEventTriggerBase.cs │ │ ├── RoutedEventTriggerBehavior.cs │ │ ├── SelectAllOnGotFocusBehavior.cs │ │ ├── SelectListBoxItemOnPointerMovedBehavior.cs │ │ ├── SelectingItemsControlBehavior.cs │ │ ├── ShowBehaviorBase.cs │ │ ├── ShowOnDoubleTappedBehavior.cs │ │ ├── ShowOnKeyDownBehavior.cs │ │ ├── ShowOnTappedBehavior.cs │ │ ├── ShowPointerPositionBehavior.cs │ │ ├── TextBoxSelectAllTextBehavior.cs │ │ ├── ToggleIsExpandedOnDoubleTappedBehavior.cs │ │ └── ValueChangedTriggerBehavior.cs │ ├── Avalonia.Xaml.Interactions.DragAndDrop/ │ │ ├── Avalonia.Xaml.Interactions.DragAndDrop.csproj │ │ ├── ContextDragBehavior.cs │ │ ├── ContextDropBehavior.cs │ │ ├── DropHandlerBase.cs │ │ ├── IDragHandler.cs │ │ ├── IDropHandler.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ └── TypedDragBehavior.cs │ ├── Avalonia.Xaml.Interactions.Draggable/ │ │ ├── Avalonia.Xaml.Interactions.Draggable.csproj │ │ ├── CanvasDragBehavior.cs │ │ ├── GridDragBehavior.cs │ │ ├── ItemDragBehavior.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── SelectionAdorner.cs │ │ └── Styles.axaml │ ├── Avalonia.Xaml.Interactions.Events/ │ │ ├── Avalonia.Xaml.Interactions.Events.csproj │ │ ├── DoubleTappedEventBehavior.cs │ │ ├── GotFocusEventBehavior.cs │ │ ├── KeyDownEventBehavior.cs │ │ ├── KeyUpEventBehavior.cs │ │ ├── LostFocusEventBehavior.cs │ │ ├── PointerCaptureLostEventBehavior.cs │ │ ├── PointerEnteredEventBehavior.cs │ │ ├── PointerEventsBehavior.cs │ │ ├── PointerExitedEventBehavior.cs │ │ ├── PointerMovedEventBehavior.cs │ │ ├── PointerPressedEventBehavior.cs │ │ ├── PointerReleasedEventBehavior.cs │ │ ├── PointerWheelChangedEventBehavior.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── RightTappedEventBehavior.cs │ │ ├── ScrollGestureEndedEventBehavior.cs │ │ ├── ScrollGestureEventBehavior.cs │ │ ├── TappedEventBehavior.cs │ │ ├── TextInputEventBehavior.cs │ │ └── TextInputMethodClientRequestedEventBehavior.cs │ ├── Avalonia.Xaml.Interactions.Responsive/ │ │ ├── AdaptiveBehavior.cs │ │ ├── AdaptiveClassSetter.cs │ │ ├── Avalonia.Xaml.Interactions.Responsive.csproj │ │ └── Properties/ │ │ └── AssemblyInfo.cs │ └── Avalonia.Xaml.Interactivity/ │ ├── ActionCollection.cs │ ├── Avalonia.Xaml.Interactivity.csproj │ ├── Behavior.cs │ ├── BehaviorCollection.cs │ ├── BehaviorCollectionTemplate.cs │ ├── BehaviorOfT.cs │ ├── ComparisonConditionType.cs │ ├── IAction.cs │ ├── IBehavior.cs │ ├── ITrigger.cs │ ├── Interaction.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── System/ │ │ └── Diagnostics/ │ │ └── CodeAnalysis/ │ │ └── TrimmingAttributes.cs │ ├── Trigger.cs │ ├── TriggerOfT.cs │ └── TypeConverterHelper.cs └── tests/ ├── Avalonia.Xaml.Interactions.UnitTests/ │ ├── App.axaml │ ├── App.axaml.cs │ ├── Avalonia.Xaml.Interactions.UnitTests.csproj │ ├── Core/ │ │ ├── CallMethodAction001.axaml │ │ ├── CallMethodAction001.axaml.cs │ │ ├── CallMethodAction002.axaml │ │ ├── CallMethodAction002.axaml.cs │ │ ├── CallMethodActionTests.CallMethodAction_001.Linux.verified.txt │ │ ├── CallMethodActionTests.CallMethodAction_001.OSX.verified.txt │ │ ├── CallMethodActionTests.CallMethodAction_001.Windows.verified.txt │ │ ├── CallMethodActionTests.CallMethodAction_002.Linux.verified.txt │ │ ├── CallMethodActionTests.CallMethodAction_002.OSX.verified.txt │ │ ├── CallMethodActionTests.CallMethodAction_002.Windows.verified.txt │ │ ├── CallMethodActionTests.cs │ │ ├── ChangePropertyAction001.axaml │ │ ├── ChangePropertyAction001.axaml.cs │ │ ├── ChangePropertyAction002.axaml │ │ ├── ChangePropertyAction002.axaml.cs │ │ ├── ChangePropertyActionTests.ChangePropertyAction_001.Linux.verified.txt │ │ ├── ChangePropertyActionTests.ChangePropertyAction_001.OSX.verified.txt │ │ ├── ChangePropertyActionTests.ChangePropertyAction_001.Windows.verified.txt │ │ ├── ChangePropertyActionTests.ChangePropertyAction_002.Linux.verified.txt │ │ ├── ChangePropertyActionTests.ChangePropertyAction_002.OSX.verified.txt │ │ ├── ChangePropertyActionTests.ChangePropertyAction_002.Windows.verified.txt │ │ ├── ChangePropertyActionTests.cs │ │ ├── Command.cs │ │ ├── DataTriggerBehavior001.axaml │ │ ├── DataTriggerBehavior001.axaml.cs │ │ ├── DataTriggerBehaviorTests.DataTriggerBehavior_001.Linux.verified.txt │ │ ├── DataTriggerBehaviorTests.DataTriggerBehavior_001.OSX.verified.txt │ │ ├── DataTriggerBehaviorTests.DataTriggerBehavior_001.Windows.verified.txt │ │ ├── DataTriggerBehaviorTests.cs │ │ ├── EventTriggerBehavior001.axaml │ │ ├── EventTriggerBehavior001.axaml.cs │ │ ├── EventTriggerBehavior002.axaml │ │ ├── EventTriggerBehavior002.axaml.cs │ │ ├── EventTriggerBehavior003.axaml │ │ ├── EventTriggerBehavior003.axaml.cs │ │ ├── EventTriggerBehavior004.axaml │ │ ├── EventTriggerBehavior004.axaml.cs │ │ ├── EventTriggerBehaviorTests.EventTriggerBehavior_001.Linux.verified.txt │ │ ├── EventTriggerBehaviorTests.EventTriggerBehavior_001.OSX.verified.txt │ │ ├── EventTriggerBehaviorTests.EventTriggerBehavior_001.Windows.verified.txt │ │ ├── EventTriggerBehaviorTests.EventTriggerBehavior_002.Linux.verified.txt │ │ ├── EventTriggerBehaviorTests.EventTriggerBehavior_002.OSX.verified.txt │ │ ├── EventTriggerBehaviorTests.EventTriggerBehavior_002.Windows.verified.txt │ │ ├── EventTriggerBehaviorTests.EventTriggerBehavior_003.Linux.verified.txt │ │ ├── EventTriggerBehaviorTests.EventTriggerBehavior_003.OSX.verified.txt │ │ ├── EventTriggerBehaviorTests.EventTriggerBehavior_003.Windows.verified.txt │ │ ├── EventTriggerBehaviorTests.EventTriggerBehavior_004.Linux.verified.txt │ │ ├── EventTriggerBehaviorTests.EventTriggerBehavior_004.OSX.verified.txt │ │ ├── EventTriggerBehaviorTests.EventTriggerBehavior_004.Windows.verified.txt │ │ ├── EventTriggerBehaviorTests.cs │ │ ├── InvokeCommandAction001.axaml │ │ ├── InvokeCommandAction001.axaml.cs │ │ ├── InvokeCommandAction002.axaml │ │ ├── InvokeCommandAction002.axaml.cs │ │ ├── InvokeCommandAction003.axaml │ │ ├── InvokeCommandAction003.axaml.cs │ │ ├── InvokeCommandAction004.axaml │ │ ├── InvokeCommandAction004.axaml.cs │ │ ├── InvokeCommandActionTests.InvokeCommandAction_001.Linux.verified.txt │ │ ├── InvokeCommandActionTests.InvokeCommandAction_001.OSX.verified.txt │ │ ├── InvokeCommandActionTests.InvokeCommandAction_001.Windows.verified.txt │ │ ├── InvokeCommandActionTests.InvokeCommandAction_002.Linux.verified.txt │ │ ├── InvokeCommandActionTests.InvokeCommandAction_002.OSX.verified.txt │ │ ├── InvokeCommandActionTests.InvokeCommandAction_002.Windows.verified.txt │ │ ├── InvokeCommandActionTests.InvokeCommandAction_003.Linux.verified.txt │ │ ├── InvokeCommandActionTests.InvokeCommandAction_003.OSX.verified.txt │ │ ├── InvokeCommandActionTests.InvokeCommandAction_003.Windows.verified.txt │ │ ├── InvokeCommandActionTests.InvokeCommandAction_004.Linux.verified.txt │ │ ├── InvokeCommandActionTests.InvokeCommandAction_004.OSX.verified.txt │ │ ├── InvokeCommandActionTests.InvokeCommandAction_004.Windows.verified.txt │ │ ├── InvokeCommandActionTests.cs │ │ └── TestValueConverter.cs │ ├── HeadlessWindowExtensions.cs │ ├── ModuleInit.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ └── TestAppBuilder.cs ├── Avalonia.Xaml.Interactivity.UnitTests/ │ ├── App.axaml │ ├── App.axaml.cs │ ├── Avalonia.Xaml.Interactivity.UnitTests.csproj │ ├── BehaviorCollectionTemplate001.axaml │ ├── BehaviorCollectionTemplate001.axaml.cs │ ├── BehaviorCollectionTemplateTests.cs │ ├── BehaviorCollectionTest.cs │ ├── BehaviorOfTTests.cs │ ├── BehaviorTests.cs │ ├── InteractionTest.cs │ ├── InteractionTests.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── StubAction.cs │ ├── StubBehavior.cs │ ├── TestAppBuilder.cs │ ├── TestUitilties.cs │ ├── TriggerOfTTests.cs │ └── TriggerTests.cs └── Directory.Packages.props ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # editorconfig.org # top-most EditorConfig file root = true # Default settings: # A newline ending every file # Use 4 spaces as indentation [*] insert_final_newline = true indent_style = space indent_size = 4 # C# files [*.cs] # New line preferences csharp_new_line_before_open_brace = all csharp_new_line_before_else = true csharp_new_line_before_catch = true csharp_new_line_before_finally = true csharp_new_line_before_members_in_object_initializers = true csharp_new_line_before_members_in_anonymous_types = true csharp_new_line_between_query_expression_clauses = true # Indentation preferences csharp_indent_block_contents = true csharp_indent_braces = false csharp_indent_case_contents = true csharp_indent_switch_labels = true csharp_indent_labels = one_less_than_current # avoid this. unless absolutely necessary dotnet_style_qualification_for_field = false:suggestion dotnet_style_qualification_for_property = false:suggestion dotnet_style_qualification_for_method = false:suggestion dotnet_style_qualification_for_event = false:suggestion # prefer var csharp_style_var_for_built_in_types = true csharp_style_var_when_type_is_apparent = true csharp_style_var_elsewhere = true:suggestion # use language keywords instead of BCL types dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion dotnet_style_predefined_type_for_member_access = true:suggestion # name all constant fields using PascalCase dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style dotnet_naming_symbols.constant_fields.applicable_kinds = field dotnet_naming_symbols.constant_fields.required_modifiers = const dotnet_naming_style.pascal_case_style.capitalization = pascal_case # static fields should have s_ prefix dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style dotnet_naming_symbols.static_fields.applicable_kinds = field dotnet_naming_symbols.static_fields.required_modifiers = static dotnet_naming_style.static_prefix_style.required_prefix = s_ dotnet_naming_style.static_prefix_style.capitalization = camel_case # internal and private fields should be _camelCase dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style dotnet_naming_symbols.private_internal_fields.applicable_kinds = field dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal dotnet_naming_style.camel_case_underscore_style.required_prefix = _ dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case # use accessibility modifiers dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion # Code style defaults dotnet_sort_system_directives_first = true csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = false # Expression-level preferences dotnet_style_object_initializer = true:suggestion dotnet_style_collection_initializer = true:suggestion dotnet_style_explicit_tuple_names = true:suggestion dotnet_style_coalesce_expression = true:suggestion dotnet_style_null_propagation = true:suggestion # Expression-bodied members csharp_style_expression_bodied_methods = false:none csharp_style_expression_bodied_constructors = false:none csharp_style_expression_bodied_operators = false:none csharp_style_expression_bodied_properties = true:none csharp_style_expression_bodied_indexers = true:none csharp_style_expression_bodied_accessors = true:none # Pattern matching csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion csharp_style_pattern_matching_over_as_with_null_check = true:suggestion csharp_style_inlined_variable_declaration = true:suggestion # Null checking preferences csharp_style_throw_expression = true:suggestion csharp_style_conditional_delegate_call = true:suggestion # Space preferences csharp_space_after_cast = false csharp_space_after_colon_in_inheritance_clause = true csharp_space_after_comma = true csharp_space_after_dot = false csharp_space_after_keywords_in_control_flow_statements = true csharp_space_after_semicolon_in_for_statement = true csharp_space_around_binary_operators = before_and_after csharp_space_around_declaration_statements = do_not_ignore csharp_space_before_colon_in_inheritance_clause = true csharp_space_before_comma = false csharp_space_before_dot = false csharp_space_before_open_square_brackets = false csharp_space_before_semicolon_in_for_statement = false csharp_space_between_empty_square_brackets = false csharp_space_between_method_call_empty_parameter_list_parentheses = false csharp_space_between_method_call_name_and_opening_parenthesis = false csharp_space_between_method_call_parameter_list_parentheses = false csharp_space_between_method_declaration_empty_parameter_list_parentheses = false csharp_space_between_method_declaration_name_and_open_parenthesis = false csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false csharp_space_between_square_brackets = false # Xaml files [*.{xaml,axaml}] indent_style = space indent_size = 2 # Xml project files [*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] indent_size = 2 # Xml build files [*.builds] indent_size = 2 # Xml files [*.{xml,stylecop,resx,ruleset}] indent_size = 2 # Xml config files [*.{props,targets,config,nuspec}] indent_size = 2 # Shell scripts [*.sh] end_of_line = lf [*.{cmd, bat}] end_of_line = crlf # Verify settings [*.{received,verified}.{txt,xml,json}] charset = "utf-8-bom" end_of_line = lf indent_size = unset indent_style = unset insert_final_newline = false tab_width = unset trim_trailing_whitespace = false ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain # VerifyTests *.png binary *.verified.txt text eol=lf working-tree-encoding=UTF-8 *.verified.xml text eol=lf working-tree-encoding=UTF-8 *.verified.json text eol=lf working-tree-encoding=UTF-8 ================================================ FILE: .github/.github/stale.yml ================================================ # https://probot.github.io/apps/stale/ # Number of days of inactivity before an issue becomes stale daysUntilStale: 60 # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 # Issues with these labels will never be considered stale exemptLabels: - pinned - security # Label to use when marking an issue as stale staleLabel: stale # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable # closeComment: false ================================================ FILE: .github/FUNDING.yml ================================================ github: [wieslawsoltes] ================================================ FILE: .github/stale.yml ================================================ # https://probot.github.io/apps/stale/ # Number of days of inactivity before an issue becomes stale daysUntilStale: 60 # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 # Issues with these labels will never be considered stale exemptLabels: - pinned - security # Label to use when marking an issue as stale staleLabel: stale # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: true ================================================ FILE: .github/workflows/build.yml ================================================ name: CI on: push: branches: - master - release/* pull_request: branches: - master jobs: build: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] name: Build ${{ matrix.os }} runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v1 - name: Setup .NET Core uses: actions/setup-dotnet@v1 - name: Build Release run: dotnet build --configuration Release - name: Test Release run: dotnet test --configuration Release ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # .NET Core project.lock.json project.fragment.lock.json artifacts/ **/Properties/launchSettings.json *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Typescript v1 declaration files typings/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it tools/** # !tools/packages.config # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # macOS .DS_Store # VerifyTests *.received.* ================================================ FILE: .nuke/build.schema.json ================================================ { "$schema": "http://json-schema.org/draft-04/schema#", "title": "Build Schema", "$ref": "#/definitions/build", "definitions": { "build": { "type": "object", "properties": { "Configuration": { "type": "string", "description": "configuration" }, "Continue": { "type": "boolean", "description": "Indicates to continue a previously failed build attempt" }, "Help": { "type": "boolean", "description": "Shows the help text for this build assembly" }, "Host": { "type": "string", "description": "Host for execution. Default is 'automatic'", "enum": [ "AppVeyor", "AzurePipelines", "Bamboo", "Bitrise", "GitHubActions", "GitLab", "Jenkins", "Rider", "SpaceAutomation", "TeamCity", "Terminal", "TravisCI", "VisualStudio", "VSCode" ] }, "NoLogo": { "type": "boolean", "description": "Disables displaying the NUKE logo" }, "Partition": { "type": "string", "description": "Partition to use on CI" }, "Plan": { "type": "boolean", "description": "Shows the execution plan (HTML)" }, "Profile": { "type": "array", "description": "Defines the profiles to load", "items": { "type": "string" } }, "PublishFramework": { "type": "string", "description": "publish-framework" }, "PublishProject": { "type": "string", "description": "publish-project" }, "PublishRuntime": { "type": "string", "description": "publish-runtime" }, "PublishSelfContained": { "type": "boolean", "description": "publish-self-contained" }, "Root": { "type": "string", "description": "Root directory during build execution" }, "Skip": { "type": "array", "description": "List of targets to be skipped. Empty list skips all dependencies", "items": { "type": "string", "enum": [ "Clean", "Compile", "Pack", "Publish", "Restore", "Test" ] } }, "Solution": { "type": "string", "description": "Path to a solution file that is automatically loaded" }, "Target": { "type": "array", "description": "List of targets to be invoked. Default is '{default_target}'", "items": { "type": "string", "enum": [ "Clean", "Compile", "Pack", "Publish", "Restore", "Test" ] } }, "Verbosity": { "type": "string", "description": "Logging verbosity during build execution. Default is 'Normal'", "enum": [ "Minimal", "Normal", "Quiet", "Verbose" ] }, "VersionSuffix": { "type": "string", "description": "version-suffix" } } } } } ================================================ FILE: .nuke/parameters.json ================================================ { "$schema": "./build.schema.json", "Solution": "AvaloniaBehaviors.sln" } ================================================ FILE: AvaloniaBehaviors.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28315.86 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}" ProjectSection(SolutionItems) = preProject tests\Directory.Packages.props = tests\Directory.Packages.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{9B9E3891-2366-4253-A952-D08BCEB71098}" ProjectSection(SolutionItems) = preProject samples\Directory.Packages.props = samples\Directory.Packages.props EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BehaviorsTestApplication", "samples\BehaviorsTestApplication\BehaviorsTestApplication.csproj", "{EA113F1A-D8D7-4142-9948-353270E7EBAE}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Xaml.Behaviors", "src\Avalonia.Xaml.Behaviors\Avalonia.Xaml.Behaviors.csproj", "{2D9FCE6A-DF13-4037-BC33-7EDB06102A2E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Xaml.Interactivity", "src\Avalonia.Xaml.Interactivity\Avalonia.Xaml.Interactivity.csproj", "{14006EB4-66E9-4B58-BF5F-5A1983BF4621}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Xaml.Interactions", "src\Avalonia.Xaml.Interactions\Avalonia.Xaml.Interactions.csproj", "{ED03EEAD-938F-447B-BF0B-30857AE78BFF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Xaml.Interactivity.UnitTests", "tests\Avalonia.Xaml.Interactivity.UnitTests\Avalonia.Xaml.Interactivity.UnitTests.csproj", "{C2229490-39CA-4192-A28C-1B5EC186E027}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Xaml.Interactions.UnitTests", "tests\Avalonia.Xaml.Interactions.UnitTests\Avalonia.Xaml.Interactions.UnitTests.csproj", "{80D2C64D-B560-475D-941F-D5F28DDB2591}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{84224365-32B6-46AF-85A2-45640E6D7EEB}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{2913483A-3E55-44AE-86BB-88511F372191}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig build\behaviors.public.snk = build\behaviors.public.snk build.ps1 = build.ps1 build.sh = build.sh global.json = global.json azure-pipelines.yml = azure-pipelines.yml Directory.Build.props = Directory.Build.props Directory.Packages.props = Directory.Packages.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "props", "props", "{40A43022-EBA7-4E1D-8BB0-2691E41B5622}" ProjectSection(SolutionItems) = preProject build\SignAssembly.props = build\SignAssembly.props build\SourceLink.props = build\SourceLink.props build\XUnit.props = build\XUnit.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{721A55B7-C5B0-44E2-803A-56E291C672FE}" ProjectSection(SolutionItems) = preProject NuGet.Config = NuGet.Config EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{8A0733E3-A184-4BD4-8CB7-478977F3F5FB}" ProjectSection(SolutionItems) = preProject LICENSE.TXT = LICENSE.TXT README.md = README.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "git", "git", "{0B395300-CACE-470D-81A9-69B922FB79DE}" ProjectSection(SolutionItems) = preProject .gitattributes = .gitattributes .gitignore = .gitignore EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\build\_build.csproj", "{005E7A00-4EA8-43F6-868E-1F8965D39CAA}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DragAndDropSample", "samples\DragAndDropSample\DragAndDropSample.csproj", "{438F2B54-FEDB-48C4-B14A-E47541DA443F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DraggableDemo", "samples\DraggableDemo\DraggableDemo.csproj", "{D5855901-BEEF-4179-96E8-9DD276C4DAEE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github", "github", "{9D4D9F1A-6EE5-4B7D-8991-0601F80F39CD}" ProjectSection(SolutionItems) = preProject .github\workflows\build.yml = .github\workflows\build.yml EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Xaml.Interactions.Responsive", "src\Avalonia.Xaml.Interactions.Responsive\Avalonia.Xaml.Interactions.Responsive.csproj", "{BEC165AA-392E-4193-A762-576A1EB8D40E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Xaml.Interactions.Draggable", "src\Avalonia.Xaml.Interactions.Draggable\Avalonia.Xaml.Interactions.Draggable.csproj", "{681EB965-E5B2-4A6F-9938-578FD19E8A15}" 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.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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {EA113F1A-D8D7-4142-9948-353270E7EBAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EA113F1A-D8D7-4142-9948-353270E7EBAE}.Debug|Any CPU.Build.0 = Debug|Any CPU {EA113F1A-D8D7-4142-9948-353270E7EBAE}.Release|Any CPU.ActiveCfg = Release|Any CPU {EA113F1A-D8D7-4142-9948-353270E7EBAE}.Release|Any CPU.Build.0 = Release|Any CPU {2D9FCE6A-DF13-4037-BC33-7EDB06102A2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2D9FCE6A-DF13-4037-BC33-7EDB06102A2E}.Debug|Any CPU.Build.0 = Debug|Any CPU {2D9FCE6A-DF13-4037-BC33-7EDB06102A2E}.Release|Any CPU.ActiveCfg = Release|Any CPU {2D9FCE6A-DF13-4037-BC33-7EDB06102A2E}.Release|Any CPU.Build.0 = Release|Any CPU {14006EB4-66E9-4B58-BF5F-5A1983BF4621}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {14006EB4-66E9-4B58-BF5F-5A1983BF4621}.Debug|Any CPU.Build.0 = Debug|Any CPU {14006EB4-66E9-4B58-BF5F-5A1983BF4621}.Release|Any CPU.ActiveCfg = Release|Any CPU {14006EB4-66E9-4B58-BF5F-5A1983BF4621}.Release|Any CPU.Build.0 = Release|Any CPU {ED03EEAD-938F-447B-BF0B-30857AE78BFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ED03EEAD-938F-447B-BF0B-30857AE78BFF}.Debug|Any CPU.Build.0 = Debug|Any CPU {ED03EEAD-938F-447B-BF0B-30857AE78BFF}.Release|Any CPU.ActiveCfg = Release|Any CPU {ED03EEAD-938F-447B-BF0B-30857AE78BFF}.Release|Any CPU.Build.0 = Release|Any CPU {C2229490-39CA-4192-A28C-1B5EC186E027}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C2229490-39CA-4192-A28C-1B5EC186E027}.Debug|Any CPU.Build.0 = Debug|Any CPU {C2229490-39CA-4192-A28C-1B5EC186E027}.Release|Any CPU.ActiveCfg = Release|Any CPU {C2229490-39CA-4192-A28C-1B5EC186E027}.Release|Any CPU.Build.0 = Release|Any CPU {80D2C64D-B560-475D-941F-D5F28DDB2591}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {80D2C64D-B560-475D-941F-D5F28DDB2591}.Debug|Any CPU.Build.0 = Debug|Any CPU {80D2C64D-B560-475D-941F-D5F28DDB2591}.Release|Any CPU.ActiveCfg = Release|Any CPU {80D2C64D-B560-475D-941F-D5F28DDB2591}.Release|Any CPU.Build.0 = Release|Any CPU {005E7A00-4EA8-43F6-868E-1F8965D39CAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {005E7A00-4EA8-43F6-868E-1F8965D39CAA}.Debug|Any CPU.Build.0 = Debug|Any CPU {005E7A00-4EA8-43F6-868E-1F8965D39CAA}.Release|Any CPU.ActiveCfg = Release|Any CPU {005E7A00-4EA8-43F6-868E-1F8965D39CAA}.Release|Any CPU.Build.0 = Release|Any CPU {438F2B54-FEDB-48C4-B14A-E47541DA443F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {438F2B54-FEDB-48C4-B14A-E47541DA443F}.Debug|Any CPU.Build.0 = Debug|Any CPU {438F2B54-FEDB-48C4-B14A-E47541DA443F}.Release|Any CPU.ActiveCfg = Release|Any CPU {438F2B54-FEDB-48C4-B14A-E47541DA443F}.Release|Any CPU.Build.0 = Release|Any CPU {D5855901-BEEF-4179-96E8-9DD276C4DAEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D5855901-BEEF-4179-96E8-9DD276C4DAEE}.Debug|Any CPU.Build.0 = Debug|Any CPU {D5855901-BEEF-4179-96E8-9DD276C4DAEE}.Release|Any CPU.ActiveCfg = Release|Any CPU {D5855901-BEEF-4179-96E8-9DD276C4DAEE}.Release|Any CPU.Build.0 = Release|Any CPU {BEC165AA-392E-4193-A762-576A1EB8D40E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BEC165AA-392E-4193-A762-576A1EB8D40E}.Debug|Any CPU.Build.0 = Debug|Any CPU {BEC165AA-392E-4193-A762-576A1EB8D40E}.Release|Any CPU.ActiveCfg = Release|Any CPU {BEC165AA-392E-4193-A762-576A1EB8D40E}.Release|Any CPU.Build.0 = Release|Any CPU {681EB965-E5B2-4A6F-9938-578FD19E8A15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {681EB965-E5B2-4A6F-9938-578FD19E8A15}.Debug|Any CPU.Build.0 = Debug|Any CPU {681EB965-E5B2-4A6F-9938-578FD19E8A15}.Release|Any CPU.ActiveCfg = Release|Any CPU {681EB965-E5B2-4A6F-9938-578FD19E8A15}.Release|Any CPU.Build.0 = Release|Any CPU {EF243B1A-FF29-41E9-B44B-2E55A73F6CCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EF243B1A-FF29-41E9-B44B-2E55A73F6CCE}.Debug|Any CPU.Build.0 = Debug|Any CPU {EF243B1A-FF29-41E9-B44B-2E55A73F6CCE}.Release|Any CPU.ActiveCfg = Release|Any CPU {EF243B1A-FF29-41E9-B44B-2E55A73F6CCE}.Release|Any CPU.Build.0 = Release|Any CPU {D647E09E-BBF2-4432-A4E0-80FBB921C1EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D647E09E-BBF2-4432-A4E0-80FBB921C1EE}.Debug|Any CPU.Build.0 = Debug|Any CPU {D647E09E-BBF2-4432-A4E0-80FBB921C1EE}.Release|Any CPU.ActiveCfg = Release|Any CPU {D647E09E-BBF2-4432-A4E0-80FBB921C1EE}.Release|Any CPU.Build.0 = Release|Any CPU {648FFF6A-A8F5-491C-AC1C-19FC2061D6B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {EA113F1A-D8D7-4142-9948-353270E7EBAE} = {9B9E3891-2366-4253-A952-D08BCEB71098} {2D9FCE6A-DF13-4037-BC33-7EDB06102A2E} = {84224365-32B6-46AF-85A2-45640E6D7EEB} {14006EB4-66E9-4B58-BF5F-5A1983BF4621} = {84224365-32B6-46AF-85A2-45640E6D7EEB} {ED03EEAD-938F-447B-BF0B-30857AE78BFF} = {84224365-32B6-46AF-85A2-45640E6D7EEB} {C2229490-39CA-4192-A28C-1B5EC186E027} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {80D2C64D-B560-475D-941F-D5F28DDB2591} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {40A43022-EBA7-4E1D-8BB0-2691E41B5622} = {2913483A-3E55-44AE-86BB-88511F372191} {721A55B7-C5B0-44E2-803A-56E291C672FE} = {2913483A-3E55-44AE-86BB-88511F372191} {0B395300-CACE-470D-81A9-69B922FB79DE} = {2913483A-3E55-44AE-86BB-88511F372191} {005E7A00-4EA8-43F6-868E-1F8965D39CAA} = {2913483A-3E55-44AE-86BB-88511F372191} {438F2B54-FEDB-48C4-B14A-E47541DA443F} = {9B9E3891-2366-4253-A952-D08BCEB71098} {D5855901-BEEF-4179-96E8-9DD276C4DAEE} = {9B9E3891-2366-4253-A952-D08BCEB71098} {9D4D9F1A-6EE5-4B7D-8991-0601F80F39CD} = {2913483A-3E55-44AE-86BB-88511F372191} {BEC165AA-392E-4193-A762-576A1EB8D40E} = {84224365-32B6-46AF-85A2-45640E6D7EEB} {681EB965-E5B2-4A6F-9938-578FD19E8A15} = {84224365-32B6-46AF-85A2-45640E6D7EEB} {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E64CD420-15B1-487C-9806-41EBE6DC15A4} EndGlobalSection EndGlobal ================================================ FILE: Directory.Build.props ================================================  11.0.10.8 Wiesław Šoltés Wiesław Šoltés Copyright © Wiesław Šoltés 2024 MIT https://github.com/wieslawsoltes/AvaloniaBehaviors latest latest true ================================================ FILE: Directory.Packages.props ================================================  true 11.0.0 ================================================ FILE: LICENSE.TXT ================================================ The MIT License (MIT) Copyright (c) Wiesław Šoltés Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: NuGet.Config ================================================  ================================================ FILE: README.md ================================================ # Repository was moved! New location is https://github.com/wieslawsoltes/Avalonia.Xaml.Behaviors # Avalonia XAML Behaviors [![Gitter](https://badges.gitter.im/wieslawsoltes/AvaloniaBehaviors.svg)](https://gitter.im/wieslawsoltes/AvaloniaBehaviors?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Build Status](https://dev.azure.com/wieslawsoltes/GitHub/_apis/build/status/wieslawsoltes.AvaloniaBehaviors?repoName=wieslawsoltes%2FAvaloniaBehaviors&branchName=master)](https://dev.azure.com/wieslawsoltes/GitHub/_build/latest?definitionId=90&repoName=wieslawsoltes%2FAvaloniaBehaviors&branchName=master) [![CI](https://github.com/wieslawsoltes/AvaloniaBehaviors/actions/workflows/build.yml/badge.svg)](https://github.com/wieslawsoltes/AvaloniaBehaviors/actions/workflows/build.yml) [![NuGet](https://img.shields.io/nuget/v/Avalonia.Xaml.Behaviors.svg)](https://www.nuget.org/packages/Avalonia.Xaml.Behaviors) [![NuGet](https://img.shields.io/nuget/dt/Avalonia.Xaml.Interactivity.svg)](https://www.nuget.org/packages/Avalonia.Xaml.Interactivity) [![MyGet](https://img.shields.io/myget/xamlbehaviors-nightly/vpre/Avalonia.Xaml.Behaviors.svg?label=myget)](https://www.myget.org/gallery/xamlbehaviors-nightly) **AvaloniaBehaviors** is a port of [Windows UWP](https://github.com/Microsoft/XamlBehaviors) version of XAML Behaviors for [Avalonia](https://github.com/AvaloniaUI/Avalonia) XAML. Avalonia XAML Behaviors is an easy-to-use means of adding common and reusable interactivity to your [Avalonia](https://github.com/AvaloniaUI/Avalonia) applications with minimal code. Avalonia port is available only for managed applications. Use of XAML Behaviors is governed by the MIT License. ![](https://i.ytimg.com/vi/pffBS-yQ_uM/hqdefault.jpg) ## Building Avalonia XAML Behaviors First, clone the repository or download the latest zip. ``` git clone https://github.com/wieslawsoltes/AvaloniaBehaviors.git ``` ### Build on Windows using script * [.NET Core](https://www.microsoft.com/net/download?initial-os=windows). Open up a command-prompt and execute the commands: ``` .\build.ps1 ``` ### Build on Linux using script * [.NET Core](https://www.microsoft.com/net/download?initial-os=linux). Open up a terminal prompt and execute the commands: ``` ./build.sh ``` ### Build on OSX using script * [.NET Core](https://www.microsoft.com/net/download?initial-os=macos). Open up a terminal prompt and execute the commands: ``` ./build.sh ``` ## NuGet Avalonia XamlBehaviors is delivered as a NuGet package. You can find the packages here [NuGet](https://www.nuget.org/packages/Avalonia.Xaml.Behaviors/) and install the package like this: `Install-Package Avalonia.Xaml.Behaviors` or by using nightly build feed: * Add `https://www.myget.org/F/xamlbehaviors-nightly/api/v2` to your package sources * Alternative nightly build feed `https://pkgs.dev.azure.com/wieslawsoltes/GitHub/_packaging/Nightly/nuget/v3/index.json` * Update your package using `XamlBehaviors` feed and install the package like this: `Install-Package Avalonia.Xaml.Behaviors -Pre` ### Package Sources * https://api.nuget.org/v3/index.json * https://www.myget.org/F/avalonia-ci/api/v2 ## Resources * [GitHub source code repository.](https://github.com/wieslawsoltes/AvaloniaBehaviors) ## License Avalonia XAML Behaviors is licensed under the [MIT license](LICENSE.TXT). ================================================ FILE: _config.yml ================================================ theme: jekyll-theme-cayman ================================================ FILE: azure-pipelines.yml ================================================ name: $(date:yyyyMMdd)$(rev:-rr) resources: repositories: - repository: templates endpoint: wieslawsoltes type: github name: wieslawsoltes/BuildTemplates ref: refs/tags/v2.0.0 variables: BuildConfiguration: 'Release' BuildPlatform: 'Any CPU' PublishFramework: 'net8.0' PublishProject: 'BehaviorsTestApplication' PublishRuntime: '' jobs: - template: Test-PowerShell.yml@templates parameters: name: 'Test_Windows' vmImage: 'windows-2022' BuildConfiguration: ${{ variables.BuildConfiguration }} - template: Test-Bash.yml@templates parameters: name: 'Test_Linux' vmImage: 'ubuntu-20.04' BuildConfiguration: ${{ variables.BuildConfiguration }} - template: Test-Bash.yml@templates parameters: name: 'Test_macOS' vmImage: 'macOS-11' BuildConfiguration: ${{ variables.BuildConfiguration }} - template: Pack-MyGet.yml@templates parameters: name: 'Pack_MyGet' vmImage: 'windows-2022' BuildConfiguration: ${{ variables.BuildConfiguration }} - template: Pack-NuGet.yml@templates parameters: name: 'Pack_NuGet' vmImage: 'windows-2022' BuildConfiguration: ${{ variables.BuildConfiguration }} - template: Publish-PowerShell.yml@templates parameters: name: 'Publish_Windows' vmImage: 'windows-2022' BuildConfiguration: ${{ variables.BuildConfiguration }} PublishFramework: ${{ variables.PublishFramework }} PublishProject: ${{ variables.PublishProject }} PublishRuntime: 'win-x64' - template: Publish-Bash.yml@templates parameters: name: 'Publish_Linux' vmImage: 'ubuntu-20.04' BuildConfiguration: ${{ variables.BuildConfiguration }} PublishFramework: ${{ variables.PublishFramework }} PublishProject: ${{ variables.PublishProject }} PublishRuntime: 'linux-x64' - template: Publish-Bash.yml@templates parameters: name: 'Publish_macOS_x64' vmImage: 'macOS-11' BuildConfiguration: ${{ variables.BuildConfiguration }} PublishFramework: ${{ variables.PublishFramework }} PublishProject: ${{ variables.PublishProject }} PublishRuntime: 'osx-x64' - template: Publish-Bash.yml@templates parameters: name: 'Publish_macOS_arm64' vmImage: 'macOS-11' BuildConfiguration: ${{ variables.BuildConfiguration }} PublishFramework: ${{ variables.PublishFramework }} PublishProject: ${{ variables.PublishProject }} PublishRuntime: 'osx-arm64' ================================================ FILE: build/SignAssembly.props ================================================ True $(MSBuildThisFileDirectory)\behaviors.public.snk false true ================================================ FILE: build/SourceLink.props ================================================ true false true embedded $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb true true ================================================ FILE: build/XUnit.props ================================================  ================================================ FILE: build/build/Build.cs ================================================ using System.Collections.Generic; using Nuke.Common; using Nuke.Common.Git; using Nuke.Common.ProjectModel; using Nuke.Common.Tools.DotNet; using Nuke.Common.IO; using static Nuke.Common.IO.FileSystemTasks; using static Nuke.Common.IO.PathConstruction; using static Nuke.Common.Tools.DotNet.DotNetTasks; class Build : NukeBuild { public static int Main() => Execute(x => x.Compile); [Solution] readonly Solution Solution; [GitRepository] readonly GitRepository GitRepository; [Parameter("configuration")] public string Configuration { get; set; } [Parameter("version-suffix")] public string VersionSuffix { get; set; } [Parameter("publish-framework")] public string PublishFramework { get; set; } [Parameter("publish-runtime")] public string PublishRuntime { get; set; } [Parameter("publish-project")] public string PublishProject { get; set; } [Parameter("publish-self-contained")] public bool PublishSelfContained { get; set; } = true; AbsolutePath SourceDirectory => RootDirectory / "src"; AbsolutePath TestsDirectory => RootDirectory / "tests"; AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts"; protected override void OnBuildInitialized() { Configuration = Configuration ?? "Release"; VersionSuffix = VersionSuffix ?? ""; } private void DeleteDirectories(IReadOnlyCollection directories) { foreach (var directory in directories) { DeleteDirectory(directory); } } Target Clean => _ => _ .Executes(() => { DeleteDirectories(GlobDirectories(SourceDirectory, "**/bin", "**/obj")); DeleteDirectories(GlobDirectories(TestsDirectory, "**/bin", "**/obj")); EnsureCleanDirectory(ArtifactsDirectory); }); Target Restore => _ => _ .DependsOn(Clean) .Executes(() => { DotNetRestore(s => s .SetProjectFile(Solution)); }); Target Compile => _ => _ .DependsOn(Restore) .Executes(() => { DotNetBuild(s => s .SetProjectFile(Solution) .SetConfiguration(Configuration) .SetVersionSuffix(VersionSuffix) .EnableNoRestore()); }); Target Test => _ => _ .DependsOn(Compile) .Executes(() => { DotNetTest(s => s .SetProjectFile(Solution) .SetConfiguration(Configuration) .SetLoggers("trx") .SetResultsDirectory(ArtifactsDirectory / "TestResults") .EnableNoBuild() .EnableNoRestore()); }); Target Pack => _ => _ .DependsOn(Test) .Executes(() => { DotNetPack(s => s .SetProject(Solution) .SetConfiguration(Configuration) .SetVersionSuffix(VersionSuffix) .SetOutputDirectory(ArtifactsDirectory / "NuGet") .EnableNoBuild() .EnableNoRestore()); }); Target Publish => _ => _ .DependsOn(Test) .Requires(() => PublishRuntime) .Requires(() => PublishFramework) .Requires(() => PublishProject) .Executes(() => { DotNetPublish(s => s .SetProject(Solution.GetProject(PublishProject)) .SetConfiguration(Configuration) .SetVersionSuffix(VersionSuffix) .SetFramework(PublishFramework) .SetRuntime(PublishRuntime) .SetSelfContained(PublishSelfContained) .SetOutput(ArtifactsDirectory / "Publish" / PublishProject + "-" + PublishFramework + "-" + PublishRuntime)); }); } ================================================ FILE: build/build/_build.csproj ================================================ Exe net8.0 false False CS0649;CS0169 1 true ================================================ FILE: build.cmd ================================================ :; set -eo pipefail :; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) :; ${SCRIPT_DIR}/build.sh "$@" :; exit $? @ECHO OFF powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* ================================================ FILE: build.ps1 ================================================ [CmdletBinding()] Param( [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] [string[]]$BuildArguments ) Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent ########################################################################### # CONFIGURATION ########################################################################### $BuildProjectFile = "$PSScriptRoot\build\build\_build.csproj" $TempDirectory = "$PSScriptRoot\\.nuke\temp" $DotNetGlobalFile = "$PSScriptRoot\\global.json" $DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" $DotNetChannel = "Current" $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 $env:DOTNET_MULTILEVEL_LOOKUP = 0 ########################################################################### # EXECUTION ########################################################################### function ExecSafe([scriptblock] $cmd) { & $cmd if ($LASTEXITCODE) { exit $LASTEXITCODE } } # If dotnet CLI is installed globally and it matches requested version, use for execution if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` $(dotnet --version) -and $LASTEXITCODE -eq 0) { $env:DOTNET_EXE = (Get-Command "dotnet").Path } else { # Download install script $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) # If global.json exists, load expected version if (Test-Path $DotNetGlobalFile) { $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { $DotNetVersion = $DotNetGlobal.sdk.version } } # Install by channel or version $DotNetDirectory = "$TempDirectory\dotnet-win" if (!(Test-Path variable:DotNetVersion)) { ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } } else { ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } } $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" } Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } ================================================ FILE: build.sh ================================================ #!/usr/bin/env bash bash --version 2>&1 | head -n 1 set -eo pipefail SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) ########################################################################### # CONFIGURATION ########################################################################### BUILD_PROJECT_FILE="$SCRIPT_DIR/build/build/_build.csproj" TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp" DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" DOTNET_CHANNEL="Current" export DOTNET_CLI_TELEMETRY_OPTOUT=1 export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 export DOTNET_MULTILEVEL_LOOKUP=0 ########################################################################### # EXECUTION ########################################################################### function FirstJsonValue { perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" } # If dotnet CLI is installed globally and it matches requested version, use for execution if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then export DOTNET_EXE="$(command -v dotnet)" else # Download install script DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" mkdir -p "$TEMP_DIRECTORY" curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" chmod +x "$DOTNET_INSTALL_FILE" # If global.json exists, load expected version if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") if [[ "$DOTNET_VERSION" == "" ]]; then unset DOTNET_VERSION fi fi # Install by channel or version DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" if [[ -z ${DOTNET_VERSION+x} ]]; then "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path else "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path fi export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" fi echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)" "$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" ================================================ FILE: global.json ================================================ { "sdk": { "version": "8.0.100", "rollForward": "latestMinor", "allowPrerelease": true } } ================================================ FILE: samples/BehaviorsTestApplication/App.axaml ================================================ Black White Gray Red Green Blue Yellow Pink ================================================ FILE: samples/BehaviorsTestApplication/App.axaml.cs ================================================ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using BehaviorsTestApplication.Views; namespace BehaviorsTestApplication; public class App : Application { public override void Initialize() { AvaloniaXamlLoader.Load(this); } public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) { desktopLifetime.MainWindow = new MainWindow(); } else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) { singleViewLifetime.MainView = new MainView(); } base.OnFrameworkInitializationCompleted(); } } ================================================ FILE: samples/BehaviorsTestApplication/BehaviorsTestApplication.csproj ================================================  WinExe net8.0 False False enable ================================================ FILE: samples/BehaviorsTestApplication/Converters/ClassesToStringConverter.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using Avalonia; using Avalonia.Controls; using Avalonia.Data.Converters; namespace BehaviorsTestApplication.Converters; public class ClassesToStringConverter : IMultiValueConverter { public static ClassesToStringConverter Instance = new(); public object Convert(IList? values, Type targetType, object? parameter, CultureInfo culture) { if (values?.Count == 2 && values[0] is int && values[1] is Classes classes) { return string.Join(" ", classes); } return AvaloniaProperty.UnsetValue; } } ================================================ FILE: samples/BehaviorsTestApplication/Program.cs ================================================ using System; using Avalonia; using Avalonia.ReactiveUI; using Avalonia.Xaml.Interactions.Core; using Avalonia.Xaml.Interactivity; namespace BehaviorsTestApplication; class Program { [STAThread] private static void Main(string[] args) { BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } public static AppBuilder BuildAvaloniaApp() { GC.KeepAlive(typeof(Interaction).Assembly); GC.KeepAlive(typeof(ComparisonConditionType).Assembly); return AppBuilder.Configure() .UsePlatformDetect() .UseReactiveUI() .LogToTrace(); } } ================================================ FILE: samples/BehaviorsTestApplication/SideBar.axaml ================================================ ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/ButtonClickEventTriggerBehaviorView.axaml.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace BehaviorsTestApplication.Views.Pages; public partial class ButtonClickEventTriggerBehaviorView : UserControl { public ButtonClickEventTriggerBehaviorView() { InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/CallMethodActionView.axaml ================================================  ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/CallMethodActionView.axaml.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace BehaviorsTestApplication.Views.Pages; public partial class CallMethodActionView : UserControl { public CallMethodActionView() { InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/ChangeAvaloniaPropertyActionView.axaml ================================================  ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/ChangeAvaloniaPropertyActionView.axaml.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace BehaviorsTestApplication.Views.Pages; public partial class ChangeAvaloniaPropertyActionView : UserControl { public ChangeAvaloniaPropertyActionView() { InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/ChangePropertyActionView.axaml ================================================  ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/ChangePropertyActionView.axaml.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace BehaviorsTestApplication.Views.Pages; public partial class ChangePropertyActionView : UserControl { public ChangePropertyActionView() { InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/CustomActionView.axaml ================================================  ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/CustomActionView.axaml.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace BehaviorsTestApplication.Views.Pages; public partial class CustomActionView : UserControl { public CustomActionView() { InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/CustomBehaviorView.axaml ================================================  ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/CustomBehaviorView.axaml.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace BehaviorsTestApplication.Views.Pages; public partial class CustomBehaviorView : UserControl { public CustomBehaviorView() { InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/DataTriggerBehaviorView.axaml ================================================  ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/DataTriggerBehaviorView.axaml.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace BehaviorsTestApplication.Views.Pages; public partial class DataTriggerBehaviorView : UserControl { public DataTriggerBehaviorView() { InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/EditableListBoxView.axaml ================================================  ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/EditableListBoxView.axaml.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace BehaviorsTestApplication.Views.Pages; public partial class EditableListBoxView : UserControl { public EditableListBoxView() { InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/EditableTreeViewView.axaml ================================================  ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/EditableTreeViewView.axaml.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace BehaviorsTestApplication.Views.Pages; public partial class EditableTreeViewView : UserControl { public EditableTreeViewView() { InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/EventTriggerBehaviorView.axaml ================================================  ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/EventTriggerBehaviorView.axaml.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace BehaviorsTestApplication.Views.Pages; public partial class EventTriggerBehaviorView : UserControl { public EventTriggerBehaviorView() { InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/InvokeCommandActionView.axaml ================================================  ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/InvokeCommandActionView.axaml.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace BehaviorsTestApplication.Views.Pages; public partial class InvokeCommandActionView : UserControl { public InvokeCommandActionView() { InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/RoutedEventTriggerBehaviorView.axaml ================================================  ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/RoutedEventTriggerBehaviorView.axaml.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace BehaviorsTestApplication.Views.Pages; public partial class RoutedEventTriggerBehaviorView : UserControl { public RoutedEventTriggerBehaviorView() { InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/ValueChangedTriggerBehaviorView.axaml ================================================  ================================================ FILE: samples/BehaviorsTestApplication/Views/Pages/ValueChangedTriggerBehaviorView.axaml.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace BehaviorsTestApplication.Views.Pages; public partial class ValueChangedTriggerBehaviorView : UserControl { public ValueChangedTriggerBehaviorView() { InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/Directory.Packages.props ================================================  true 11.0.10 ================================================ FILE: samples/DragAndDropSample/App.axaml ================================================ ================================================ FILE: samples/DragAndDropSample/App.axaml.cs ================================================ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using DragAndDropSample.ViewModels; using DragAndDropSample.Views; namespace DragAndDropSample; public class App : Application { public override void Initialize() { AvaloniaXamlLoader.Load(this); } public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { desktop.MainWindow = new MainWindow {DataContext = new MainWindowViewModel(),}; } base.OnFrameworkInitializationCompleted(); } } ================================================ FILE: samples/DragAndDropSample/Behaviors/BaseDataGridDropHandler.cs ================================================ using System.Collections.ObjectModel; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.VisualTree; using Avalonia.Xaml.Interactions.DragAndDrop; using DragAndDropSample.ViewModels; namespace DragAndDropSample.Behaviors; public abstract class BaseDataGridDropHandler : DropHandlerBase where T : ViewModelBase { private const string rowDraggingUpStyleClass = "DraggingUp"; private const string rowDraggingDownStyleClass = "DraggingDown"; protected abstract T MakeCopy(ObservableCollection parentCollection, T item); protected abstract bool Validate(DataGrid dg, DragEventArgs e, object? sourceContext, object? targetContext, bool bExecute); public override bool Validate(object? sender, DragEventArgs e, object? sourceContext, object? targetContext, object? state) { if (e.Source is Control c && sender is DataGrid dg) { bool valid = Validate(dg, e, sourceContext, targetContext, false); if (valid) { var row = FindDataGridRowFromChildView(c); string direction = e.Data.Contains("direction") ? (string) e.Data.Get("direction")! : "down"; ApplyDraggingStyleToRow(row!, direction); ClearDraggingStyleFromAllRows(sender, exceptThis: row); } return valid; } ClearDraggingStyleFromAllRows(sender); return false; } public override bool Execute(object? sender, DragEventArgs e, object? sourceContext, object? targetContext, object? state) { ClearDraggingStyleFromAllRows(sender); if (e.Source is Control && sender is DataGrid dg) { return Validate(dg, e, sourceContext, targetContext, true); } return false; } public override void Cancel(object? sender, RoutedEventArgs e) { base.Cancel(sender, e); // this is necessary to clear adorner borders when mouse leaves DataGrid // they would remain even after changing screens ClearDraggingStyleFromAllRows(sender); } protected bool RunDropAction(DataGrid dg, DragEventArgs e, bool bExecute, T sourceItem, T targetItem, ObservableCollection items) { int sourceIndex = items.IndexOf(sourceItem); int targetIndex = items.IndexOf(targetItem); if (sourceIndex < 0 || targetIndex < 0) { return false; } switch (e.DragEffects) { case DragDropEffects.Copy: { if (bExecute) { var clone = MakeCopy(items, sourceItem); InsertItem(items, clone, targetIndex + 1); dg.SelectedIndex = targetIndex + 1; } return true; } case DragDropEffects.Move: { if (bExecute) { MoveItem(items, sourceIndex, targetIndex); dg.SelectedIndex = targetIndex; } return true; } case DragDropEffects.Link: { if (bExecute) { SwapItem(items, sourceIndex, targetIndex); dg.SelectedIndex = targetIndex; } return true; } default: return false; } } private static DataGridRow? FindDataGridRowFromChildView(StyledElement sourceChild) { int maxDepth = 16; DataGridRow? row = null; StyledElement? current = sourceChild; while (maxDepth --> 0 || row is null) { if (current is DataGridRow dgr) row = dgr; current = current?.Parent; } return row; } private static DataGridRowsPresenter? GetRowsPresenter(Visual v) { foreach (var cv in v.GetVisualChildren()) { if (cv is DataGridRowsPresenter dgrp) return dgrp; else if (GetRowsPresenter(cv) is DataGridRowsPresenter dgrp2) return dgrp2; } return null; } private static void ClearDraggingStyleFromAllRows(object? sender, DataGridRow? exceptThis = null) { if (sender is DataGrid dg) { var presenter = GetRowsPresenter(dg); if (presenter is null) return; foreach (var r in presenter.Children) { if (r == exceptThis) continue; if (r!.Classes.Contains(rowDraggingUpStyleClass)) { r?.Classes?.Remove(rowDraggingUpStyleClass); } if (r!.Classes.Contains(rowDraggingDownStyleClass)) { r?.Classes?.Remove(rowDraggingDownStyleClass); } } } } private static void ApplyDraggingStyleToRow(DataGridRow row, string direction) { if (direction == "up") { if (row.Classes.Contains(rowDraggingDownStyleClass) == true) { row.Classes.Remove(rowDraggingDownStyleClass); } if (row.Classes.Contains(rowDraggingUpStyleClass) == false) { row.Classes.Add(rowDraggingUpStyleClass); } } else if (direction == "down") { if (row.Classes.Contains(rowDraggingUpStyleClass) == true) { row.Classes.Remove(rowDraggingUpStyleClass); } if (row.Classes.Contains(rowDraggingDownStyleClass) == false) { row.Classes.Add(rowDraggingDownStyleClass); } } } } ================================================ FILE: samples/DragAndDropSample/Behaviors/ContextDragWithDirectionBehavior.cs ================================================ using System; using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Xaml.Interactions.DragAndDrop; using Avalonia.Xaml.Interactivity; namespace DragAndDropSample.Behaviors; /// /// /// public sealed class ContextDragWithDirectionBehavior : Behavior { private Point _dragStartPoint; private PointerEventArgs? _triggerEvent; private bool _lock; private bool _captured; /// /// /// public static readonly StyledProperty ContextProperty = AvaloniaProperty.Register(nameof(Context)); /// /// /// public static readonly StyledProperty HandlerProperty = AvaloniaProperty.Register(nameof(Handler)); /// /// /// public static readonly StyledProperty HorizontalDragThresholdProperty = AvaloniaProperty.Register(nameof(HorizontalDragThreshold), 3); /// /// /// public static readonly StyledProperty VerticalDragThresholdProperty = AvaloniaProperty.Register(nameof(VerticalDragThreshold), 3); /// /// /// public object? Context { get => GetValue(ContextProperty); set => SetValue(ContextProperty, value); } /// /// /// public IDragHandler? Handler { get => GetValue(HandlerProperty); set => SetValue(HandlerProperty, value); } /// /// /// public double HorizontalDragThreshold { get => GetValue(HorizontalDragThresholdProperty); set => SetValue(HorizontalDragThresholdProperty, value); } /// /// /// public double VerticalDragThreshold { get => GetValue(VerticalDragThresholdProperty); set => SetValue(VerticalDragThresholdProperty, value); } /// protected override void OnAttachedToVisualTree() { AssociatedObject?.AddHandler(InputElement.PointerPressedEvent, AssociatedObject_PointerPressed, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble); AssociatedObject?.AddHandler(InputElement.PointerReleasedEvent, AssociatedObject_PointerReleased, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble); AssociatedObject?.AddHandler(InputElement.PointerMovedEvent, AssociatedObject_PointerMoved, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble); AssociatedObject?.AddHandler(InputElement.PointerCaptureLostEvent, AssociatedObject_CaptureLost, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble); } /// protected override void OnDetachedFromVisualTree() { AssociatedObject?.RemoveHandler(InputElement.PointerPressedEvent, AssociatedObject_PointerPressed); AssociatedObject?.RemoveHandler(InputElement.PointerReleasedEvent, AssociatedObject_PointerReleased); AssociatedObject?.RemoveHandler(InputElement.PointerMovedEvent, AssociatedObject_PointerMoved); AssociatedObject?.RemoveHandler(InputElement.PointerCaptureLostEvent, AssociatedObject_CaptureLost); } private async Task DoDragDrop(PointerEventArgs triggerEvent, object? value, string upOrDown) { var data = new DataObject(); data.Set(ContextDropBehavior.DataFormat, value!); data.Set("direction", upOrDown); var effect = DragDropEffects.None; if (triggerEvent.KeyModifiers.HasFlag(KeyModifiers.Alt)) { effect |= DragDropEffects.Link; } else if (triggerEvent.KeyModifiers.HasFlag(KeyModifiers.Shift)) { effect |= DragDropEffects.Move; } else if (triggerEvent.KeyModifiers.HasFlag(KeyModifiers.Control)) { effect |= DragDropEffects.Copy; } else { effect |= DragDropEffects.Move; } await DragDrop.DoDragDrop(triggerEvent, data, effect); } private void Released() { _triggerEvent = null; _lock = false; } private void AssociatedObject_PointerPressed(object? sender, PointerPressedEventArgs e) { var properties = e.GetCurrentPoint(AssociatedObject).Properties; if (properties.IsLeftButtonPressed) { if (e.Source is Control control && AssociatedObject?.DataContext == control.DataContext) { _dragStartPoint = e.GetPosition(null); _triggerEvent = e; _lock = true; _captured = true; } } } private void AssociatedObject_PointerReleased(object? sender, PointerReleasedEventArgs e) { if (_captured) { if (e.InitialPressMouseButton == MouseButton.Left && _triggerEvent is { }) { Released(); } _captured = false; } } private async void AssociatedObject_PointerMoved(object? sender, PointerEventArgs e) { var properties = e.GetCurrentPoint(AssociatedObject).Properties; if (_captured && properties.IsLeftButtonPressed && _triggerEvent is { }) { var point = e.GetPosition(null); var diff = _dragStartPoint - point; var horizontalDragThreshold = HorizontalDragThreshold; var verticalDragThreshold = VerticalDragThreshold; if (Math.Abs(diff.X) > horizontalDragThreshold || Math.Abs(diff.Y) > verticalDragThreshold) { if (_lock) { _lock = false; } else { return; } var context = Context ?? AssociatedObject?.DataContext; Handler?.BeforeDragDrop(sender, _triggerEvent, context); await DoDragDrop(_triggerEvent, context, diff.Y > 0 ? "up" : "down"); Handler?.AfterDragDrop(sender, _triggerEvent, context); _triggerEvent = null; } } } private void AssociatedObject_CaptureLost(object? sender, PointerCaptureLostEventArgs e) { Released(); _captured = false; } } ================================================ FILE: samples/DragAndDropSample/Behaviors/ItemsDataGridDropHandler.cs ================================================ using System.Collections.ObjectModel; using Avalonia.Controls; using Avalonia.Input; using Avalonia.VisualTree; using DragAndDropSample.ViewModels; namespace DragAndDropSample.Behaviors; public sealed class ItemsDataGridDropHandler : BaseDataGridDropHandler { protected override ItemViewModel MakeCopy(ObservableCollection parentCollection, ItemViewModel item) => new() { Title = item.Title }; protected override bool Validate(DataGrid dg, DragEventArgs e, object? sourceContext, object? targetContext, bool bExecute) { if (sourceContext is not ItemViewModel sourceItem || targetContext is not MainWindowViewModel vm || dg.GetVisualAt(e.GetPosition(dg)) is not Control targetControl || targetControl.DataContext is not ItemViewModel targetItem) { return false; } var items = vm.Items; return RunDropAction(dg, e, bExecute, sourceItem, targetItem, items); } } ================================================ FILE: samples/DragAndDropSample/Behaviors/ItemsListBoxDropHandler.cs ================================================ using Avalonia.Controls; using Avalonia.Input; using Avalonia.VisualTree; using Avalonia.Xaml.Interactions.DragAndDrop; using DragAndDropSample.ViewModels; namespace DragAndDropSample.Behaviors; public class ItemsListBoxDropHandler : DropHandlerBase { private bool Validate(ListBox listBox, DragEventArgs e, object? sourceContext, object? targetContext, bool bExecute) where T : ItemViewModel { if (sourceContext is not T sourceItem || targetContext is not MainWindowViewModel vm || listBox.GetVisualAt(e.GetPosition(listBox)) is not Control targetControl || targetControl.DataContext is not T targetItem) { return false; } var items = vm.Items; var sourceIndex = items.IndexOf(sourceItem); var targetIndex = items.IndexOf(targetItem); if (sourceIndex < 0 || targetIndex < 0) { return false; } switch (e.DragEffects) { case DragDropEffects.Copy: { if (bExecute) { var clone = new ItemViewModel() { Title = sourceItem.Title + "_copy" }; InsertItem(items, clone, targetIndex + 1); } return true; } case DragDropEffects.Move: { if (bExecute) { MoveItem(items, sourceIndex, targetIndex); } return true; } case DragDropEffects.Link: { if (bExecute) { SwapItem(items, sourceIndex, targetIndex); } return true; } default: return false; } } public override bool Validate(object? sender, DragEventArgs e, object? sourceContext, object? targetContext, object? state) { if (e.Source is Control && sender is ListBox listBox) { return Validate(listBox, e, sourceContext, targetContext, false); } return false; } public override bool Execute(object? sender, DragEventArgs e, object? sourceContext, object? targetContext, object? state) { if (e.Source is Control && sender is ListBox listBox) { return Validate(listBox, e, sourceContext, targetContext, true); } return false; } } ================================================ FILE: samples/DragAndDropSample/Behaviors/NodesListBoxDropHandler.cs ================================================ using Avalonia.Controls; using Avalonia.Input; using Avalonia.VisualTree; using Avalonia.Xaml.Interactions.DragAndDrop; using DragAndDropSample.ViewModels; namespace DragAndDropSample.Behaviors; public class NodesListBoxDropHandler : DropHandlerBase { private bool Validate(ListBox listBox, DragEventArgs e, object? sourceContext, object? targetContext, bool bExecute) where T : NodeViewModel { if (sourceContext is not T sourceNode || targetContext is not MainWindowViewModel vm || listBox.GetVisualAt(e.GetPosition(listBox)) is not Control targetControl || targetControl.DataContext is not T targetNode) { return false; } var nodes = vm.Nodes; var sourceIndex = nodes.IndexOf(sourceNode); var targetIndex = nodes.IndexOf(targetNode); if (sourceIndex < 0 || targetIndex < 0) { return false; } switch (e.DragEffects) { case DragDropEffects.Copy: { if (bExecute) { var clone = new NodeViewModel() { Title = sourceNode.Title + "_copy" }; InsertItem(nodes, clone, targetIndex + 1); } return true; } case DragDropEffects.Move: { if (bExecute) { MoveItem(nodes, sourceIndex, targetIndex); } return true; } case DragDropEffects.Link: { if (bExecute) { SwapItem(nodes, sourceIndex, targetIndex); } return true; } default: return false; } } public override bool Validate(object? sender, DragEventArgs e, object? sourceContext, object? targetContext, object? state) { if (e.Source is Control && sender is ListBox listBox) { return Validate(listBox, e, sourceContext, targetContext, false); } return false; } public override bool Execute(object? sender, DragEventArgs e, object? sourceContext, object? targetContext, object? state) { if (e.Source is Control && sender is ListBox listBox) { return Validate(listBox, e, sourceContext, targetContext, true); } return false; } } ================================================ FILE: samples/DragAndDropSample/Behaviors/NodesTreeViewDropHandler.cs ================================================ using Avalonia.Controls; using Avalonia.Input; using Avalonia.VisualTree; using Avalonia.Xaml.Interactions.DragAndDrop; using DragAndDropSample.ViewModels; namespace DragAndDropSample.Behaviors; public class NodesTreeViewDropHandler : DropHandlerBase { private bool Validate(TreeView treeView, DragEventArgs e, object? sourceContext, object? targetContext, bool bExecute) where T : NodeViewModel { if (sourceContext is not T sourceNode || targetContext is not MainWindowViewModel vm || treeView.GetVisualAt(e.GetPosition(treeView)) is not Control targetControl || targetControl.DataContext is not T targetNode) { return false; } var sourceParent = sourceNode.Parent; var targetParent = targetNode.Parent; var sourceNodes = sourceParent is not null ? sourceParent.Nodes : vm.Nodes; var targetNodes = targetParent is not null ? targetParent.Nodes : vm.Nodes; if (sourceNodes is not null && targetNodes is not null) { var sourceIndex = sourceNodes.IndexOf(sourceNode); var targetIndex = targetNodes.IndexOf(targetNode); if (sourceIndex < 0 || targetIndex < 0) { return false; } switch (e.DragEffects) { case DragDropEffects.Copy: { if (bExecute) { var clone = new NodeViewModel() { Title = sourceNode.Title + "_copy" }; InsertItem(targetNodes, clone, targetIndex + 1); } return true; } case DragDropEffects.Move: { if (bExecute) { if (sourceNodes == targetNodes) { MoveItem(sourceNodes, sourceIndex, targetIndex); } else { sourceNode.Parent = targetParent; MoveItem(sourceNodes, targetNodes, sourceIndex, targetIndex); } } return true; } case DragDropEffects.Link: { if (bExecute) { if (sourceNodes == targetNodes) { SwapItem(sourceNodes, sourceIndex, targetIndex); } else { sourceNode.Parent = targetParent; targetNode.Parent = sourceParent; SwapItem(sourceNodes, targetNodes, sourceIndex, targetIndex); } } return true; } } } return false; } public override bool Validate(object? sender, DragEventArgs e, object? sourceContext, object? targetContext, object? state) { if (e.Source is Control && sender is TreeView treeView) { return Validate(treeView, e, sourceContext, targetContext, false); } return false; } public override bool Execute(object? sender, DragEventArgs e, object? sourceContext, object? targetContext, object? state) { if (e.Source is Control && sender is TreeView treeView) { return Validate(treeView, e, sourceContext, targetContext, true); } return false; } } ================================================ FILE: samples/DragAndDropSample/DragAndDropSample.csproj ================================================  WinExe net8.0 False False enable ================================================ FILE: samples/DragAndDropSample/Program.cs ================================================ using System; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.ReactiveUI; using Avalonia.Xaml.Interactions.Core; using Avalonia.Xaml.Interactivity; namespace DragAndDropSample; class Program { // Initialization code. Don't use any Avalonia, third-party APIs or any // SynchronizationContext-reliant code before AppMain is called: things aren't initialized // yet and stuff might break. [STAThread] public static void Main(string[] args) => BuildAvaloniaApp() .StartWithClassicDesktopLifetime(args); // Avalonia configuration, don't remove; also used by visual designer. public static AppBuilder BuildAvaloniaApp() { GC.KeepAlive(typeof(Interaction).Assembly); GC.KeepAlive(typeof(ComparisonConditionType).Assembly); return AppBuilder.Configure() .UsePlatformDetect() .LogToTrace() .UseReactiveUI(); } } ================================================ FILE: samples/DragAndDropSample/ViewLocator.cs ================================================ 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; } } ================================================ FILE: samples/DragAndDropSample/ViewModels/ItemViewModel.cs ================================================ using ReactiveUI; namespace DragAndDropSample.ViewModels; public class ItemViewModel : ViewModelBase { private string? _title; public string? Title { get => _title; set => this.RaiseAndSetIfChanged(ref _title, value); } public override string? ToString() => _title; } ================================================ FILE: samples/DragAndDropSample/ViewModels/MainWindowViewModel.cs ================================================ using System.Collections.ObjectModel; using ReactiveUI; namespace DragAndDropSample.ViewModels; public class MainWindowViewModel : ViewModelBase { private ObservableCollection _items; private ObservableCollection _nodes; public ObservableCollection Items { get => _items; set => this.RaiseAndSetIfChanged(ref _items, value); } public ObservableCollection Nodes { get => _nodes; set => this.RaiseAndSetIfChanged(ref _nodes, value); } public MainWindowViewModel() { _items = new ObservableCollection() { 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}, }; 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}, }; 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}, }; _nodes = new ObservableCollection() { node0, node1, node2 }; } } ================================================ FILE: samples/DragAndDropSample/ViewModels/NodeViewModel.cs ================================================ using System.Collections.ObjectModel; using ReactiveUI; namespace DragAndDropSample.ViewModels; public class NodeViewModel : ViewModelBase { private string? _title; private NodeViewModel? _parent; private ObservableCollection? _nodes; public string? Title { get => _title; set => this.RaiseAndSetIfChanged(ref _title, value); } public NodeViewModel? Parent { get => _parent; set => this.RaiseAndSetIfChanged(ref _parent, value); } public ObservableCollection? Nodes { get => _nodes; set => this.RaiseAndSetIfChanged(ref _nodes, value); } public override string? ToString() => _title; } ================================================ FILE: samples/DragAndDropSample/ViewModels/ViewModelBase.cs ================================================ using ReactiveUI; namespace DragAndDropSample.ViewModels; public class ViewModelBase : ReactiveObject { } ================================================ FILE: samples/DragAndDropSample/Views/MainWindow.axaml ================================================ M15 18a1 1 0 100-2 1 1 0 000 2zm1-6a1 1 0 11-2 0 1 1 0 012 0zm-7 6a1 1 0 100-2 1 1 0 000 2zm0-5a1 1 0 100-2 1 1 0 000 2zm7-6a1 1 0 11-2 0 1 1 0 012 0zM9 8a1 1 0 100-2 1 1 0 000 2z ================================================ FILE: samples/DragAndDropSample/Views/MainWindow.axaml.cs ================================================ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace DragAndDropSample.Views; public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); #if DEBUG this.AttachDevTools(); #endif } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/DraggableDemo/App.axaml ================================================ ================================================ FILE: samples/DraggableDemo/App.axaml.cs ================================================ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; namespace DraggableDemo; public class App : Application { public override void Initialize() { AvaloniaXamlLoader.Load(this); } public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { desktop.MainWindow = new MainWindow(); } base.OnFrameworkInitializationCompleted(); } } ================================================ FILE: samples/DraggableDemo/DraggableDemo.csproj ================================================  WinExe net8.0 False False enable ================================================ FILE: samples/DraggableDemo/MainWindow.axaml ================================================ ================================================ FILE: samples/DraggableDemo/MainWindow.axaml.cs ================================================ using System.Collections.Generic; using System.Collections.ObjectModel; using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; using DraggableDemo.Models; namespace DraggableDemo; public partial class MainWindow : Window { public IList Items { get; set; } public IList Strings { get; } public IList Tiles { get; set; } public MainWindow() { InitializeComponent(); Items = new ObservableCollection() { new () { Title = "Item1", X = 30, Y = 30 }, new () { Title = "Item2", X = 90, Y = 30 }, new () { Title = "Item3", X = 120, Y = 60 }, new () { Title = "Item4", X = 45, Y = 90 }, new () { Title = "Item5", X = 60, Y = 120 }, new () { Title = "Item6", X = 150, Y = 180 }, new () { Title = "Item7", X = 250, Y = 120 }, new () { Title = "Item8", X = 300, Y = 150 } }; Strings = new ObservableCollection(); for (var i = 0; i < 1_000; i++) { Strings.Add($"Item {i+1} / {1000}"); } Tiles = new ObservableCollection() { new () { Title = "Tile1", Column = 0, Row = 0, ColumnSpan = 1, RowSpan = 1, Background = "Red" }, new () { Title = "Tile2", Column = 0, Row = 1, ColumnSpan = 1, RowSpan = 1, Background = "Green" }, new () { Title = "Tile3", Column = 1, Row = 0, ColumnSpan = 1, RowSpan = 2, Background = "Blue" }, new () { Title = "Tile4", Column = 2, Row = 0, ColumnSpan = 1, RowSpan = 2, Background = "Cyan" }, }; DataContext = this; #if DEBUG this.AttachDevTools(); #endif } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } ================================================ FILE: samples/DraggableDemo/Models/Item.cs ================================================ namespace DraggableDemo.Models; public class Item { public string? Title { get; set; } public double X { get; set; } public double Y { get; set; } public override string? ToString() => Title; } ================================================ FILE: samples/DraggableDemo/Models/Tile.cs ================================================ namespace DraggableDemo.Models; public class Tile { public string? Title { get; set; } public int Column { get; set; } public int Row { get; set; } public int ColumnSpan { get; set; } public int RowSpan { get; set; } public string? Background { get; set; } public override string? ToString() => Title; } ================================================ FILE: samples/DraggableDemo/Program.cs ================================================ using System; using Avalonia; using Avalonia.Xaml.Interactions.Core; using Avalonia.Xaml.Interactivity; namespace DraggableDemo; class Program { public static void Main(string[] args) => BuildAvaloniaApp() .StartWithClassicDesktopLifetime(args); public static AppBuilder BuildAvaloniaApp() { GC.KeepAlive(typeof(Interaction).Assembly); GC.KeepAlive(typeof(ComparisonConditionType).Assembly); return AppBuilder.Configure() .UsePlatformDetect() .LogToTrace(); } } ================================================ FILE: samples/DraggableDemo/Styles/Custom.axaml ================================================ 0 0 0 2 ================================================ FILE: src/Avalonia.Xaml.Behaviors/Avalonia.Xaml.Behaviors.csproj ================================================  netstandard2.0;net6.0;net8.0 False enable $(NoWarn);NU5128 Avalonia.Xaml.Behaviors Easily add interactivity to your Avalonia apps using XAML Behaviors. Behaviors encapsulate reusable functionalities for elements that can be easily added to your XAML without the need for more imperative code. Avalonia;Behavior;Action;Behaviors;Actions;Managed;C#;Interaction;Interactivity;Interactions;Xaml ================================================ FILE: src/Avalonia.Xaml.Interactions/Avalonia.Xaml.Interactions.csproj ================================================  netstandard2.0;net6.0;net8.0 Library bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml enable Avalonia.Xaml.Interactions Easily add interactivity to your Avalonia apps using XAML Behaviors. Behaviors encapsulate reusable functionalities for elements that can be easily added to your XAML without the need for more imperative code. Avalonia;Behavior;Action;Behaviors;Actions;Managed;C#;Interaction;Interactivity;Interactions;Xaml ================================================ FILE: src/Avalonia.Xaml.Interactions/Core/CallMethodAction.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Reactive; using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Core; /// /// An action that calls a method on a specified object when invoked. /// public class CallMethodAction : AvaloniaObject, IAction { private Type? _targetObjectType; private readonly List _methodDescriptors = new(); 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)); /// /// Identifies the avalonia property. /// 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 { get => GetValue(MethodNameProperty); set => SetValue(MethodNameProperty, value); } /// /// Gets or sets the object that exposes the method of interest. This is a avalonia property. /// [ResolveByName] public object? TargetObject { get => GetValue(TargetObjectProperty); set => SetValue(TargetObjectProperty, value); } static CallMethodAction() { MethodNameProperty.Changed.Subscribe( new AnonymousObserver>(MethodNameChanged)); TargetObjectProperty.Changed.Subscribe( new AnonymousObserver>(TargetObjectChanged)); } private static void MethodNameChanged(AvaloniaPropertyChangedEventArgs e) { if (e.Sender is not CallMethodAction callMethodAction) { return; } callMethodAction.UpdateMethodDescriptors(); } [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] private static void TargetObjectChanged(AvaloniaPropertyChangedEventArgs e) { if (e.Sender is not CallMethodAction callMethodAction) { return; } var newValue = e.NewValue.GetValueOrDefault(); if (newValue is not null) { var newType = newValue.GetType(); callMethodAction.UpdateTargetType(newType); } } /// /// 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. /// True if the method is called; else false. [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] public virtual object Execute(object? sender, object? parameter) { if (!IsEnabled) { return false; } var target = GetValue(TargetObjectProperty) is not null ? TargetObject : sender; if (target is null || string.IsNullOrEmpty(MethodName)) { return false; } UpdateTargetType(target.GetType()); var methodDescriptor = FindBestMethod(parameter); if (methodDescriptor is null) { if (TargetObject is not null) { throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, "Cannot find method named {0} on object of type {1} that matches the expected signature.", MethodName, _targetObjectType)); } return false; } var parameters = methodDescriptor.Parameters; switch (parameters.Length) { case 0: methodDescriptor.MethodInfo.Invoke(target, null); return true; case 2: methodDescriptor.MethodInfo.Invoke(target, new[] { target, parameter! }); return true; default: return false; } } [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] private MethodDescriptor? FindBestMethod(object? parameter) { if (parameter is null) { return _cachedMethodDescriptor; } var parameterTypeInfo = parameter.GetType().GetTypeInfo(); MethodDescriptor? mostDerivedMethod = null; // Loop over the methods looking for the one whose type is closest to the type of the given parameter. foreach (var currentMethod in _methodDescriptors) { var currentTypeInfo = currentMethod.SecondParameterTypeInfo; if (currentTypeInfo is not null && currentTypeInfo.IsAssignableFrom(parameterTypeInfo)) { if (mostDerivedMethod is null || !currentTypeInfo.IsAssignableFrom(mostDerivedMethod.SecondParameterTypeInfo)) { mostDerivedMethod = currentMethod; } } } return mostDerivedMethod ?? _cachedMethodDescriptor; } private void UpdateTargetType(Type newTargetType) { if (newTargetType == _targetObjectType) { return; } _targetObjectType = newTargetType; UpdateMethodDescriptors(); } [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] private void UpdateMethodDescriptors() { _methodDescriptors.Clear(); _cachedMethodDescriptor = null; if (string.IsNullOrEmpty(MethodName) || _targetObjectType is null) { return; } // Find all public methods that match the given name and have either no parameters, // or two parameters where the first is of type Object. foreach (var method in _targetObjectType.GetRuntimeMethods()) { if (string.Equals(method.Name, MethodName, StringComparison.Ordinal) && (method.ReturnType == typeof(void) || method.ReturnType == typeof(Task)) && method.IsPublic) { var parameters = method.GetParameters(); if (parameters.Length == 0) { // There can be only one parameterless method of the given name. _cachedMethodDescriptor = new MethodDescriptor(method, parameters); } else if (parameters.Length == 2 && parameters[0].ParameterType == typeof(object)) { _methodDescriptors.Add(new MethodDescriptor(method, parameters)); } } } // We didn't find a parameterless method, so we want to find a method that accepts null // as a second parameter, but if we have more than one of these it is ambiguous which // we should call, so we do nothing. if (_cachedMethodDescriptor is null) { foreach (var method in _methodDescriptors) { var typeInfo = method.SecondParameterTypeInfo; if (typeInfo is not null && (!typeInfo.IsValueType || typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>))) { if (_cachedMethodDescriptor is not null) { _cachedMethodDescriptor = null; return; } _cachedMethodDescriptor = method; } } } } [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] [DebuggerDisplay($"{{{nameof(MethodInfo)}}}")] private class MethodDescriptor(MethodInfo methodInfo, ParameterInfo[] methodParameters) { public MethodInfo MethodInfo { get; private set; } = methodInfo; public ParameterInfo[] Parameters { get; private set; } = methodParameters; public int ParameterCount => Parameters.Length; public TypeInfo? SecondParameterTypeInfo { get => ParameterCount < 2 ? null : Parameters[1].ParameterType.GetTypeInfo(); } } } ================================================ FILE: src/Avalonia.Xaml.Interactions/Core/ChangePropertyAction.cs ================================================ using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; using Avalonia.Controls; using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Core; /// /// An action that will change a specified property to a specified value when invoked. /// public class ChangePropertyAction : AvaloniaObject, IAction { 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) { return AppDomain.CurrentDomain.GetAssemblies() .Reverse() .Select(assembly => assembly.GetType(name)) .FirstOrDefault(t => t is not null) ?? AppDomain.CurrentDomain.GetAssemblies() .Reverse() .SelectMany(assembly => assembly.GetTypes()) .FirstOrDefault(t => t.Name == name); } [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] private static AvaloniaProperty? FindAttachedProperty(object? targetObject, string propertyName) { if (targetObject is null) { return null; } var propertyNames = propertyName.Trim().Trim(s_trimChars).Split(s_separator); if (propertyNames.Length != 2) { return null; } var targetPropertyTypeName = propertyNames[0]; var targetPropertyName = propertyNames[1]; var targetType = GetTypeByName(targetPropertyTypeName) ?? targetObject.GetType(); var registeredAttached = AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(targetType); foreach (var avaloniaProperty in registeredAttached) { if (avaloniaProperty.OwnerType.Name == targetPropertyTypeName && avaloniaProperty.Name == targetPropertyName) { return avaloniaProperty; } } var registeredInherited = AvaloniaPropertyRegistry.Instance.GetRegisteredInherited(targetType); foreach (var avaloniaProperty in registeredInherited) { if (avaloniaProperty.Name == targetPropertyName) { return avaloniaProperty; } } return null; } /// /// Identifies the avalonia property. /// public static readonly StyledProperty PropertyNameProperty = AvaloniaProperty.Register(nameof(PropertyName)); /// /// Identifies the avalonia property. /// public static readonly StyledProperty TargetObjectProperty = AvaloniaProperty.Register(nameof(TargetObject)); /// /// Identifies the avalonia property. /// public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register(nameof(Value)); /// /// Gets or sets the name of the property to change. This is a avalonia property. /// public string PropertyName { get => GetValue(PropertyNameProperty); set => SetValue(PropertyNameProperty, value); } /// /// Gets or sets the value to set. This is a avalonia property. /// public object? Value { get => GetValue(ValueProperty); set => SetValue(ValueProperty, value); } /// /// Gets or sets the object whose property will be changed. /// If is not set or cannot be resolved, the sender of will be used. This is a avalonia property. /// [ResolveByName] public object? TargetObject { get => GetValue(TargetObjectProperty); set => SetValue(TargetObjectProperty, 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. /// True if updating the property value succeeds; else false. public virtual object Execute(object? sender, object? parameter) { object? targetObject; if (GetValue(TargetObjectProperty) is not null) { targetObject = TargetObject; } else { targetObject = sender; } if (targetObject is null) { return false; } if (targetObject is AvaloniaObject avaloniaObject) { if (PropertyName.Contains('.')) { var avaloniaProperty = FindAttachedProperty(targetObject, PropertyName); if (avaloniaProperty is not null) { UpdateAvaloniaPropertyValue(avaloniaObject, avaloniaProperty); return true; } return false; } else { var avaloniaProperty = AvaloniaPropertyRegistry.Instance.FindRegistered(avaloniaObject, PropertyName); if (avaloniaProperty is not null) { UpdateAvaloniaPropertyValue(avaloniaObject, avaloniaProperty); return true; } } } UpdatePropertyValue(targetObject); return true; } [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] private void UpdatePropertyValue(object targetObject) { var targetType = targetObject.GetType(); var targetTypeName = targetType.Name; 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, targetTypeName)); } else if (!propertyInfo.CanWrite) { throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, "Cannot find a property named {0} on type {1}.", PropertyName, targetTypeName)); } Exception? innerException = null; try { object? result = null; var propertyType = propertyInfo.PropertyType; var propertyTypeInfo = propertyType.GetTypeInfo(); if (Value is null) { // The result can be null if the type is generic (nullable), or the default value of the type in question result = propertyTypeInfo.IsValueType ? Activator.CreateInstance(propertyType) : null; } else if (propertyTypeInfo.IsAssignableFrom(Value.GetType().GetTypeInfo())) { result = Value; } else { var valueAsString = Value.ToString(); if (valueAsString is not null) { result = propertyTypeInfo.IsEnum ? Enum.Parse(propertyType, valueAsString, false) : TypeConverterHelper.Convert(valueAsString, propertyType); } } propertyInfo.SetValue(targetObject, result, Array.Empty()); } catch (FormatException e) { innerException = e; } catch (ArgumentException e) { innerException = e; } if (innerException is not null) { throw new ArgumentException(string.Format( 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, propertyInfo.PropertyType.Name), innerException); } } [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] private void UpdateAvaloniaPropertyValue(AvaloniaObject avaloniaObject, AvaloniaProperty property) { ValidateAvaloniaProperty(property); Exception? innerException = null; try { object? result = null; var propertyType = property.PropertyType; var propertyTypeInfo = propertyType.GetTypeInfo(); if (Value is null) { // The result can be null if the type is generic (nullable), or the default value of the type in question result = propertyTypeInfo.IsValueType ? Activator.CreateInstance(propertyType) : null; } else if (propertyTypeInfo.IsAssignableFrom(Value.GetType().GetTypeInfo())) { result = Value; } else { var valueAsString = Value.ToString(); if (valueAsString is not null) { result = propertyTypeInfo.IsEnum ? Enum.Parse(propertyType, valueAsString, false) : TypeConverterHelper.Convert(valueAsString, propertyType); } } avaloniaObject.SetValue(property, result); } catch (FormatException e) { innerException = e; } catch (ArgumentException e) { innerException = e; } if (innerException is not null) { throw new ArgumentException(string.Format( 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, avaloniaObject.GetType().Name), innerException); } } /// /// Ensures the property is not null and can be written to. /// private void ValidateAvaloniaProperty(AvaloniaProperty? property) { if (property is null) { throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, "Cannot find a property named {0}.", PropertyName)); } else if (property.IsReadOnly) { throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, "Cannot find a property named {0}.", PropertyName)); } } } ================================================ FILE: src/Avalonia.Xaml.Interactions/Core/DataTriggerBehavior.cs ================================================ using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; using Avalonia.Reactive; using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Core; /// /// A behavior that performs actions when the bound data meets a specified condition. /// public class DataTriggerBehavior : Trigger { /// /// Identifies the avalonia property. /// public static readonly StyledProperty BindingProperty = AvaloniaProperty.Register(nameof(Binding)); /// /// Identifies the avalonia property. /// public static readonly StyledProperty ComparisonConditionProperty = AvaloniaProperty.Register(nameof(ComparisonCondition)); /// /// Identifies the avalonia property. /// public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register(nameof(Value)); /// /// Gets or sets the bound object that the will listen to. This is a avalonia property. /// public object? Binding { get => GetValue(BindingProperty); set => SetValue(BindingProperty, value); } /// /// Gets or sets the type of comparison to be performed between and . This is a avalonia property. /// public ComparisonConditionType ComparisonCondition { get => GetValue(ComparisonConditionProperty); set => SetValue(ComparisonConditionProperty, value); } /// /// Gets or sets the value to be compared with the value of . This is a avalonia property. /// public object? Value { get => GetValue(ValueProperty); set => SetValue(ValueProperty, value); } static DataTriggerBehavior() { BindingProperty.Changed.Subscribe( new AnonymousObserver>(OnValueChanged)); ComparisonConditionProperty.Changed.Subscribe( new AnonymousObserver>(OnValueChanged)); ValueProperty.Changed.Subscribe( 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) { var value = rightOperand.ToString(); var destinationType = leftOperand.GetType(); if (value is not null) { rightOperand = TypeConverterHelper.Convert(value, destinationType); } } var leftComparableOperand = leftOperand as IComparable; var rightComparableOperand = rightOperand as IComparable; if (leftComparableOperand is not null && rightComparableOperand is not null) { return EvaluateComparable(leftComparableOperand, operatorType, rightComparableOperand); } switch (operatorType) { case ComparisonConditionType.Equal: return Equals(leftOperand, rightOperand); case ComparisonConditionType.NotEqual: return !Equals(leftOperand, rightOperand); case ComparisonConditionType.LessThan: case ComparisonConditionType.LessThanOrEqual: case ComparisonConditionType.GreaterThan: case ComparisonConditionType.GreaterThanOrEqual: { throw leftComparableOperand switch { null when rightComparableOperand is null => new ArgumentException(string.Format( CultureInfo.CurrentCulture, "Binding property of type {0} and Value property of type {1} cannot be used with operator {2}.", leftOperand?.GetType().Name ?? "null", rightOperand?.GetType().Name ?? "null", operatorType.ToString())), null => new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Binding property of type {0} cannot be used with operator {1}.", leftOperand?.GetType().Name ?? "null", operatorType.ToString())), _ => new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Value property of type {0} cannot be used with operator {1}.", rightOperand?.GetType().Name ?? "null", operatorType.ToString())) }; } } return false; } /// /// 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; try { convertedOperand = Convert.ChangeType(rightOperand, leftOperand.GetType(), CultureInfo.CurrentCulture); } catch (FormatException) { // FormatException: Convert.ChangeType("hello", typeof(double), ...); } catch (InvalidCastException) { // InvalidCastException: Convert.ChangeType(4.0d, typeof(Rectangle), ...); } if (convertedOperand is null) { return operatorType == ComparisonConditionType.NotEqual; } var comparison = leftOperand.CompareTo((IComparable)convertedOperand); return operatorType switch { ComparisonConditionType.Equal => comparison == 0, ComparisonConditionType.NotEqual => comparison != 0, ComparisonConditionType.LessThan => comparison < 0, ComparisonConditionType.LessThanOrEqual => comparison <= 0, ComparisonConditionType.GreaterThan => comparison > 0, ComparisonConditionType.GreaterThanOrEqual => comparison >= 0, _ => false }; } private static void OnValueChanged(AvaloniaPropertyChangedEventArgs args) { if (args.Sender is not DataTriggerBehavior behavior || behavior.AssociatedObject is null) { 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) { // Some value has changed--either the binding value, reference value, or the comparison condition. Re-evaluate the equation. if (Compare(behavior.Binding, behavior.ComparisonCondition, behavior.Value)) { Interaction.ExecuteActions(behavior.AssociatedObject, behavior.Actions, args); } } } } ================================================ FILE: src/Avalonia.Xaml.Interactions/Core/EventTriggerBehavior.cs ================================================ using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; using Avalonia.Xaml.Interactivity; using Avalonia.Controls; using Avalonia.Reactive; 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 { private const string EventNameDefaultValue = "AttachedToVisualTree"; /// /// Identifies the avalonia property. /// public static readonly StyledProperty EventNameProperty = AvaloniaProperty.Register(nameof(EventName), EventNameDefaultValue); /// /// Identifies the avalonia property. /// public static readonly StyledProperty SourceObjectProperty = AvaloniaProperty.Register(nameof(SourceObject)); private object? _resolvedSource; private Delegate? _eventHandler; private bool _isLoadedEventRegistered; /// /// Gets or sets the name of the event to listen for. This is a avalonia property. /// public string EventName { get => GetValue(EventNameProperty); set => SetValue(EventNameProperty, value); } /// /// 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. /// [ResolveByName] public object? SourceObject { get => GetValue(SourceObjectProperty); set => SetValue(SourceObjectProperty, value); } static EventTriggerBehavior() { EventNameProperty.Changed.Subscribe( new AnonymousObserver>(EventNameChanged)); SourceObjectProperty.Changed.Subscribe( new AnonymousObserver>(SourceObjectChanged)); } [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] private static void EventNameChanged(AvaloniaPropertyChangedEventArgs e) { if (e.Sender is not EventTriggerBehavior behavior) { return; } if (behavior.AssociatedObject is null || behavior._resolvedSource is null) { return; } var oldEventName = e.OldValue.GetValueOrDefault(); var newEventName = e.NewValue.GetValueOrDefault(); if (oldEventName is not null) { behavior.UnregisterEvent(oldEventName); } if (newEventName is not null) { behavior.RegisterEvent(newEventName); } } private static void SourceObjectChanged(AvaloniaPropertyChangedEventArgs e) { if (e.Sender is EventTriggerBehavior behavior) { behavior.SetResolvedSource(behavior.ComputeResolvedSource()); } } /// /// Called after the behavior is attached to the . /// protected override void OnAttached() { base.OnAttached(); SetResolvedSource(ComputeResolvedSource()); } /// /// Called when the behavior is being detached from its . /// protected override void OnDetaching() { base.OnDetaching(); SetResolvedSource(null); } private void SetResolvedSource(object? newSource) { if (AssociatedObject is null || _resolvedSource == newSource) { return; } if (_resolvedSource is not null) { UnregisterEvent(EventName); } _resolvedSource = newSource; if (_resolvedSource is not null) { RegisterEvent(EventName); } } private object? ComputeResolvedSource() { // If the SourceObject property is set at all, we want to use it. It is possible that it is data // bound and bindings haven't been evaluated yet. Plus, this makes the API more predictable. if (GetValue(SourceObjectProperty) is not null) { return SourceObject; } return AssociatedObject; } [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] private void RegisterEvent(string eventName) { if (string.IsNullOrEmpty(eventName)) { return; } if (eventName != EventNameDefaultValue) { if (_resolvedSource is null) { return; } var sourceObjectType = _resolvedSource.GetType(); var eventInfo = sourceObjectType.GetRuntimeEvent(EventName); if (eventInfo is null) { throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, "Cannot find an event named {0} on type {1}.", EventName, sourceObjectType.Name)); } var methodInfo = typeof(EventTriggerBehavior).GetTypeInfo().GetDeclaredMethod("AttachedToVisualTree"); if (methodInfo is not null) { var eventHandlerType = eventInfo.EventHandlerType; if (eventHandlerType is not null) { _eventHandler = methodInfo.CreateDelegate(eventHandlerType, this); if (_eventHandler is not null) { eventInfo.AddEventHandler(_resolvedSource, _eventHandler); } } } } else if (!_isLoadedEventRegistered) { if (_resolvedSource is Control element && !IsElementLoaded(element)) { _isLoadedEventRegistered = true; element.AttachedToVisualTree += AttachedToVisualTree; } } } [RequiresUnreferencedCode("This functionality is not compatible with trimming.")] private void UnregisterEvent(string eventName) { if (string.IsNullOrEmpty(eventName)) { return; } if (eventName != EventNameDefaultValue) { if (_eventHandler is null) { return; } if (_resolvedSource is not null) { var eventInfo = _resolvedSource.GetType().GetRuntimeEvent(eventName); eventInfo?.RemoveEventHandler(_resolvedSource, _eventHandler); } _eventHandler = null; } else if (_isLoadedEventRegistered) { _isLoadedEventRegistered = false; if (_resolvedSource is Control element) { element.AttachedToVisualTree -= AttachedToVisualTree; } } } /// /// Raised when the control is attached to a rooted visual tree. /// /// The sender object. /// The event args. protected virtual void AttachedToVisualTree(object? sender, object eventArgs) { Interaction.ExecuteActions(_resolvedSource, Actions, eventArgs); } private static bool IsElementLoaded(Control element) => element.Parent is not null; } ================================================ FILE: src/Avalonia.Xaml.Interactions/Core/InvokeCommandAction.cs ================================================ using System.Windows.Input; using Avalonia.Xaml.Interactivity; using Avalonia.Data.Converters; namespace Avalonia.Xaml.Interactions.Core; /// /// Executes a specified when invoked. /// public class InvokeCommandAction : AvaloniaObject, IAction { /// /// Identifies the avalonia property. /// public static readonly StyledProperty IsEnabledProperty = AvaloniaProperty.Register(nameof(IsEnabled), defaultValue: true); /// /// Identifies the avalonia property. /// public static readonly StyledProperty CommandProperty = AvaloniaProperty.Register(nameof(Command)); /// /// Identifies the avalonia property. /// public static readonly StyledProperty CommandParameterProperty = AvaloniaProperty.Register(nameof(CommandParameter)); /// /// Identifies the avalonia property. /// public static readonly StyledProperty InputConverterProperty = AvaloniaProperty.Register(nameof(InputConverter)); /// /// Identifies the avalonia property. /// public static readonly StyledProperty InputConverterParameterProperty = AvaloniaProperty.Register(nameof(InputConverterParameter)); /// /// Identifies the avalonia property. /// /// The string.Empty used for default value string means the invariant culture. 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. /// public ICommand? Command { get => GetValue(CommandProperty); set => SetValue(CommandProperty, value); } /// /// Gets or sets the parameter that is passed to . /// If this is not set, the parameter from the method will be used. /// This is an optional avalonia property. /// public object? CommandParameter { get => GetValue(CommandParameterProperty); set => SetValue(CommandParameterProperty, value); } /// /// Gets or sets the converter that is run on the parameter from the method. /// This is an optional avalonia property. /// public IValueConverter? InputConverter { get => GetValue(InputConverterProperty); set => SetValue(InputConverterProperty, value); } /// /// Gets or sets the parameter that is passed to the /// method of . /// This is an optional avalonia property. /// public object? InputConverterParameter { get => GetValue(InputConverterParameterProperty); set => SetValue(InputConverterParameterProperty, value); } /// /// Gets or sets the language that is passed to the /// method of . /// This is an optional avalonia property. /// public string? InputConverterLanguage { get => GetValue(InputConverterLanguageProperty); set => SetValue(InputConverterLanguageProperty, value); } /// /// Specifies whether the EventArgs of the event that triggered this action should be passed to the Command as a parameter. /// public bool PassEventArgsToCommand { get; set; } /// /// 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. /// True if the command is successfully executed; else false. public virtual object Execute(object? sender, object? parameter) { if (IsEnabled != true || Command is null) { return false; } object? resolvedParameter = default; if (IsSet(CommandParameterProperty)) { resolvedParameter = CommandParameter; } else if (InputConverter is not null) { resolvedParameter = InputConverter.Convert( parameter, typeof(object), InputConverterParameter, InputConverterLanguage is not null ? new System.Globalization.CultureInfo(InputConverterLanguage) : System.Globalization.CultureInfo.CurrentCulture); } else { if (PassEventArgsToCommand) { resolvedParameter = parameter; } } if (!Command.CanExecute(resolvedParameter)) { return false; } Command.Execute(resolvedParameter); return true; } } ================================================ FILE: src/Avalonia.Xaml.Interactions/Properties/AssemblyInfo.cs ================================================ using System.Runtime.CompilerServices; using Avalonia.Metadata; [assembly: InternalsVisibleTo("Avalonia.Xaml.Interactions.UnitTests, PublicKey=00240000048000009400000006020000002400005253413100040000010001002940ed211918fcf63c506fad1d3f7f958b21ff8f06fd2089398296173f9ca93a69b9b380a828bf13fa80d1745beeb917ec3692f4d10e44b4c941619fc7bbd5052b26880697e6fa3f0ce322c4fa902d20b67a48b4144371218f6d39ad39145ea1fe5484052dd51a2ee62af3acd0759bcf92aaefec03978ded3cfaa84798e92de8")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Xaml.Interactions")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Xaml.Interactions.Core")] ================================================ FILE: src/Avalonia.Xaml.Interactions.Custom/AddClassAction.cs ================================================ using Avalonia.Controls; using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Custom; /// /// Adds a specified to the collection when invoked. /// public class AddClassAction : AvaloniaObject, IAction { /// /// Identifies the avalonia property. /// public static readonly StyledProperty ClassNameProperty = AvaloniaProperty.Register(nameof(ClassName)); /// /// Identifies the avalonia property. /// public static readonly StyledProperty StyledElementProperty = AvaloniaProperty.Register(nameof(StyledElement)); /// /// Identifies the avalonia property. /// public static readonly StyledProperty RemoveIfExistsProperty = AvaloniaProperty.Register(nameof(RemoveIfExists)); /// /// Gets or sets the class name that should be added. This is a avalonia property. /// public string ClassName { get => GetValue(ClassNameProperty); set => SetValue(ClassNameProperty, value); } /// /// Gets or sets the target styled element that class name that should be added to. This is a avalonia property. /// [ResolveByName] public StyledElement? StyledElement { get => GetValue(StyledElementProperty); set => SetValue(StyledElementProperty, value); } /// /// Gets or sets the flag indicated whether to remove the class if already exists before adding. This is a avalonia property. /// public bool RemoveIfExists { get => GetValue(RemoveIfExistsProperty); set => SetValue(RemoveIfExistsProperty, 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. /// True if the class is successfully added; else false. public object Execute(object? sender, object? parameter) { var target = GetValue(StyledElementProperty) is not null ? StyledElement : sender as StyledElement; if (target is null || string.IsNullOrEmpty(ClassName)) { return false; } if (RemoveIfExists && target.Classes.Contains(ClassName)) { target.Classes.Remove(ClassName); } target.Classes.Add(ClassName); return true; } } ================================================ FILE: src/Avalonia.Xaml.Interactions.Custom/AttachedToVisualTreeBehavior.cs ================================================ using System.Reactive.Disposables; using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Custom; /// /// A base class for behaviors using attached to visual tree event. /// /// public abstract class AttachedToVisualTreeBehavior : DisposingBehavior where T : Visual { private CompositeDisposable? _disposables; /// protected override void OnAttached(CompositeDisposable disposables) { _disposables = disposables; } /// protected override void OnAttachedToVisualTree() { OnAttachedToVisualTree(_disposables!); } /// /// Called after the behavior is attached to the visual tree. /// /// The group of disposable resources that are disposed together protected abstract void OnAttachedToVisualTree(CompositeDisposable disposable); } ================================================ FILE: src/Avalonia.Xaml.Interactions.Custom/Avalonia.Xaml.Interactions.Custom.csproj ================================================ netstandard2.0;net6.0;net8.0 Library bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml enable Avalonia.Xaml.Interactions.Custom Easily add interactivity to your Avalonia apps using XAML Behaviors. Behaviors encapsulate reusable functionalities for elements that can be easily added to your XAML without the need for more imperative code. Avalonia;Behavior;Action;Behaviors;Actions;Managed;C#;Interaction;Interactivity;Interactions;Xaml ================================================ FILE: src/Avalonia.Xaml.Interactions.Custom/BindPointerOverBehavior.cs ================================================ using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.Data; using Avalonia.Input; namespace Avalonia.Xaml.Interactions.Custom; /// /// /// public class BindPointerOverBehavior : DisposingBehavior { /// /// /// public static readonly StyledProperty IsPointerOverProperty = AvaloniaProperty.Register(nameof(IsPointerOver), defaultBindingMode: BindingMode.TwoWay); /// /// /// public bool IsPointerOver { get => GetValue(IsPointerOverProperty); set => SetValue(IsPointerOverProperty, value); } /// /// /// /// protected override void OnAttached(CompositeDisposable disposables) { if (AssociatedObject is null) { return; } var dispose = Observable .FromEventPattern(AssociatedObject, nameof(PropertyChanged)) .Select(x => x.EventArgs) .Subscribe(new AnonymousObserver(e => { if (e.Property == InputElement.IsPointerOverProperty) { IsPointerOver = e.NewValue is true; } })); disposables.Add(dispose); disposables.Add(Disposable.Create(() => IsPointerOver = false)); } } ================================================ FILE: src/Avalonia.Xaml.Interactions.Custom/BindTagToVisualRootDataContextBehavior.cs ================================================ using System; using Avalonia.Controls; using Avalonia.VisualTree; using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Custom; /// /// Binds AssociatedObject object Tag property to root visual DataContext. /// public class BindTagToVisualRootDataContextBehavior : Behavior { private IDisposable? _disposable; /// protected override void OnAttachedToVisualTree() { var visualRoot = (Control?)AssociatedObject?.GetVisualRoot(); if (visualRoot is not null) { _disposable = BindDataContextToTag(visualRoot, AssociatedObject); } } /// protected override void OnDetachedFromVisualTree() { _disposable?.Dispose(); } private static IDisposable? BindDataContextToTag(Control source, Control? target) { if (source is null) throw new ArgumentNullException(nameof(source)); if (target is null) throw new ArgumentNullException(nameof(target)); var data = source.GetObservable(StyledElement.DataContextProperty); return data is not null ? target.Bind(Control.TagProperty, data) : null; } } ================================================ FILE: src/Avalonia.Xaml.Interactions.Custom/BindingBehavior.cs ================================================ using System.Reactive.Disposables; using Avalonia.Controls; using Avalonia.Data; namespace Avalonia.Xaml.Interactions.Custom; /// /// /// public class BindingBehavior : AttachedToVisualTreeBehavior { /// /// /// public static readonly StyledProperty TargetPropertyProperty = AvaloniaProperty.Register(nameof(TargetProperty)); /// /// /// public static readonly StyledProperty TargetObjectProperty = AvaloniaProperty.Register(nameof(TargetObject)); /// /// /// public static readonly StyledProperty BindingProperty = AvaloniaProperty.Register(nameof(Binding)); /// /// /// public AvaloniaProperty? TargetProperty { get => GetValue(TargetPropertyProperty); set => SetValue(TargetPropertyProperty, value); } /// /// /// [ResolveByName] public AvaloniaObject? TargetObject { get => GetValue(TargetObjectProperty); set => SetValue(TargetObjectProperty, value); } /// /// /// [AssignBinding] public IBinding? Binding { get => GetValue(BindingProperty); set => SetValue(BindingProperty, value); } /// /// /// /// protected override void OnAttachedToVisualTree(CompositeDisposable disposable) { if (TargetObject is not null && TargetProperty is not null && Binding is not null) { var dispose = TargetObject.Bind(TargetProperty, Binding); disposable.Add(dispose); } } } ================================================ FILE: src/Avalonia.Xaml.Interactions.Custom/BoundsObserverBehavior.cs ================================================ 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; }))); } } } ================================================ FILE: src/Avalonia.Xaml.Interactions.Custom/ButtonClickEventTriggerBehavior.cs ================================================ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Xaml.Interactivity; namespace Avalonia.Xaml.Interactions.Custom; /// /// A behavior that listens for a event on its source and executes its actions when that event is fired. /// public class ButtonClickEventTriggerBehavior : Trigger