如何计算范围滑块中boder过程的精确边距值?

时间:2015-06-03 03:13:25

标签: c# wpf xaml

我创建了一个RangeSlider控件: XAML:

<Style x:Key="RangeSliderRepeatButton" TargetType="{x:Type RepeatButton}">
    <Setter Property="SnapsToDevicePixels" Value="true" />
    <Setter Property="OverridesDefaultStyle" Value="true" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type RepeatButton}">
                <Border Background="Transparent" Height="6" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="RangeSliderThumbStyle" TargetType="{x:Type Thumb}">
    <Setter Property="SnapsToDevicePixels" Value="true" />
    <Setter Property="OverridesDefaultStyle" Value="true" />
    <Setter Property="VerticalAlignment" Value="Center" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Thumb">
                    <Grid Margin="0,0,0,10">
                        <Label x:Name="PART_ValueOfSlider" Content="{Binding Value, RelativeSource={RelativeSource AncestorType={x:Type Slider}}}"
                                   Margin="0 -20 0 0"
                                   HorizontalAlignment="Center"
                                   Foreground="Red"/>
                        <Path x:Name="PART_Rectangle" Fill="Gray">
                            <Path.Data>
                                <RectangleGeometry Rect="0,0 10 9" RadiusX="2" RadiusY="2"></RectangleGeometry>
                            </Path.Data>
                            <Path.Effect>
                                <DropShadowEffect ShadowDepth="2" BlurRadius="3" Color="Black" Opacity="0.4" Direction="270" />
                            </Path.Effect>
                        </Path>
                        <Path x:Name="PART_Traingle" Data="M 0 8 L 5 14 L 10 8 Z" Fill="Gray" >
                            <Path.Effect>
                                <DropShadowEffect ShadowDepth="1" BlurRadius="0" Color="Black" Opacity="0.4" Direction="270" />
                            </Path.Effect>
                        </Path>
                    </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="PART_Rectangle" Property="Fill" Value="{DynamicResource LabledSliderThumbHoverBrush}" />
                        <Setter TargetName="PART_Traingle" Property="Fill" Value="{DynamicResource LabledSliderThumbHoverBrush}" />
                    </Trigger>
                    <Trigger Property="IsMouseCaptured" Value="True">
                        <Setter TargetName="PART_Rectangle" Property="Fill" Value="{DynamicResource LabledSliderThumbFocusesBrush}" />
                        <Setter TargetName="PART_Traingle" Property="Fill" Value="{DynamicResource LabledSliderThumbFocusesBrush}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="RangeSlider" TargetType="{x:Type Slider}">
    <Setter Property="Focusable" Value="False"/>
    <Setter Property="SnapsToDevicePixels" Value="true" />
    <Setter Property="OverridesDefaultStyle" Value="true" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Slider}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="30" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" MinWidth="{TemplateBinding MinWidth}"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Track Grid.Column="0" x:Name="PART_Track">
                        <Track.DecreaseRepeatButton>
                            <RepeatButton Style="{StaticResource RangeSliderRepeatButton}" Command="Slider.DecreaseLarge" />
                        </Track.DecreaseRepeatButton>
                        <Track.Thumb>
                            <Thumb x:Name="PART_Thumb" Style="{StaticResource RangeSliderThumbStyle}"/>
                        </Track.Thumb>
                        <Track.IncreaseRepeatButton>
                            <RepeatButton Style="{StaticResource RangeSliderRepeatButton}" Command="Slider.IncreaseLarge" />
                        </Track.IncreaseRepeatButton>
                    </Track>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Grid x:Name="LayoutRoot" Background="#FF878889">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="47*"/>
        <ColumnDefinition Width="353*"/>
    </Grid.ColumnDefinitions>
    <Border VerticalAlignment="Center" 
                            BorderBrush="Black" 
                            Background="Black" 
                            Height="10" 
                            Grid.Column="0"
                            BorderThickness="1"
                            Padding="2"
                            CornerRadius="4" Grid.ColumnSpan="2" Margin="0,5"/>

    <Border x:Name="progressBorder" 
                            SnapsToDevicePixels="True" 
                            Background="Blue"  
                            BorderBrush="Blue" 
                            Height="6"
                            VerticalAlignment="Center" Grid.ColumnSpan="2" Margin="0,7"
                            />
    <Slider x:Name="LowerSlider"
            Minimum="{Binding RelativeSource={RelativeSource AncestorType=local:RangeSlider}, Path=Minimum, Mode=TwoWay}"
            Maximum="{Binding RelativeSource={RelativeSource AncestorType=local:RangeSlider}, Path=Maximum, Mode=TwoWay}"
            Value="{Binding RelativeSource={RelativeSource AncestorType=local:RangeSlider}, Path=LowerValue, Mode=TwoWay}"
            Style="{StaticResource SliderStyle}" Grid.ColumnSpan="2" />
    <Slider x:Name="UpperSlider"
            Minimum="{Binding RelativeSource={RelativeSource AncestorType=local:RangeSlider}, Path=Minimum, Mode=TwoWay}"
            Maximum="{Binding RelativeSource={RelativeSource AncestorType=local:RangeSlider}, Path=Maximum, Mode=TwoWay}"
            Value="{Binding RelativeSource={RelativeSource AncestorType=local:RangeSlider}, Path=UpperValue, Mode=TwoWay}"
            Style="{StaticResource SliderStyle}" Grid.Column="1" />
