有没有办法使用滑块的 ControlTemplate 触发值更改事件

问题描述 投票:0回答:1

我正在尝试设计一个 ControlTemplate,使 Slider 看起来像 NumericUpDown 控件。

它有一个三列的网格,其中有一个递减按钮、一个显示当前值的 TextBlock 和一个递增按钮。

是的,我知道 WPF 中有一些 NumericUpDown 控件的模仿,它们在某些库中使用代码隐藏或自定义控件,但我正在寻找一种通过向按钮的 Click 事件添加 EventTriggers 来增加和减少值的偷偷摸摸的方法在模板上。

我可以通过调用这些 EventTriggers 中的 ValueChanged 或其他滑块事件来实现我的目标吗?

注意:带有 DoubleAnimation 的 Storyboard 几乎可以工作,但是,唉,由于 Freezable 问题,我无法将动画的“To”属性绑定到 Slider 本身的递增或递减 Value 属性。

wpf data-binding controltemplate
1个回答
0
投票

在这种情况下,我建议您创建一个派生自 RangeBase 的自定义控件。

实现示例:

using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace WpfCustomControlsCore
{
    public class NumericUpDown : RangeBase
    {
        static NumericUpDown()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(typeof(NumericUpDown)));
            SmallChangeProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(1.0));
            MaximumProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(100.0));
            CommandManager.RegisterClassCommandBinding(typeof(NumericUpDown), new(UpValueCommand, OnUpValueExecute, OnUpDownValueCanExecute));
            CommandManager.RegisterClassCommandBinding(typeof(NumericUpDown), new(DownValueCommand, OnDownValueExecute, OnUpDownValueCanExecute));
        }

        private static void OnUpValueExecute(object sender, ExecutedRoutedEventArgs e)
        {
            NumericUpDown numericUpDown = (NumericUpDown)sender;
            numericUpDown.Value += numericUpDown.SmallChange;
        }

        private static void OnUpDownValueCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            NumericUpDown numericUpDown = (NumericUpDown)sender;
            e.CanExecute = numericUpDown.SmallChange > 0.0;
        }

        private static void OnDownValueExecute(object sender, ExecutedRoutedEventArgs e)
        {
            NumericUpDown numericUpDown = (NumericUpDown)sender;
            numericUpDown.Value -= numericUpDown.SmallChange;
        }

        public static readonly RoutedUICommand UpValueCommand = new("Up Value", nameof(UpValueCommand), typeof(NumericUpDown));
        public static readonly RoutedUICommand DownValueCommand = new("Down Value", nameof(DownValueCommand), typeof(NumericUpDown));


        /// <summary>Duplicate property, for passing the value back from Value to the source.</summary>
        public double Value2
        {
            get => (double)GetValue(Value2Property);
            set => SetValue(Value2Property, value);
        }

        // Using a DependencyProperty as the backing store for Value2.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty Value2Property =
            DependencyProperty.Register(
                nameof(Value2),
                typeof(double),
                typeof(NumericUpDown),
                new FrameworkPropertyMetadata(0.0)
                {
                    BindsTwoWayByDefault = true,
                    PropertyChangedCallback = async (d, e) =>
                    {
                        NumericUpDown nud = (NumericUpDown)d;
                        if (!e.NewValue.Equals(nud.Value))
                        {
                            await nud.Dispatcher.BeginInvoke(() => nud.Value2 = nud.Value, System.Windows.Threading.DispatcherPriority.Send);
                        }
                    }
                });

        protected override void OnValueChanged(double oldValue, double newValue)
        {
            base.OnValueChanged(oldValue, newValue);
            SetValue(Value2Property, newValue);
        }

    }
}

在主题\Generic.xaml中

    <Style TargetType="{x:Type local:NumericUpDown}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:NumericUpDown}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="4*"/>
                                <ColumnDefinition Width="25" />
                            </Grid.ColumnDefinitions>
                            <TextBox Grid.Column="0"
                                     Text="{Binding Value,
                                                    RelativeSource={RelativeSource TemplatedParent},
                                                    UpdateSourceTrigger=PropertyChanged,
                                                    Mode=TwoWay}"/>
                            <Grid Grid.Column="1">
                                <Grid.RowDefinitions>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>
                                <Button Grid.Row="0" FontSize="10"
                                        Command="{x:Static local:NumericUpDown.UpValueCommand}">
                                    <TextBlock Text="▲"
                                               VerticalAlignment="Top"/>
                                </Button>
                                <Button Grid.Row="1"
                                        FontSize="10"
                                        Command="{x:Static local:NumericUpDown.DownValueCommand}">
                                    <TextBlock Text="▼"
                                               VerticalAlignment="Top"/>
                                </Button>
                            </Grid>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

使用示例:

    public class DoubleItem
    {
        public double Number { get; set; }
    }
    <ContentControl VerticalAlignment="Center" HorizontalAlignment="Center">
        <StackPanel Orientation="Horizontal" >
            <Border Background="LightGreen" BorderBrush="Green" BorderThickness="2"
                    Padding="20" Margin="10">
                <StackPanel MinWidth="100">
                    <FrameworkElement.DataContext>
                        <local:DoubleItem Number="-3"/>
                    </FrameworkElement.DataContext>
                    <TextBlock Text="{Binding Number}" Margin="5"/>
                    <custctrl:NumericUpDown Margin="5"
                                    Value="{Binding Number}"
                                    Maximum="10" Minimum="5"
                                    />
                    <TextBox Text="{Binding Number, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
                </StackPanel>
            </Border>
            <Border Background="LightGreen" BorderBrush="Green" BorderThickness="2"
                    Padding="20" Margin="10">
                <StackPanel MinWidth="100">
                    <FrameworkElement.DataContext>
                        <local:DoubleItem Number="-3"/>
                    </FrameworkElement.DataContext>
                    <TextBlock Text="{Binding Number}" Margin="5"/>
                    <custctrl:NumericUpDown Margin="5"
                                    Value="{Binding Number}"
                                    Maximum="10" Minimum="5"
                                    Value2="{Binding Number}"/>
                    <TextBox Text="{Binding Number, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
                </StackPanel>
            </Border>
        </StackPanel>
    </ContentControl>

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.