2011-03-22 113 views
3

我试图创建一个滑块有两个大拇指对我的应用程序,作为一个范围滑块使用,但我遇到了问题。对我来说,基本要求是获得带有刻度线和两个拇指的单个滑块,这两个滑块均使用IsSnapToTickEnabled =“true”进行设置。WPF滑块有两个大拇指

我在搜索帮助(例如this one)时发现了一些范围滑块示例,但我无法修改它以添加刻度标记并强制拇指对齐刻度。获取刻度线和捕捉链接中的范围滑块工作将是理想的。

我试图修改滑块的模板,并添加另一个拇指,但后来我不知道怎么去选择拇指的价值。

有没有人有一个滑块的样品有两个大拇指,刻度线,并捕捉到蜱启用?我发现的所有范围滑块样本都使用了两个滑块,但它们都不允许刻度线或对齐刻度线。

谢谢。

回答

2

我最终修改一个在我原来的职位的链接。我将它与TickBar组合起来,并自己进行计算以提供对勾功能。

+1

我想要做同样的事情。你介意分享结果吗? – Lukas 2011-07-26 22:02:29

+0

@Lukas:我希望毕竟这一次,你能找到一个适合你的解决方案。但为了以防万一,请注意,我已经为这个问题添加了一个答案,它提供了所有必要的细节,并在这里提供了一个独立的答案。 – 2014-12-04 06:00:23

11

我意识到这个问题了三岁。不过,我一直使用带有多个拇指的滑块作为练习来学习更多关于WPF的例子,并且在我试图弄清楚如何做到这一点时遇到了这个问题。不幸的是,这个链接的例子似乎不再存在(一个很好的例子说明为什么StackOverflow的问题和答案不应该使用链接来提供对问题或答案至关重要的任何细节)。

我看了大量关于该主题的样本和文章,虽然我没有找到一个专门启用蜱,有足够的信息,有我的数字出来。我发现one article特别好,因为它非常清楚并且重点突出,同时还揭示了一些非常有用的技术,这些技术是完成这项任务的关键。

我的最终结果是这样的:

correct DoubleThumbSlider

所以对于谁可能想要么做同样的事情其他人的利益,或谁只是想了解一般的技术比较好,这里是如何您制作了一个双拇指滑块控件,该控件支持基本滑块&hellip的各种打勾功能;


起点是UserControl类本身。在Visual Studio中,为项目添加一个新的UserControl类。现在,添加您想要支持的所有属性。不幸的是,我还没有找到一种机制,只允许将属性委托给UserControl中的适当滑块实例,所以这意味着要为每个属性写入新的属性。

根据先决条件(即其他成员需要声明的成员)工作,我想要的功能之一是限制每个滑块的行程,使其不能被拖过另一个。我决定实现这个使用CoerceValueCallback的性质,所以我需要回调方法:

private static object LowerValueCoerceValueCallback(DependencyObject target, object valueObject) 
{ 
    DoubleThumbSlider targetSlider = (DoubleThumbSlider)target; 
    double value = (double)valueObject; 

    return Math.Min(value, targetSlider.UpperValue); 
} 

private static object UpperValueCoerceValueCallback(DependencyObject target, object valueObject) 
{ 
    DoubleThumbSlider targetSlider = (DoubleThumbSlider)target; 
    double value = (double)valueObject; 

    return Math.Max(value, targetSlider.LowerValue); 
} 

在,我只需要我的情况MinimumMaximumIsSnapToTickEnabledTickFrequencyTickPlacementTicks从底层滑块和两个新属性映射到各个滑块值LowerValueHigherValue。首先,我不得不宣布DependencyProperty对象:

public static readonly DependencyProperty MinimumProperty = 
    DependencyProperty.Register("Minimum", typeof(double), typeof(DoubleThumbSlider), new UIPropertyMetadata(0d)); 
public static readonly DependencyProperty LowerValueProperty = 
    DependencyProperty.Register("LowerValue", typeof(double), typeof(DoubleThumbSlider), new UIPropertyMetadata(0d, null, LowerValueCoerceValueCallback)); 
public static readonly DependencyProperty UpperValueProperty = 
    DependencyProperty.Register("UpperValue", typeof(double), typeof(DoubleThumbSlider), new UIPropertyMetadata(1d, null, UpperValueCoerceValueCallback)); 
public static readonly DependencyProperty MaximumProperty = 
    DependencyProperty.Register("Maximum", typeof(double), typeof(DoubleThumbSlider), new UIPropertyMetadata(1d)); 
public static readonly DependencyProperty IsSnapToTickEnabledProperty = 
    DependencyProperty.Register("IsSnapToTickEnabled", typeof(bool), typeof(DoubleThumbSlider), new UIPropertyMetadata(false)); 
