Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

New Custom Slider for Silverlight 2 media players

My Silverlight teammates Akshay Johar and Andre Michaud have built a new custom slider for use in Silverlight 2 media players.

When you scrub the default slider in a MediaElement today, it generates a whole slew of valueChanged events, resulting in a huge number of seeks per second. This can confuse media playback, particularly when streaming from Windows Media Services, which then gets flooded with many requests a second.

Essentially it detects a “crazy mode” scrub, and quietly ignores it while the “crazy mode” is in place. Once out of this mode, it happily sends and receives a successful seek.

So, the player only fires ValueChanged on mouse up (on the slider thumb or the slider tracker), or the final seek request of a cluster that comes in at once.

It’s also uses Tim Heuer’s absolute value slider.

Now, let me try this new Windows Live Writer plug-in for Insert Code Snippet for the C# code.

(EDIT) A sample project is also now available for download.

 1: using System;
 2: using System.Net;
 3: using System.Windows;
 4: using System.Windows.Controls;
 5: using System.Windows.Documents;
 6: using System.Windows.Ink;
 7: using System.Windows.Input;
 8: using System.Windows.Media;
 9: using System.Windows.Media.Animation;
 10: using System.Windows.Shapes;
 11: using System.Windows.Controls.Primitives;
 12: using System.Windows.Threading;
 13:  
 14: namespace VirtualizedSlider
 15: {
 16:     public class CustomSlider : Slider
 17:     {
 18:         public Thumb horizontalThumb;
 19:         private FrameworkElement horizontalLeftTrack;
 20:         private FrameworkElement horizontalRightTrack;
 21:         private double oldValue = 0, newValue = 0, prevNewValue = 0;
 22:         public event RoutedPropertyChangedEventHandler<double> MyValueChanged;
 23:         public event RoutedPropertyChangedEventHandler<double> MyValueChangedInDrag;
 24:         
 25:         private DispatcherTimer dragtimer = new DispatcherTimer();
 26:         private double dragTimeElapsed = 0;
 27:         private const short DragWaitThreshold = 200, DragWaitInterval = 100;
 28:         public Rectangle progressRect = null;
 29:         private bool dragSeekJustFired = false;
 30:  
 31:         public CustomSlider()
 32:         {
 33:             this.ValueChanged += new RoutedPropertyChangedEventHandler<double>(CustomSlider_ValueChanged);
 34:             dragtimer.Interval = new TimeSpan(0, 0, 0, 0, DragWaitInterval);
 35:             dragtimer.Tick += new EventHandler(dragtimer_Tick);
 36:             
 37:         }
 38:  
 39:         void dragtimer_Tick(object sender, EventArgs e)
 40:         {
 41:             dragTimeElapsed += DragWaitInterval;
 42:             if (dragTimeElapsed >= DragWaitThreshold)
 43:             {
 44:                 RoutedPropertyChangedEventHandler<double> handler = MyValueChangedInDrag;
 45:                 if ((handler != null) && (newValue != prevNewValue))
 46:                 {
 47:                     handler(this, new RoutedPropertyChangedEventArgs<double>(oldValue, newValue));
 48:                     dragSeekJustFired = true;
 49:                     prevNewValue = newValue;
 50:                 }
 51:                 dragTimeElapsed = 0;
 52:             }
 53:         }
 54:  
 55:         void CustomSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
 56:         {
 57:             oldValue = e.OldValue;
 58:             newValue = e.NewValue;
 59:             if (horizontalThumb.IsDragging)
 60:             {
 61:                 dragTimeElapsed = 0;
 62:                 dragtimer.Stop();
 63:                 dragtimer.Start();
 64:                 dragSeekJustFired = false;
 65:             }
 66:         }
 67:  
 68:         public override void OnApplyTemplate()
 69:         {
 70:             base.OnApplyTemplate();
 71:  
 72:             horizontalThumb = GetTemplateChild("HorizontalThumb") as Thumb;
 73:  
 74:             horizontalLeftTrack = GetTemplateChild("LeftTrack") as FrameworkElement;
 75:             horizontalRightTrack = GetTemplateChild("RightTrack") as FrameworkElement;
 76:             progressRect = GetTemplateChild("progressRect") as Rectangle;
 77:  
 78:             if (horizontalLeftTrack != null) horizontalLeftTrack.MouseLeftButtonDown += new MouseButtonEventHandler(OnMoveThumbToMouse);
 79:             if (horizontalRightTrack != null) horizontalRightTrack.MouseLeftButtonDown += new MouseButtonEventHandler(OnMoveThumbToMouse);
 80:  
 81:             horizontalThumb.DragCompleted += new DragCompletedEventHandler(DragCompleted);
 82:             progressRect.Width = this.Width;            
 83:         }
 84:  
 85:         private void OnMoveThumbToMouse(object sender, MouseButtonEventArgs e)
 86:         {
 87:             e.Handled = true;
 88:             Point p = e.GetPosition(this);
 89:  
 90:             if (this.Orientation == Orientation.Horizontal)
 91:             {
 92:                 Value = (p.X - (horizontalThumb.ActualWidth / 2)) / (ActualWidth - horizontalThumb.ActualWidth) * Maximum;
 93:             }
 94:             RoutedPropertyChangedEventHandler<double> handler = MyValueChanged;
 95:             if (handler != null)
 96:             {
 97:                 handler(this, new RoutedPropertyChangedEventArgs<double>(oldValue, Value));
 98:             }
 99:         }
 100:  
 101:         private void DragCompleted(object sender, DragCompletedEventArgs e)
 102:         {
 103:             dragtimer.Stop();
 104:             dragTimeElapsed = 0;
 105:             RoutedPropertyChangedEventHandler<double> handler = MyValueChanged;
 106:             if ((handler != null) && (!dragSeekJustFired))
 107:             {
 108:                 handler(this, new RoutedPropertyChangedEventArgs<double>(oldValue, this.Value));
 109:             }
 110:         }
 111:     }
 112: }

 

