locked
Determine if user has clicked on 'control' RRS feed

  • Question

  • Hi,

    In order to override some behaviour, I need to subscribe to the preview mouse down event and then do hit testing to determine what was clicked.  For the most part I have this working.  However, I'd like to reliably know when the user has clicked on any part of a control (like text box, combo box, scrollbar, etc) and handle clicks within a control differently than when the use clicks on a 'background' part of a region.

    I'm playing with something like this:

    private bool ClickedOnControlPart(RoutedEventArgs e)
            {
                bool clickedOnControlPart = false;

                if (e != null && !(e.OriginalSource is Adorner))
                {
                    Visual visual = e.OriginalSource as Visual;
                    while (visual != null && !clickedOnControlPart)
                    {
                        if (visual.GetType().Namespace == "System.Windows.Controls.Primitives" ||
                            visual.GetType().BaseType.Namespace == "System.Windows.Controls.Primitives")
                            clickedOnControlPart = true;
                        visual = VisualTreeHelper.GetParent(visual) as Visual;
                    }
                }
                return clickedOnControlPart;
            }

    is there a better way to determine when the user has clicked on any given control?

    Thanks,

    Notre

    Monday, December 13, 2010 8:29 PM

Answers

  • Hi Notre,

    Thanks for your reply.

    I might want to use

    visual is Control
    

    to determine whether the current element is a Control, since the search in visual tree will eventually loop to a Control if it's a part of control.

    If you have a different defination for what is a Control, would you please kindly elaborate it?

    Thank you and have a nice day!


    Best regards,
    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Notre Friday, December 17, 2010 5:36 PM
    Friday, December 17, 2010 2:28 AM

