2017-01-22 3 views
0

Я пытаюсь создать плавающий слайдер. Поскольку анимации являются незамерзающими, кроме того, как только вы перетаскиваете ползунок, его значение изменяется, а большой палец переместится в позицию, было бы сложно одушевить реальный большой палец.Как сделать плавающий слайдер?

Так что я сделал, чтобы анимировать поддельный большой палец вместо этого (я называю это крючком большого пальца) и сделать фактический большой палец прозрачным. это было довольно круто, но проблем мало.

, как вы видите, в следующем формате GIF, Маржа крючковатым пальцем не реагирует на ширину колеи: (. Левое поле всплывающий из коде)

enter image description here

Как я могу сделать маржа реагирует? (Я не хочу исправлять эту проблему из-за кода)

Эта часть является шаблоном для стиля слайдера.

<Grid Width="{TemplateBinding Width}" 
     Height="{TemplateBinding Height}"> 

    <Track x:Name="PART_Track"> 
     <Track.DecreaseRepeatButton> 
      <RepeatButton x:Name="PART_SelectionRange" 
       BorderBrush="{TemplateBinding BorderBrush}" 
       Background="{TemplateBinding Background}" 
       Style="{StaticResource PlaybackScrollRepeatButtonStyle}" 
       Command="{x:Static Slider.DecreaseLarge}"/> 
     </Track.DecreaseRepeatButton> 
     <Track.IncreaseRepeatButton> 
      <RepeatButton 
       BorderBrush="{TemplateBinding BorderBrush}" 
       Background="{TemplateBinding Background}" 
       Style="{StaticResource PlaybackScrollRepeatButtonStyle}" 
       Command="{x:Static Slider.IncreaseLarge}"/> 
     </Track.IncreaseRepeatButton> 
     <!-- Transparent thumb with width of 1px and auto height. --> 
     <Track.Thumb> 
      <Thumb x:Name="Thumb" 
       Style="{StaticResource PlaybackSliderHiddenThumbStyle}"/> 
     </Track.Thumb> 
    </Track> 

    <!-- This is the fake thumb. its margin is animated in codebehind. --> 
    <ContentControl x:Name="HookedThumb" HorizontalAlignment="Left" 
     Style="{StaticResource PlaybackSliderHookedThumbStyle}" 
     Background="{StaticResource Playback.Slider.Thumb.Brush}" 
     BorderBrush="{StaticResource Playback.Slider.Thumb.Border}"/> 
</Grid> 

Я знаю причину, почему это не реагирует, потому что я использую HorizontalAlignment="Left" для крючковатым пальцем. Если я использую HorizontalAlignment="Stretch", он все равно не реагирует. это то, что вы видите:

enter image description here

(отделенного кода изменилась, поскольку маржа рассчитывается по-разному)

Теперь это ближе к большому пальцу, но по-прежнему не реагирует.

Я открыт с любым решением. не волнуйтесь об анимации. Я буду иметь дело с этим. то, что я хочу, - это сделать крючок большого пальца восприимчивым, как главный большой палец.

Другой идеей, которую я имел, было сделать границу с большим пальцем, прикрепленной к ней, и просто анимировать ширину рамки, но я не могу получить правильный стиль. любая помощь оценивается.


Вот код позади в случае, если вы хотите знать (когда HorizontalAlignment="Stretch")

// following will animate margin of hooked thumb to the location of thumb. 

var cc = (ContentControl)PlaybackSlider.Template.FindName("HookedThumb", PlaybackSlider); 
var track = (Track)PlaybackSlider.Template.FindName("PART_Track", PlaybackSlider); 
var selection = (RepeatButton)PlaybackSlider.Template.FindName("PART_SelectionRange", PlaybackSlider); 
var thumb = track.Thumb; 

cc.Margin = new Thickness(-track.ActualWidth + cc.Width, 0, 0, 0); // starting margin 

var keyframe = new SplineThicknessKeyFrame(); // holds the target keyframe. 

var animator = new ThicknessAnimationUsingKeyFrames // this will animate margin 
{ 
    KeyFrames = new ThicknessKeyFrameCollection { keyframe }, 
    BeginTime = TimeSpan.Zero, 
    Duration = TimeSpan.FromMilliseconds(200), 
    DecelerationRatio = 1 
}; 

var storyboard = new Storyboard // we use storyboard so we can stop and begin animation at any time. 
{ 
    Children = new TimelineCollection { animator } 
}; 

// setup storyboard with target property and dependency object. 
Storyboard.SetTarget(animator, cc); 
Storyboard.SetTargetProperty(animator, new PropertyPath(MarginProperty)); 