public static readonly DependencyProperty TickFrequencyProperty = 
    DependencyProperty.Register("TickFrequency", typeof(double), typeof(DoubleThumbSlider), new UIPropertyMetadata(0.1d)); 
public static readonly DependencyProperty TickPlacementProperty = 
    DependencyProperty.Register("TickPlacement", typeof(TickPlacement), typeof(DoubleThumbSlider), new UIPropertyMetadata(TickPlacement.None)); 
public static readonly DependencyProperty TicksProperty = 
    DependencyProperty.Register("Ticks", typeof(DoubleCollection), typeof(DoubleThumbSlider), new UIPropertyMetadata(null)); 

做完这些,我现在可以写属性本身:

public double Minimum 
{ 
    get { return (double)GetValue(MinimumProperty); } 
    set { SetValue(MinimumProperty, value); } 
} 

public double LowerValue 
{ 
    get { return (double)GetValue(LowerValueProperty); } 
    set { SetValue(LowerValueProperty, value); } 
} 

public double UpperValue 
{ 
    get { return (double)GetValue(UpperValueProperty); } 
    set { SetValue(UpperValueProperty, value); } 
} 

public double Maximum 
{ 
    get { return (double)GetValue(MaximumProperty); } 
    set { SetValue(MaximumProperty, value); } 
} 

public bool IsSnapToTickEnabled 
{ 
    get { return (bool)GetValue(IsSnapToTickEnabledProperty); } 
    set { SetValue(IsSnapToTickEnabledProperty, value); } 
} 

public double TickFrequency 
{ 
    get { return (double)GetValue(TickFrequencyProperty); } 
    set { SetValue(TickFrequencyProperty, value); } 
} 

public TickPlacement TickPlacement 
{ 
    get { return (TickPlacement)GetValue(TickPlacementProperty); } 
    set { SetValue(TickPlacementProperty, value); } 
} 

public DoubleCollection Ticks 
{ 
    get { return (DoubleCollection)GetValue(TicksProperty); } 
    set { SetValue(TicksProperty, value); } 
} 

现在,这些都需要挂接到底层Slider控制将弥补UserControl。所以我添加两个Slider控制,再加上绑定的属性相应的属性在我UserControl

<Grid> 
    <Slider x:Name="lowerSlider" 
      VerticalAlignment="Center" 
      Minimum="{Binding ElementName=root, Path=Minimum}" 
      Maximum="{Binding ElementName=root, Path=Maximum}" 
      Value="{Binding ElementName=root, Path=LowerValue, Mode=TwoWay}" 
      IsSnapToTickEnabled="{Binding ElementName=root, Path=IsSnapToTickEnabled}" 
      TickFrequency="{Binding ElementName=root, Path=TickFrequency}" 
      TickPlacement="{Binding ElementName=root, Path=TickPlacement}" 
      Ticks="{Binding ElementName=root, Path=Ticks}" 
      /> 
    <Slider x:Name="upperSlider" 
      VerticalAlignment="Center" 
      Minimum="{Binding ElementName=root, Path=Minimum}" 
      Maximum="{Binding ElementName=root, Path=Maximum}" 
      Value="{Binding ElementName=root, Path=UpperValue, Mode=TwoWay}" 
      IsSnapToTickEnabled="{Binding ElementName=root, Path=IsSnapToTickEnabled}" 
      TickFrequency="{Binding ElementName=root, Path=TickFrequency}" 
      TickPlacement="{Binding ElementName=root, Path=TickPlacement}" 
      Ticks="{Binding ElementName=root, Path=Ticks}" 
      /> 
</Grid> 

请注意,在这里,我已经给了我UserControl命名为“根”,并引用即在Binding声明中。大多数属性直接转到UserControl中的相同属性,但当然每个Slider控件的个人属性都映射到UserControl的适当LowerValueUpperValue属性。

现在,这是最棘手的部分。如果你只是停在这里,你会得到的东西看起来是这样的: incorrect DoubleThumbSlider 第二Slider对象是完全在第一的顶部,导致其轨道掩盖第一Slider大拇指。这不仅仅是一个视觉问题;顶部的第二个对象接收所有的鼠标点击,从而防止第一个被调整。

为了解决这个问题,我编辑了第二个滑块的样式,以去除妨碍这些视觉元素的操作。我将它们留给第一个滑块,以提供控件的实际轨道视觉效果。不幸的是,我找不到一种方法来声明性地覆盖我需要改变的部分。但是,使用Visual Studio,您可以创建现有的样式,根据需要,然后可以编辑的整个副本:

  1. 切换到WPF设计“设计”模式,您UserControl
  2. 右击然后从弹出式菜单中选择“编辑模板/编辑副本...”

就这么简单。 :)这将在您的UserControl XAML中的Slider声明中添加一个Style属性,引用您刚创建的新样式。

