【问题标题】:Where is the WPF Numeric UpDown control?WPF 数字 UpDown 控件在哪里?
【发布时间】:2010-10-24 21:16:42
【问题描述】:

进入第一个严肃的 WPF 项目。似乎缺少很多基本控件。具体来说,我正在寻找 Numeric UpDown 控件。有没有我错过的带外版本?真的不想写自己的控件。

我不想使用 WindowsFormHost 并在其上添加一个 WinForm ctl。我希望它完全是 WPF,没有任何遗留垃圾。

谢谢

【问题讨论】:

  • 扩展 WPF 工具包有一个:NumericUpDown!alt text
  • 这条评论一直是我博客访问量最大的驱动力。直到今天。搞笑。
  • 问题是“WPF 数字 UpDown 控件在哪里”。这些答案都没有回答这个问题。我希望这种答案的形式是“他们决定在 wpf 中摆脱它,因为......”。但是我不知道这个答案
  • WPF 工具包解决的问题多于解决的问题,它包含很多明显的错误;并且没有来自 Microsoft 的 NumericUpDown 控件……见鬼,WPF 还活着吗?

标签: wpf controls numericupdown updown


【解决方案1】:

只需在Extended.Wpf.Toolkit 中使用IntegerUpDown 控件 你可以这样使用它:

  1. 将以下命名空间添加到您的 XAML:

    xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"

  2. 在您希望控件使用的 XAML 中:

    <xctk:IntegerUpDown Name="myUpDownControl" />

【讨论】:

  • 对于那些对第三方封闭源代码控制感到疑惑/不满意的人,请注意,该库的免费版本是根据 Microsoft 公共许可证发布的,这基本上意味着您可以获得它背后的完整源代码(阅读有关详细信息的完整许可文本)。 UpDown 控件是免费版本的一部分。该库还有一个付费版本,其中包含更多控件/主题。
  • 为了方便起见,有没有办法让这些自定义 WPF 控件显示在 VS 工具箱中?我在“工具 -> 选择工具箱项目”下找不到它们。
  • 需要注意的是,从 v3.7.0 版本开始,wpftoolkit 的开源版本是在专有的非商业许可下提供的,而不是 MS-PL (reference)
  • Xceed 更新了他们的公告,称 [github.com/xceedsoftware/wpftoolkit](they 将继续在 MS-PL 下发布 3.8.x 版本,直到 2020 年 12 月 31 日)。 “这将是在此类许可下发布的最后一个版本,下一个版本将在我们的新社区许可下发布,仅允许非商业用途。”
  • Xceed 正在为 PLUS 版本(支持 .net core 3)收费
【解决方案2】:

我自己做的;

xaml

<Grid  Height="23" Margin="152,63,11,0" VerticalAlignment="Top">
    <TextBox x:Name="txtNum" x:FieldModifier="private" Text="0" TextChanged="txtNum_TextChanged" Margin="3,2,13,3" />
    <Button x:Name="cmdUp" x:FieldModifier="private" FontSize="10" Padding="0,-4,0,0" Content="▲" Width="10" Click="cmdUp_Click" Margin="33,2,1,13" />
    <Button x:Name="cmdDown" x:FieldModifier="private" FontSize="10" Padding="0,-4,0,0" Content="▼" Width="10" Click="cmdDown_Click" Margin="33,12,1,3" />
</Grid>

以及背后的代码

private int _numValue = 0;

public int NumValue
{
    get {  return _numValue; }
    set
    {
        _numValue = value;
        txtNum.Text = value.ToString();
    }
}

public NumberUpDown()
{
    InitializeComponent();
    txtNum.Text = _numValue.ToString();
}

private void cmdUp_Click(object sender, RoutedEventArgs e)
{
    NumValue++;
}

private void cmdDown_Click(object sender, RoutedEventArgs e)
{
    NumValue--;
}

private void txtNum_TextChanged(object sender, TextChangedEventArgs e)
{
    if (txtNum == null)
    {
        return;
    }

    if (!int.TryParse(txtNum.Text, out _numValue))
        txtNum.Text = _numValue.ToString();
}

