I have been struggling sometime now trying to handle the following scenario, which seems quite simple at first. This problem highlights the difficulties one can face when doing WPF and/or SilverLight developments: while the most cases are working just fine and great, you will regularly face issues you were not expecting. Making WPF/SilverLight development rather complex.
The scenario is the following: I have a page, composed of several controls. The composing controls are themselves composite controls : there are composed of multiple UI controls
I want to style my page the following way: whenever the user moves his mouse cursor over a control, I want to play an animation to highlight this control. And whenever the user moves his mouse cursor out of a control, I want to play an animation to remove highlight on this control
Let illustrate this with a concrete example: I have a page showing a grid view of products and a form view displaying the details of the selected product. The grid view and the form view are two composite user controls composed of other controls.
Whenever the grid view is focused, I want the following visual result:
And whenever the form view is focused, I want the following visual result:
Well... seems quite obvious at first: why not just add a Trigger on the control, triggered by a changing value of the property "IsMouseOver" (for WPF. For SilverLight there is no "IsMouseOver, so instead we should add EventTriggers on "MouseEnter" / "MouseLeave"). When "IsMouseOver" changes, then we can then just play the adequate storyboard.
However the reality is more complex than that... The main control will receive MouseEnter and MouseLeave events (with associated changes of "IsMouseOver" ) whenever the mouse cursor enters/leaves the targeted control AND when the mouse cursor enters/leaves any of the sub-controls composing the targeted control!
Which results in a flickering display: in our example, while moving the mouse inside the scope of the product form view, the form view will be constanlty switched from highlighted to not-higlighted when for example the mouse enters the scope of a TextBox.
I have tried quite a few things to fix that issue. I registered to the MouseEnter/MouseLeave events, in order to generate custom events that will really reflect entering/leaving the main control, and hiding entering/leaving internal controls. However this was a failure and there is no easy way to really be able to know if a MouseLeave event on the main control is because we're really leaving the main control or entering a sub-control.
I have made some research to see what others are doing but could not find a satisfactory answer. I had to admit this is not an easy task on WPF/SilverLight where it just should be!!
So here is the final solution that I implemented:
The problem is really on the MouseLeave event: we have no easy way to figure out if we are really leaving the main control or just entering a sub-control. No problem on the MouseEnter event: whenever we receive a MouseEnter event it means that we are entering the main control or already are over the main control and entering a sub control. But the important point is: we know we are over the main control.
The solution is therefore the following
- Only rely on MouseEnter event: whenever a MouseEnter event is received, we do the animation for Highlighting the control (and keep a flag set to do the animation only once)
- We know we are leaving a control, when we are entering another one (in our example we know we are leaving the product grid view when we are entering the product form view.
- So: when a MouseEnter event is received, in addition to do the animation for Highlighting the controlwe, we also do the animation to remove higlight from all other controls
That's it! It is working great!
And a few quick tips for doing it on WPF, using the MVVM pattern:
- Add an "IsActiveView" Property on the ViewModel of each composite control
- In the main page loaded event, register to the composite controls MouseEnter events, and in the handlers: set/unset accordingly the "IsActiveView" property of each control
- Create DataTriggers binded on the "IsActiveView" to trigger the appropriate animation
Hope it can help others facing the same issue!
Comments
You can follow this conversation by subscribing to the comment feed for this post.