Today's post starts off with an interactive exercise, first download the sample project file and run the program in there. The program looks like this.
When you've run the program, perform a simple exercise, move your mouse in and out of the extra large buttons and note what happens.
The buttons are essentially a ViewBox control containing the XAML which makes up the buttons. The viewbox is then contained inside a grid and scaled to the grid's size.
Code is tied to the PointerEntered and PointerExited events of the Viewbox which basically just changes the background color of the Grid which contains it. If you haven't already run the program and then move the mouse in and out of all of the buttons, do so now.
After running the program and trying out what I mentioned you should have noticed the following problem, given that a button looks like this.
- Button 1 didn't seem to respond to mouse events in the green area
- Buttons 2 and 3 did.
So what's happening and why the difference in behavior? Let's take a look at Button 1's XAML construction. (Behold my l33t diagraming skills!)
Note that it is made up of two vector paths (the Stroke and the [Path])
Now let's take a look at Button 2's XAML construction.
Compared to Button 1, there's an extra Ellipse element (the green circle) called hitAssist which is sitting behind the blue circle and 'i' which makes up the button.
By now you should have figured out why Button 1's mouse events where behaving so weirdly, for Button 1 the only elements which have a physical appearance are the circle and the 'i' and that's exactly what triggers the mouse events.
Whereas Button 2 has an additional hitAssist element which fills up the empty gap between the circle and the 'i' and hence the mouse events are triggered as if they're one consecutive element.
"But what about the Grids that are containing the strokes? Wouldn't THEY trigger the mouse entry and exit events?" But they don't! In XAML you'll use a LOT of Grids, Panels and Canvas to layout your elements, you do NOT want every single container to simple fire mouse events!
So how DOES an element qualify for mouse interactivity? Well, I gave a hint just now already. It must have a PHYSICAL appearance. In more easy to understand terms... Make sure the element has a Background Brush assigned to it. The moment a background brush is assigned to an element it will start participating in mouse events because the mouse is able to know if it's running over an element's background brush or not.
So now we know that what we want to do is to fill up gaps in our interactive elements so it makes more sense when users are using them. "But I don't want my sleek outline buttons to have an ugly unadaptable background color!" you say. Well, that's why there's a Button 3 in the sample.
Construction wise it looks just like Button 2, except the Ellipse element that's covering the gap is called hitAssistClear and it's can't be seen. "But didn't you just say that only elements which have a physical appearance can have mouse events?" Because it IS still physically there, it just has an Opacity property value of 0%. It's like a really really really clear piece of glass, you can't see it but it's there! Just remember:-
Any element that has a Visibility property value of Visible will participate in mouse events even if Opacity is 0%. Setting Visibility to Collapsed will remove it from mouse events too. And just in case you didn't realize, there's a property on every element called IsHitTestVisible setting that to FALSE will remove it from mouse events too.
And that is what the name of this whole notion of seeing wheter the mouse is over an element or not : Hit Testing
It's something that I feel all XAML developers and designers should know about, yet I don't really see anyone talking about it much. If you think this is not important, what if the button was only this small?
Notice the hit area of 'i' is now merely a few pixels, do you want your users to have to pixel hunt in order to click a button? Would YOU like to pixel hunt in order to click a button? This experience gets WORSE if your user is using touch to navigate your app!
ps. The button icon you see was created using the TOTALLY EXCELLENT Metro Studio by SyncFusion. Hmm... I should submit a feature request where they insert a hit test element into their XAML output.