【讨论】:

  • WPF .net 4 中的文本框不存在 textChanged 事件
  • 这个方法有一些限制,1) TextBox 不像 Winforms 那样用控件水平拉伸,更不重要的是,2) 不像 Winforms NumericUpDown,这个控件允许垂直拉伸,也就是无害,但它looks a bit silly。注意Sonorx's answer中的解决方案不存在这些问题。
  • NumValue 应该是允许绑定的依赖属性,MinValue 和 MaxValue 等其他属性也不错。
  • 这个解决方案很糟糕。它是一个自定义控件,不支持 wpf 数据绑定、路由事件和命令。
  • 非常 WPF-ish :)
【解决方案3】:

这是我自己的带有 Up 和 Down 键捕获的 UserControl 示例。

Xaml 代码:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="13" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="13" />
        <RowDefinition Height="13" />
    </Grid.RowDefinitions>
    <TextBox Name="NUDTextBox"  Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" TextAlignment="Right" PreviewKeyDown="NUDTextBox_PreviewKeyDown" PreviewKeyUp="NUDTextBox_PreviewKeyUp" TextChanged="NUDTextBox_TextChanged"/>
    <RepeatButton Name="NUDButtonUP"  Grid.Column="1" Grid.Row="0" FontSize="8" FontFamily="Marlett" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Click="NUDButtonUP_Click">5</RepeatButton>
    <RepeatButton Name="NUDButtonDown"  Grid.Column="1" Grid.Row="1" FontSize="8"  FontFamily="Marlett" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="13" VerticalAlignment="Bottom" Click="NUDButtonDown_Click">6</RepeatButton>
</Grid>

还有代码:

public partial class NumericUpDown : UserControl
{
    int minvalue = 0, 
        maxvalue = 100,
        startvalue = 10;
    public NumericUpDown()
    {
        InitializeComponent();
        NUDTextBox.Text = startvalue.ToString();
    }

    private void NUDButtonUP_Click(object sender, RoutedEventArgs e)
    {
        int number;
        if (NUDTextBox.Text != "") number = Convert.ToInt32(NUDTextBox.Text);
        else number = 0;
        if (number < maxvalue)
            NUDTextBox.Text = Convert.ToString(number + 1); 
    }

    private void NUDButtonDown_Click(object sender, RoutedEventArgs e)
    {
        int number;
        if (NUDTextBox.Text != "") number = Convert.ToInt32(NUDTextBox.Text);
        else number = 0;
        if (number > minvalue)
            NUDTextBox.Text = Convert.ToString(number - 1); 
    }