Slider控件实际上有两个主要的控件模板,一个用于水平方向,一个用于垂直方向。我将在这里描述对水平模板的更改;我认为如何对垂直模板进行类似的更改将是显而易见的。

我使用Visual Studio的“转到定义”功能,迅速得到我所需要的模板的一部分:找到你的UserControlSliderStyle属性,单击样式的名称,然后按F12。这将带您到主对象Style,您将在其中找到水平模板的Setter(根据Orientation的值,垂直模板由SetterTrigger控制)。点击水平模板的名称(当我这样做的时候是“SliderHorizo​​ntal”,但我猜它可能会改变,当然对于其他类型的控件会有所不同)。

一旦你到达ControlTemplate,从不应该使用的元素中删除所有的视觉属性。这意味着删除一些元素,并从无法完全删除的元素中删除Background,BorderBrush,BorderThickness, Fill等。在我的情况下,我完全删除了RepeatButton,并修改了我需要的其他元素,以便它们不显示或占用任何空间(因此它们不会接收鼠标点击)。我结束了这个:

<ControlTemplate x:Key="SliderHorizontal" TargetType="{x:Type Slider}"> 
    <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="True"> 
    <Grid> 
     <Grid.RowDefinitions> 
     <RowDefinition Height="Auto"/> 
     <RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/> 
     <RowDefinition Height="Auto"/> 
     </Grid.RowDefinitions> 
     <TickBar x:Name="TopTick" Fill="{TemplateBinding Foreground}" Height="4" Margin="0,0,0,2" Placement="Top" Grid.Row="0" Visibility="Collapsed"/> 
     <TickBar x:Name="BottomTick" Fill="{TemplateBinding Foreground}" Height="4" Margin="0,2,0,0" Placement="Bottom" Grid.Row="2" Visibility="Collapsed"/> 
     <Border x:Name="TrackBackground" Grid.Row="1" VerticalAlignment="center"> 
     <Canvas> 
      <Rectangle x:Name="PART_SelectionRange" /> 
     </Canvas> 
     </Border> 
     <Track x:Name="PART_Track" Grid.Row="1"> 
     <Track.Thumb> 
      <Thumb x:Name="Thumb" Focusable="False" Height="18" OverridesDefaultStyle="True" Template="{StaticResource SliderThumbHorizontalDefault}" VerticalAlignment="Center" Width="11"/> 
     </Track.Thumb> 
     </Track> 
    </Grid> 
    </Border> 
    <!-- I left the ControlTemplate.Triggers element just as it was, no changes --> 
</ControlTemplate> 

这就是它的一切。 :)

最后一件事:以上假设Slider的股票样式不会改变。即第二个滑块的样式被复制并硬编码到程序中,但该硬编码样式仍然取决于从中复制的样式的布局。如果该库存样式发生变化,则第一个滑块的布局可能会发生变化,从而导致第二个滑块不再排列或者看起来不正确。

如果这是一个问题,那么你就可以在一定程度不同的方式处理模板:而不是修改SliderHorizontal模板,做它的一个副本,并引用它的Style,改变两者的名字,而改变的副本Style,以便它引用复制的模板而不是原件。然后您只需修改副本,并将第一个Slider的样式设置为未修改的Style,并将第二个Slider的样式设置为修改的样式。

除了这里展示的技术外,其他人可能希望做的事情略有不同。例如,我完全放弃了重复按钮,这意味着您只能拖动拇指。点击拇指外的轨道不会影响它。此外,大拇指仍然像在基本控制中那样工作,拇指的中间是拇指值的指示符。这意味着,当您将一个拇指拖到另一个拇指时,他们会将第二个拇指放在第一个拇指的上方(即,第一个拇指不可拖动,直到您移动第二个拇指才能看到第一个拇指)。

改变这些行为不应该太难,但确实需要额外的工作。您可以在大拇指上添加一个边距,以防止它们彼此重叠(但是,您还需要在显示刻度时更改拇指形状,以及调整轨道边距,以使所有内容都排成一行) 。您可以将它们放入,但不要移除重复按钮,而是调整其位置,以便按照您想要的方式使用两个拇指进行操作。

我将这些任务作为练习给读者。 :)

+0

嗨,我怎么能找到'SliderThumbHorizo​​ntalDefault'模板? – cKNet 2015-02-20 16:11:06

+0

@CKocer:如果您按照上述说明操作,即使用“编辑模板/编辑副本...”命令,Visual Studio会将该模板以及所需的所有内容添加到UserControl的资源中,重新编辑。如果您在XAML中找到对其的引用,则可以使用Visual Studio的“转到定义”命令(即** F12 **)快速导航到它。 – 2015-02-20 19:12:50

+1

这个应该是答案。标记的答案不显示任何代码,对社区没有帮助。这个问题也是一样。 – 2016-02-02 11:06:24