WPF自定义滑块PART_SelectionRange落后于拇指

4
我为我的应用程序创建了自定义滑块和拇指模板。它的工作正常,除了PART_SelectionRange矩形之外,该矩形从拇指后面略微开始,并且到达最大值时明显落后于它。
我不知道如何解决这个问题,因为看起来问题在控制PART_SelectionRange矩形大小的任何类的行为中。 此视频说明了问题(抱歉,我不知道如何内联它)。
<ControlTemplate x:Key="SliderThumbHorizontalDefault" TargetType="{x:Type Thumb}">
    <Grid>
        <!-- SliderThumb -->
        <Path x:Name="grip" Data="M-0.4,6.4 C1.2,1 9,1 10.5,6.5 12,11.787571 5.0267407,18.175417 5.0267407,18.175417 5.0267407,18.175417 -2,11.8 -0.4,6.4 z"
            Fill="White" Stretch="Fill" SnapsToDevicePixels="True"
            Stroke="Black" StrokeThickness="1" UseLayoutRounding="True" />
        <ContentPresenter Margin="0 4 0 0"  Content="{Binding Value, RelativeSource={RelativeSource AncestorType={x:Type Slider}}}" TextBlock.Foreground="Black" TextBlock.FontSize="20" TextBlock.TextAlignment="Center" />
    </Grid>
</ControlTemplate>

<ControlTemplate x:Key="SliderHorizontal" TargetType="{x:Type Slider}">
    <ControlTemplate.Resources>
        <vc:TickConverter x:Key="TickConverter" />
    </ControlTemplate.Resources>
    <Grid Height="{StaticResource SliderHeight}">
        <Border x:Name="TrackBackground" BorderBrush="{StaticResource SliderThumb.Track.Border}" BorderThickness="0"
                Background="White"
                CornerRadius="{Binding ActualHeight, Converter={StaticResource HalvingConverter}, ElementName=TrackBackground}">
            <!-- This rectangle is what lags behind! -->
            <Rectangle x:Name="PART_SelectionRange" Fill="SlateBlue"
                        HorizontalAlignment="Left" RadiusY="3" RadiusX="3" />
        </Border>
        <Track x:Name="PART_Track" >
            <Track.Thumb>
                <Thumb x:Name="Thumb" Focusable="False" Width="35" Height="47" OverridesDefaultStyle="True"
                            Template="{StaticResource SliderThumbHorizontalDefault}" Margin="-14.65,-55,-16,12"/>
            </Track.Thumb>
        </Track>
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsSelectionRangeEnabled" Value="true">
            <Setter Property="Visibility" TargetName="PART_SelectionRange" Value="Visible" />
        </Trigger>
        <Trigger Property="IsKeyboardFocused" Value="true">
            <Setter Property="Foreground" TargetName="Thumb" Value="Blue" />
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

<Style TargetType="{x:Type Slider}">
    <Setter Property="MinHeight" Value="{StaticResource SliderHeight}" />
    <Setter Property="MaxHeight" Value="{StaticResource SliderHeight}" />
    <Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
    <Setter Property="IsSelectionRangeEnabled" Value="True" />
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="BorderBrush" Value="Transparent" />
    <Setter Property="Foreground" Value="{StaticResource SliderThumb.Static.Foreground}" />
    <Setter Property="SelectionStart" Value="{Binding Minimum, RelativeSource={RelativeSource Self}}" />
    <Setter Property="SelectionEnd" Value="{Binding Value, RelativeSource={RelativeSource Self}}" />
    <Setter Property="Template" Value="{StaticResource SliderHorizontal}" />
</Style>

此时,我不知道应该尝试修改什么。我相信我已经尝试从xaml更改了每个有意义的属性,所以我认为问题在于Slider类,但是无法理解具体问题出在哪里。

2个回答

1

很遗憾,您无法仅使用自定义StyleControlTemplate更改Slider的此行为。

原因是Slider控件根据Thumb的宽度调整选择范围的宽度。这会导致在将滑块位置移向其Maximum值时,选择范围条会“后退”。如果您希望选择范围恰好跟随Value,则需要将Thumb宽度设置为0。但是,显然,由于不再进行命中测试,因此您将无法移动拇指。

这是相应的选择范围大小和位置调整的当前实现

if (DoubleUtil.AreClose(range, 0d) || (DoubleUtil.AreClose(trackSize.Width, thumbSize.Width)))
{
    valueToSize = 0d;
}
else
{
    valueToSize = Math.Max(0.0, (trackSize.Width - thumbSize.Width) / range);
}

rangeElement.Width = ((SelectionEnd - SelectionStart) * valueToSize);
if (IsDirectionReversed)
{
    Canvas.SetLeft(rangeElement, (thumbSize.Width * 0.5) + Math.Max(Maximum - SelectionEnd, 0) * valueToSize);
}
else
{
    Canvas.SetLeft(rangeElement, (thumbSize.Width * 0.5) + Math.Max(SelectionStart - Minimum, 0) * valueToSize);
}

您现在有两个选择:

  • 实现自己的滑块控件,不根据 Thumb 宽度调整其选择范围
  • 或者从您的 ControlTemplate 中的选择矩形中删除 "PART_SelectionRange" 名称,并使用 BindingIValueConverter(或 Behavior,或附加属性)手动控制外观

1

根据@dymanoid的回答,我手动实现了这个行为(在无法创建新滑块后)。

我移除了PART_SelectionRange控件(必须将其设为透明,否则它不会编译),并用另一个矩形替换了它。

<Border x:Name="TrackBackground" BorderBrush="{StaticResource SliderThumb.Track.Border}" BorderThickness="0"
        Background="{StaticResource SliderThumb.Track.Background}"
        CornerRadius="{Binding ActualHeight, Converter={StaticResource HalvingConverter}, ElementName=TrackBackground}">
    <Rectangle x:Name="SelectionRange" Fill="{StaticResource SliderThumb.Track.BackgroundSelected}"
                HorizontalAlignment="Left" RadiusY="3" RadiusX="3">
        <Rectangle.Width>
            <MultiBinding Converter="{StaticResource SliderToSelectionWidthConverter}">
                <Binding Path="Value" RelativeSource="{RelativeSource TemplatedParent}"/>
                <Binding Path="Maximum" RelativeSource="{RelativeSource TemplatedParent}"/>
                <Binding Path="Minimum" RelativeSource="{RelativeSource TemplatedParent}"/>
                <Binding Path="ActualWidth" RelativeSource="{RelativeSource TemplatedParent}"/>
            </MultiBinding>
        </Rectangle.Width>
    </Rectangle> 
</Border>

重要的是,它的宽度受到滑块的Value、Minimum、Maximum和ActualWidth属性的限制。使用这个转换器:
class SliderToSelectionWidthConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var Value = (double) values[0];
        var Minimum = (double) values[1];
        var Maximum = (double) values[2];
        var ActualWidth = (double) values[3];
        return (1 - (Value - Minimum) / (Maximum - Minimum)) * ActualWidth;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

这些值可以用于计算SelectionRange矩形的实际宽度。为什么默认Slider没有做到这一点,我不知道。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接