diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/ActionCollection.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/ActionCollection.cs index 3daff6d0..5426021c 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/ActionCollection.cs +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/ActionCollection.cs @@ -47,7 +47,7 @@ private static void VerifyType(DependencyObject item) { if (!(item is IAction)) { - throw new InvalidOperationException(ResourceHelper.GetString("NonActionAddedToActionCollectionExceptionMessage")); + throw new InvalidOperationException(ResourceHelper.NonActionAddedToActionCollectionExceptionMessage); } } } diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/BehaviorCollection.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/BehaviorCollection.cs index 9a3129e9..6549b6b3 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/BehaviorCollection.cs +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/BehaviorCollection.cs @@ -60,7 +60,7 @@ public void Attach(DependencyObject associatedObject) if (this.AssociatedObject != null) { - throw new InvalidOperationException(ResourceHelper.GetString("CannotAttachBehaviorMultipleTimesExceptionMessage")); + throw new InvalidOperationException(ResourceHelper.CannotAttachBehaviorMultipleTimesExceptionMessage); } Debug.Assert(associatedObject != null, "The previous checks should keep us from ever setting null here."); @@ -162,12 +162,12 @@ private IBehavior VerifiedAttach(DependencyObject item) IBehavior behavior = item as IBehavior; if (behavior == null) { - throw new InvalidOperationException(ResourceHelper.GetString("NonBehaviorAddedToBehaviorCollectionExceptionMessage")); + throw new InvalidOperationException(ResourceHelper.NonBehaviorAddedToBehaviorCollectionExceptionMessage); } if (this._oldCollection.Contains(behavior)) { - throw new InvalidOperationException(ResourceHelper.GetString("DuplicateBehaviorInCollectionExceptionMessage")); + throw new InvalidOperationException(ResourceHelper.DuplicateBehaviorInCollectionExceptionMessage); } if (this.AssociatedObject != null) diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/BehaviorOfT.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/BehaviorOfT.cs index 4d482b76..731a1a73 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/BehaviorOfT.cs +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/BehaviorOfT.cs @@ -40,7 +40,7 @@ protected override void OnAttached() { string actualType = base.AssociatedObject.GetType().FullName; string expectedType = typeof (T).FullName; - string message = string.Format(ResourceHelper.GetString("InvalidAssociatedObjectExceptionMessage"), actualType, expectedType); + string message = string.Format(ResourceHelper.InvalidAssociatedObjectExceptionMessage, actualType, expectedType); throw new InvalidOperationException(message); } } diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/CallMethodAction.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/CallMethodAction.cs index 620662b0..83f69d5b 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/CallMethodAction.cs +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/CallMethodAction.cs @@ -19,7 +19,7 @@ namespace Microsoft.Xaml.Interactivity; /// /// An action that calls a method on a specified object when invoked. /// -#if NET8_0_OR_GREATER +#if NET5_0_OR_GREATER [RequiresUnreferencedCode("This action is not trim-safe.")] #endif public sealed class CallMethodAction : DependencyObject, IAction diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ChangeDependencyPropertyAction.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ChangeDependencyPropertyAction.cs new file mode 100644 index 00000000..3852ba76 --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ChangeDependencyPropertyAction.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if WinUI +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Markup; +#else +using Windows.UI.Xaml; +using Windows.UI.Xaml.Markup; +#endif + +namespace Microsoft.Xaml.Interactivity; + +/// +/// An action that will change a specified dependency property to a specified value when invoked. +/// +[ContentProperty(Name = "Value")] +public sealed class ChangeDependencyPropertyAction : DependencyObject, IAction +{ + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty TargetObjectProperty = DependencyProperty.Register( + "TargetObject", + typeof(DependencyObject), + typeof(ChangeDependencyPropertyAction), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( + "Value", + typeof(object), + typeof(ChangeDependencyPropertyAction), + new PropertyMetadata(null)); + + /// + /// Gets or sets the dependency property to change. This is not a dependency property, due to framework restrictions. + /// + public DependencyProperty DependencyProperty { get; set; } + + /// + /// Gets or sets the value to set. This is a dependency 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 dependency property. + /// + public DependencyObject TargetObject + { + get => (DependencyObject)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. + /// Ignored + /// True if updating the dependency property value succeeds; else false. + public object Execute(object sender, object parameter) + { + DependencyObject targetObject; + if (ReadLocalValue(TargetObjectProperty) != DependencyProperty.UnsetValue) + { + targetObject = TargetObject; + } + else + { + targetObject = sender as DependencyObject; + } + + if (targetObject == null || DependencyProperty == null) + { + return false; + } + + targetObject.SetValue(DependencyProperty, Value); + return true; + } +} \ No newline at end of file diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ChangePropertyAction.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ChangePropertyAction.cs index 9de5e718..0bd7b97f 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ChangePropertyAction.cs +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ChangePropertyAction.cs @@ -8,8 +8,10 @@ #if WinUI using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Markup; #else using Windows.UI.Xaml; +using Windows.UI.Xaml.Markup; #endif namespace Microsoft.Xaml.Interactivity; @@ -17,9 +19,10 @@ namespace Microsoft.Xaml.Interactivity; /// /// An action that will change a specified property to a specified value when invoked. /// -#if NET8_0_OR_GREATER -[RequiresUnreferencedCode("This action is not trim-safe.")] +#if NET5_0_OR_GREATER +[RequiresUnreferencedCode("This action is not trim-safe, as it uses reflection to set the property. Use ChangeDependencyPropertyAction for dependency properties, or write your own purpose-specific action.")] #endif +[ContentProperty(Name = "Value")] public sealed class ChangePropertyAction : DependencyObject, IAction { /// diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/CheckedTriggerBehavior.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/CheckedTriggerBehavior.cs new file mode 100644 index 00000000..62e2bd70 --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/CheckedTriggerBehavior.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if WinUI +using Microsoft.UI.Xaml.Controls.Primitives; +#else +using Windows.UI.Xaml.Controls.Primitives; +#endif + +namespace Microsoft.Xaml.Interactivity; + +/// +/// A behavior that listens to an and executes its actions when the button is checked. +/// +public sealed class CheckedTriggerBehavior : EventTriggerBehaviorBase +{ + /// + protected override bool RegisterEventCore(ToggleButton source) + { + source.Checked += OnEvent; + return true; + } + + /// + protected override void UnregisterEventCore(ToggleButton source) + { + source.Checked -= OnEvent; + } +} diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/DataBindingHelper.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/DataBindingHelper.cs index 93dde39a..8a05ce3e 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/DataBindingHelper.cs +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/DataBindingHelper.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; -#if NET8_0_OR_GREATER +#if NET5_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif using System.Reflection; @@ -30,7 +30,7 @@ internal static class DataBindingHelper /// bindings on the action may not be up-to-date. This routine is called before the action /// is executed in order to guarantee that all bindings are refreshed with the most current data. /// -#if NET8_0_OR_GREATER +#if NET5_0_OR_GREATER [RequiresUnreferencedCode("This method accesses all fields of input action objects.")] #endif public static void RefreshDataBindingsOnActions(ActionCollection actions) @@ -45,7 +45,7 @@ public static void RefreshDataBindingsOnActions(ActionCollection actions) } private static IEnumerable GetDependencyProperties( -#if NET8_0_OR_GREATER +#if NET5_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] #endif Type type) diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/DataTriggerBehavior.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/DataTriggerBehavior.cs index 90c2b899..1ddf4c67 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/DataTriggerBehavior.cs +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/DataTriggerBehavior.cs @@ -16,7 +16,7 @@ namespace Microsoft.Xaml.Interactivity; /// /// A behavior that performs actions when the bound data meets a specified condition. /// -#if NET8_0_OR_GREATER +#if NET5_0_OR_GREATER [RequiresUnreferencedCode("This behavior is not trim-safe.")] #endif public sealed class DataTriggerBehavior : Trigger diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/EventTriggerBehavior.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/EventTriggerBehavior.cs index 3850333b..b9da65c6 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/EventTriggerBehavior.cs +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/EventTriggerBehavior.cs @@ -2,17 +2,17 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Diagnostics.CodeAnalysis; using System.Reflection; #if WinUI using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Media; #else using Windows.UI.Xaml; -using Windows.UI.Xaml.Media; #endif -#if WINDOWS_UWP && !NET8_0_OR_GREATER + +#if NET5_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#else using System.Runtime.InteropServices.WindowsRuntime; #endif @@ -21,35 +21,23 @@ namespace Microsoft.Xaml.Interactivity; /// /// A behavior that listens for a specified event on its source and executes its actions when that event is fired. /// -#if NET8_0_OR_GREATER -[RequiresUnreferencedCode("This behavior is not trim-safe.")] +#if NET5_0_OR_GREATER +[RequiresUnreferencedCode("This behavior is not trim-safe, as it uses reflection to register the event handler. Use the provided event-specific trigger behaviors, or make your own by deriving from EventTriggerBehaviorBase.")] #endif -public sealed class EventTriggerBehavior : Trigger +public sealed class EventTriggerBehavior : EventTriggerBehaviorBase { /// /// Identifies the dependency property. /// - [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] public static readonly DependencyProperty EventNameProperty = DependencyProperty.Register( "EventName", typeof(string), typeof(EventTriggerBehavior), new PropertyMetadata("Loaded", new PropertyChangedCallback(EventTriggerBehavior.OnEventNameChanged))); - /// - /// Identifies the dependency property. - /// - [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] - public static readonly DependencyProperty SourceObjectProperty = DependencyProperty.Register( - "SourceObject", - typeof(object), - typeof(EventTriggerBehavior), - new PropertyMetadata(null, new PropertyChangedCallback(EventTriggerBehavior.OnSourceObjectChanged))); - - private object _resolvedSource; private Delegate _eventHandler; - private bool _isLoadedEventRegistered; -#if !NET8_0_OR_GREATER + private EventInfo _event; // null if EventName == Loaded +#if !NET5_0_OR_GREATER private bool _isWindowsRuntimeEvent; private Func _addEventHandlerMethod; private Action _removeEventHandlerMethod; @@ -67,201 +55,103 @@ public EventTriggerBehavior() /// public string EventName { - get - { - return (string)this.GetValue(EventTriggerBehavior.EventNameProperty); - } - - set - { - this.SetValue(EventTriggerBehavior.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 dependency property. - /// - public object SourceObject - { - get - { - return (object)this.GetValue(EventTriggerBehavior.SourceObjectProperty); - } - - set - { - this.SetValue(EventTriggerBehavior.SourceObjectProperty, value); - } - } - - /// - /// Called after the behavior is attached to the . - /// - protected override void OnAttached() - { - base.OnAttached(); - this.SetResolvedSource(this.ComputeResolvedSource()); + get => (string)GetValue(EventNameProperty); + set => SetValue(EventNameProperty, value); } - /// - /// Called when the behavior is being detached from its . - /// - protected override void OnDetaching() - { - base.OnDetaching(); - this.SetResolvedSource(null); - } - - private void SetResolvedSource(object newSource) - { - if (this.AssociatedObject == null || this._resolvedSource == newSource) - { - return; - } - - if (this._resolvedSource != null) - { - this.UnregisterEvent(this.EventName); - } - - this._resolvedSource = newSource; - - if (this._resolvedSource != null) - { - this.RegisterEvent(this.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 (this.ReadLocalValue(EventTriggerBehavior.SourceObjectProperty) != DependencyProperty.UnsetValue) - { - return this.SourceObject; - } - - return this.AssociatedObject; - } - - private void RegisterEvent(string eventName) + /// + protected override bool RegisterEventCore(DependencyObject source) { + var eventName = EventName; if (string.IsNullOrEmpty(eventName)) { - return; + return false; } if (eventName != "Loaded") { - Type sourceObjectType = this._resolvedSource.GetType(); - EventInfo info = sourceObjectType.GetRuntimeEvent(eventName); + Type sourceObjectType = source.GetType(); + var info = sourceObjectType.GetRuntimeEvent(eventName); if (info == null) { - return; + return false; } MethodInfo methodInfo = typeof(EventTriggerBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent"); - this._eventHandler = methodInfo.CreateDelegate(info.EventHandlerType, this); + _eventHandler = methodInfo.CreateDelegate(info.EventHandlerType, this); -#if NET8_0_OR_GREATER - info.AddEventHandler(this._resolvedSource, this._eventHandler); -#else - this._isWindowsRuntimeEvent = EventTriggerBehavior.IsWindowsRuntimeEvent(info); - if (this._isWindowsRuntimeEvent) +#if !NET5_0_OR_GREATER + _isWindowsRuntimeEvent = IsWindowsRuntimeEvent(info); + if (_isWindowsRuntimeEvent) { - this._addEventHandlerMethod = add => (EventRegistrationToken)info.AddMethod.Invoke(this._resolvedSource, new object[] { add }); - this._removeEventHandlerMethod = token => info.RemoveMethod.Invoke(this._resolvedSource, new object[] { token }); + _addEventHandlerMethod = add => (EventRegistrationToken)info.AddMethod.Invoke(source, new object[] { add }); + _removeEventHandlerMethod = token => info.RemoveMethod.Invoke(source, new object[] { token }); - WindowsRuntimeMarshal.AddEventHandler(this._addEventHandlerMethod, this._removeEventHandlerMethod, this._eventHandler); - } - else - { - info.AddEventHandler(this._resolvedSource, this._eventHandler); + WindowsRuntimeMarshal.AddEventHandler(_addEventHandlerMethod, _removeEventHandlerMethod, _eventHandler); + + return true; } #endif + + info.AddEventHandler(source, _eventHandler); + _event = info; + + return true; } - else if (!this._isLoadedEventRegistered) + else if (source is FrameworkElement element && !LoadedTriggerBehavior.IsElementLoaded(element)) { - FrameworkElement element = this._resolvedSource as FrameworkElement; - if (element != null && !EventTriggerBehaviorHelpers.IsElementLoaded(element)) - { - this._isLoadedEventRegistered = true; - element.Loaded += this.OnEvent; - } + element.Loaded += OnEvent; + return true; + } + else + { + return false; } } - private void UnregisterEvent(string eventName) + /// + protected override void UnregisterEventCore(DependencyObject source) { - if (string.IsNullOrEmpty(eventName)) + // Don't use the EventName property in this function. By the point this is called, it might have changed. +#if !NET5_0_OR_GREATER + if (_isWindowsRuntimeEvent) { + WindowsRuntimeMarshal.RemoveEventHandler(_removeEventHandlerMethod, _eventHandler); + _isWindowsRuntimeEvent = false; + _addEventHandlerMethod = null; + _removeEventHandlerMethod = null; + _eventHandler = null; + return; } - - if (eventName != "Loaded") - { - if (this._eventHandler == null) - { - return; - } - - EventInfo info = this._resolvedSource.GetType().GetRuntimeEvent(eventName); -#if NET8_0_OR_GREATER - info.RemoveEventHandler(this._resolvedSource, this._eventHandler); -#else - if (this._isWindowsRuntimeEvent) - { - WindowsRuntimeMarshal.RemoveEventHandler(this._removeEventHandlerMethod, this._eventHandler); - } - else - { - info.RemoveEventHandler(this._resolvedSource, this._eventHandler); - } #endif - this._eventHandler = null; + if (_event != null) + { + _event.RemoveEventHandler(source, _eventHandler); + _event = null; + _eventHandler = null; } - else if (this._isLoadedEventRegistered) + else { - this._isLoadedEventRegistered = false; - FrameworkElement element = (FrameworkElement)this._resolvedSource; - element.Loaded -= this.OnEvent; + ((FrameworkElement)source).Loaded -= OnEvent; } } - private void OnEvent(object sender, object eventArgs) - { - Interaction.ExecuteActions(this._resolvedSource, this.Actions, eventArgs); - } - - private static void OnSourceObjectChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args) - { - EventTriggerBehavior behavior = (EventTriggerBehavior)dependencyObject; - behavior.SetResolvedSource(behavior.ComputeResolvedSource()); - } - private static void OnEventNameChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args) { EventTriggerBehavior behavior = (EventTriggerBehavior)dependencyObject; - if (behavior.AssociatedObject == null || behavior._resolvedSource == null) - { - return; - } - - string oldEventName = (string)args.OldValue; - string newEventName = (string)args.NewValue; - behavior.UnregisterEvent(oldEventName); - behavior.RegisterEvent(newEventName); + behavior.UnregisterEvent(); + behavior.RegisterEvent(); } -#if !NET8_0_OR_GREATER +#if !NET5_0_OR_GREATER private static bool IsWindowsRuntimeEvent(EventInfo eventInfo) { return eventInfo != null && - EventTriggerBehavior.IsWindowsRuntimeType(eventInfo.EventHandlerType) && - EventTriggerBehavior.IsWindowsRuntimeType(eventInfo.DeclaringType); + IsWindowsRuntimeType(eventInfo.EventHandlerType) && + IsWindowsRuntimeType(eventInfo.DeclaringType); } private static bool IsWindowsRuntimeType(Type type) @@ -269,7 +159,8 @@ private static bool IsWindowsRuntimeType(Type type) if (type != null) { // This will only work when using built-in WinRT interop, ie. where .winmd files are directly - // referenced instead of generated projections. That is, this would not work on modern .NET. + // referenced instead of generated projections. That is, this would not work on .NET 5 or higher, + // where CsWinRT is used instead. return type.AssemblyQualifiedName.EndsWith("ContentType=WindowsRuntime", StringComparison.Ordinal); } @@ -277,36 +168,3 @@ private static bool IsWindowsRuntimeType(Type type) } #endif } - -internal static class EventTriggerBehaviorHelpers -{ - // This method has to be outside of 'EventTriggerBehavior', because it's actually trim-safe. - // We want to allow other callers inside the library use this without getting trim warnings. - public static bool IsElementLoaded(FrameworkElement element) - { - if (element == null) - { - return false; - } - - UIElement rootVisual = default; - if (element.XamlRoot != null) - { - rootVisual = element.XamlRoot.Content; - } - else if (Window.Current != null) - { - rootVisual = Window.Current.Content; - } - - DependencyObject parent = element.Parent; - if (parent == null) - { - // If the element is the child of a ControlTemplate it will have a null parent even when it is loaded. - // To catch that scenario, also check it's parent in the visual tree. - parent = VisualTreeHelper.GetParent(element); - } - - return (parent != null || (rootVisual != null && element == rootVisual)); - } -} diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/EventTriggerBehaviorBase.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/EventTriggerBehaviorBase.cs new file mode 100644 index 00000000..5f6542e8 --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/EventTriggerBehaviorBase.cs @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Xaml.Interactivity; + +#if WinUI +using Microsoft.UI.Xaml; +#else +using Windows.UI.Xaml; +#endif + +namespace Microsoft.Xaml.Interactivity; + +/// +/// A base behavior that listens for an event on its source and executes its actions when that event is fired. +/// +public abstract class EventTriggerBehaviorBase : Trigger where T : DependencyObject +{ + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty SourceObjectProperty = DependencyProperty.Register( + "SourceObject", + typeof(T), + typeof(EventTriggerBehaviorBase), + new PropertyMetadata(null, new PropertyChangedCallback(OnSourceObjectChanged))); + + private T _resolvedSource; + private bool _isEventRegistered; + + /// + /// Initializes a new instance of the class. + /// + public EventTriggerBehaviorBase() + { + } + + /// + /// 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 dependency property. + /// + public T SourceObject + { + get => (T)GetValue(SourceObjectProperty); + set => SetValue(SourceObjectProperty, value); + } + + /// + protected override void OnAttached() + { + base.OnAttached(); + SetResolvedSource(ComputeResolvedSource()); + } + + /// + protected override void OnDetaching() + { + base.OnDetaching(); + SetResolvedSource(null); + } + + /// + /// Contains the core logic to register an event handler + /// + /// The object for which to register the event handler + /// true if the event handler got registered, false if it did not + protected abstract bool RegisterEventCore(T source); + + /// + /// Contains the core logic to unregister an event handler + /// + /// The object for which to unregister the event handler + protected abstract void UnregisterEventCore(T source); + + /// + /// The method that should be called when the event is triggered + /// + /// The event's sender + /// The event arguments + protected void OnEvent(object sender, object eventArgs) + { + Interaction.ExecuteActions(_resolvedSource, Actions, eventArgs); + } + + /// + /// Registers the event handler + /// + protected void RegisterEvent() + { + if (!_isEventRegistered) + { + _isEventRegistered = RegisterEventCore(_resolvedSource); + } + } + + /// + /// Unregisters the event handler + /// + protected void UnregisterEvent() + { + if (_isEventRegistered) + { + _isEventRegistered = false; + UnregisterEventCore(_resolvedSource); + } + } + + private void SetResolvedSource(T newSource) + { + if (AssociatedObject == null || _resolvedSource == newSource) + { + return; + } + + if (_resolvedSource != null) + { + UnregisterEvent(); + } + + _resolvedSource = newSource; + + if (_resolvedSource != null) + { + RegisterEvent(); + } + } + + private T 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 (ReadLocalValue(SourceObjectProperty) != DependencyProperty.UnsetValue) + { + return SourceObject; + } + + return AssociatedObject; + } + + private static void OnSourceObjectChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args) + { + EventTriggerBehaviorBase behavior = (EventTriggerBehaviorBase)dependencyObject; + behavior.SetResolvedSource(behavior.ComputeResolvedSource()); + } +} diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/GoToStateAction.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/GoToStateAction.cs index f8b1e823..37677723 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/GoToStateAction.cs +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/GoToStateAction.cs @@ -125,7 +125,7 @@ public object Execute(object sender, object parameter) } FrameworkElement element = sender as FrameworkElement; - if (element == null || !EventTriggerBehaviorHelpers.IsElementLoaded(element)) + if (element == null || !LoadedTriggerBehavior.IsElementLoaded(element)) { return false; } diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/GotFocusTriggerBehavior.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/GotFocusTriggerBehavior.cs new file mode 100644 index 00000000..ed690f0d --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/GotFocusTriggerBehavior.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if WinUI +using Microsoft.UI.Xaml; +#else +using Windows.UI.Xaml; +#endif + +namespace Microsoft.Xaml.Interactivity; + +/// +/// A behavior that listens to an and executes its actions when the UI element gets focus. +/// +public sealed class GotFocusTriggerBehavior : EventTriggerBehaviorBase +{ + /// + protected override bool RegisterEventCore(UIElement source) + { + source.GotFocus += OnEvent; + return true; + } + + /// + protected override void UnregisterEventCore(UIElement source) + { + source.GotFocus -= OnEvent; + } +} diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/KeyboardAcceleratorInvokedTriggerBehavior.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/KeyboardAcceleratorInvokedTriggerBehavior.cs new file mode 100644 index 00000000..b823d330 --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/KeyboardAcceleratorInvokedTriggerBehavior.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if WinUI +using Microsoft.UI.Xaml.Input; +#else +using Windows.UI.Xaml.Input; +#endif + +namespace Microsoft.Xaml.Interactivity; + +/// +/// A behavior that listens to an and executes its actions when the accelerator is invoked. +/// +public sealed class KeyboardAcceleratorInvokedTriggerBehavior : EventTriggerBehaviorBase +{ + /// + protected override bool RegisterEventCore(KeyboardAccelerator source) + { + source.Invoked += OnEvent; + return true; + } + + /// + protected override void UnregisterEventCore(KeyboardAccelerator source) + { + source.Invoked -= OnEvent; + } +} diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/LoadedTriggerBehavior.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/LoadedTriggerBehavior.cs new file mode 100644 index 00000000..164de9c8 --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/LoadedTriggerBehavior.cs @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if WinUI +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Media; +#else +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; +#endif + +namespace Microsoft.Xaml.Interactivity; + +/// +/// A behavior that listens to an and executes its actions when the element is loaded. +/// +public sealed class LoadedTriggerBehavior : EventTriggerBehaviorBase +{ + /// + protected override bool RegisterEventCore(FrameworkElement source) + { + if (!IsElementLoaded(source)) + { + source.Loaded += OnEvent; + return true; + } + else + { + return false; + } + } + + /// + protected override void UnregisterEventCore(FrameworkElement source) + { + source.Loaded -= OnEvent; + } + + internal static bool IsElementLoaded(FrameworkElement element) + { + if (element == null) + { + return false; + } + + UIElement rootVisual = default; + if (element.XamlRoot != null) + { + rootVisual = element.XamlRoot.Content; + } + else if (Window.Current != null) + { + rootVisual = Window.Current.Content; + } + + DependencyObject parent = element.Parent; + if (parent == null) + { + // If the element is the child of a ControlTemplate it will have a null parent even when it is loaded. + // To catch that scenario, also check it's parent in the visual tree. + parent = VisualTreeHelper.GetParent(element); + } + + return (parent != null || (rootVisual != null && element == rootVisual)); + } +} diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/LostFocusTriggerBehavior.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/LostFocusTriggerBehavior.cs new file mode 100644 index 00000000..07ef905c --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/LostFocusTriggerBehavior.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if WinUI +using Microsoft.UI.Xaml; +#else +using Windows.UI.Xaml; +#endif + +namespace Microsoft.Xaml.Interactivity; + +/// +/// A behavior that listens to an and executes its actions when the UI element loses focus. +/// +public sealed class LostFocusTriggerBehavior : EventTriggerBehaviorBase +{ + /// + protected override bool RegisterEventCore(UIElement source) + { + source.LostFocus += OnEvent; + return true; + } + + /// + protected override void UnregisterEventCore(UIElement source) + { + source.LostFocus -= OnEvent; + } +} diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ManipulationCompletedTriggerBehavior.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ManipulationCompletedTriggerBehavior.cs new file mode 100644 index 00000000..0c4a5252 --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ManipulationCompletedTriggerBehavior.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if WinUI +using Microsoft.UI.Xaml; +#else +using Windows.UI.Xaml; +#endif + +namespace Microsoft.Xaml.Interactivity; + +/// +/// A behavior that listens to an and executes its actions when the UI element completes manipulation. +/// +public sealed class ManipulationCompletedTriggerBehavior : EventTriggerBehaviorBase +{ + /// + protected override bool RegisterEventCore(UIElement source) + { + source.ManipulationCompleted += OnEvent; + return true; + } + + /// + protected override void UnregisterEventCore(UIElement source) + { + source.ManipulationCompleted -= OnEvent; + } +} diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ManipulationStartedTriggerBehavior.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ManipulationStartedTriggerBehavior.cs new file mode 100644 index 00000000..0772e11b --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ManipulationStartedTriggerBehavior.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if WinUI +using Microsoft.UI.Xaml; +#else +using Windows.UI.Xaml; +#endif + +namespace Microsoft.Xaml.Interactivity; + +/// +/// A behavior that listens to an and executes its actions when the UI element starts manipulation. +/// +public sealed class ManipulationStartedTriggerBehavior : EventTriggerBehaviorBase +{ + /// + protected override bool RegisterEventCore(UIElement source) + { + source.ManipulationStarted += OnEvent; + return true; + } + + /// + protected override void UnregisterEventCore(UIElement source) + { + source.ManipulationStarted -= OnEvent; + } +} diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/PointerEnteredTriggerBehavior.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/PointerEnteredTriggerBehavior.cs new file mode 100644 index 00000000..56a22d88 --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/PointerEnteredTriggerBehavior.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if WinUI +using Microsoft.UI.Xaml; +#else +using Windows.UI.Xaml; +#endif + +namespace Microsoft.Xaml.Interactivity; + +/// +/// A behavior that listens to an and executes its actions when the pointer enters the UI element. +/// +public sealed class PointerEnteredTriggerBehavior : EventTriggerBehaviorBase +{ + /// + protected override bool RegisterEventCore(UIElement source) + { + source.PointerEntered += OnEvent; + return true; + } + + /// + protected override void UnregisterEventCore(UIElement source) + { + source.PointerEntered -= OnEvent; + } +} diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/PointerExitedTriggerBehavior.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/PointerExitedTriggerBehavior.cs new file mode 100644 index 00000000..fa92cf1f --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/PointerExitedTriggerBehavior.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if WinUI +using Microsoft.UI.Xaml; +#else +using Windows.UI.Xaml; +#endif + +namespace Microsoft.Xaml.Interactivity; + +/// +/// A behavior that listens to an and executes its actions when the pointer exits the UI element. +/// +public sealed class PointerExitedTriggerBehavior : EventTriggerBehaviorBase +{ + /// + protected override bool RegisterEventCore(UIElement source) + { + source.PointerExited += OnEvent; + return true; + } + + /// + protected override void UnregisterEventCore(UIElement source) + { + source.PointerExited -= OnEvent; + } +} diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ResourceHelper.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ResourceHelper.cs index 2c6b0c76..a71d8c31 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ResourceHelper.cs +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/ResourceHelper.cs @@ -3,99 +3,47 @@ namespace Microsoft.Xaml.Interactivity; -using Windows.ApplicationModel.Resources; - -internal static class ResourceHelper +internal static partial class ResourceHelper { -#if NET8_0_OR_GREATER && !MODERN_WINDOWS_UWP - private static ResourceLoader strings = new ResourceLoader(ResourceLoader.GetDefaultResourceFilePath(), "Microsoft.Xaml.Interactivity/Strings"); -#endif - - public static string GetString(string resourceName) - { -#if !NET8_0_OR_GREATER || MODERN_WINDOWS_UWP - ResourceLoader strings = ResourceLoader.GetForCurrentView("Microsoft.Xaml.Interactivity/Strings"); -#endif - return strings.GetString(resourceName); - } - public static string CallMethodActionValidMethodNotFoundExceptionMessage - { - get - { - return ResourceHelper.GetString("CallMethodActionValidMethodNotFoundExceptionMessage"); - } - } + => Strings.GetString(nameof(CallMethodActionValidMethodNotFoundExceptionMessage)); public static string ChangePropertyActionCannotFindPropertyNameExceptionMessage - { - get - { - return ResourceHelper.GetString("ChangePropertyActionCannotFindPropertyNameExceptionMessage"); - } - } + => Strings.GetString(nameof(ChangePropertyActionCannotFindPropertyNameExceptionMessage)); public static string ChangePropertyActionCannotSetValueExceptionMessage - { - get - { - return ResourceHelper.GetString("ChangePropertyActionCannotSetValueExceptionMessage"); - } - } + => Strings.GetString(nameof(ChangePropertyActionCannotSetValueExceptionMessage)); public static string ChangePropertyActionPropertyIsReadOnlyExceptionMessage - { - get - { - return ResourceHelper.GetString("ChangePropertyActionPropertyIsReadOnlyExceptionMessage"); - } - } + => Strings.GetString(nameof(ChangePropertyActionPropertyIsReadOnlyExceptionMessage)); public static string GoToStateActionTargetHasNoStateGroups - { - get - { - return ResourceHelper.GetString("GoToStateActionTargetHasNoStateGroups"); - } - } + => Strings.GetString(nameof(GoToStateActionTargetHasNoStateGroups)); public static string CannotAttachBehaviorMultipleTimesExceptionMessage - { - get - { - return ResourceHelper.GetString("CannotAttachBehaviorMultipleTimesExceptionMessage"); - } - } + => Strings.GetString(nameof(CannotAttachBehaviorMultipleTimesExceptionMessage)); public static string CannotFindEventNameExceptionMessage - { - get - { - return ResourceHelper.GetString("CannotFindEventNameExceptionMessage"); - } - } + => Strings.GetString(nameof(CannotFindEventNameExceptionMessage)); + + public static string InvalidAssociatedObjectExceptionMessage + => Strings.GetString(nameof(InvalidAssociatedObjectExceptionMessage)); + + public static string NonActionAddedToActionCollectionExceptionMessage + => Strings.GetString(nameof(NonActionAddedToActionCollectionExceptionMessage)); + + public static string NonBehaviorAddedToBehaviorCollectionExceptionMessage + => Strings.GetString(nameof(NonBehaviorAddedToBehaviorCollectionExceptionMessage)); + + public static string DuplicateBehaviorInCollectionExceptionMessage + => Strings.GetString(nameof(DuplicateBehaviorInCollectionExceptionMessage)); public static string InvalidLeftOperand - { - get - { - return ResourceHelper.GetString("InvalidLeftOperand"); - } - } + => Strings.GetString(nameof(InvalidLeftOperand)); public static string InvalidRightOperand - { - get - { - return ResourceHelper.GetString("InvalidRightOperand"); - } - } + => Strings.GetString(nameof(InvalidRightOperand)); public static string InvalidOperands - { - get - { - return ResourceHelper.GetString("InvalidOperands"); - } - } + => Strings.GetString(nameof(InvalidOperands)); } diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/TappedTriggerBehavior.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/TappedTriggerBehavior.cs new file mode 100644 index 00000000..b9bf0ee5 --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/TappedTriggerBehavior.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if WinUI +using Microsoft.UI.Xaml; +#else +using Windows.UI.Xaml; +#endif + +namespace Microsoft.Xaml.Interactivity; + +/// +/// A behavior that listens to an and executes its actions when the UI element gets tapped. +/// +public sealed class TappedTriggerBehavior : EventTriggerBehaviorBase +{ + /// + protected override bool RegisterEventCore(UIElement source) + { + source.Tapped += OnEvent; + return true; + } + + /// + protected override void UnregisterEventCore(UIElement source) + { + source.Tapped -= OnEvent; + } +} diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/UnloadedTriggerBehavior.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/UnloadedTriggerBehavior.cs new file mode 100644 index 00000000..593fd879 --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Core/UnloadedTriggerBehavior.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if WinUI +using Microsoft.UI.Xaml; +#else +using Windows.UI.Xaml; +#endif + +namespace Microsoft.Xaml.Interactivity; + +/// +/// A behavior that listens to an and executes its actions when the element is unloaded. +/// +public sealed class UnloadedTriggerBehavior : EventTriggerBehaviorBase +{ + /// + protected override bool RegisterEventCore(FrameworkElement source) + { + source.Unloaded += OnEvent; + return true; + } + + /// + protected override void UnregisterEventCore(FrameworkElement source) + { + source.Unloaded -= OnEvent; + } +} diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Microsoft.Xaml.Interactivity.Shared.projitems b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Microsoft.Xaml.Interactivity.Shared.projitems index 6cb6dc74..a192356b 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Microsoft.Xaml.Interactivity.Shared.projitems +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Microsoft.Xaml.Interactivity.Shared.projitems @@ -14,17 +14,30 @@ + + + + + + + + + + + + + diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Properties/AssemblyInfo.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Properties/AssemblyInfo.cs index 8c258ea4..3695bee6 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Properties/AssemblyInfo.cs +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/Properties/AssemblyInfo.cs @@ -8,7 +8,7 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -#if !NET8_0_OR_GREATER +#if !NET5_0_OR_GREATER [assembly: AssemblyTitle("Microsoft.Xaml.Interactivity")] [assembly: AssemblyCompany("Microsoft")] [assembly: AssemblyProduct("Microsoft.Xaml.Interactivity")] diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/TriggerOfT.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/TriggerOfT.cs index 90c8ea70..3f321bea 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/TriggerOfT.cs +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Shared/TriggerOfT.cs @@ -41,7 +41,7 @@ protected override void OnAttached() { string actualType = base.AssociatedObject.GetType().FullName; string expectedType = typeof(T).FullName; - string message = string.Format(ResourceHelper.GetString("InvalidAssociatedObjectExceptionMessage"), actualType, expectedType); + string message = string.Format(ResourceHelper.InvalidAssociatedObjectExceptionMessage, actualType, expectedType); throw new InvalidOperationException(message); } } diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Uwp/Core/ResourceHelper.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Uwp/Core/ResourceHelper.cs new file mode 100644 index 00000000..864736e3 --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Uwp/Core/ResourceHelper.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Xaml.Interactivity; + +using Windows.ApplicationModel.Resources; + +internal static partial class ResourceHelper +{ + private static ResourceLoader Strings => ResourceLoader.GetForCurrentView("Microsoft.Xaml.Interactivity/Strings"); +} \ No newline at end of file diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Uwp/Microsoft.Xaml.Interactivity.Uwp.csproj b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Uwp/Microsoft.Xaml.Interactivity.Uwp.csproj index 4e6720c8..8e93a268 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Uwp/Microsoft.Xaml.Interactivity.Uwp.csproj +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.Uwp/Microsoft.Xaml.Interactivity.Uwp.csproj @@ -1,7 +1,6 @@  Library - $(DefineConstants);MODERN_WINDOWS_UWP Microsoft.Xaml.Interactivity net8.0-windows10.0.19041.0 10.0.19041.57 diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.WinUI/Core/ResourceHelper.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.WinUI/Core/ResourceHelper.cs new file mode 100644 index 00000000..fb54f9b7 --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity.WinUI/Core/ResourceHelper.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Xaml.Interactivity; + +using Windows.ApplicationModel.Resources; + +internal static partial class ResourceHelper +{ + private static ResourceLoader Strings { get; } = new(ResourceLoader.GetDefaultResourceFilePath(), "Microsoft.Xaml.Interactivity/Strings"); +} \ No newline at end of file diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity/Core/ResourceHelper.cs b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity/Core/ResourceHelper.cs new file mode 100644 index 00000000..864736e3 --- /dev/null +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity/Core/ResourceHelper.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Xaml.Interactivity; + +using Windows.ApplicationModel.Resources; + +internal static partial class ResourceHelper +{ + private static ResourceLoader Strings => ResourceLoader.GetForCurrentView("Microsoft.Xaml.Interactivity/Strings"); +} \ No newline at end of file diff --git a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity/Microsoft.Xaml.Interactivity.csproj b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity/Microsoft.Xaml.Interactivity.csproj index 761af2dd..de26b6e2 100644 --- a/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity/Microsoft.Xaml.Interactivity.csproj +++ b/src/BehaviorsSDKManaged/Microsoft.Xaml.Interactivity/Microsoft.Xaml.Interactivity.csproj @@ -108,6 +108,7 @@ +