locked
Tapped event gone? (Canvas on Canvas) RRS feed

  • Question

  • Hi,

    I ran into a problem, where putting a temporary canvas on top of another canvas blocked the tapped event from firing somehow (pointer pressed and pointer released still fires).

    Here's xaml and C# for a MainPage that will expose the problem (my app was called App2 by default). Any insight into why this is happening would be appreciated.

    MainPage.xaml

    <Page
        x:Class="App2.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:App2"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Canvas x:Name="BGCanvas" Width="200" Height="200" Background="Blue">
            </Canvas>
        </Grid>
    </Page>

    MainPage.xaml.cs

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    
    // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
    
    namespace App2
    {
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
                BGCanvas.PointerPressed += BGCanvas_PointerPressed;
                BGCanvas.PointerReleased += BGCanvas_PointerReleased;
                BGCanvas.Tapped += BGCanvas_Tapped;
            }
    
            void BGCanvas_Tapped(object sender, TappedRoutedEventArgs e)
            {
                Debug.WriteLine("Tapped");
                BGCanvas.Children.Clear();
            }
    
            void BGCanvas_PointerReleased(object sender, PointerRoutedEventArgs e)
            {
                Debug.WriteLine("Released");
            }
    
            void BGCanvas_PointerPressed(object sender, PointerRoutedEventArgs e)
            {
                Debug.WriteLine("Pressed");
                Canvas C2 = new Canvas();
                C2.Width = 100;
                C2.Height = 100;
                C2.Background = new SolidColorBrush(Windows.UI.Colors.Green);
                BGCanvas.Children.Add(C2);
            }
    
            /// <summary>
            /// Invoked when this page is about to be displayed in a Frame.
            /// </summary>
            /// <param name="e">Event data that describes how this page was reached.  The Parameter
            /// property is typically used to configure the page.</param>
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
            }
        }
    }

    Edit: To clarify, in order to reproduce the problem, click on the blue canvas without making the tapped event fire (click and drag). Then try tapping on the green rectangle.
    • Edited by T Hofmann Friday, October 12, 2012 4:19 PM
    Friday, October 12, 2012 4:18 PM

Answers

  • Tapped event definitely is routed, but you've got a sneaky bug in your code.  Every time you see another PointerPressed event on BGCanvas... which you do every time you start a tap... you add ANOTHER new child canvas to the BGCanvas.   Since they weren't in the tree when the PointerPressed event is fired, they don't see the pair of events thus don't emit a Tapped event, thus don't bubble up said Tapped event.

    Use a Boolean value to determine whether you want to add a child Canvas in the pointer Pressed event, and reset this value when you see the Tapped event (or other time). 

    Hope this helps,

    Matt


    XAML SDET Lead : Input / DirectManipulation / Accessibility

    Tuesday, October 16, 2012 7:11 PM

