0

I have a a custom Panel that raises a RoutedEvent which is defined globally in a static class :

 public class CompressItemsToFitStackPanel : StackPanel
 {
    protected override Size ArrangeOverride(Size arrangeSize)
    {        
        // some logic 
        // Raise Attached Event     
        CustomEventManager.RaiseArrangeEvent(this);
        return base.ArrangeOverride(arrangeSize); 
    }         
 }

My attached Event:

 public static class CustomEventManager
{                        
    public static readonly RoutedEvent ArrangeEvent = EventManager.RegisterRoutedEvent("Arrange",
                                RoutingStrategy.Bubble,
                                typeof(RoutedEventHandler),
                                typeof(CustomEventManager));

    internal static void RaiseArrangeEvent(UIElement target)
    {
        var args = new RoutedEventArgs(ArrangeEvent);
        target.RaiseEvent(args);            
    }       
} 

this Panel is the items panel for an Items control , the ItemsTemplate as an EventTrigger which i wan't to fire when the attached event is raised:

  <DataTemplate DataType="{x:Type local:Checker}" x:Key="CheckerTempalte">

    <Ellipse x:Name="ellipse" Style="{StaticResource checkerStyle}">
        <Ellipse.Triggers>
            <EventTrigger RoutedEvent="local:CustomEventManager.Arrange">
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimationUsingKeyFrames 
                                    Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" 
                                    Storyboard.TargetName="ellipse"
                                >
                            <EasingDoubleKeyFrame KeyTime="0:0:0" Value="0" />
                            <EasingDoubleKeyFrame KeyTime="0:0:1" Value="{Binding Val}" />
                        </DoubleAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>                    
        </Ellipse.Triggers>        
    </Ellipse>

The Event trigger is not triggered ,

Maybe i'm not using AttachedEvents correctly or it is not Declared correctly i need the event from the panel to propagate and trigger the EventTriggers in the Child elements ,

any ideas what i'm doing wrong ?

EDIT :

After dkozl's insight i came to the conclusion that i need an AddXXXHandler and RemoveXXXHandler in order for the XAML to add/ remove the handler for the EventTrigger

    public static void AddArrangeHandler(DependencyObject d, RoutedEventHandler handler)
    {
        UIElement uie = d as UIElement;
        if (uie != null)
        {
            uie.AddHandler(CustomEventManager.ArrangeEvent, handler);
        }
    }

    public static void RemoveArrangeHandler(DependencyObject d, RoutedEventHandler handler)
    {
        UIElement uie = d as UIElement;
        if (uie != null)
        {
            uie.RemoveHandler(CustomEventManager.ArrangeEvent, handler);
        }
    } 

but still nothing happens , and i never reach these methods .

EDIT 2 :

thanks to dkozl's comments below ,

the Event is raised for each child element , since he the ellipses are down the Visual Tree .

  protected override Size ArrangeOverride(Size arrangeSize)
  {                    
      foreach (UIElement child in children)
      {                        
           CustomEventManager.RaiseArrangeEvent(child);                                            
      } 
  }

but still nothing happens , iv'e also tested the 'StoryBoard' by giving the 'EventTrigger' the 'MouseEnter' event and moving my mouse over the element , it works fine.

still , even raising the event on each Ellipse still does not work ... any ideas ?

Another point of interest is that the child elements are actually the 'Checker' type and not the Ellipse which the DataTemplate represents , i don't get how 'Checker' is considered a UIElement .?

eran otzap
  • 12,293
  • 20
  • 84
  • 139

2 Answers2

1

What you've created is standard RoutedEvent, not an attached one. Add/Remove handler is different for attached events. You'll need two separate methods (AddArrangeChildrenHandler and RemoveArrangeChildrenHandler). It has been explained on MSDN site

UPDATE: I've copied updated Ellipse definition and CustomEventManager class into my application, added test button which calls CustomEventManager.RaiseArrangeEvent(ellipse); when clicked and it works for me.

I also had to add Ellipse.RenderTransorm of TransformGroup, fourth transformation being TranslateTransform to make it work like in the example

UPDATE2: Event is raised on the panel where the Ellipse is placed which means that bubbling event would never reach it as it will start from the panel and go up the visual tree to Window never reaching children of the panel