    private void NUDTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
    {

        if (e.Key == Key.Up)
        {
            NUDButtonUP.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
            typeof(Button).GetMethod("set_IsPressed", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(NUDButtonUP, new object[] { true }); 
        }


        if (e.Key == Key.Down)
        {
            NUDButtonDown.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
            typeof(Button).GetMethod("set_IsPressed", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(NUDButtonDown, new object[] { true }); 
        }
    }

    private void NUDTextBox_PreviewKeyUp(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Up)
            typeof(Button).GetMethod("set_IsPressed", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(NUDButtonUP, new object[] { false });

        if (e.Key == Key.Down)
            typeof(Button).GetMethod("set_IsPressed", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(NUDButtonDown, new object[] { false });
    }

    private void NUDTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        int number = 0;
        if (NUDTextBox.Text!="")
            if (!int.TryParse(NUDTextBox.Text, out number)) NUDTextBox.Text = startvalue.ToString();
        if (number > maxvalue)  NUDTextBox.Text = maxvalue.ToString();
        if (number < minvalue) NUDTextBox.Text = minvalue.ToString();
        NUDTextBox.SelectionStart = NUDTextBox.Text.Length;

    }

}

【讨论】:

  • 字体“Marlett”是否保证存在于用户的系统中?
  • @Qwertie 自 3.11 以来默认在 Windows 中,AFAIR。
  • 这是一个很好的解决方案,似乎也适用于 UWP XAML。 PreviewKeyDown / PreviewKeyUP 方法有点问题。
  • 很好的解决方案。只需将 this.DataContext = this; 添加到构造函数并将私有字段更改为公共属性,您就可以直接在消费 XAML 中摆弄这些(请参阅 this howto
  • 您可能希望使用这些 Unicode 字符:▲ ▼
【解决方案4】:
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:numericButton2">


    <Style TargetType="{x:Type local:NumericUpDown}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:NumericUpDown}">              
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="*"/>
                            </Grid.RowDefinitions>
                            <RepeatButton Grid.Row="0" Name="Part_UpButton"/>
                            <ContentPresenter Grid.Row="1"></ContentPresenter>
                            <RepeatButton Grid.Row="2" Name="Part_DownButton"/>
                        </Grid>                  
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

    <Window x:Class="numericButton2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:numericButton2"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <local:NumericUpDown Margin="181,94,253,161" x:Name="ufuk" StepValue="4" Minimum="0" Maximum="20">            
            </local:NumericUpDown>
            <TextBlock Margin="211,112,279,0" Text="{Binding ElementName=ufuk, Path=Value}" Height="20" VerticalAlignment="Top"></TextBlock>
        </Grid>
    </Window>
public class NumericUpDown : Control
{
    private RepeatButton _UpButton;
    private RepeatButton _DownButton;
    public readonly static DependencyProperty MaximumProperty;
    public readonly static DependencyProperty MinimumProperty;
    public readonly static DependencyProperty ValueProperty;
    public readonly static DependencyProperty StepProperty;   
    static NumericUpDown()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(typeof(NumericUpDown)));
        MaximumProperty = DependencyProperty.Register("Maximum", typeof(int), typeof(NumericUpDown), new UIPropertyMetadata(10));
        MinimumProperty = DependencyProperty.Register("Minimum", typeof(int), typeof(NumericUpDown), new UIPropertyMetadata(0));
        StepProperty = DependencyProperty.Register("StepValue", typeof(int), typeof(NumericUpDown), new FrameworkPropertyMetadata(5));
        ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new FrameworkPropertyMetadata(0));
    }
    #region DpAccessior
    public int Maximum
    {
        get { return (int)GetValue(MaximumProperty); }
        set { SetValue(MaximumProperty, value); }
    }
    public int Minimum
    {
        get { return (int)GetValue(MinimumProperty); }
        set { SetValue(MinimumProperty, value); }
    }
    public int Value
    {
        get { return (int)GetValue(ValueProperty); }
        set { SetCurrentValue(ValueProperty, value); }
    }
    public int StepValue
    {
        get { return (int)GetValue(StepProperty); }
        set { SetValue(StepProperty, value); }
    }
    #endregion
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        _UpButton = Template.FindName("Part_UpButton", this) as RepeatButton;
        _DownButton = Template.FindName("Part_DownButton", this) as RepeatButton;
        _UpButton.Click += _UpButton_Click;
        _DownButton.Click += _DownButton_Click;
    }

    void _DownButton_Click(object sender, RoutedEventArgs e)
    {
        if (Value > Minimum)
        {
            Value -= StepValue;
            if (Value < Minimum)
                Value = Minimum;
        }
    }

    void _UpButton_Click(object sender, RoutedEventArgs e)
    {
        if (Value < Maximum)
        {
            Value += StepValue;
            if (Value > Maximum)
                Value = Maximum;
        }
    }

}

【讨论】:

  • OK.... 很晚才参加这个聚会,但偶然发现了它,以防万一...这样,如果有人绑定到您的 NumericUpDown 的 Value 属性,则设置 Value在按钮处理程序中将中断绑定,除非他们已将绑定模式设置为 TwoWay。如果您将 Value 属性的元数据默认绑定为双向,则此控件的用户会更容易,例如在注册时使用 'new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)'。
