Get Microsoft Silverlight

Thursday, July 30, 2009

Slider Preview as Expression Blend 3 Behavior

Behaviors is one of the new features Expression blend 3 has introduced to make interactivity easy. Behavior allows us to wrap the interaction logic as a very discrete unit and attach to any UI elements. My previous blog post about Attaching preview behavior to a Slider control, is a good scenario to make as a Behavior. I have used Attached Dependency property as a technique to make it happen in the earlier post, though the concept is almost same but Blend Behavior gives a neat and clean abstraction. It also allows a Blend user to just drag-drop to any Slider control. So this is a great functionality which helps boost the re-usability in a cool way.

Problem : Create a behavior to display the value of a Slider control when hover over.

First step is to make a class which derived from Behavior class. public class PreviewSliderBehaviour : Behavior<Slider> Here I am explicitly giving Slider class as my targeted UI Element type since this behavior is meaningless to other elements. Now we need to override two methods of Behavior class, protected override void OnAttached() and protected override void OnDetaching() . I can subscribe mouse events in the OnAttached() method and unsubscribe the same in the OnDetaching() method.

     protected override void OnAttached()
{
m_attachedObject = AssociatedObject;
// Gives you the instance of the Slider you attached this behavior on to

if (m_attachedObject != null)
{
m_attachedObject.PreviewMouseMove +=
new System.Windows.Input.MouseEventHandler(item_PreviewMouseMove);
m_attachedObject.MouseLeave +=
new System.Windows.Input.MouseEventHandler(item_MouseLeave);
}
base.OnAttached();
}
/// Called when the behavior is removed from the DependencyObject
protected override void OnDetaching()
{
if (m_attachedObject != null)
{
m_attachedObject.PreviewMouseMove -=
new System.Windows.Input.MouseEventHandler(item_PreviewMouseMove);
m_attachedObject.MouseLeave -=
new System.Windows.Input.MouseEventHandler(item_MouseLeave);
}
base.OnDetaching();
}

The important logic lies in the MouseMove handler, it creates a ContentAdorner(a custom adorner, you can see in the source code) and sets the value calculated here to the ContentAdorner.Content property. This also sets the ContentAdorner’s placement point to the mouse point so that the adorner moves along with the mouse move.

     void item_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
Slider slider = sender
as Slider;
if (popup == null)
{
//Creates a new ContentAdorner with the given Style, Content Style is a Dependancy property of this behaviour
popup =
new ContentAdorner(slider, ContentStyle);
AdornerLayer layer = AdornerLayer.GetAdornerLayer(slider);
layer.Add(popup);
}
popup.Visibility = Visibility.Visible;
//Finding out the PART_Track from the controltemplate of the Slider
Track _track = slider.Template.FindName(
"PART_Track", slider) as Track;
//Calculates the mouse position
Point position = e.MouseDevice.GetPosition(_track);
//This method gives the exact preview value of the Slider
double value = _track.ValueFromPoint(position);
if (slider.SmallChange != 0.0)
{
double diff = value % slider.SmallChange;
value -= diff;
}
//Setting the value as the content of the ContentAdorner
popup.Content = Math.Max(slider.Minimum, Math.Min(slider.Maximum,
value));
position = e.GetPosition(slider);
position.Y = slider.ActualHeight / 2.0;
popup.PlacementOffset = position;
}

Now it is pretty easy to add this behavior to any Slider control, if you are in Expression Blend just opens the behavior tab and drag and drop this on to any Slider in the designer area. The XAML code will looks like,

<Slider x:Name="sld" Minimum="00" Maximum="100"    SmallChange="0.5" IsSnapToTickEnabled="True" TickFrequency="0.5">
<i:Interaction.Behaviors>
<local:PreviewSliderBehaviour ContentStyle=
"{StaticResource PreviewStyle}"/>
</i:Interaction.Behaviors>

</Slider>

Source Code

Few other links you may be interested to read about Behaviors

Gallery of Behaviors