And here’s the XAML from the progressSliderStyle, demonstrating how the custom slider knows about mouse clicks on the track (the “LeftTrack” and “RightTrack” rectangles inserted in the template handle this):

 1: <Setter Property="Template">
 2:                  <Setter.Value>
 3:                      <ControlTemplate TargetType="Slider">
 4:                          <Grid x:Name="Root">
 5:                              <Grid.Resources>
 6:                                  <ControlTemplate x:Key="RepeatButtonTemplate">
 7:                                      <Grid x:Name="Root" Opacity="0" Background="Transparent"/>
 8:                                  </ControlTemplate>
 9:                              </Grid.Resources>
 10:                              <vsm:VisualStateManager.VisualStateGroups>
 11:                                  <vsm:VisualStateGroup x:Name="CommonStates">
 12:                                      <vsm:VisualState x:Name="Normal"/>
 13:                                      <vsm:VisualState x:Name="MouseOver"/>
 14:                                      <vsm:VisualState x:Name="Disabled">
 15:                                          <Storyboard>
 16:                                              <DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="(UIElement.Opacity)" To="0.5"/>
 17:                                          </Storyboard>
 18:                                      </vsm:VisualState>
 19:                                  </vsm:VisualStateGroup>
 20:                              </vsm:VisualStateManager.VisualStateGroups>
 21:  
 22:                              <!-- Horizontal Template -->
 23:                              <Grid x:Name="HorizontalTemplate" Background="{TemplateBinding Background}">
 24:                                  <Grid.ColumnDefinitions>
 25:                                      <ColumnDefinition Width="Auto"/>
 26:                                      <ColumnDefinition Width="Auto"/>
 27:                                      <ColumnDefinition Width="*"/>
 28:                                  </Grid.ColumnDefinitions>
 29:  
 30:                                  <!-- Track Layer -->
 31:                                  <Rectangle x:Name="progressRect" Stroke="#FFA3AEB9" StrokeThickness="{TemplateBinding BorderThickness}" Fill="Red" Grid.Column="0" Grid.ColumnSpan="3" Height="3" Width="5" RadiusX="1" RadiusY="1" Margin="5,0,5,0" Canvas.ZIndex="1" Visibility="Collapsed" HorizontalAlignment="Left" />
 32:                                  <Rectangle Stroke="#FFA3AEB9" StrokeThickness="{TemplateBinding BorderThickness}" Fill="#FFE6EFF7" Grid.Column="0" Grid.ColumnSpan="3" Height="3" RadiusX="1" RadiusY="1" Margin="5,0,5,0"  Canvas.ZIndex="0" />
 33:  
 34:                                  <!-- Repeat Buttons + Thumb -->
 35:                                  <RepeatButton x:Name="HorizontalTrackLargeChangeDecreaseRepeatButton" IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}" Grid.Column="0" Canvas.ZIndex="1"/>
 36:                                  <Rectangle x:Name="LeftTrack" Grid.Column="0" Fill="#00FFFFFF" Cursor="Hand" Canvas.ZIndex="1"/>
 37:                                  <Thumb Height="18" x:Name="HorizontalThumb" Width="11" Grid.Column="1" IsTabStop="True" Canvas.ZIndex="1"/>
 38:                                  <RepeatButton x:Name="HorizontalTrackLargeChangeIncreaseRepeatButton" IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}" Grid.Column="2" Canvas.ZIndex="1"/>
 39:                                  <Rectangle x:Name="RightTrack" Grid.Column="2" Fill="#00FFFFFF" Cursor="Hand" Canvas.ZIndex="1"/>
 40:                              </Grid>
 41:                          </Grid>
 42:                      </ControlTemplate>
 43:                  </Setter.Value>
 44:              </Setter>

Tags:

Follow the Discussion

  • abnerabner

    Hello Ben,

    This is Rick over at uvntv.com

    Can you make this code available for download

    Thanks

  • abnerabner

    Hello Ben,

    This is Rick over at uvntv.com

    Can you make this code available for download

    Thanks

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.