【解决方案5】:

给出的答案是可以的。但是,当鼠标离开控件时,我希望按钮自动隐藏。这是我基于上述vercin 答案的代码:

风格

<Style TargetType="{x:Type v:IntegerTextBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type v:IntegerTextBox}">
                    <Grid Background="Transparent">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <TextBox Name="tbmain" Grid.ColumnSpan="2" Grid.RowSpan="2"
                                 Text="{Binding Value, Mode=TwoWay, NotifyOnSourceUpdated=True, 
                            NotifyOnValidationError=True, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type v:IntegerTextBox}}}" 
                                               Style="{StaticResource ValidationStyle}" />
                        <RepeatButton Name="PART_UpButton" BorderThickness="0" Grid.Column="1" Grid.Row="0"
                                      Width="13" Background="Transparent">
                            <Path Fill="Black" Data="M 0 3 L 6 3 L 3 0 Z"/>
                        </RepeatButton>
                        <RepeatButton Name="PART_DownButton" BorderThickness="0" Grid.Column="1" Grid.Row="1"
                                      Width="13" Background="Transparent">
                            <Path Fill="Black" Data="M 0 0 L 3 3 L 6 0 Z"/>
                        </RepeatButton>

                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver"  Value="False">
                            <Setter Property="Visibility" TargetName="PART_UpButton" Value="Collapsed"/>
                            <Setter Property="Visibility" TargetName="PART_DownButton" Value="Collapsed"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

代码

public partial class IntegerTextBox : UserControl
{
    public IntegerTextBox()
    {
        InitializeComponent();
    }

    public int Maximum
    {
        get { return (int)GetValue(MaximumProperty); }
        set { SetValue(MaximumProperty, value); }
    }
    public readonly static DependencyProperty MaximumProperty = DependencyProperty.Register(
        "Maximum", typeof(int), typeof(IntegerTextBox), new UIPropertyMetadata(int.MaxValue));



    public int Minimum
    {
        get { return (int)GetValue(MinimumProperty); }
        set { SetValue(MinimumProperty, value); }
    }
    public readonly static DependencyProperty MinimumProperty = DependencyProperty.Register(
        "Minimum", typeof(int), typeof(IntegerTextBox), new UIPropertyMetadata(int.MinValue));


    public int Value
    {
        get { return (int)GetValue(ValueProperty); }
        set { SetCurrentValue(ValueProperty, value); }
    }
    public readonly static DependencyProperty ValueProperty = DependencyProperty.Register(
        "Value", typeof(int), typeof(IntegerTextBox), new UIPropertyMetadata(0, (o,e)=>
        {
            IntegerTextBox tb = (IntegerTextBox)o;
            tb.RaiseValueChangedEvent(e);
        }));

    public event EventHandler<DependencyPropertyChangedEventArgs> ValueChanged;
    private void RaiseValueChangedEvent(DependencyPropertyChangedEventArgs e)
    {
        ValueChanged?.Invoke(this, e);
    }


    public int Step
    {
        get { return (int)GetValue(StepProperty); }
        set { SetValue(StepProperty, value); }
    }
    public readonly static DependencyProperty StepProperty = DependencyProperty.Register(
        "Step", typeof(int), typeof(IntegerTextBox), new UIPropertyMetadata(1));



    RepeatButton _UpButton;
    RepeatButton _DownButton;
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        _UpButton = Template.FindName("PART_UpButton", this) as RepeatButton;
        _DownButton = Template.FindName("PART_DownButton", this) as RepeatButton;
        _UpButton.Click += btup_Click;
        _DownButton.Click += btdown_Click;
    }


    private void btup_Click(object sender, RoutedEventArgs e)
    {
        if (Value < Maximum)
        {
            Value += Step;
            if (Value > Maximum)
                Value = Maximum;
        }
    }

    private void btdown_Click(object sender, RoutedEventArgs e)
    {
        if (Value > Minimum)
        {
            Value -= Step;
            if (Value < Minimum)
                Value = Minimum;
        }
    }

}

