I got another question about an issue in Silverlight 2.0 Beta 2. My collegue who is at the moment working on a video-player came to me and asked me why MouseLeftButtonDown- and Up didn't work on the slider and after 2 minutes of Googling we found out that he wasn't the only one faced with this problem. I started by Googling a bit more. Google is a developers best friend, but I couldn't find a solution. I found the reason for the problem, which I confirmed by checking the sliders code in Reflector. It is actually not a change in Silverlight as such, but in the controls that ship with Silverlight. For some reason Microsoft changed the implementation of their controls from Beta 1 to 2 and started letting each individual template part handle their on mouse events.So each part of the control encapsulates itsfunctionality nicely and sets the Handled-property of the EventArgs to true in their eventhandlers. This causes the event to stop bubbling and also renders the MouseLeftButtonDown, Up and so on the control more or less useless.
The easy solution that some guys suggested, which was also my first solution, was to inherit the control and override the OnMouseLeftButtonDown method. This doesn't really work in my case though. In the case of Button, it might, but the slider doesn't offer that possibility since there are no protected methods to override. The Buttons base class, ButtonBase, implements a couple of protected virtual methods to override, but the slider makes these methods private. So I went through the template for the slider control to figure out how it worked and came up with a solution. I created a class that inherited from Slider. This gave me access to the protected method GetTemplateChild(), which gave me access to elements in the template. I then created a property of type Thumb. The reason for this choice was that I was only interested in mouse events for that specific part of the control, but it works with any part of the template. In that property I then called GetTemplateChild() passing in the name of the thumb, "HorizontalThumb", and casting it to Thumb. Note that the name is for the template for the thumb used when the control is in horizontal mode. Changing to vertical will require that you change the name. This looked nice and might have worked if I hadn't tried to get hold of the Thumb as early as in the Load event. By that time, the ApplyTemplate() had apparently not executed and I got a null reference. So I added some logic to check for null and call ApplyTemplate in those cases. After that I could just add my control and handle the events from my Thumb.
This method should be possible to use on basically all controls that might be causing your headaches. Finding the name of the template parts can be dont in several ways. One is disassembling the control in Reflector. Microsoft stores the names of the parts in constants, using relatively easily understood names. The names are also available among the attributes addorning the class. Another way is to disassemble System.Windows.g.resources resource in Reflector. All default templates XAML is available in generic.xaml. And the final one is to open the control in Blend 2.5 and edit the controls template and then take a look at the xaml that it creates. However you do it, it should help you out. It might be an ugly solution, but it's the only one I can see at the moment.
Here is the full code for my custom slider. It's not a lot of code, and perhaps not very beautiful, but it will work as an example of what I'm talking about.
[code:c#]
public class MySlider : Slider
{
Thumb _horizontalThumb = null;
public Thumb Thumb
{
get
{
if (_horizontalThumb != null)
return _horizontalThumb;
_horizontalThumb = this.GetTemplateChild("HorizontalThumb") as Thumb;
if (_horizontalThumb == null)
{
this.ApplyTemplate();
_horizontalThumb = this.GetTemplateChild("HorizontalThumb") as Thumb;
}
return _horizontalThumb;
}
}
}
[/code]