午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

WPFにもNumericUpDownみたいなのをユーザーコントロールで、その2

昨日の続き

f:id:gogowaten:20200621161930g:plain
数字だけ入力
入力制限は一昨日のをコピペして、あとはクリックしたときに文字列全部を選択するのを追加した

github.com


UserControl1.xaml

<UserControl x:Class="ControlLibraryCore20200620.UserControl1"
             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:ControlLibraryCore20200620"
             mc:Ignorable="d" 
             d:DesignHeight="40" d:DesignWidth="200">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition/>
      <RowDefinition/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition/>
      <ColumnDefinition Width="16"/>
    </Grid.ColumnDefinitions>

    <RepeatButton Grid.Row="0" Grid.Column="1" IsTabStop="False">
      <RepeatButton.Content>
        <Viewbox x:Name="ViewBoxUp" Margin="1">
          <Polygon Points="1,0 2,1 0,1 1,0" Fill="Gray"/>
        </Viewbox>
      </RepeatButton.Content>
    </RepeatButton>
    <RepeatButton Grid.Row="1" Grid.Column="1" IsTabStop="False">
      <RepeatButton.Content>
        <Viewbox x:Name="ViewBoxDown" Margin="1">
          <Polygon Points="0,0 2,0 1,1 0,0" Fill="Gray"/>
        </Viewbox>
      </RepeatButton.Content>
    </RepeatButton>

    <TextBox x:Name="MyTextBox" Grid.RowSpan="2" Grid.Column="0"
             TextAlignment="Right" VerticalContentAlignment="Center"
             InputMethod.IsInputMethodSuspended="True"
             PreviewKeyDown="MyTextBox_PreviewKeyDown"
             PreviewTextInput="MyTextBox_PreviewTextInput"
             LostFocus="MyTextBox_LostFocus"
             CommandManager.PreviewExecuted="MyTextBox_PreviewExecuted"
             GotFocus="MyTextBox_GotFocus"
             PreviewMouseLeftButtonDown="MyTextBox_PreviewMouseLeftButtonDown">

    </TextBox>
  </Grid>
</UserControl>


UserControl1.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ControlLibraryCore20200620
{
    /// <summary>
    /// Interaction logic for UserControl1.xaml
    /// </summary>
    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
        }

        //スペースキーが押されたのを無効にする
        private void MyTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Space) e.Handled = true;
        }

        //入力の制限、数字とハイフンとピリオドだけ通す
        private void MyTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            var textbox = (TextBox)sender;
            string str = textbox.Text;//文字列
            var inputStr = e.Text;//入力された文字            

            //正規表現で入力文字の判定、数字とピリオド、ハイフンならtrue
            bool neko = new System.Text.RegularExpressions.Regex("[0-9.-]").IsMatch(inputStr);

            //入力文字が数値とピリオド、ハイフン以外だったら無効
            if (neko == false)
            {
                e.Handled = true;//無効
                return;//終了
            }

            //キャレット(カーソル)位置が先頭(0)じゃないときの、ハイフン入力は無効
            if (textbox.CaretIndex != 0 && inputStr == "-") { e.Handled = true; return; }

            //2つ目のハイフン入力は無効(全選択時なら許可)
            if (textbox.SelectedText != str)
            {
                if (str.Contains("-") && inputStr == "-") { e.Handled = true; return; }
            }

            //2つ目のピリオド入力は無効
            if (str.Contains(".") && inputStr == ".") { e.Handled = true; return; }
        }

        //フォーカス消失時、不自然な文字を削除
        private void MyTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            //ピリオドの削除
            //先頭か末尾にあった場合は削除
            var tb = (TextBox)sender;
            string text = tb.Text;
            if (text.StartsWith('.') || text.EndsWith('.'))
            {
                text = text.Replace(".", "");
            }

            // -. も変なのでピリオドだけ削除
            text = text.Replace("-.", "-");

            //数値がないのにハイフンやピリオドがあった場合は削除
            if (text == "-" || text == ".")
                text = "";

            tb.Text = text;
        }

        //
        private void MyTextBox_PreviewExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            //貼り付け無効
            if (e.Command == ApplicationCommands.Paste)
            {
                e.Handled = true;
            }
        }

        //focusしたときにテキストを全選択
        private void MyTextBox_GotFocus(object sender, RoutedEventArgs e)
        {
            var tb = sender as TextBox;
            tb.SelectAll();
        }

        //        | オールトの雲
        //http://ooltcloud.sakura.ne.jp/blog/201311/article_30013700.html
        //クリックしたときにテキストを全選択
        private void MyTextBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var tb = sender as TextBox;
            if (tb.IsFocused == false)
            {
                tb.Focus();
                e.Handled = true;
            }
        }
    }
}


XAML
RepeatButton部分
f:id:gogowaten:20200621194727p:plain
Tabキーで遷移するときにRepeatButtonは無視するようにしたのが、19と26行目のIsTabStop="False"

TextBox部分
f:id:gogowaten:20200621195130p:plain
36~40は一昨日のコピペ
41行目GotFocusはフォーカスを得たときに、テキスト全選択
42行目PreviewMouseLeftButtonDownは左クリック時にもテキスト全選択
この部分の処理は
f:id:gogowaten:20200621201704p:plain
フォーカスを得たときにテキストを全選択は、TextBoxのSelectAllメソッドを実行するだけ
これでいいはずなんだけど、クリックで選択(フォーカス)されたときは、一瞬だけ全選択されるんだけど直後に解除されてしまう。どうやらそういう仕様みたいで、これを解決したのが
| オールトの雲 ooltcloud.sakura.ne.jp
こちらで紹介されていた方法
f:id:gogowaten:20200621201740p:plain
PreviewMouseLeftButtonDown時にフォーカスして(111行目)、それ以降の動作をキャンセルする(112行目)
ここまで書いたらビルドしてDLLファイルを更新
f:id:gogowaten:20200621202304p:plain
ビルドしないと動作確認用のプロジェクトで参照しているDLLファイルは、古い状態のままなので変更が反映されない

動作確認用アプリのデザイン画面
f:id:gogowaten:20200621203157p:plain
Tabキーでの遷移の状態を見るために2つ並べた



関連記事
次回は明日
gogowaten.hatenablog.com

前回は昨日
gogowaten.hatenablog.com

コピペ元は一昨日
gogowaten.hatenablog.com