All replies

  • Sorry but i didn´t understand your problem.

    This

         BGCanvas.Children.Clear();

    is done in Tapped event, why? maybe you want do this in PointerReleased, not?

    Why you add the canvas inside the another canvas with other color?

    Canvas class

    Tapped Occurs when an otherwise unhandled Tap interaction occurs over the hit test area of this element. (Inherited from UIElement)

    PointerPressed Occurs when the pointer device initiates a Press action within this element. (Inherited from UIElement)
    PointerReleased Occurs when the pointer device that previously initiated a Press action is released, while within this element. (Inherited from UIElement)





    Sara Silva

    Friday, October 12, 2012 11:12 PM
  • again, i don´t know what you want. you need to be clear and explain that you really want and what is wrong. :/  you want the tapped event raise and it's not working???

    Because:

    Tapped Occurs when an otherwise unhandled Tap interaction occurs over the hit test area of this element. (Inherited from UIElement)


    Sara Silva

    Friday, October 12, 2012 11:33 PM
  • Possibly this adaptation helps to achieve the wanted effect; it keeps the tapped events to be reported thru the base Canvas as per Sara's explanation:

            void BGCanvas_PointerPressed(object sender, PointerRoutedEventArgs e)
            {
                Debug.WriteLine("Pressed");
                Canvas C2 = new Canvas();
                C2.Width = 100;
                C2.Height = 100;
                C2.Background = new SolidColorBrush(Windows.UI.Colors.Green);
                C2.IsHitTestVisible = false;
                BGCanvas.Children.Add(C2);
            }
    

    Saturday, October 13, 2012 9:07 AM
  • Hi,
    I tried many things and it worked for me when I added only one canvas you can make your logic code as canvas child appear or disappear 
    here is my code
    public MainPage()
            {
                this.InitializeComponent();
                BGCanvas.PointerPressed += BGCanvas_PointerPressed;
                BGCanvas.PointerReleased += BGCanvas_PointerReleased;
                BGCanvas.Tapped += BGCanvas_Tapped;
                
            }
    
            void BGCanvas_Tapped(object sender, TappedRoutedEventArgs e)
            {
                Debug.WriteLine("Tapped");
                BGCanvas.Children.Clear();
                FrameworkElement feSource = e.OriginalSource as FrameworkElement;
                switch (feSource.Name)
                {
                    case "BGCanvas":
                        Debug.WriteLine("BGCanvas");
                        break;
                    case "AddedCanvas":
                        Debug.WriteLine("AddedCanvas");
                        break;
                    case "CancelButton":
                        // do something ... 
                        break;
                }
                
            }
    
            void BGCanvas_PointerReleased(object sender, PointerRoutedEventArgs e)
            {
                Debug.WriteLine("Released");
                FrameworkElement feSource = e.OriginalSource as FrameworkElement;
                switch (feSource.Name)
                {
                    case "BGCanvas":
                        Debug.WriteLine("BGCanvasR");
                        break;
                    case "AddedCanvas":
                        Debug.WriteLine("AddedCanvasR");
                        break;
                    case "CancelButton":
                        // do something ... 
                        break;
                }
            }
    
            bool flag = true;
    
            void BGCanvas_PointerPressed(object sender, PointerRoutedEventArgs e)
            {
                if (flag)
                {
                    //  Debug.WriteLine("Pressed");
                    Canvas C2 = new Canvas();
                    C2.Name = "AddedCanvas";
                    C2.Width = 100;
                    C2.Height = 100;
                    C2.Background = new SolidColorBrush(Windows.UI.Colors.Green);
                    BGCanvas.Children.Add(C2);
                    flag = false;
                }
            }
    But take care of something I gave C2 a name if you want to make it dynamic remove the name
    Regards,



    Ibraheem Osama Mohamed | My Blog | @IbraheemOsamaMo

    (If my reply answers your question, please propose it as an answer)


    Saturday, October 13, 2012 9:27 AM
  • Thanks for the answers everyone. Though, I'm not asking for help in getting the canvas to disappear on Tap. 

    I'm saying that (at least on my machine) when you follow my instructions, you will be able to tap the green rectangle and you will not receive a tapped event.

    I simply want to know why this is. Is it a bug? Is it intended? Can I expect it on other occasions?

    Monday, October 15, 2012 11:40 PM
  • again, i don´t know what you want. you need to be clear and explain that you really want and what is wrong. :/  you want the tapped event raise and it's not working???

    Because:

    Tapped Occurs when an otherwise unhandled Tap interaction occurs over the hit test area of this element. (Inherited from UIElement)


    Sara Silva

    Yes, that is my problem. I want the tapped event to raise. What does unhandled tap interaction mean then? What about my code makes it fire when I tap on the blue rectangle (even if the green one appears where I tap) but not if you tap the green rectangle?
    Monday, October 15, 2012 11:45 PM
  • Possibly this adaptation helps to achieve the wanted effect; it keeps the tapped events to be reported thru the base Canvas as per Sara's explanation:

            void BGCanvas_PointerPressed(object sender, PointerRoutedEventArgs e)
            {
                Debug.WriteLine("Pressed");
                Canvas C2 = new Canvas();
                C2.Width = 100;
                C2.Height = 100;
                C2.Background = new SolidColorBrush(Windows.UI.Colors.Green);
                C2.IsHitTestVisible = false;
                BGCanvas.Children.Add(C2);
            }

    While this accomplishes the wanted behaviour, could you explain why it is necessary?

    Shouldn't a Tapped event be routed?

    Tuesday, October 16, 2012 5:11 PM
  • Tapped event definitely is routed, but you've got a sneaky bug in your code.  Every time you see another PointerPressed event on BGCanvas... which you do every time you start a tap... you add ANOTHER new child canvas to the BGCanvas.   Since they weren't in the tree when the PointerPressed event is fired, they don't see the pair of events thus don't emit a Tapped event, thus don't bubble up said Tapped event.

    Use a Boolean value to determine whether you want to add a child Canvas in the pointer Pressed event, and reset this value when you see the Tapped event (or other time). 

    Hope this helps,

    Matt


    XAML SDET Lead : Input / DirectManipulation / Accessibility

    Tuesday, October 16, 2012 7:11 PM
  • Thanks for the answer Matt.

    So to clarify, if and only if one of the trees is a sub tree of the other, the pressed + released events will be paired?

    Wednesday, October 17, 2012 4:36 PM
  • I'm not sure I understand what you're saying but to reiterate:

    1) A particular UIElement must see both PointerPressed and PointerReleased , within a certain timeframe, to fire Tapped.

    2) If a new UIElement is added to the tree after a PointerPressed event is fired, (say, in its handler) it will not receive this event thus not qualify for rule #1.  This is the bug from your sample code.

    3) If the Tapped event is unhandled at a specific layer, the UIElement's parent will be given a chance to handle the event.  This repeats until the root element is given the chance to handle the event or some element in between sets Handled=True during a Tapped event handler.  (Events Route upwards).

    -Matt


    XAML SDET Lead : Input / DirectManipulation / Accessibility


    Thursday, October 18, 2012 4:43 PM