WPF development essay collection - TextBox with increasing and decreasing button

Posted by datamodeler on Wed, 05 Jan 2022 00:20:28 +0100

1, Foreword

Today, let's share how to implement a TextBox control with an increment and decrement button

2, Text

1. The previous blog shared an article on customizing XamlIcon control. This time, we will implement today's custom control directly on the basis of that project

2. First, add two icon resources, an increase button and a decrease button. For the specific process, please refer to the article I wrote earlier

3. Then add a custom control, as shown in the figure

4. After adding, a Themes folder will be generated in the project, with a generic Xaml file. This file can be used to write control styles. It will automatically generate the most basic control styles, and the NumberTextBox is used for background logic

5. First write the style of the control. As follows, the specific details of the style are not described, that is, two custom XamlIcon controls are added as the increment and decrement buttons, and then a TextBlock is added as the prompt text

<Style TargetType="{x:Type ctls:NumberTextBox}">
    <Setter Property="Cursor" Value="IBeam" />
    <Setter Property="FocusVisualStyle" Value="{x:Null}" />
    <Setter Property="Foreground" Value="#555" />
    <Setter Property="VerticalAlignment" Value="Center" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ctls:NumberTextBox}">
                <Border
                    Name="Border"
                    Background="White"
                    BorderBrush="#AAA"
                    BorderThickness="1"
                    CornerRadius="3">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition />
                            <ColumnDefinition Width="30" />
                        </Grid.ColumnDefinitions>
                        <ScrollViewer
                            x:Name="PART_ContentHost"
                            Margin="6,0,0,0"
                            VerticalAlignment="{TemplateBinding VerticalAlignment}"
                            Focusable="false"
                            HorizontalScrollBarVisibility="Hidden"
                            VerticalScrollBarVisibility="Hidden" />
                        <TextBlock
                            x:Name="Hint"
                            Margin="6,0,0,0"
                            VerticalAlignment="{Binding ElementName=PART_ContentHost, Path=VerticalAlignment}"
                            Foreground="#c9ccd7"
                            Text="{TemplateBinding Tag}"
                            Visibility="Collapsed" />
                        <Border
                            Grid.Column="1"
                            Background="Transparent"
                            BorderBrush="#eee"
                            BorderThickness="1,0,0,0">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition />
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition />
                                </Grid.RowDefinitions>
                                <ctls:XamlIcon
                                    x:Name="InButton"
                                    VerticalAlignment="Center"
                                    Cursor="Hand"
                                    Icon="angle_up" />
                                <Rectangle
                                    Grid.Row="1"
                                    Height="1"
                                    Fill="#eee" />
                                <ctls:XamlIcon
                                    x:Name="DeButton"
                                    Grid.Row="2"
                                    VerticalAlignment="Center"
                                    Cursor="Hand"
                                    Icon="angle_down" />
                            </Grid>
                        </Border>
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="Text" Value="{x:Null}">
                        <Setter TargetName="Hint" Property="Visibility" Value="{x:Static Visibility.Visible}" />
                    </Trigger>
                    <Trigger Property="Text" Value="">
                        <Setter TargetName="Hint" Property="Visibility" Value="{x:Static Visibility.Visible}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

6. Then write the background code. The background code is also relatively simple. Here, two dependency attributes are simply added, one is the progressive value and the other is the minimum value. Then, in the overload of OnApplyTemplate, add mouse click events to the two controls added to the style. Let's start with this basic implementation. Complex functions are added later, such as limiting the ability to input numbers. Today, we will mainly introduce the general implementation method.

public class NumberTextBox : TextBox
{
    public int Step
    {
        get { return (int)GetValue(StepProperty); }
        set { SetValue(StepProperty, value); }
    }

    public static readonly DependencyProperty StepProperty =
        DependencyProperty.Register("Step", typeof(int), typeof(NumberTextBox), new PropertyMetadata(1));

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

    static NumberTextBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(NumberTextBox), new FrameworkPropertyMetadata(typeof(NumberTextBox)));
    }

    public NumberTextBox()
    {
        InputMethod.SetIsInputMethodEnabled(this, false);
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        XamlIcon DeButton = (XamlIcon)this.Template.FindName("DeButton", this);
        XamlIcon InButton = (XamlIcon)this.Template.FindName("InButton", this);

        DeButton.MouseLeftButtonDown += DeButton_MouseLeftButtonDown;
        InButton.MouseLeftButtonDown += InButton_MouseLeftButtonDown;
    }

    private void DeButton_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (!String.IsNullOrEmpty(this.Text))
        {
            this.Text = int.Parse(this.Text) - Step < Minimum ? Minimum + "" : int.Parse(this.Text) - Step + "";
            this.SelectionStart = this.Text.Length;
        }
        else
        {
            this.Text = Minimum + "";
            this.SelectionStart = this.Text.Length;
        }
    }

    private void InButton_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (!String.IsNullOrEmpty(this.Text))
        {
            this.Text = int.Parse(this.Text) + Step + "";
            this.SelectionStart = this.Text.Length;
        }
        else
        {
            this.Text = Minimum + "";
            this.SelectionStart = this.Text.Length;
        }
    }
}

7. Test the control function. Add custom controls to the main interface, as shown below

<Window x:Class="XamlIconDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:ctls="clr-namespace:XamlIconDemo.Controls"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:XamlIconDemo"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <ctls:NumberTextBox
            Width="200"
            Height="40"
            FontSize="22"
            Minimum="0"
            Step="5"
            VerticalAlignment="Center"
            HorizontalAlignment="Center"
            Tag="please enter a number..."/>
    </Grid>
</Window>

8. The implementation results are as follows. The basic functions have been realized, but they are not perfect. If necessary, they can be improved by themselves. That's all for today's sharing