【讨论】:

  • 最佳答案在这里。正确使用依赖属性。
【解决方案6】:

让我们享受一些 hacky 的东西:
这里是SliderStyle作为NumericUpDown,简单易用,没有任何隐藏代码或第三方库。

<Style TargetType="{x:Type Slider}">
    <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>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <TextBox Grid.RowSpan="2" Height="Auto"
                                Margin="0" Padding="0"
                                VerticalAlignment="Stretch" VerticalContentAlignment="Center"
                                Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Value}">
                        <TextBox.InputBindings>
                            <KeyBinding Gesture="Up" Command="{x:Static Slider.IncreaseSmall}" />
                            <KeyBinding Gesture="Down" Command="{x:Static Slider.DecreaseSmall}" />
                            <KeyBinding Gesture="PageUp" Command="{x:Static Slider.IncreaseLarge}" />
                            <KeyBinding Gesture="PageDown" Command="{x:Static Slider.DecreaseLarge}" />
                        </TextBox.InputBindings>
                    </TextBox>
                    <RepeatButton Grid.Row="0" Grid.Column="1"
                                    Command="{x:Static Slider.IncreaseSmall}"
                                    Style="{StaticResource RepeatButtonStyle}">
                        <Path Data="M4,0 L0,4 8,4 Z" Fill="Black" />
                    </RepeatButton>
                    <RepeatButton Grid.Row="1" Grid.Column="1"
                                    Command="{x:Static Slider.DecreaseSmall}"
                                    Style="{StaticResource RepeatButtonStyle}">
                        <Path Data="M0,0 L4,4 8,0 Z" Fill="Black" />
                    </RepeatButton>
                    <Border x:Name="TrackBackground" Visibility="Collapsed">
                        <Rectangle x:Name="PART_SelectionRange" Visibility="Collapsed" />
                    </Border>
                    <Thumb x:Name="Thumb" Visibility="Collapsed" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

如果你确实想对TextBox 应用输入限制,试试这个:

public static class InputLimit
{
    public static string GetDecimalValueProxy(TextBox obj) => (string)obj.GetValue(DecimalValueProxyProperty);

    public static void SetDecimalValueProxy(TextBox obj, string value) => obj.SetValue(DecimalValueProxyProperty, value);

    // Using a DependencyProperty as the backing store for DecimalValueProxy.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DecimalValueProxyProperty =
        DependencyProperty.RegisterAttached("DecimalValueProxy", typeof(string), typeof(InputLimit),
            new FrameworkPropertyMetadata("0", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, CoerceDecimalValueProxy));

    private static object CoerceDecimalValueProxy(DependencyObject d, object baseValue)
    {
        if (decimal.TryParse(baseValue as string, out _)) return baseValue;
        return DependencyProperty.UnsetValue;
    }
}

并将xaml TextBox部分修改为:

<TextBox Grid.RowSpan="2" Height="Auto"
            Margin="0" Padding="0"
            VerticalAlignment="Stretch" VerticalContentAlignment="Center"
            local:InputLimit.DecimalValueProxy="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Value}"
            Text="{Binding RelativeSource={RelativeSource Self}, Path=(local:InputLimit.DecimalValueProxy), UpdateSourceTrigger=PropertyChanged}">

【讨论】:

  • 很抱歉,我不能对此投票至少 10 次。仅适用于出色的 XAML 解决方案。它有一些限制,但仅是 XAML,这些限制不再重要。
  • @AlexandruDicu 我添加了一些 BLACK MAGIC 使其支持输入限制。
【解决方案7】:

在 WPF 中使用 VerticalScrollBarTextBlock 控件。在后面的代码中,添加以下代码:

在构造函数中,为滚动条定义一个事件处理程序:

scrollBar1.ValueChanged += new RoutedPropertyChangedEventHandler<double>(scrollBar1_ValueChanged);
scrollBar1.Minimum = 0;
scrollBar1.Maximum = 1;
scrollBar1.SmallChange = 0.1;