dkozl
  • 32,814
  • 8
  • 87
  • 89
  • yes i see that now , i'll update the question , because even when creating it with an attached event it still does not trigger the EventTrigger , The EventTrigger is in fact the Handler in this case . – eran otzap May 18 '13 at 15:17
  • i Edited the question so the Attached Event is properly Declared . – eran otzap May 18 '13 at 15:22
  • I assume that your CustomEventManager implements both static methods `public static void AddArrangeHandler(DependencyObject d, RoutedEventHandler handler) {...}` and `public static void RemoveArrangeHandler(DependencyObject d, RoutedEventHandler handler) {...}` – dkozl May 18 '13 at 15:40
  • i don't understand why i need the handler here , i don't have an handler in the stack panel , i wan't the EventTrigger in each panel item to handle the event , i assume the EventTrigger are translated into RoutedEventHandlers behind the scenes . – eran otzap May 18 '13 at 15:50
  • Your `CustomEventManager` class is not handling events. It's just for attaching and removing handlers to UIElements, in your case Ellipse, which normally don't support such events. Basically `AddArrangeHandler(...)` does `(UIElement)d.AddHandler(...)` and `RemoveArrangeHandler()` does the opposite. It's still handled by this `UIElement` object. – dkozl May 18 '13 at 15:57
  • i understand that , but isn't the EventTrigger for that Event i added declarativlly acts as the handler for each ellipse ? – eran otzap May 18 '13 at 16:01
  • EventTrigger needs this `CustomEventManager.AddArrangeHandler(...)` to add event handler to Ellipse element and when it's raised it will run your StoryBoard – dkozl May 18 '13 at 16:08
  • o'k after further reading i just came to the same conclusion , thanks , i'll had the handlers and try again . – eran otzap May 18 '13 at 16:10
  • i edited the question , it still does not work , and i never reach the Add/Remove Handler methods. – eran otzap May 18 '13 at 16:26
  • I've run your code and it works fine when I raise it directly on the `Ellipse`. Just for the test I've changed `Storyboard.TargetProperty="Width"` and it made me wonder. Which control raises the event? Bubbling event goes from control up to a `Window` so if it's raised on a container it will never reach your `Ellipse` – dkozl May 18 '13 at 17:27
  • what do you mean by directly on the Ellipse the event trigger is defined as Ellipse's Trigger ? do you mean raise it from the Ellipse and not from the panel CustomEventManager.RaiseArrangeEvent(child) ; ? – eran otzap May 18 '13 at 17:29
  • I mean that for test I added a button which calls `Events.CustomEventManager.RaiseArrangeEvent(ellipse);` when clicked. I ask what is the target, when you raise the event, in your case because if it's something higher in visual tree then the `Ellipse` it will not receive as it will go up to `Window` as bubbling events do. – dkozl May 18 '13 at 17:36
  • the Target is the Ellipse itself – eran otzap May 18 '13 at 17:49
  • Then it should work. I copied your code and it works for me. The only thing I changed was `EasingDoubleKeyFrame` values and `Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"` as it was throwing an exception in my case – dkozl May 18 '13 at 17:54
  • would you mind adding what you did to your answer ? because i still haven't gotten it to work i raise the event for each ellipse as to what i understand you did ... i even tried it on Width property but still nothing .. – eran otzap May 18 '13 at 18:03
  • added `Ellipse.RenderTransform` of `TransformGroup`, fourth transformation being `TranslateTransform` and now it works like specified in your example – dkozl May 18 '13 at 18:08
  • Going back to visual tree. In your example you raise the event on the panel `CustomEventManager.RaiseArrangeEvent(this);` which means it will go from your panel up the visual tree to `Window`. Is the `Ellipse` on the panel? If so bubbling event will not reach it – dkozl May 18 '13 at 18:29
  • o'k your right i should Raise the event on each child element , – eran otzap May 18 '13 at 18:42
0

You should maybe consider a code only approach to this problem. If you wish to define the animations in XAML you can possibly StaticResource them into properties on the CompressToFitStackPanel. But if your DataTemplate is referring to a specific transform by index you are definitely losing the power and abstraction of DataTemplate'ing.

Reevaluate your problem space and what the design is supposed to solve. Use ILSpy or Reflector to analyze how other Panels solved problems with properties or Attached Dependency Properties (Grid.Column, Grid.Row).

Place more of the burden on the new layout panel and less on those who use it.

Good luck.

cbuteau
  • 736
  • 1
  • 7
  • 15