Action beginAnimation =() => 
{ 
    storyboard.Stop(); // stop animation. change target, begin animation again. 

    var left = -track.ActualWidth + selection.Width*2; // calculate left margin 

    var correction = left; // set correction to the left margin 

    // prevent moving thumb out of range 
    if (correction < -track.ActualWidth + cc.Width) correction = -track.ActualWidth + cc.Width; 
    if (left + cc.Width > track.ActualWidth) 
     correction = track.ActualWidth - cc.Width; 

    // set new target frame 
    keyframe.Value = new Thickness(correction, 0, 0, 0); 

    storyboard.Begin(); 
}; 

// following are the handlers that begins the animation. 

PlaybackSlider.ValueChanged += (o, e) => 
{ 
    if(thumb.IsDragging) return; 
    beginAnimation(); 
}; 
thumb.DragStarted += (o, e) => beginAnimation(); 
thumb.DragDelta += (o, e) => beginAnimation(); 
thumb.DragCompleted += (o, e) => beginAnimation(); 

Вот стили: Триггеры исключенные.

<!-- playback repeat button style --> 
<Style x:Key="PlaybackScrollRepeatButtonStyle" TargetType="{x:Type RepeatButton}" BasedOn="{StaticResource {x:Type RepeatButton}}"> 
    <Setter Property="IsTabStop" Value="False"/> 
    <Setter Property="Focusable" Value="False"/> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type RepeatButton}"> 
       <Border Background="Transparent"> 
        <Rectangle Fill="{TemplateBinding Background}" 
           Stroke="{TemplateBinding BorderBrush}" 
           VerticalAlignment="Center" 
           StrokeThickness="0.5" 
           Height="3"/> 
       </Border> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

<!-- playback slider thumb--> 
<Style x:Key="PlaybackSliderHiddenThumbStyle" TargetType="{x:Type Thumb}" BasedOn="{StaticResource {x:Type Thumb}}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type Thumb}"> 
       <Canvas Background="Transparent" Width="1px"/> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

<!-- playback slider thumb decoy--> 
<Style x:Key="PlaybackSliderHookedThumbStyle" TargetType="{x:Type ContentControl}" BasedOn="{StaticResource {x:Type ContentControl}}"> 
    <Setter Property="IsEnabled" Value="False"/> 
    <Setter Property="IsHitTestVisible" Value="False"/> 
    <Setter Property="Focusable" Value="False"/> 
    <Setter Property="IsTabStop" Value="False"/> 
    <Setter Property="Width" Value="17"/> 
    <Setter Property="Height" Value="17"/> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type ContentControl}"> 
       <Border Background="Transparent"> 
        <Ellipse x:Name="Ellipse" 
         Fill="{TemplateBinding Background}" 
         Stroke="{TemplateBinding BorderBrush}" 
         StrokeThickness="1.5"/> 
       </Border> 

      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

<!-- playback slider style --> 
<Style x:Key="PlaybackSliderStyle" TargetType="{x:Type Slider}" BasedOn="{StaticResource {x:Type Slider}}"> 
    <Setter Property="Background" Value="{StaticResource Playback.Slider.Brush}"/> 
    <Setter Property="BorderBrush" Value="{StaticResource Playback.Slider.Border}"/> 
    <Setter Property="Foreground" Value="{StaticResource Playback.Slider.SelectionRange}"/> 
    <Setter Property="SelectionStart" Value="{Binding Minimum, RelativeSource={RelativeSource Self}}"/> 
    <Setter Property="SelectionEnd" Value="{Binding Value, RelativeSource={RelativeSource Self}}"/> 
    <Setter Property="IsSelectionRangeEnabled" Value="True"/> 
    <Setter Property="IsMoveToPointEnabled" Value="True"/> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type Slider}"> 

       <Grid Width="{TemplateBinding Width}" 
         Height="{TemplateBinding Height}"> 

        <Track x:Name="PART_Track"> 
         <Track.DecreaseRepeatButton> 
          <RepeatButton x:Name="PART_SelectionRange" 
           BorderBrush="{TemplateBinding BorderBrush}" 
           Background="{TemplateBinding Background}" 
           Style="{StaticResource PlaybackScrollRepeatButtonStyle}" 
           Command="{x:Static Slider.DecreaseLarge}"/> 
         </Track.DecreaseRepeatButton> 
         <Track.IncreaseRepeatButton> 
          <RepeatButton 
           BorderBrush="{TemplateBinding BorderBrush}" 
           Background="{TemplateBinding Background}" 
           Style="{StaticResource PlaybackScrollRepeatButtonStyle}" 
           Command="{x:Static Slider.IncreaseLarge}"/> 
         </Track.IncreaseRepeatButton> 
         <Track.Thumb> 
          <Thumb x:Name="Thumb" 
           Style="{StaticResource PlaybackSliderHiddenThumbStyle}"/> 
         </Track.Thumb> 
        </Track> 

        <ContentControl x:Name="HookedThumb" HorizontalAlignment="Stretch" 
         Style="{StaticResource PlaybackSliderHookedThumbStyle}" 
         Background="{StaticResource Playback.Slider.Thumb.Brush}" 
         BorderBrush="{StaticResource Playback.Slider.Thumb.Border}"/> 
       </Grid> 

      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