然后在事件处理程序中,添加:

void scrollBar1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    FteHolderText.Text = scrollBar1.Value.ToString();
}

这是我代码中的原始 sn-p...进行必要的更改.. :)

public NewProjectPlan()
{
    InitializeComponent();

    this.Loaded += new RoutedEventHandler(NewProjectPlan_Loaded);

    scrollBar1.ValueChanged += new RoutedPropertyChangedEventHandler<double>(scrollBar1_ValueChanged);
    scrollBar1.Minimum = 0;
    scrollBar1.Maximum = 1;
    scrollBar1.SmallChange = 0.1;

    // etc...
}

void scrollBar1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    FteHolderText.Text = scrollBar1.Value.ToString();
}

【讨论】:

  • 您的解决方案非常聪明,但是按钮以相反的顺序工作(向上按钮减小值,而向下按钮增加它)。我找到了另一个可能有助于解决该问题的线程:stackoverflow.com/a/27246296/637968 - 只需旋转滚动条
【解决方案8】:

您可以使用我编写的作为 WPFControls 库的一部分的 WPF 的 NumericUpDown 控件。

【讨论】:

  • 一个不错的小库,展示了如何编写数字上/下控件。我唯一遇到的问题是必须在演示中重新引用控件程序集并从 nuget 安装 MSHTML。谢谢!
  • 图书馆做得很好。很有用。谢谢。
【解决方案9】:

为继续回答 9 年的问题而道歉。

我已经按照@Michael 的回答进行了操作。

我以 UserControl 的身份进行操作,我可以像 Controls 元素一样拖放。我使用 Nuget 的 MaterialDesign 主题来获得雪佛龙图标和按钮波纹效果。

Micheal 运行的 NumericUpDown 修改后如下:-

用户控制代码:-

TemplateNumericUpDown.xaml

<UserControl x:Class="UserControlTemplate.TemplateNumericUpDown"
             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:UserControlTemplate"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" MinHeight="48">
    <Grid Background="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="60"/>
        </Grid.ColumnDefinitions>
        <TextBox x:Name="txtNum" x:FieldModifier="private" Text="{Binding Path=NumValue}" TextChanged="TxtNum_TextChanged" FontSize="36" BorderThickness="0" VerticalAlignment="Center" Padding="5,0"/>
        <Grid Grid.Column="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="30*"/>
                <RowDefinition Height="30*"/>
            </Grid.RowDefinitions>
            <Grid Background="#FF673AB7">
                <Viewbox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="Auto" Width="Auto">
                    <materialDesign:PackIcon Kind="ChevronUp" Foreground="White" Height="32.941" Width="32"/>
                </Viewbox>
                <Button x:Name="cmdUp" x:FieldModifier="private" Click="CmdUp_Click" Height="Auto" BorderBrush="{x:Null}" Background="{x:Null}"/>
            </Grid>
            <Grid Grid.Row="1" Background="#FF673AB7">
                <Viewbox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="Auto" Width="Auto">
                    <materialDesign:PackIcon Kind="ChevronDown" Foreground="White" Height="32.942" Width="32"/>
                </Viewbox>
                <Button x:Name="cmdDown" x:FieldModifier="private" Click="CmdDown_Click" Height="Auto" BorderBrush="{x:Null}" Background="{x:Null}"/>
            </Grid>
        </Grid>
    </Grid>
</UserControl>

TemplateNumericUpDown.cs

using System.Windows;
using System.Windows.Controls;

namespace UserControlTemplate
{
    /// <summary>
    /// Interaction logic for TemplateNumericUpDown.xaml
    /// </summary>
    public partial class TemplateNumericUpDown : UserControl
    {
        private int _numValue = 0;
        public TemplateNumericUpDown()
        {
            InitializeComponent();
            txtNum.Text = _numValue.ToString();
        }
        public int NumValue
        {
            get { return _numValue; }
            set
            {
                if (value >= 0)
                {
                    _numValue = value;
                    txtNum.Text = value.ToString();
                }
            }
        }