</Grid>

RangeSlider.cs

public partial class RangeSlider : UserControl
{
    public RangeSlider()
    {
        this.InitializeComponent();
        this.LayoutUpdated += new EventHandler(RangeSlider_LayoutUpdated);
    }

    void RangeSlider_LayoutUpdated(object sender, EventArgs e)
    {
        SetProgressBorder();
    }

    private void SetProgressBorder()
    {
        double lowerPoint = (this.ActualWidth * (LowerValue - Minimum)) / (Maximum - Minimum);
        double upperPoint = (this.ActualWidth * (UpperValue - Minimum)) / (Maximum - Minimum);
        upperPoint = this.ActualWidth - upperPoint;

        progressBorder.Margin = new Thickness(lowerPoint, 0, upperPoint, 0);
    }

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

    public double Maximum
    {
        get { return (double)GetValue(MaximumProperty); }
        set { SetValue(MaximumProperty, 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 static readonly DependencyProperty MinimumProperty =
        DependencyProperty.Register("Minimum", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(0d, new PropertyChangedCallback(PropertyChanged)));

    public static readonly DependencyProperty LowerValueProperty =
        DependencyProperty.Register("LowerValue", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(10d, new PropertyChangedCallback(PropertyChanged)));

    public static readonly DependencyProperty UpperValueProperty =
        DependencyProperty.Register("UpperValue", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(90d, new PropertyChangedCallback(PropertyChanged)));

    public static readonly DependencyProperty MaximumProperty =
        DependencyProperty.Register("Maximum", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(100d, new PropertyChangedCallback(PropertyChanged)));

    private static void PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        RangeSlider slider = (RangeSlider)d;
        if (e.Property == RangeSlider.LowerValueProperty)
        {
            slider.UpperSlider.Value = Math.Max(slider.UpperSlider.Value, slider.LowerSlider.Value);
        }
        else if (e.Property == RangeSlider.UpperValueProperty)
        {
            slider.LowerSlider.Value = Math.Min(slider.UpperSlider.Value, slider.LowerSlider.Value);
        }
        slider.SetProgressBorder();
    }
}

Label x:Name="PART_ValueOfSlider"包含较小的值时,它可以正常工作。 work fine

Label x:Name="PART_ValueOfSlider"具有较大值时,ProgressBorder显示不会像我预期的那样显示 Not work fine yet

如何设置ProgressBorder工作正常?谢谢你的帮助!!!

2 个答案:

答案 0 :(得分:1)

我正在使用一个滑块Just Like You但它的工作对我来说很好Slider的Xaml代码

<ControlTemplate x:Key="TimeRangeSliderTemplate" TargetType="{x:Type Slider}">
    <StackPanel>
        <Border SnapsToDevicePixels="true" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>

                <Rectangle x:Name="PART_SelectionRange"/>

                <Track x:Name="PART_Track" Grid.Row="1">
                    <Track.Thumb>
                        <Thumb x:Name="Thumb">
                            <Thumb.Template>
                                <ControlTemplate TargetType="Thumb">
                                        <Rectangle Fill="Red" 
                                               Stroke="Black"
                                               StrokeThickness="1" 
                                               Width="10"
                                               Height="18"
                                               SnapsToDevicePixels="True"/>
                                </ControlTemplate>
                            </Thumb.Template>
                        </Thumb>
                    </Track.Thumb>
                </Track>
            </Grid>
        </Border>
    </StackPanel>
</ControlTemplate>

<Style TargetType="{x:Type sliders:TimeRangeSlider}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type sliders:TimeRangeSlider}">
                <Grid VerticalAlignment="Top">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding ElementName=Part_LowerSlider,Path=Value}"  Foreground="Red" VerticalAlignment="Top" Margin="{Binding ElementName=Part_SelectedRect,Path=Margin}"/>
                        <TextBlock x:Name="PART_RIGHTTHUMPVALUE" Text="{Binding ElementName=Part_UpperSlider,Path=Value}"  Foreground="Red" VerticalAlignment="Top" HorizontalAlignment="Left"/>
                    </StackPanel>
                    <Grid Grid.Row="1">
                        <Border BorderThickness="0,1,0,0" BorderBrush="Black" VerticalAlignment="Center" Height="1" Margin="5,0,5,0"/>

