View Issue Details

IDProjectCategoryView StatusLast Update
0002634NoesisGUIUnrealpublic2023-09-26 20:30
ReporterThisIsMyUserName Assigned Tosfernandez  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
Product Version3.2.1 
Target Version3.2.2Fixed in Version3.2.2 
Summary0002634: StoryboardCompletedTrigger is invoked for all items in list
DescriptionWhen Storyboard is played for item in list, the StoryboardCompletedTrigger is invoked for all items in list.

Noesis Forum post - https://www.noesisengine.com/forums/viewtopic.php?t=2976
Steps To ReproduceUsing Noesis 3.2.1 and Unreal Engine

Create a DataTemplate that contains a Storyboard and StoryboardCompletedTrigger.

Create a list of DataTemplate items (at least 2 items)

Play the Storyboard on 1 item

Observe StoryboardCompletedTrigger function being called on every item in the list
TagsC++, Unreal
PlatformAny

Relationships

related to 0002653 resolvedsfernandez ControlStoryboardAction inside ControlTemplates only works for one instance 
related to 0002052 resolvedsfernandez Storyboard Completed event triggered for all instances of a template 

Activities

sfernandez

sfernandez

2023-06-29 12:35

manager   ~0008572

Could you please try the attahed patch, it should fix your problem.
StoryboardCompletedTrigger.patch (8,694 bytes)   
Index: Include/NsApp/ControlStoryboardAction.h
===================================================================
--- Include/NsApp/ControlStoryboardAction.h	(revision 12571)
+++ Include/NsApp/ControlStoryboardAction.h	(working copy)
@@ -14,6 +14,12 @@
 #include <NsApp/StoryboardAction.h>
 
 
+namespace Noesis
+{
+class FrameworkElement;
+class Storyboard;
+}
+
 namespace NoesisApp
 {
 
@@ -68,6 +74,10 @@
     void SetControlStoryboardOption(ControlStoryboardOption option);
     //@}
 
+    /// Returns the element that is used as target to play the storyboard
+    static Noesis::FrameworkElement* FindStoryboardTarget(Noesis::Storyboard* storyboard,
+        Noesis::DependencyObject* associatedObject);
+
 public:
     static const Noesis::DependencyProperty* ControlStoryboardOptionProperty;
 
Index: Include/NsApp/StoryboardCompletedTrigger.h
===================================================================
--- Include/NsApp/StoryboardCompletedTrigger.h	(revision 12571)
+++ Include/NsApp/StoryboardCompletedTrigger.h	(working copy)
@@ -15,6 +15,7 @@
 
 namespace Noesis
 {
+class FrameworkElement;
 struct TimelineEventArgs;
 }
 
@@ -64,6 +65,7 @@
 
     /// From AttachableObject
     //@{
+    void OnAttached() override;
     void OnDetaching() override;
     //@}
 
@@ -75,6 +77,9 @@
 private:
     void OnStoryboardCompleted(Noesis::BaseComponent* sender, const Noesis::TimelineEventArgs& e);
 
+private:
+    Noesis::FrameworkElement* mTarget;
+
     NS_DECLARE_REFLECTION(StoryboardCompletedTrigger, StoryboardTrigger)
 };
 
Index: Src/ControlStoryboardAction.cpp
===================================================================
--- Src/ControlStoryboardAction.cpp	(revision 12571)
+++ Src/ControlStoryboardAction.cpp	(working copy)
@@ -9,6 +9,7 @@
 #include <NsGui/Storyboard.h>
 #include <NsGui/ResourceDictionary.h>
 #include <NsGui/FrameworkElement.h>
+#include <NsGui/FrameworkTemplate.h>
 #include <NsCore/ReflectionImplement.h>
 #include <NsCore/ReflectionImplementEnum.h>
 