ответ

0

Я был в состоянии решить эту проблему, после просмотра this answer,

Я использовал Tag собственности и мульти-связывание с помощью умножения преобразователя для того, чтобы исправить эту проблему.

enter image description here

Это новый стиль для слайдера.

<Grid Width="{TemplateBinding Width}" 
     Height="{TemplateBinding Height}"> 

    <Track x:Name="PART_Track"> 
     <Track.DecreaseRepeatButton> 
      <RepeatButton x:Name="PART_SelectionRange" 
       BorderBrush="{TemplateBinding BorderBrush}" 
       Background="{TemplateBinding Background}" 
       Style="{StaticResource PlaybackScrollRepeatButtonStyle}" 
       Command="{x:Static Slider.DecreaseLarge}"/> 
     </Track.DecreaseRepeatButton> 
     <Track.IncreaseRepeatButton> 
      <RepeatButton 
       BorderBrush="{TemplateBinding BorderBrush}" 
       Background="{TemplateBinding Background}" 
       Style="{StaticResource PlaybackScrollRepeatButtonStyle}" 
       Command="{x:Static Slider.IncreaseLarge}"/> 
     </Track.IncreaseRepeatButton> 
     <Track.Thumb> 
      <Thumb x:Name="Thumb" 
       Style="{StaticResource PlaybackSliderThumbStyle}"/> 
     </Track.Thumb> 
    </Track> 

    <WrapPanel VerticalAlignment="Center"> 
     <Border x:Name="Border" Margin="0,0,-8.5,0"> 
      <!-- Tag is used for animating width --> 
      <Border.Tag> 
       <system:Double>0.0</system:Double> 
      </Border.Tag> 
      <Border.Width> 
       <MultiBinding Converter="{StaticResource MultiplyConverter}"> 
        <Binding Path="ActualWidth" ElementName="PART_Track"/> 
        <Binding Path="Tag" RelativeSource="{RelativeSource Self}" /> 
       </MultiBinding> 
      </Border.Width> 
     </Border> 
     <ContentControl x:Name="ThumbShadow" 
      Style="{StaticResource PlaybackSliderThumbStyleShadow}" 
      Background="{StaticResource Playback.Slider.Thumb.Brush}" 
      BorderBrush="{StaticResource Playback.Slider.Thumb.Border}"/> 
    </WrapPanel> 
</Grid> 

Вот код-за анимировать Tag свойства в диапазоне от 0 до 1.

private void AttachAnimation(Slider slider) 
{ 
    var track = (Track)slider.Template.FindName("PART_Track", slider); 
    var thumb = (Thumb)slider.Template.FindName("Thumb", slider); 
    var shadow = (ContentControl)slider.Template.FindName("ThumbShadow", slider); 
    var selection = (RepeatButton)slider.Template.FindName("PART_SelectionRange", slider); 
    var border = (Border)slider.Template.FindName("Border", slider); 
    var center = shadow.Width/2; 

    DoubleAnimation animation = new DoubleAnimation 
    { 
     DecelerationRatio = 1, 
     Duration = TimeSpan.FromMilliseconds(200) 
    }; 

    Storyboard storyboard = new Storyboard 
    { 
     Children = new TimelineCollection { animation } 
    }; 

    Storyboard.SetTarget(animation, border); 
    Storyboard.SetTargetProperty(animation, new PropertyPath("Tag")); 

    Action beginAnimation =() => 
    { 
     var correction = selection.ActualWidth; 
     if (selection.ActualWidth + center > track.ActualWidth) 
      correction = track.ActualWidth - center; // prevent going outside of bounds. 

     var ratio = correction/track.ActualWidth; 

     if (animation.To == ratio) 
      return; // to prevent animation freeze when there is no need for animation change. 

     animation.To = ratio; 

     storyboard.Stop(border); 
     storyboard.Begin(border, true); 
    }; 

    slider.ValueChanged += async (o, args) => 
    { 
     if(thumb.IsDragging) return; // drag delta will handle animation. 
     await Task.Delay(10); // wait for value changes 
     beginAnimation(); 
    }; 
    thumb.DragStarted += (o, args) => beginAnimation(); 
    thumb.DragDelta += (o, args) => beginAnimation(); 
    thumb.DragCompleted += async (o, args) => 
    { 
     if (args.Canceled) await Task.Delay(10); // wait for value changes 
     beginAnimation(); 
    }; 

    // at startup initialize values. 
    var init = selection.ActualWidth; 
    if (selection.ActualWidth + center > track.ActualWidth) init -= center; 
    border.Tag = init/track.ActualWidth; 

    storyboard.Begin(); // this will warm up animation to be ready for first time. 
}