Я понимаю, что этому вопросу более трех лет. Тем не менее, я использовал пример слайдера с более чем одним большим пальцем как упражнение, чтобы узнать больше о WPF, и натолкнулся на этот вопрос, когда пытался выяснить, как это сделать. К сожалению, связанный пример, по-видимому, больше не существует (хороший пример того, почему вопросы и ответы StackOverflow не должны использовать ссылки для какой-либо детали, которая важна для вопроса или ответа).
Я просмотрел большое количество примеров и статей по этой теме, и хотя я не нашел ни одного, который бы специально включал тики, там было достаточно информации, чтобы я мог это выяснить. Я нашел одну статью особенно хорошей в том смысле, что она была достаточно ясной и точной, и в то же время раскрыла пару действительно полезных приемов, которые являются ключевыми в выполнении этой задачи.
Мой конечный результат выглядит так:
Итак, для блага других, которые могут захотеть сделать то же самое или просто хотят лучше понять общие приемы, вот как вы создаете ползунковый регулятор с двумя пальцами, который поддерживает различные функции галочек основного слайдер & 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);
}
В моем случае мне потребовались только Minimum
, Maximum
, IsSnapToTickEnabled
, TickFrequency
, TickPlacement
и Ticks
из нижележащих ползунков и два новых свойства для сопоставления с отдельными значениями ползунка LowerValue
и HigherValue
. Сначала я должен был объявить 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
имя root и сослался на это в объявлениях Binding
. Большинство свойств напрямую переходят к идентичным свойствам в UserControl
, но, конечно, отдельные свойства Value
для каждого элемента управления Slider
сопоставляются с соответствующими свойствами LowerValue
и UpperValue
UserControl
.
Теперь вот самая хитрая часть. Если вы просто остановитесь здесь, вы получите что-то похожее на это:
Второй объект Slider
находится полностью поверх первого, в результате чего его дорожка закрывает первый большой палец Slider
. Это не просто визуальная проблема; второй объект Slider
, находящийся сверху, получает все щелчки мышью, предотвращая настройку первого Slider
.
Чтобы исправить это, я отредактировал стиль для второго ползунка, чтобы удалить те визуальные элементы, которые мешают. Я оставил их для первого ползунка, чтобы обеспечить фактические визуальные эффекты для элемента управления. К сожалению, я не смог найти способ декларативного переопределения только тех частей, которые мне нужно было изменить. Но с помощью Visual Studio вы можете создать полную копию существующего стиля, которую затем можно отредактировать при необходимости:
- Переключитесь в режим «Дизайн» в конструкторе WPF для вашего
UserControl
- Щелкните правой кнопкой мыши по ползунку и выберите «Редактировать шаблон / Редактировать копию ...» во всплывающем меню
Это так просто. :) Это добавит атрибут Style
к объявлению Slider
в вашем UserControl
XAML, ссылаясь на новый стиль, который вы только что создали.
Элемент управления Slider
на самом деле имеет два основных шаблона элемента управления: один для горизонтальной ориентации и один для вертикальной. Я опишу изменения в горизонтальном шаблоне здесь; Я предполагаю, что будет очевидно, как сделать подобные изменения в вертикальном шаблоне.
Я использую функцию «Перейти к определению» в Visual Studio, чтобы быстро добраться до нужной мне части шаблона: найдите атрибут Style
в Slider
вашего UserControl
, нажмите на название стиля и нажмите F12 . Это приведет вас к основному объекту Style
, где вы найдете Setter
для горизонтального шаблона (вертикальный шаблон управляется Setter
в Trigger
на основе значения Orientation
). Нажмите на название горизонтального шаблона (когда я делал это, это был «SliderHorizontal», но я думаю, что он может измениться, и, конечно, он будет другим для других типов элементов управления).
Как только вы доберетесь до 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
на измененный.
Помимо техник, продемонстрированных здесь, другие могут захотеть сделать что-то немного по-другому. Например, я полностью отбросил кнопки повтора, что означает, что вы можете только перетаскивать большой палец. Нажатие на дорожку за пределами большого пальца не влияет на это. Кроме того, большие пальцы по-прежнему работают так же, как и в базовом элементе управления Slider
, при этом середина большого пальца является индикатором того, где находится значение большого пальца. Это означает, что когда вы перетаскиваете один большой палец на другой, они оказываются со вторым большим пальцем сверху первого (т.е. первый большой палец не будет перетаскиваться, пока вы не переместите второй большой палец, чтобы увидеть первый).
Изменение этого поведения не должно быть слишком сложным, но оно требует дополнительной работы. Вы можете добавить поле для больших пальцев, чтобы они не накладывались друг на друга (но затем вы также захотите изменить форму большого пальца при отображении отметок, а также отрегулировать поле дорожки, чтобы все по-прежнему выравнивалось) , Вместо того, чтобы удалять кнопки повтора, вы можете оставить их на месте, но отрегулировать их положение так, чтобы они работали так, как вы хотите, двумя большими пальцами.
Я оставляю эти задания в качестве упражнения для читателя. :)