gpt4 book ai didi

带有两个拇指的 WPF slider

转载 作者:行者123 更新时间:2023-12-02 11:24:29 25 4
gpt4 key购买 nike

我正在尝试为我的应用程序创建一个带有两个拇指的滑块,用作范围滑块,但遇到了问题。我的基本要求是获得一个带有刻度线和两个拇指的滑块,它们都设置为 IsSnapToTickEnabled="true"。

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

我尝试修改滑块的模板并向其添加另一个拇指,但随后我不知道如何获取所选拇指的值。

有没有人有一个带有两个拇指、刻度线和对齐刻度的滑块示例?我发现的所有范围滑块示例都使用两个相互重叠的滑块,并且它们都不允许刻度线或对齐刻度线。

谢谢。

最佳答案

我意识到这个问题已经三年多了。但是,我一直在使用具有多个拇指的滑块示例作为练习来了解有关 WPF 的更多信息,并且在我试图弄清楚如何执行此操作时遇到了这个问题。不幸的是,链接的示例似乎不再存在(一个很好的例子,说明了为什么 StackOverflow 问题和答案不应该使用对问题或答案至关重要的任何细节的链接)。

我查看了大量关于该主题的示例和文章,虽然我没有找到专门启用刻度线的示例和文章,但那里有足够的信息让我弄清楚。我找到了 one article特别好,因为它相当清晰和切中要害,同时揭示了一些非常有用的技术,它们是完成这项任务的关键。

我的最终结果是这样的:

correct DoubleThumbSlider

因此,为了其他可能想要做同样事情的人,或者只是想更好地理解一般技术的人的利益,这里是如何制作一个支持基本滑块的各种刻度功能的双拇指滑块控件......

起点是UserControl类本身。在 Visual Studio 中,添加一个新的 UserControl类到项目。现在,添加您想要支持的所有属性。不幸的是,我还没有找到一种机制可以简单地将属性委托(delegate)给 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);
}

就我而言,我只需要 Minimum , Maximum , IsSnapToTickEnabled , TickFrequency , TickPlacement , 和 Ticks来自底层滑块,以及两个映射到各个滑块值的新属性, 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控件,将属性绑定(bind)到我的 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名称“root”,并在 Binding 中引用了该名称声明。大多数属性直接指向 UserControl 中的相同属性。 ,但当然是个人 Value每个属性 Slider控件映射到适当的 LowerValueUpperValue UserControl 的属性(property).

现在,这是最棘手的部分。如果你只是停在这里,你会得到如下所示的内容:
incorrect DoubleThumbSlider
第二个 Slider对象完全位于第一个之上,导致其轨道覆盖了第一个 Slider拇指。这也不仅仅是视觉问题;第二个 Slider位于顶部的对象接收所有鼠标点击,阻止第一个 Slider从根本上调整。

为了解决这个问题,我编辑了第二个滑块的样式以删除那些妨碍的视觉元素。我将它们留给第一个滑块,以提供控件的实际轨道视觉效果。不幸的是,我无法找到一种方法来声明性地覆盖我需要更改的部分。但是使用 Visual Studio,您可以创建现有样式的完整副本,然后可以根据需要对其进行编辑:
  • 在 WPF 设计器中为您的 UserControl 切换到“设计”模式
  • 右键单击滑块并从弹出菜单中选择“编辑模板/编辑副本...”

  • 就这么简单。 :) 这将添加一个 Style归因于 Slider您的 UserControl 中的声明XAML,引用您刚刚创建的新样式。
    Slider控件实际上有两种主要的控件模板,一种用于水平方向,一种用于垂直方向。我将在这里描述水平模板的变化;我认为如何对垂直模板进行类似的更改是显而易见的。

    我使用 Visual Studio 的“转到定义”功能快速找到我需要的模板部分:找到 Style Slider 中的属性您的 UserControl ,单击样式名称并按 F12。这将带您进入主 Style对象,您会在其中找到 Setter对于水平模板(垂直模板由 Setter 中的 Trigger 控制,基于 Orientation 值)。单击水平模板的名称(我这样做时它是“SliderHorizo​​ntal”,但我想它可能会改变,当然对于其他类型的控件会有所不同)。

    一旦你到达 ControlTemplate , 从不应使用的元素中删除所有视觉属性。这意味着删除一些元素,并删除 Background , BorderBrush , BorderThickness , Fill等来自您无法完全删除的元素。就我而言,我删除了 RepeatButton s 完全,并修改了我需要的其他元素,以便它们不显示或占用任何空间(因此它们不会收到鼠标点击)。我结束了这个:
    <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不会改变。 IE。第二个滑块的样式被复制并硬编码到程序中,但该硬编码样式仍然取决于复制它的股票样式的布局。如果该股票样式发生变化,则第一个滑块的布局可能会发生变化,使第二个滑块不再对齐或以其他方式看起来不正确。

    如果这是一个问题,那么您可以稍微不同地处理模板:而不是修改 SliderHorizontal模板,复制它和 Style引用它,更改两者的名称,并更改 Style 的副本以便它引用复制的模板而不是原始模板。然后你只要修改副本,设置第一个 Slider的样式到未修改 Style ,以及第二个 Slider 的样式到修改后的。

    除了此处演示的技术之外,其他人可能希望以稍微不同的方式做事。例如,我完全放弃了重复按钮,这意味着您只能拖动拇指。单击拇指外的轨道不会影响它。此外,拇指仍然像在基本 Slider 中一样工作。控件,拇指的中间是拇指值所在位置的指示器。这意味着当您将一个拇指拖动到另一个拇指时,它们会以第二个拇指在第一个拇指的顶部结束(即第一个拇指将无法拖动,直到您将第二个拇指移动到足以看到第一个拇指为止)。

    改变这些行为应该不会太难,但它确实需要额外的工作。您可以为拇指添加边距以防止它们相互重叠(但是您还需要在显示刻度时更改拇指形状,以及调整轨道边距,以便所有内容仍然对齐) .您可以将重复按钮留在原处,而不是移除重复按钮,而是调整它们的位置,以便它们用两个拇指按您想要的方式操作。

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

    关于带有两个拇指的 WPF slider ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5395957/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com