【发布时间】:2022-01-06 03:25:42
【问题描述】:
我的问题是基于对这篇文章的回答之一: Where is the WPF Numeric UpDown control? Squirrel.Downy 先生回答。 我想要完成的是一个数字 updown 控件,当按钮被按下更长的时间时,它会以更大的量增加/减少,否则增加/减少是正常量。此外,当达到最大值/最小值时,按钮应禁用。 我有一个基于 Slider 的样式,其中包含 2 个 HoldButton 类型的按钮(向上/向下,从 RepeatButton 派生)和一个只读的 TextBlock 值。 在 HoldButton 中,我有 2 个 ICommand 依赖项属性。这些是 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控件并在新类中实现逻辑。
-
我不确定触发器是否会起作用,因为值、最小值和最大值都是浮动的
-
您必须扩展 Slider。在 OnApplyTemplate 覆盖中,您抓住向上和向下的重复按钮。然后为 Value 属性注册一个属性更改回调。在此回调中,您可以根据当前值与最小值和最大值相比禁用向上或向下按钮。
-
你能帮我一些关于 OnApplyTemplate() 的事情吗?我必须把这个放在哪里?我将 ValueChanged 事件处理程序添加到部分类,但我需要到达向上和向下按钮。我喜欢这样做的 mvvm 方式...
-
请看我的回答。它 100% 符合 MVVM。
标签: c# wpf mvvm numericupdown