AN INTRODUCTION TO BEHAVIORS, TRIGGERS, AND ACTIONS

Using a Behavior to magnify your WPF applications

Wednesday, July 29, 2009

Attaching Preview behavior to a WPF Slider control

PreviewSlider PreviewSlider1

Today I am sharing about a common usage of Slider control for the Timeline scenarios as we can see in many Media players. The above pictures shows timeline/scrubber control of some popular video players(Hulu and Youtube). We can edit the Slider Style and ControlTemplate to create the same look and feel of the above two scrubbers except the preview value get displayed when we hover over.

While figuring out an easy way to build this, I got two different options. The first and the obvious solution is to extend the Slider class to create a new custom control, which will have a new DependancyProperty called PreviewValue to hold the mouse over value. The biggest challenge in this approach is to make this control as much flexible(design and dev friendly) because people may customize the control by giving different Size and Margins to the Track and other parts of the Control template, which will make our assumptions invalid in some cases. And another issue is having a totally modified Controltemplate for the new Slider makes ‘Integrators’ life hard, because this control will introduce one mandatory visual inside the control template with a PART_naming convention.

Would it be cool if we can achieve this by adding couple of Attached properties to any regular Slider control? Yes that seems to be an elegant solution to me. So I have created a static class -PreviewSliderHelper to hold the required attached dependancy properties. The XAML code for the Slider is shown below. You need to specify a Style in the resource which will set to the preview display visual. The tooltip like visual is created using a custom Adorner and repositioning it based on the mouse move, I will discuss the details about this generic adorner implementation in another blog post.

image

And the final UI came out like this. Get the Executable

Jobi_mediaPlayer

The main technical details you might wanted to know is how to grab the preview value on the mouse over. We have to find out the PART_Track inside the slider and call ValueFromPoint() function to get the exact Slider.Value for each mouse move.

image 

Source Code -Of course there are lot to improve on this messy code :) ,but feel free to share your thoughts on this

Friday, July 17, 2009

An easy way to update all the UI Property Bindings of an Instance at once! – Silverlight and WPF

Imagine that you have a lot of properties in a class and you are very sure that all of those properties are getting updated at the same point of time. What generally we do in the ViewModel scenario is that we create Property setters for all these and raise PropertyChanged event so as to get the data reflected in the UI layer bindings. But that is a real resource waste when you really know the time of all your property updates. The solution is really easy as per the MSDN documentation for PropertyChanged Event in the remarks section it says “The PropertyChanged event can indicate all properties on the object have changed by using either a null reference (Nothing in Visual Basic) or String.Empty as the property name in the PropertyChangedEventArgs.” It seems that this is a great saver in terms of code writing and execution, since we need to raise only one PropertyChanged Event per class just after the property updation.

Think about a Stock Trading scenario as an example to this, you must have a Quote class which generally will have lot of properties in it, and there will be an Update method on a timer may update this quote object. Imagine that this class is bound to a DataTemplate at XAML level.

    public class Quote : ViewModelBase
{
public string Symbol { get; set; }
public double Last { get; set; }
public double Change { get; set; }
public double Bid { get; set; }
public double Ask { get; set; }
public double Open { get; set; }
public string Percentage { get; set; }
public double Volume { get; set; }

public bool IsRefreshed
{
get { return _isRefreshed; }
set
{
_isRefreshed = value;
RaisePropertyChanged(null);
}
}
private bool _isRefreshed;
}

Isn't the code very neat with the awesome Auto-Implemented Properties rather than having fields and expanded setters with RaiseProertyChanged in all those?. Now in the Stock Update method you can set all these properties and set IsRefreshed=true which in turn calls RaisePropertyChanged(null) and makes the WPF/Silverlight binding infrastructure to re-evaluate all the bindings associated with the UI.

I recommend using the MVVM implementation by Josh Smith for the basic ViewModel architecture. You can download his MVVM Foundation from codeplex - http://mvvmfoundation.codeplex.com/