        private void CmdUp_Click(object sender, RoutedEventArgs e)
        {
            NumValue++;
        }

        private void CmdDown_Click(object sender, RoutedEventArgs e)
        {
            NumValue--;
        }

        private void TxtNum_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (txtNum == null)
            {
                return;
            }

            if (!int.TryParse(txtNum.Text, out _numValue))
                txtNum.Text = _numValue.ToString();
        }
    }
}

在 MyPageDesign.xaml 上,拖放创建的用户控件将具有 &lt;UserControlTemplate:TemplateNumericUpDown HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100"/&gt;

为了从模板中获取值,我使用

string Value1 = JournalNumStart.NumValue;
string Value2 = JournalNumEnd.NumValue;

我还没有很好的技能来绑定基于 FontSize 元素的控件的高度,所以我在用户控件中手动设置了我的页面字体大小。

** 注意:- 我已在我的程序中将“Archieve”名称更改为 Archive =)

【讨论】:

  • 向上/向下按钮的可用性很糟糕:它们太小了,不方便。它们需要与文本框的高度至少相同(并且是方形的)。我知道它需要更多空间,但它至少为您提供了可用的按钮。否则根本没有理由插入它们。
【解决方案10】:

我有一个简单但有用的解决方案。 代码如下:

<Grid Name="TVGrid" Background="#7F000000">  <ScrollBar Background="Black" Orientation="Vertical" Height="35" HorizontalAlignment="Left" Margin="215,254,0,0" Minimum="0" Maximum="10" LargeChange="10" Value="{Binding ElementName=channeltext2, Path=Text}" x:Name="scroll" VerticalAlignment="Top" Width="12" RenderTransformOrigin="0.5,0.5" ValueChanged="scroll_ValueChanged" >  
        <ScrollBar.RenderTransform>  
            <TransformGroup>  
                <ScaleTransform/>  
                <SkewTransform/>  
                <RotateTransform Angle="-180"/>  
                <TranslateTransform/>  
            </TransformGroup>  
        </ScrollBar.RenderTransform>  
    </ScrollBar>  
    <TextBox Name="channeltext" HorizontalContentAlignment="Center" FontSize="20"  Background="Black" Foreground="White" Height="35" HorizontalAlignment="Left" Margin="147,254,0,0" VerticalAlignment="Top" Width="53" Text="0" />  
    <TextBox Name="channeltext2" Visibility="Hidden" HorizontalContentAlignment="Center" FontSize="20"  Background="Black" Foreground="White" Height="35" HorizontalAlignment="Left" Margin="147,254,0,0" VerticalAlignment="Top" Width="53" Text="0" />  </Grid>  

