我正在开发一个应用程序,其中我有很多带有标签的滑块,如下所示:
为了重复使用,我想创建一个自定义LabeledSlider
控件,其中包含左右标签字符串的额外属性。一个简单的UserControl
起初看起来很合适,直到我意识到我必须重新实现我计划使用的所有Slider
属性和方法(其中有很多)。 / p>
所以从Slider
继承似乎是更好的选择,但看起来我必须创建一个自定义样式才能这样做。有没有办法从Slider
派生并在保留现有样式的同时添加这些标签?
对于它的价值,我正在使用MahApps.Metro。
答案 0 :(得分:4)
如果您想避免使用UserControl实现自定义滑块所带来的所有管道,您可以直接使用Slider控件和自定义控件模板。
自定义控件模板将定义显示自定义滑块所需的所有UI元素;像两个标签一样,"内部"滑块控件(UI中显示的实际滑块)以及任何其他所需/必需的UI元素。
这种控制模板的简单形式如何显示(不完整):
<Slider
TickPlacement="Both"
TickFrequency="0.5"
BorderBrush="Red"
BorderThickness="3" >
<Slider.Template>
<ControlTemplate TargetType="Slider">
<Border
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="Left label" Grid.Column="0" Grid.Row="0" />
<TextBlock Text="Right label" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Right" />
<Slider Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1"
BorderThickness="0"
Value="{Binding Value, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
TickFrequency="{TemplateBinding TickFrequency}
TickPlacement="{TemplateBinding TickPlacement}"
...
/>
</Grid>
</Border>
</ControlTemplate>
</Slider.Template>
</Slider>
请注意&#34;外部&#34;的所有属性滑块应该传递到&#34;内部&#34;滑块应该具有相应的TemplateBindings用于&#34;内部&#34;滑块(如 Value , TickFrequency 和 TickPlacement 属性所示)。
注意 Value 属性的模板绑定。对于任何需要双向的模板绑定,速记形式{TemplateName SourceProperty}
不会单向执行。对于双向模板绑定,绑定应声明为{Binding SourceProperty, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}
。
&#34;外部&#34;的一些属性。滑块,你可能不希望通过&#34;内部&#34;滑块。也许您希望边框(滑块控件支持边框)将滑块与文本标签一起环绕。对于这种情况,我上面的示例控件模板具有围绕&#34;内部&#34;滑块和文本标签。请注意&#34;外部&#34;与边界相关的属性。使用控件模板的滑块控件正在模板绑定到此Border元素,而不是&#34; inner&#34;滑块。
但是,边界的处理方式仍然存在问题。如果您(或其他人)在某个时间定义了包含边框的滑块的默认样式,则此默认样式的相应边框参数也将应用于&#34;内部&#34;滑块 - 一些不受欢迎的东西。为防止这种情况发生,我的示例控件模板为&#34; inner&#34;显式设置 BorderThickness 的值。滑块。
对于控件模板中UI元素的任何其他属性,以类似的方式执行此操作,您不希望它们各自的默认样式受到影响。
如果您希望能够通过绑定更改标签的文本,则需要为它们引入一些属性。实现此目的的一种方法是将它们实现为附加属性。它们的实现可以相当简单:
public static class SliderExtensions
{
public static string GetLeftLabel(DependencyObject obj)
{
return (string)obj.GetValue(LeftLabelProperty);
}
public static void SetLeftLabel(DependencyObject obj, string value)
{
obj.SetValue(LeftLabelProperty, value);
}
public static readonly DependencyProperty LeftLabelProperty = DependencyProperty.RegisterAttached(
"LeftLabel",
typeof(string),
typeof(SliderExtensions)
);
public static string GetRightLabel(DependencyObject obj)
{
return (string)obj.GetValue(RightLabelProperty);
}
public static void SetRightLabel(DependencyObject obj, string value)
{
obj.SetValue(RightLabelProperty, value);
}
public static readonly DependencyProperty RightLabelProperty = DependencyProperty.RegisterAttached(
"RightLabel",
typeof(string),
typeof(SliderExtensions)
);
}
上面的代码提供了两个附加属性(类型为string
),称为&#34; SliderExtensions.LeftLabel&#34;和&#34; SliderExtensions.RightLabel&#34;。然后你可以按如下方式使用它们:
<Slider
local:SliderExtensions.LeftLabel="Port"
local:SliderExtensions.RightLabel="Starboard"
TickPlacement="Both"
TickFrequency="0.5"
BorderBrush="Red"
BorderThickness="3" >
<Slider.Template>
<ControlTemplate TargetType="Slider">
...
<TextBlock Text="{TemplateBinding local:SliderExtensions.LeftLabel}" Grid.Column="0" Grid.Row="0" />
<TextBlock Text="{TemplateBinding local:SliderExtensions.RightLabel}" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Right" />
...
...
如果您不想使用附加属性,您还可以从标准Slider类派生自己的自定义滑块控件,并将 LeftLabel 和 RightLabel 实现为此自定义滑块类的依赖项属性。
public class MyCustomSlider : Slider
{
public string LeftLabel
{
get { return (string)GetValue(LeftLabelProperty); }
set { SetValue(LeftLabelProperty, value); }
}
public static readonly DependencyProperty LeftLabelProperty = DependencyProperty.Register(
nameof(LeftLabel),
typeof(string),
typeof(MyCustomSlider)
);
public string RightLabel
{
get { return (string)GetValue(RightLabelProperty); }
set { SetValue(RightLabelProperty, value); }
}
public static readonly DependencyProperty RightLabelProperty = DependencyProperty.Register(
nameof(RightLabel),
typeof(string),
typeof(MyCustomSlider)
);
}
此方法的另一个好处是,您可以专门为MyCustomSlider类型定义默认样式。有关该主题的更多信息,请参阅this answer。
如果您愿意这样做,请不要忘记调整控件模板的目标类型:
<local:MyCustomSlider
LeftLabel="Port"
RightLabel="Starboard"
TickPlacement="Both"
TickFrequency="0.5"
BorderBrush="Red"
BorderThickness="3" >
<local:MyCustomSlider.Template>
<ControlTemplate TargetType="TargetType="local:MyCustomSlider"">
...
<TextBlock Text="{TemplateBinding LeftLabel}" Grid.Column="0" Grid.Row="0" />
<TextBlock Text="{TemplateBinding RightLabel}" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Right" />
...
...
您不必强制使用字符串属性和TextBlocks作为标签。您可以使用ContentPresenter
代替TextBlock
,并创建object
类型的 LeftLabel 和 RightLabel 属性。这样你仍然可以使用文本标签,但是 - 如果需要 - 你也可以使用任何其他内容(例如图像)。
附注:我的答案基于标准的WPF滑块控件。如果你使用MahApps.Metro提供的滑块控件(我只知道很少),某些细节 - 例如命名,类型,属性的存在或不存在 - 可能与我的答案显示的不同。