@@ -60,6 +61,32 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
+Noesis::FrameworkElement* ControlStoryboardAction::FindStoryboardTarget(
+    Noesis::Storyboard* storyboard, Noesis::DependencyObject* associatedObject)
+{
+    Noesis::IUITreeNode* node = storyboard->GetNodeParent();
+
+    // For Storyboards defined in the Template Resources always use the associated object
+    Noesis::ResourceDictionary* resources = Noesis::DynamicCast<Noesis::ResourceDictionary*>(node);
+    if (resources != 0 &&
+        Noesis::DynamicCast<Noesis::FrameworkTemplate*>(resources->GetNodeParent()) != 0)
+    {
+        return Noesis::FindTreeElement(Noesis::DynamicCast<Noesis::IUITreeNode*>(associatedObject));
+    }
+
+    // Next try with the scope where Storyboard was defined
+    Noesis::FrameworkElement* target = Noesis::FindTreeElement(node);
+    if (target != 0 && target->IsConnectedToView())
+    {
+        return target;
+    }
+
+    // In case Storyboard was defined in Application resources or the Resources of a
+    // template element, we next try with the scope of the associated object
+    return Noesis::FindTreeElement(Noesis::DynamicCast<Noesis::IUITreeNode*>(associatedObject));
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
 void ControlStoryboardAction::Invoke(BaseComponent*)
 {
     Noesis::Storyboard* storyboard = GetStoryboard();
@@ -66,14 +93,7 @@
     DependencyObject* associatedObject = GetAssociatedObject();
     if (associatedObject != 0 && storyboard != 0)
     {
-        // First try with the scope where Storyboard was defined as target
-        Noesis::FrameworkElement* target = FindTreeElement(storyboard->GetNodeParent());
-        if (target == 0 || !target->IsConnectedToView())
-        {
-            // In case Storyboard was defined in Application resources or the Resources of a
-            // template element, we next try with the scope of the associated object as target
-            target = Noesis::FindTreeElement(Noesis::DynamicCast<IUITreeNode*>(associatedObject));
-        }
+        Noesis::FrameworkElement* target = FindStoryboardTarget(storyboard, associatedObject);
 
         // Execute the Storyboard action if we have a valid target connected to the UI tree
         if (target != 0 && target->IsConnectedToView())
@@ -83,7 +103,7 @@
                 case ControlStoryboardOption_Play:
                 {
                     Noesis::IUITreeNode* node = Noesis::DynamicCast<IUITreeNode*>(associatedObject);
-                    Noesis::FrameworkElement* ns = FindTreeElement(node);
+                    Noesis::FrameworkElement* ns = Noesis::FindTreeElement(node);
                     storyboard->Begin(target, ns, true);
                     break;
                 }
@@ -105,7 +125,7 @@
                     else
                     {
                         Noesis::IUITreeNode* node = Noesis::DynamicCast<IUITreeNode*>(associatedObject);
-                        Noesis::FrameworkElement* ns = FindTreeElement(node);
+                        Noesis::FrameworkElement* ns = Noesis::FindTreeElement(node);
                         storyboard->Begin(target, ns, true);
                     }
                     break;
Index: Src/StoryboardCompletedTrigger.cpp
===================================================================
--- Src/StoryboardCompletedTrigger.cpp	(revision 12571)
+++ Src/StoryboardCompletedTrigger.cpp	(working copy)
@@ -5,7 +5,9 @@
 
 
 #include <NsApp/StoryboardCompletedTrigger.h>
+#include <NsApp/ControlStoryboardAction.h>
 #include <NsGui/Storyboard.h>
+#include <NsGui/FrameworkElement.h>
 #include <NsGui/ResourceDictionary.h>
 #include <NsCore/ReflectionImplementEmpty.h>
 
@@ -14,7 +16,7 @@
 
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
-StoryboardCompletedTrigger::StoryboardCompletedTrigger()
+StoryboardCompletedTrigger::StoryboardCompletedTrigger(): mTarget(0)
 {
 }
 
@@ -57,6 +59,17 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
+void StoryboardCompletedTrigger::OnAttached()
+{
+    Noesis::Storyboard* storyboard = GetStoryboard();
+    if (storyboard != 0)
+    {
+        Noesis::DependencyObject* associatedObject = GetAssociatedObject();
+        mTarget = ControlStoryboardAction::FindStoryboardTarget(storyboard, associatedObject);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
 void StoryboardCompletedTrigger::OnDetaching()
 {
     Noesis::Storyboard* storyboard = GetStoryboard();
@@ -64,11 +77,14 @@
     {
         storyboard->Completed() -= MakeDelegate(this,
             &StoryboardCompletedTrigger::OnStoryboardCompleted);
+
+        mTarget = 0;
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
-void StoryboardCompletedTrigger::OnStoryboardChanged(const Noesis::DependencyPropertyChangedEventArgs& e)
+void StoryboardCompletedTrigger::OnStoryboardChanged(
+    const Noesis::DependencyPropertyChangedEventArgs& e)
 {
     Noesis::Storyboard* oldStoryboard = e.OldValue<Noesis::Ptr<Noesis::Storyboard>>();
     Noesis::Storyboard* newStoryboard = e.NewValue<Noesis::Ptr<Noesis::Storyboard>>();
@@ -77,18 +93,27 @@
     {
         oldStoryboard->Completed() -= MakeDelegate(this,
             &StoryboardCompletedTrigger::OnStoryboardCompleted);
+
+        mTarget = 0;
     }
     if (newStoryboard != 0)
     {
         newStoryboard->Completed() += MakeDelegate(this,
             &StoryboardCompletedTrigger::OnStoryboardCompleted);
+
+        Noesis::DependencyObject* associatedObject = GetAssociatedObject();
+        mTarget = ControlStoryboardAction::FindStoryboardTarget(newStoryboard, associatedObject);
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
-void StoryboardCompletedTrigger::OnStoryboardCompleted(BaseComponent*, const Noesis::TimelineEventArgs&)
+void StoryboardCompletedTrigger::OnStoryboardCompleted(BaseComponent*,
+    const Noesis::TimelineEventArgs& e)
 {
-    InvokeActions(0);
+    if (e.target == mTarget)
+    {
+        InvokeActions(0);
+    }
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////

Issue History

Date Modified Username Field Change
2023-06-28 12:43 ThisIsMyUserName New Issue
2023-06-28 12:43 ThisIsMyUserName Tag Attached: C++
2023-06-28 12:43 ThisIsMyUserName Tag Attached: Unreal
2023-06-28 12:48 sfernandez Assigned To => sfernandez
2023-06-28 12:48 sfernandez Status new => assigned
2023-06-28 12:48 sfernandez Product Version 3.2 => 3.2.1
2023-06-28 12:48 sfernandez Target Version => 3.2.2
2023-06-29 12:35 sfernandez Note Added: 0008572
2023-06-29 12:35 sfernandez File Added: StoryboardCompletedTrigger.patch
2023-06-29 12:35 sfernandez Status assigned => feedback
2023-07-24 13:00 sfernandez Relationship added related to 0002653
2023-07-24 13:01 sfernandez Relationship added related to 0002052
2023-09-26 20:30 sfernandez Status feedback => resolved
2023-09-26 20:30 sfernandez Resolution open => fixed
2023-09-26 20:30 sfernandez Fixed in Version => 3.2.2