【讨论】:

    【解决方案11】:

    这是另一个开源控件,它有许多不同的输入方法(鼠标拖动、鼠标滚轮、光标键、文本框编辑),支持多种数据类型和用例:

    https://github.com/Dirkster99/NumericUpDownLib

    【讨论】:

      【解决方案12】:

      这是对另一个答案的修改,但支持绑定

      <UserControl x:Class="YourNamespace.Controls.NumericUpDown"
                   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:NetReactorLM.Desktop.Controls"
                   mc:Ignorable="d"
                   d:DesignHeight="300" d:DesignWidth="300">
          <Grid  VerticalAlignment="Top">
              <TextBox x:Name="txtNum" x:FieldModifier="private" Text="0" TextChanged="txtNum_TextChanged" Margin="3,2,13,3" />
              <Button x:Name="cmdUp" x:FieldModifier="private" FontSize="10" Padding="0,-4,0,0" Content="▲" Width="10" Click="cmdUp_Click" Margin="33,2,1,13" />
              <Button x:Name="cmdDown" x:FieldModifier="private" FontSize="10" Padding="0,-4,0,0" Content="▼" Width="10" Click="cmdDown_Click" Margin="33,12,1,3" />
          </Grid>
      </UserControl>
      
      
      
      using System.Windows;
      using System.Windows.Controls;
      
      namespace YourNamespace.Controls
      {
          public partial class NumericUpDown : UserControl
          {
      
              public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
                  "Value", typeof(int), typeof(NumericUpDown), new PropertyMetadata(default(int)));
      
              public int Value
              {
                  get { return (int) GetValue(ValueProperty); }
                  set
                  {
                      SetValue(ValueProperty, value); 
                      txtNum.Text = value.ToString();
                  }
              }
              
              public NumericUpDown()
              {
                  InitializeComponent();
                  txtNum.Text = Value.ToString();
              }
      
              private void cmdUp_Click(object sender, RoutedEventArgs e)
              {
                  Value++;
              }
      
              private void cmdDown_Click(object sender, RoutedEventArgs e)
              {
                  Value--;
              }
      
              private void txtNum_TextChanged(object sender, TextChangedEventArgs e)
              {
                  if (txtNum == null)
                  {
                      return;
                  }
      
                  if (!int.TryParse(txtNum.Text, out var val))
                  {
                      Value = val;
                      txtNum.Text = val.ToString();
                  }
              }
          }
      }
      

      【讨论】:

      • if (txtNum == null) 为什么要检查这个?如果文本框为空,那么它的TextChanged 如何触发?
      • @HosseinEbrahimi,我使用 ReactiveUI,因此可以从任何地方触发事件,即使整个管道为空,所以我坚持这个检查,因为没有它我得到空引用异常,静态分析器也不能理解改变的事件和对象本身的关系
      【解决方案13】:

      只是一个务实的做样例:

      -右键单击您的项目(在解决方案下),选择“管理 nuget 包...”

      -在菜单中点击浏览选项卡搜索“wpftoolkit”,选择“Extended.Wpf.Toolkit

      -安装它!

      -右键单击您的用户控件工具箱,选择“Add Tab..”并将其命名为“WPF Toolkit”

      -右键单击新的“WPF Toolkit”选项卡,选择“Choose items...”

      -在菜单中点击“浏览...”按钮,寻找nugets DLL文件夹,全选 "...\packages\Extended.Wpf.Toolkit.3.5.0\lib\net40\*.dll"

      忽略有关某些 DLL 可能不包含用户控件的警告!

      准备好了:)

      【讨论】:

      • Extended.Wpf.Toolkit 现在仅用于“非商业”用途。在此处查看许可证:github.com/xceedsoftware/wpftoolkit/blob/master/license.md
      • Afaik Extended.Wpf.Toolkit 仍然存在 x64 Visual Studio 布局编辑器的问题,该问题仍然很长。所以在使用它之前请注意这一点和新的许可证。
      • 使用 VisualStudio 2022(什么是第一个 x64 VS)可以减少 Extended.Wpf.Toolkit 对 x64 构建造成的痛苦,因为错误不再浮出水面。无论如何,这是一种解决方法,并且应该在源代码中真正修复该错误。
      【解决方案14】:

      转到您项目的 NugetPackage 管理器-> 浏览并搜索 mahApps.Metro -> 将包安装到您的项目中。您将看到添加的参考:MahApps.Metro。 然后在你的 XAML 代码中添加:

      "xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"

      你想在哪里使用你的对象添加:

      <mah:NumericUpDown x:Name="NumericUpDown" ... /> 
      

      享受对象的全部可扩展性(绑定、触发器等...)。

      【讨论】:

        【解决方案15】:

        Extended.Wpf.Toolkit 免费版不支持 .net core 和 .net

        您可以使用HandyControl,它有一个 NumericUpDown 控件并且在 Wpf 和 .net 5 中运行良好。

        https://github.com/HandyOrg/HandyControl

        【讨论】:

          猜你喜欢
          • 2011-07-21
          • 2011-01-15
          • 1970-01-01
          • 2013-05-08
          • 1970-01-01
          • 1970-01-01
          • 2012-12-15
          • 2014-08-08
          • 2020-02-25
          相关资源
          最近更新 更多