达到限制时禁用向上或向下按钮的 UpDown 控件
UpDown control that disables up or down button when limit is reached
我的问题是基于对这个 post 的回答之一:
Where is the WPF Numeric UpDown control?
Squirrel.Downy先生回答。
我想要完成的是一个数字向上控件,当按下按钮的时间更长时,increases/decreases 的数量更大,否则 increase/decrease 是正常数量。此外,当达到 max/min 时,按钮应禁用。
我有一个基于 Slider 的样式,它包含 2 个 HoldButton 类型的按钮(up/down,从 RepeatButton 派生)和一个用于值的只读 TextBlock。
在 HoldButton 中,我有一个 ICommand 的 2 个依赖属性。这些是 ClickAndHoldCommand 和 ClickCommand,它们根据鼠标按钮按下的长度从 OnPreviewMouseLeftButtonDown() 或 OnPreviewMouseLeftButtonUp() 执行。在 xaml 中,它们分别绑定到 Slider.IncreaseLarge 和 Slider.IncreaseSmall。
如何在达到最大值时禁用向上按钮并在达到最小值时禁用向下按钮?困难在于,例如,当我禁用滑块时,向上鼠标事件不再起作用...
<Style TargetType="{x:Type Slider}" x:Key="NumericUpDown">
<Style.Resources>
<Style x:Key="RepeatButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Focusable" Value="false" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Padding" Value="0" />
<Setter Property="Width" Value="20" />
</Style>
</Style.Resources>
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
<Setter Property="SmallChange" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
<Grid>
<Rectangle RadiusX="10" RadiusY="10" Stroke="{StaticResource SolidBrushLightGrey}" Fill="Black" StrokeThickness="1" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="ControlName" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<TextBlock Grid.Row="1" x:Name="ControlUnits" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<usercontrols:HoldButton Grid.Row="2" Delay="250" Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.IncreaseLarge}"
ClickCommand="{x:Static Slider.IncreaseSmall}"
MaxWidth="60" Height="60" Width="60" Style="{StaticResource ButtonStyleGeneral}" Content="+">
</usercontrols:HoldButton>
<TextBlock Grid.Row="3" x:Name="Temperature" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" FontSize="30" Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Value, StringFormat=N1}" />
<usercontrols:HoldButton Grid.Row="4" Delay="250" Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.DecreaseLarge}"
ClickCommand="{x:Static Slider.DecreaseSmall}"
MaxWidth="60" Height="60" Width="60" Style="{StaticResource ButtonStyleGeneral}" Content="-">
</usercontrols:HoldButton>
<Border x:Name="TrackBackground" Visibility="Collapsed">
<Rectangle x:Name="PART_SelectionRange" Visibility="Collapsed" />
</Border>
<Thumb x:Name="Thumb" Visibility="Collapsed" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public partial class HoldButton : RepeatButton
{
private bool buttonIsHeldPressed;
public HoldButton()
{
InitializeComponent();
buttonIsHeldPressed = false;
this.PreviewMouseLeftButtonUp += OnPreviewMouseLeftButtonUp;
// RepeatButton fires click event repeatedly while button is being pressed!
this.Click += HoldButton_Click;
}
private void HoldButton_Click(object sender, RoutedEventArgs e)
{
Trace.WriteLine("HoldButton_Click()");
if (EnableClickHold)
{
if (numberButtonRepeats > 2)
{
ClickAndHoldCommand.Execute(this.CommandParameter);
e.Handled = true;
buttonIsHeldPressed = true;
}
numberButtonRepeats++;
}
}
public bool EnableClickHold
{
get { return (bool)GetValue(EnableClickHoldProperty); }
set { SetValue(EnableClickHoldProperty, value); }
}
public ICommand ClickAndHoldCommand
{
get { return (ICommand)GetValue(ClickAndHoldCommandProperty); }
set { SetValue(ClickAndHoldCommandProperty, value); }
}
public ICommand ClickCommand
{
get { return (ICommand)GetValue(ClickCommandProperty); }
set { SetValue(ClickCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for ClickAndHoldCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ClickAndHoldCommandProperty =
DependencyProperty.Register("ClickAndHoldCommand", typeof(ICommand), typeof(HoldButton), new UIPropertyMetadata(null));
// Using a DependencyProperty as the backing store for ClickCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ClickCommandProperty =
DependencyProperty.Register("ClickCommand", typeof(ICommand), typeof(HoldButton), new UIPropertyMetadata(null));
// Using a DependencyProperty as the backing store for EnableClickHold. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EnableClickHoldProperty =
DependencyProperty.Register("EnableClickHold", typeof(bool), typeof(HoldButton), new PropertyMetadata(false));
// Using a DependencyProperty as the backing store for MillisecondsToWait. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MillisecondsToWaitProperty =
DependencyProperty.Register("MillisecondsToWait", typeof(int), typeof(HoldButton), new PropertyMetadata(0));
public int MillisecondsToWait
{
get { return (int)GetValue(MillisecondsToWaitProperty); }
set { SetValue(MillisecondsToWaitProperty, value); }
}
private int numberButtonRepeats;
private void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (EnableClickHold)
{
numberButtonRepeats = 0;
if(!buttonIsHeldPressed)
{
ClickCommand?.Execute(this.CommandParameter);
}
buttonIsHeldPressed = false;
}
}
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Trace.WriteLine("OnPreviewMouseLeftButtonDown()");
if (EnableClickHold)
{
// When numberButtonRepeats comes above 1 then the button is considered to be pressed long
if (numberButtonRepeats > 1)
{
ClickAndHoldCommand?.Execute(this.CommandParameter);
}
numberButtonRepeats++;
}
}
}
<UserControl x:Class="Views.TemperatureControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Views"
xmlns:cal="http://www.caliburnproject.org"
xmlns:controls="clr-namespace:UserControls"
mc:Ignorable="d"
d:DesignHeight="250" d:DesignWidth="150">
<Slider Minimum="{Binding MinimumTemperature}"
Maximum="{Binding MaximumTemperature}"
SmallChange="{Binding TemperatureTinySteps}"
LargeChange="{Binding TemperatureSmallSteps}"
Value="{Binding ControlValue}"
Style="{StaticResource NumericUpDown}" />
</UserControl>
您应该扩展 Slider
控件并在那里实现逻辑。
最后命名 RepeatButton
元素并将 Style
移动到 Generic.xaml 文件。
public class CustomSlider : Slider
{
static CustomSlider()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomSlider), new FrameworkPropertyMetadata(typeof(CustomSlider)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.PART_IncreaseButton = GetTemplateChild(nameof(this.PART_IncreaseButton)) as UIElement;
this.PART_DecreaseButton = GetTemplateChild(nameof(this.PART_DecreaseButton)) as UIElement;
}
protected override void OnValueChanged(double oldValue, double newValue)
{
base.OnValueChanged(oldValue, newValue);
if (this.PART_IncreaseButton == null
|| this.PART_DecreaseButton == null)
{
return;
}
this.PART_IncreaseButton.IsEnabled = newValue < this.Maximum;
this.PART_DecreaseButton.IsEnabled = newValue > this.Minimum;
}
private UIElement PART_IncreaseButton { get; set; }
private UIElement PART_DecreaseButton { get; set; }
}
Generic.xaml
将 HoldButton 元素命名为 "PART_IncreaseButton"
和 "PART_IncreaseButton"
,以便您可以在模板中轻松找到它们。
<Style TargetType="{x:Type CustomSlider}">
<Style.Resources>
<Style x:Key="RepeatButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Focusable" Value="false" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Padding" Value="0" />
<Setter Property="Width" Value="20" />
</Style>
</Style.Resources>
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
<Setter Property="SmallChange" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
<Grid>
<Rectangle RadiusX="10" RadiusY="10" Stroke="{StaticResource SolidBrushLightGrey}" Fill="Black" StrokeThickness="1" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="ControlName" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<TextBlock Grid.Row="1" x:Name="ControlUnits" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<usercontrols:HoldButton x:Name="PART_IncreaseButton"
Grid.Row="2"
Delay="250"
Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.IncreaseLarge}"
ClickCommand="{x:Static Slider.IncreaseSmall}"
MaxWidth="60"
Height="60" Width="60"
Style="{StaticResource ButtonStyleGeneral}"
Content="+" />
<TextBlock Grid.Row="3" x:Name="Temperature" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" FontSize="30" Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Value, StringFormat=N1}" />
<usercontrols:HoldButton x:Name="PART_DecreaseButton"
Grid.Row="4"
Delay="250"
Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.DecreaseLarge}"
ClickCommand="{x:Static Slider.DecreaseSmall}"
MaxWidth="60"
Height="60" Width="60"
Style="{StaticResource ButtonStyleGeneral}"
Content="-" />
<Border x:Name="TrackBackground" Visibility="Collapsed">
<Rectangle x:Name="PART_SelectionRange" Visibility="Collapsed" />
</Border>
<Thumb x:Name="Thumb" Visibility="Collapsed" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
我的问题是基于对这个 post 的回答之一: Where is the WPF Numeric UpDown control? Squirrel.Downy先生回答。 我想要完成的是一个数字向上控件,当按下按钮的时间更长时,increases/decreases 的数量更大,否则 increase/decrease 是正常数量。此外,当达到 max/min 时,按钮应禁用。 我有一个基于 Slider 的样式,它包含 2 个 HoldButton 类型的按钮(up/down,从 RepeatButton 派生)和一个用于值的只读 TextBlock。 在 HoldButton 中,我有一个 ICommand 的 2 个依赖属性。这些是 ClickAndHoldCommand 和 ClickCommand,它们根据鼠标按钮按下的长度从 OnPreviewMouseLeftButtonDown() 或 OnPreviewMouseLeftButtonUp() 执行。在 xaml 中,它们分别绑定到 Slider.IncreaseLarge 和 Slider.IncreaseSmall。 如何在达到最大值时禁用向上按钮并在达到最小值时禁用向下按钮?困难在于,例如,当我禁用滑块时,向上鼠标事件不再起作用...
<Style TargetType="{x:Type Slider}" x:Key="NumericUpDown">
<Style.Resources>
<Style x:Key="RepeatButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Focusable" Value="false" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Padding" Value="0" />
<Setter Property="Width" Value="20" />
</Style>
</Style.Resources>
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
<Setter Property="SmallChange" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
<Grid>
<Rectangle RadiusX="10" RadiusY="10" Stroke="{StaticResource SolidBrushLightGrey}" Fill="Black" StrokeThickness="1" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="ControlName" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<TextBlock Grid.Row="1" x:Name="ControlUnits" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<usercontrols:HoldButton Grid.Row="2" Delay="250" Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.IncreaseLarge}"
ClickCommand="{x:Static Slider.IncreaseSmall}"
MaxWidth="60" Height="60" Width="60" Style="{StaticResource ButtonStyleGeneral}" Content="+">
</usercontrols:HoldButton>
<TextBlock Grid.Row="3" x:Name="Temperature" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" FontSize="30" Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Value, StringFormat=N1}" />
<usercontrols:HoldButton Grid.Row="4" Delay="250" Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.DecreaseLarge}"
ClickCommand="{x:Static Slider.DecreaseSmall}"
MaxWidth="60" Height="60" Width="60" Style="{StaticResource ButtonStyleGeneral}" Content="-">
</usercontrols:HoldButton>
<Border x:Name="TrackBackground" Visibility="Collapsed">
<Rectangle x:Name="PART_SelectionRange" Visibility="Collapsed" />
</Border>
<Thumb x:Name="Thumb" Visibility="Collapsed" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public partial class HoldButton : RepeatButton
{
private bool buttonIsHeldPressed;
public HoldButton()
{
InitializeComponent();
buttonIsHeldPressed = false;
this.PreviewMouseLeftButtonUp += OnPreviewMouseLeftButtonUp;
// RepeatButton fires click event repeatedly while button is being pressed!
this.Click += HoldButton_Click;
}
private void HoldButton_Click(object sender, RoutedEventArgs e)
{
Trace.WriteLine("HoldButton_Click()");
if (EnableClickHold)
{
if (numberButtonRepeats > 2)
{
ClickAndHoldCommand.Execute(this.CommandParameter);
e.Handled = true;
buttonIsHeldPressed = true;
}
numberButtonRepeats++;
}
}
public bool EnableClickHold
{
get { return (bool)GetValue(EnableClickHoldProperty); }
set { SetValue(EnableClickHoldProperty, value); }
}
public ICommand ClickAndHoldCommand
{
get { return (ICommand)GetValue(ClickAndHoldCommandProperty); }
set { SetValue(ClickAndHoldCommandProperty, value); }
}
public ICommand ClickCommand
{
get { return (ICommand)GetValue(ClickCommandProperty); }
set { SetValue(ClickCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for ClickAndHoldCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ClickAndHoldCommandProperty =
DependencyProperty.Register("ClickAndHoldCommand", typeof(ICommand), typeof(HoldButton), new UIPropertyMetadata(null));
// Using a DependencyProperty as the backing store for ClickCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ClickCommandProperty =
DependencyProperty.Register("ClickCommand", typeof(ICommand), typeof(HoldButton), new UIPropertyMetadata(null));
// Using a DependencyProperty as the backing store for EnableClickHold. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EnableClickHoldProperty =
DependencyProperty.Register("EnableClickHold", typeof(bool), typeof(HoldButton), new PropertyMetadata(false));
// Using a DependencyProperty as the backing store for MillisecondsToWait. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MillisecondsToWaitProperty =
DependencyProperty.Register("MillisecondsToWait", typeof(int), typeof(HoldButton), new PropertyMetadata(0));
public int MillisecondsToWait
{
get { return (int)GetValue(MillisecondsToWaitProperty); }
set { SetValue(MillisecondsToWaitProperty, value); }
}
private int numberButtonRepeats;
private void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (EnableClickHold)
{
numberButtonRepeats = 0;
if(!buttonIsHeldPressed)
{
ClickCommand?.Execute(this.CommandParameter);
}
buttonIsHeldPressed = false;
}
}
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Trace.WriteLine("OnPreviewMouseLeftButtonDown()");
if (EnableClickHold)
{
// When numberButtonRepeats comes above 1 then the button is considered to be pressed long
if (numberButtonRepeats > 1)
{
ClickAndHoldCommand?.Execute(this.CommandParameter);
}
numberButtonRepeats++;
}
}
}
<UserControl x:Class="Views.TemperatureControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Views"
xmlns:cal="http://www.caliburnproject.org"
xmlns:controls="clr-namespace:UserControls"
mc:Ignorable="d"
d:DesignHeight="250" d:DesignWidth="150">
<Slider Minimum="{Binding MinimumTemperature}"
Maximum="{Binding MaximumTemperature}"
SmallChange="{Binding TemperatureTinySteps}"
LargeChange="{Binding TemperatureSmallSteps}"
Value="{Binding ControlValue}"
Style="{StaticResource NumericUpDown}" />
</UserControl>
您应该扩展 Slider
控件并在那里实现逻辑。
最后命名 RepeatButton
元素并将 Style
移动到 Generic.xaml 文件。
public class CustomSlider : Slider
{
static CustomSlider()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomSlider), new FrameworkPropertyMetadata(typeof(CustomSlider)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.PART_IncreaseButton = GetTemplateChild(nameof(this.PART_IncreaseButton)) as UIElement;
this.PART_DecreaseButton = GetTemplateChild(nameof(this.PART_DecreaseButton)) as UIElement;
}
protected override void OnValueChanged(double oldValue, double newValue)
{
base.OnValueChanged(oldValue, newValue);
if (this.PART_IncreaseButton == null
|| this.PART_DecreaseButton == null)
{
return;
}
this.PART_IncreaseButton.IsEnabled = newValue < this.Maximum;
this.PART_DecreaseButton.IsEnabled = newValue > this.Minimum;
}
private UIElement PART_IncreaseButton { get; set; }
private UIElement PART_DecreaseButton { get; set; }
}
Generic.xaml
将 HoldButton 元素命名为 "PART_IncreaseButton"
和 "PART_IncreaseButton"
,以便您可以在模板中轻松找到它们。
<Style TargetType="{x:Type CustomSlider}">
<Style.Resources>
<Style x:Key="RepeatButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Focusable" Value="false" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Padding" Value="0" />
<Setter Property="Width" Value="20" />
</Style>
</Style.Resources>
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
<Setter Property="SmallChange" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
<Grid>
<Rectangle RadiusX="10" RadiusY="10" Stroke="{StaticResource SolidBrushLightGrey}" Fill="Black" StrokeThickness="1" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="ControlName" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<TextBlock Grid.Row="1" x:Name="ControlUnits" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<usercontrols:HoldButton x:Name="PART_IncreaseButton"
Grid.Row="2"
Delay="250"
Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.IncreaseLarge}"
ClickCommand="{x:Static Slider.IncreaseSmall}"
MaxWidth="60"
Height="60" Width="60"
Style="{StaticResource ButtonStyleGeneral}"
Content="+" />
<TextBlock Grid.Row="3" x:Name="Temperature" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" FontSize="30" Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Value, StringFormat=N1}" />
<usercontrols:HoldButton x:Name="PART_DecreaseButton"
Grid.Row="4"
Delay="250"
Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.DecreaseLarge}"
ClickCommand="{x:Static Slider.DecreaseSmall}"
MaxWidth="60"
Height="60" Width="60"
Style="{StaticResource ButtonStyleGeneral}"
Content="-" />
<Border x:Name="TrackBackground" Visibility="Collapsed">
<Rectangle x:Name="PART_SelectionRange" Visibility="Collapsed" />
</Border>
<Thumb x:Name="Thumb" Visibility="Collapsed" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>