                        <Rectangle x:Name="Part_SelectedRect"  Fill="Green" Height="10" HorizontalAlignment="Left"/>
                        <Slider x:Name="Part_LowerSlider"
                            Minimum="{TemplateBinding Minimum}"
                            Maximum="{TemplateBinding Maximum}"
                            Value="{TemplateBinding LowerValue}"
                            Template="{StaticResource TimeRangeSliderTemplate}"
                            Margin="0,0,0,0"/>

                        <Slider x:Name="Part_UpperSlider"
                            Minimum="{TemplateBinding Minimum}"
                            Maximum="{TemplateBinding Maximum}"
                            Value="{TemplateBinding UpperValue}"
                            Template="{StaticResource TimeRangeSliderTemplate}"
                            Margin="0,0,0,0"/>
                    </Grid>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

这是Xaml的Cs文件

 public class TimeRangeSlider : Control
{
    private const string PartName_LowerSlider = "Part_LowerSlider";
    private const string PartName_UpperSlider = "Part_UpperSlider";
    private const string PartName_SelectedRegion = "Part_SelectedRect";
    private const string PART_RIGHTTHUMPVALUE = "PART_RIGHTTHUMPVALUE";

    private Slider lowerSlider;
    private Slider upperSlider;
    Rectangle selectedRect;
    TextBlock rightText;

    public TimeRangeSlider()
    {
        this.DefaultStyleKey = typeof(TimeRangeSlider);
        this.Loaded += TimeRangeSlider_Loaded;
    }

    void TimeRangeSlider_Loaded(object sender, RoutedEventArgs e)
    {
        lowerSlider.ValueChanged += LowerSlider_ValueChanged;
        upperSlider.ValueChanged += UpperSlider_ValueChanged;

        lowerSlider.Minimum = Minimum;
        lowerSlider.Maximum = Maximum;
        lowerSlider.Value = LowerValue;

        upperSlider.Minimum = Minimum;
        upperSlider.Maximum = Maximum;
        upperSlider.Value = UpperValue;

        SetView();
    }

    public override void OnApplyTemplate()
    {
        lowerSlider = this.GetTemplateChild(PartName_LowerSlider) as Slider;
        upperSlider = this.GetTemplateChild(PartName_UpperSlider) as Slider;
        selectedRect = this.GetTemplateChild(PartName_SelectedRegion) as Rectangle;
        rightText = this.GetTemplateChild(PART_RIGHTTHUMPVALUE) as TextBlock;

        base.OnApplyTemplate();
    }


    private void LowerSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        upperSlider.Value = Math.Max(upperSlider.Value, lowerSlider.Value);
        SetView();
    }

    private void UpperSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        lowerSlider.Value = Math.Min(upperSlider.Value, lowerSlider.Value);
        SetView();
    }

    private void SetView()
    {
        LowerValue = lowerSlider.Value;
        UpperValue = upperSlider.Value;

        var unit = lowerSlider.ActualWidth / Maximum;

        var leftMargin = LowerValue * unit;

        if (UpperValue > LowerValue)
        {
            var width = (UpperValue - LowerValue) * unit;
            selectedRect.Width = width;
        }

        selectedRect.Margin = new Thickness(leftMargin, 0, 0, 0);
        rightText.Margin = new Thickness(selectedRect.Width-20, 0, 0, 0);

    }

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

    public static readonly DependencyProperty MinimumProperty =
        DependencyProperty.Register("Minimum", typeof(double), typeof(TimeRangeSlider), new UIPropertyMetadata(0d));

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

    public static readonly DependencyProperty LowerValueProperty =
        DependencyProperty.Register("LowerValue", typeof(double), typeof(TimeRangeSlider), new UIPropertyMetadata(0d));

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

    public static readonly DependencyProperty UpperValueProperty =
        DependencyProperty.Register("UpperValue", typeof(double), typeof(TimeRangeSlider), new UIPropertyMetadata(0d));

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

    public static readonly DependencyProperty MaximumProperty =
        DependencyProperty.Register("Maximum", typeof(double), typeof(TimeRangeSlider), new UIPropertyMetadata(1d));
}

以及控件的使用

 <SliderControl:TimeRangeSlider x:Name="slider" 
                           Margin="10" 
                           LowerValue="30"
                           UpperValue="70"
                           Minimum="0"
                           Maximum="100"
                           />

答案 1 :(得分:0)

画布中的元素不会影响度量。所以,使用代码:

<Canvas>
   <Label x:Name="PART_ValueOfSlider" Content="{Binding Value, RelativeSource={RelativeSource AncestorType={x:Type Slider}}}"
        Margin="0 0 0 0" 
        HorizontalAlignment="Center" 
        Foreground="Red">

     <Label.RenderTransform>
            <ScaleTransform ScaleY="-1" />
     </Label.RenderTransform>
      <Label.LayoutTransform>
           <TransformGroup>
              <ScaleTransform ScaleY="-1" />
            </TransformGroup>
      </Label.LayoutTransform>
    </Label>
</Canvas>