Getting Started with Adobe After Effects - Part 6: Motion Blur
First Time? You can support us by signing up. It takes only 5 seconds. Click here to sign up. If you already have an account, click here to login.
Loading

1st Prize - Apple iPad


DOTNET Quiz 2011 - Simulating tunneling for mouse events with Silverlight

  • In Silverlight when multiple UIElements are stacked on top of each other only the top most UIElement can receive mouse based events (mouse over, mouse down, etc.) as it blocks the UIElements under it. What is the best workaround for notifying the UIElements under the mouse about the event, regardless of if they are at the top or bottom of the UIElement stack?

    Posted on 01-05-2011 00:00 |
    Roger Peters
    909 · 0% · 30

7  Answers  

Subscribe to Notifications
  • Score
    2

    It can be done by adding a handler to following events:

    UIElement.KeyUpEvent
    UIElement.KeyDownEvent  
    UIElement.MouseLeftButtonDownEvent
    UIElement.MouseLeftButtonUpEvent
    UIElement.MouseWheelEvent
    UIElement.TextInputEvent
    UIElement.TextInputStartEvent
    UIElement.TextInputUpdateEvent
    

    Using following code:

    // set last argument to true if want to handle `handled` events
    AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown), false);
    

    On an element.

    For example for following handler:

    private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine(String.Format("Sender: {0}, OriginalSource: {1}", sender, e.OriginalSource));
    }
    

    sender will be the element down below and e.OriginalSource will be the actual element that was directly under mouse.

    [NOTE: This example was created in Silverlight 4]

    Replied on Jan 5 2011 7:47AM  . 
    decyclone
    1369 · 0% · 15
  • Score
    2

    Silverlight events are CLR events, and therefore are events can be handled using managed code. It is same as handling basic CLR events, we need to attach event handlers

    XAML Code

    <Grid x:Name="LayoutRoot" Loaded="LayoutRoot_Loaded">
      <StackPanel>
        <TextBlock Name="textBlock1">Put the mouse over this text</TextBlock>
    ...
      </StackPanel>
    </Grid>
    

    VB.NET

    Private Sub LayoutRoot_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs e)
        AddHandler textBlock1.MouseEnter, AddressOf textBlocks_MouseEnter
        AddHandler textBlock1.MouseLeave, AddressOf textBlocks_MouseLeave
    End Sub
    

    Handling Events

    Sub textBlock1_MouseEnter(ByVal sender As Object, ByVal e As MouseEventArgs) Handles textBlock1.MouseEnter
    '....
    End Sub
    Sub textBlock1_MouseLeave(ByVal sender As Object, ByVal e As MouseEventArgs) Handles textBlock1.MouseLeave
    '....
    End Sub
    

    Silverlight event-handler functions cannot be called with parameter values (even empty ones), considerable difference from event handler syntax in the HTML DOM. Visual Studio and its XAML design surface generally promote the instance-handling technique instead of the Handles keyword. This is because establishing the event handler wiring in XAML is part of typical designer-developer workflow for Silverlight and WPF, and the Handles keyword technique is incompatible with wiring the event handlers in XAML.

    C#.NET

    In C#, the syntax is to use the += operator. You instantiate the handler by declaring a new delegate that uses the event handler method name.

    void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
    {
        textBlock1.MouseEnter += new MouseEventHandler(textBlocks_MouseEnter);
        textBlock1.MouseLeave += new MouseEventHandler(textBlocks_MouseLeave);
    }
    

    To answer your original question ,here is the solution

    private void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var elements = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(this.LayoutRoot), this.LayoutRoot);
        foreach (var element in elements) {
            OnMouseButton(element, e);
        }
    }
    
    private void OnMouseButton(object sender, MouseButtonEventArgs e)
    {
    }
    
    Replied on Jan 5 2011 8:32AM  . 
    Vamshi
    131 · 1% · 376
  • Score
    6

    So far these answers only show how to attach event handlers - please read the question more carefully. The question is how to have multiple UIElements stacked on top of each other, and allow a click on the top most element notify not just that element but all elements under the mouse cursor.

    To make sure I am clear, here is XAML for 4 rectangles stacked on top of each other. If you add event handlers as suggested in previous answers, only the top most red rectangle would receive the clicked event - the correct answer must explain how to notify all 4 rectangles of the click so that the click event happens for all 4 rectangles, not just the top red one.

    <Rectangle x:Name="rect1" Fill="Pink" />
    <Rectangle x:Name="rect2" Fill="Green" />
    <Rectangle x:Name="rect3" Fill="Blue" />
    <Rectangle x:Name="rect4" Fill="Red" />
    
    Replied on Jan 5 2011 8:56AM  . 
    Roger Peters
    909 · 0% · 30
  • Score
    10

    Handle the mouse events in the panel containing the rectangles as you would normally do and then use VisualTreeHelper.FindElementsInHostCoordinates to figure out which elements are available at the current mouse position.

    Something like this:

    <Grid x:Name="LayoutRoot" Background="White" MouseLeftButtonDown="LayoutRoot_MouseLeftButtonDown">
        <Rectangle x:Name="rect1" Fill="Pink" />
        <Rectangle x:Name="rect2" Fill="Green" />
        <Rectangle x:Name="rect3" Fill="Blue" />
        <Rectangle x:Name="rect4" Fill="Red" />
    </Grid>
    

    and the code behind:

    private void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var elements = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(this.LayoutRoot), this.LayoutRoot);
        foreach (var element in elements) {
            OnMouseLeftButtonDown(element, e);
        }
    }
    
    private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
    }
    
    Replied on Jan 5 2011 11:24AM  . 
    Koen Zwikstra
    2270 · 0% · 5
  • Score
    4

    Decided to post anyway despite Koen's solution :-) I was racing to finish it so decided I wouldn't let my efforts be in vain. The main difference is I set the click handler on the rectangle itself as stated in the question rather than the layout root.

        private void Rect4MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
    
            var elements = VisualTreeHelper.FindElementsInHostCoordinates(new Rect(0,0,100,100), LayoutRoot);
            foreach (var uiElement in elements)
            {
                var rec = uiElement as Rectangle;
                if (rec != null)
                {
    
                    rec.MouseLeftButtonDown += new MouseButtonEventHandler(rec_MouseLeftButtonDown);
                    rec_MouseLeftButtonDown(rec, e);
    
                }
            }
    
        }
    
        void rec_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var rectange = sender as Rectangle;
            var color = (rectange.Fill as SolidColorBrush).Color;
            var prompt = string.Format("The rectangle with hex value {0} has recieved the event", color);
            MessageBox.Show(prompt);
        }
    

    Replied on Jan 5 2011 11:52AM  . 
    Elexis
    1823 · 0% · 10
  • Score
    0

    double post

    Replied on Jan 5 2011 11:53AM  . 
    Elexis
    1823 · 0% · 10
  • Score
    9

    I believe the solution for this question is to add a common event handler to elements (e.g. MouseLeftButtonDown in the code below). Then using FindElementsInHostCoordinates method iterate through all elements that exist at the mouse event point. FindElementsInHostCoordinates provides UIElements in the order from top to bottom so it makes it perfect tool to implement tunneling. Implementing common event handler to the object that raises the event will allow us to implement tunneling even in rare cases when we need it for events like MouseEnter or MouseLeave (as these events are not passed to any other object except where they were raised). It will also provide better control on what elements should be used in tunneling. So, the code for the solution will be:

    XAML

    <Rectangle x:Name="rect1" Fill="Pink" MouseLeftButtonDown="Common_OnMouseLeftButtonDown" /> 
    <Rectangle x:Name="rect2" Fill="Green" MouseLeftButtonDown="Common_OnMouseLeftButtonDown" /> 
    <Rectangle x:Name="rect3" Fill="Blue" MouseLeftButtonDown="Common_OnMouseLeftButtonDown" /> 
    <Rectangle x:Name="rect4" Fill="Red" MouseLeftButtonDown="Common_OnMouseLeftButtonDown" />
    

    XAML.CS

    private void Common_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
    {
    var elements = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(LayoutRoot), LayoutRoot); 
    
    foreach (var element in elements) 
    { 
    // Process MouseLeftButtonDown events here 
    } 
    }
    
    Replied on Mar 5 2011 1:38AM  . 
    Dmitry Kharlap (aka Docker)
    148 · 1% · 325

Your Answer


Sign Up or Login to post an answer.
Please note that your answer will not be considered as part of the contest because the competition is over. You can still post an answer to share your knowledge with the community.

Copyright © Rivera Informatic Private Ltd.