All replies

  • I believe your solution works (Without testing it), but you may want to know that the WPF routed event support allows you to see all events based on a heirarchy.  As you've already discovered the E.OriginalSource tells you the control.  If you mark this a E.Handled = false, that same event will bubble up in the Visual tree.  You can repeat this until you're done.   By doing it that way you don't have to "Walk the visual" tree using relflection techniques.


    Javaman
    Monday, December 13, 2010 11:31 PM
  • Hi Notre,

    Since you are performing a hit test to determine the clicked control, I think you can use VisualTreeHelper.HitTest Method (Visual, HitTestFilterCallback, HitTestResultCallback, HitTestParameters) method instead of VisualTreeHelper.HitTest Method (Visual, Point) method and leverage HitTestResultCallBack and/or HitTestFilterCallback to determine the exact control that is clicked.

    You can use HitTestFilterCallBack to define the valid candidates for the hit test. And use HitTestResultCallBack to collect all the elements that get hit in visual tree. Please use the documentations linked above for more information.

    Hope this helps.

    If you still have any doubts or concerns about this issue, please feel free to let me know.


    Best regards,
    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, December 14, 2010 6:43 AM
  • Hi Javaman,

    Thanks for your comments.  I'm not sure I fully understand them, so I wanted to get some clarification.

    Are you saying that events bubble up from the original source until they are handled?  If that is what you're saying, I realize that, as I'm using the preview mouse down event to capture all bubbled mouse down events.  The problem I'm having is trying to determine what conceptual UI element was clicked (e.g. a button), given that the OriginalSource just tells me the WPF element that was clicked (e.g. a Border), which may only be a part of a conceptual control (e.g. a button), and might be several levels down in the visual tree.

    Am I missing something in my understanding?

    Thanks,

    Notre

    Tuesday, December 14, 2010 7:24 PM
  • Hi Min,

    Thank you for your reply.  I'm not sure if I expressed my problem very clearly to begin with, so I'll try to clarify.

    When doing hit testing, either the way I'm currently doing it or using VisualTreHelper.HitTest method, the main problem I'm trying to solve is to determine when the user has conceptually clicked on a control or any part of a control vs. something that is not a control. 

    For example, I want to know whether the user has clicked on a part of a button or part of a scrollbar - that would be considered clicking on a control.  However, if the user clicked directly on a Canvas, I would consider the user to have not clicked on a control.  

    The problem I'm having is trying to determine what conceptual UI element was clicked (e.g. a button), given that the OriginalSource just tells me the WPF element that was clicked (e.g. a Border), which may only be a part of a conceptual control (e.g. a button), and might be several levels down in the visual tree.  I also don't know the set of controls that might appear on the canvas, so I can't test just for e.g. a Button control.

    Thanks,

    Notre

    Tuesday, December 14, 2010 7:33 PM
  • Hi Notre,

    I think this implementation has a code smell of "God Method" antipattern. As Javaman has suggested, you can just use bubbling based routed events (Click) for each type of control that you have. Just set Handled as true in the event handlers.

    Add another bubbling based routed event for the control. The event handler would be used only when it has not been handled by any other control.

    Note: Preview based events use tunneling strategy so it would not be bubbled up rather it would be tunneled down. You can use regular Click event. If it is not required for sure [the use of MouseDown event], I would suggest using Click event instead of MouseDown event.

    Thanks,
    Muhammad
    shujaatsiddiqi.blogspot.com


    Muhammad Siddiqi
    Tuesday, December 14, 2010 7:49 PM
  • Hi Muhammad,

    I have to confess I'd never heard of the 'God Method' antipattern and I had to look it up. I agree that this is generally not a great strategy.  The reason I'm using this approach (and I think  am using it) is that I need to change the behaviour of code that is not mine.  Specifically,  I need to change some behaviour related to the MS workflow designer.  I need some low level control (well, using the tunneling preview mouse down event), because I need to completely prevent the out-of-the-box MS workflow designer behaviour, in some cases.  That is also the reason for the hit testing strategy that I'm using...

    Thanks for the correction on tunneling vs. bubbling - you're right of course.

    Thanks,

    Notre

    Tuesday, December 14, 2010 8:23 PM
  • Hi Notre,

    Thanks for your reply.

    I understand that you want to know whether the user has clicked on a control, and since the HisTest will return the element that directly hit, you look through the visual tree to determine if this element is a part of control or not.

    My point is the HitTest method itself will walk through the VisualTree during the hit test process. So we can levelage HitTestResultCallBack to accomplish the same task to avoid walking through the visual tree again. It will be more efficient and convenient.

    Moreover, it's even better to filter out the child element of the controls via HitTestFilterCallBack before the hit test starts. Then we just need to check the directly hit element in the HitTestResultCallBack and no need to continue to search the visual tree anymore.

    Please check out the links in my last reply for details, and if you have any doubts or concerns about this issue, please feel free to let me know.


    Best regards,
    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, December 16, 2010 9:30 AM
  • Hi Min,

    Thanks again for your reply.  I did take a look at the links, and I can see how it would be more efficient to use the HitTestResultCallBack to examine the elements hit, rather than explicitly walking the visual tree a second time.

    The key challenge for me is this (bold added for emphasis):

    "I understand that you want to know whether the user has clicked on a control, and since the HisTest will return the element that directly hit, you look through the visual tree to determine if this element is a part of control or not."

    This is exactly right in terms of my goal.  The real question comes down to 'how do I know when a element is part of a control or not'? 

    What my little function attempts to do is it checks the visual tree to see if an element that is clicked has any parent element in its visual tree whose type is part of the System.Windows.Control.Primitive namespace.  Is this a valid test - ignoring any ineffficiencies?  Is there a better test for correctness?

    Thanks,

    Notre

    Thursday, December 16, 2010 5:55 PM
  • Hi Notre,

    Thanks for your reply.

    I might want to use

    visual is Control
    

    to determine whether the current element is a Control, since the search in visual tree will eventually loop to a Control if it's a part of control.

    If you have a different defination for what is a Control, would you please kindly elaborate it?

    Thank you and have a nice day!


    Best regards,
    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Notre Friday, December 17, 2010 5:36 PM
    Friday, December 17, 2010 2:28 AM
  • Hi Min,

    Yes - I think that could work!  I was missing (what should be) the obvious.  I was worried that the primitives wouldn't be Controls (e.g. ToggleButton) but it looks like they are, so I think this will work.

    Thanks for your help!

    Notre

    Friday, December 17, 2010 5:36 PM
  • Hi Notre,

    Appreciate your update and response. I am glad to hear that the problem has been fixed.

    If you have any other questions or concerns, please do not hesitate to contact us. It is always our pleasure to be of assistance.

    Have a nice day!


    Best regards,
    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, December 20, 2010 1:37 AM