none
数字入力のテキストボックスでコピペ時のBinding.ValidationRulesについて RRS feed

  • 質問

  • お世話になります。

    textBoxに数字しか入らないようにすることを考えていて下記にいいサンプルがあり、これを利用してみました。

    http://d.hatena.ne.jp/hilapon/20101021/1287641423
    http://d.hatena.ne.jp/Yoichiro/20101107/1289074958

    コピペでも文字が入らないようにするため下記のようにコードを書きましたが、数字以外がある場合、お知らせしたいと考え

    <TextBox.Text>
          <Binding Path="Value" UpdateSourceTrigger="PropertyChanged">
                  <Binding.ValidationRules>
                         <local:AngleRangeRule />
                  </Binding.ValidationRules>
          </Binding>
    </TextBox.Text>

    を追加しましたが、呼び出されないようです。どのようにしたらよいでしょうか?

    教えてください。

    Xaml

    <StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="1"> <TextBox Grid.Row="0" Grid.Column="1" x:Name="text1" Width="80" Height="23" FontSize="15" HorizontalAlignment="Left" Margin="21,0,0,0" VerticalAlignment="Center" MaxLength="6" local:PlaceHolderBehavior.PlaceHolderText="1文字+ボタン" local:NumericOnlyBehaviors.IsNumeric="True"> <!--     <TextBox.Style>
                                    <Style TargetType="TextBox">
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding IsOK}" Value="True">
                                                <Setter Property="Background" Value="Blue" />
                                                <Setter Property="ToolTip" Value="xxxxxxxxxxxxxxxxxxxxxxxxx" />
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </TextBox.Style>  -->

    <TextBox.Text> <Binding Path="Value" UpdateSourceTrigger="PropertyChanged"> <Binding.ValidationRules> <local:AngleRangeRule /> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox>

    namespace WpfHoken
    {    public class NumericOnlyBehaviors : INotifyPropertyChanged
        {
            /// <summary> 
            /// True なら入力を数字のみに制限します。 
            /// </summary> 
            public static readonly DependencyProperty IsNumericProperty =
                        DependencyProperty.RegisterAttached(
                            "IsNumeric", typeof(bool),
                            typeof(NumericOnlyBehaviors),
                            new UIPropertyMetadata(false, IsNumericChanged)
                        );
    
            // コピペOK?
            static public bool bOK = true;
    
    
            [AttachedPropertyBrowsableForType(typeof(TextBox))]
            public static bool GetIsNumeric(DependencyObject obj)
            {
                return (bool)obj.GetValue(IsNumericProperty);
            }
    
            [AttachedPropertyBrowsableForType(typeof(TextBox))]
            public static void SetIsNumeric(DependencyObject obj, bool value)
            {
                obj.SetValue(IsNumericProperty, value);
            }
    
            private static void IsNumericChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
            {
                var textBox = sender as TextBox;
                if (textBox == null) return;
    
                // イベントを登録・削除  
                textBox.KeyDown -= OnKeyDown;
                textBox.TextChanged -= OnTextChanged;
    
                var newValue = (bool)e.NewValue;
                if (newValue)
                {
                    textBox.KeyDown += OnKeyDown;
                    textBox.TextChanged += OnTextChanged;
                    DataObject.AddPastingHandler(textBox, TextBoxPastingEventHandler);
                }
            }
    
            static void OnKeyDown(object sender, KeyEventArgs e)
            {
                var textBox = sender as TextBox;
                if (textBox == null)
                    return;
    
    
                if ((Key.D0 <= e.Key && e.Key <= Key.D9) || (Key.NumPad0 <= e.Key && e.Key <= Key.NumPad9) || (Key.Delete == e.Key) || (Key.Back == e.Key) || (Key.Tab == e.Key))
                {
                    e.Handled = false;
                }
                else
                {
                    e.Handled = true;
                }
            }
    
            private static void OnTextChanged(object sender, TextChangedEventArgs e)
            {
                var textBox = sender as TextBox;
                if (textBox == null)
                    return;
    
    
                //   if (string.IsNullOrEmpty(textBox.Text))
                //   {
                //       textBox.Text = "0";
                //   }
            }
    
            // クリップボード経由の貼り付けチェック
            private static void TextBoxPastingEventHandler(object sender, DataObjectPastingEventArgs e)
            {
                var textBox = (sender as TextBox);
                var clipboard = e.DataObject.GetData(typeof(string)) as string;
               // clipboard = Validate(clipboard);
                if (IsNumeric(clipboard) && (textBox != null && !string.IsNullOrEmpty(clipboard)))
                {
                    textBox.Text = clipboard;
                    bOK = true;
                }
                else
                {
                    /* ツールチップに自分のTextをバインドする
                    textBox.SetBinding(TextBlock.ToolTipProperty, new Binding()
                    {
                        RelativeSource = new RelativeSource(RelativeSourceMode.Self),
                        Path = new PropertyPath("Text"),
                        Mode = BindingMode.OneWay
                    });
    
                    textBox.ToolTip = "数字以外の文字が含まれています。";
                    //表示位置と表示待ち時間を調整
                    textBox.SetValue(ToolTipService.HorizontalOffsetProperty, -6D);
                    textBox.SetValue(ToolTipService.VerticalOffsetProperty, -2D);
                    textBox.SetValue(ToolTipService.PlacementProperty, PlacementMode.Relative);
                    textBox.SetValue(ToolTipService.InitialShowDelayProperty, 0);
                    */
    
                    //false を返す
                    bOK = false;
                }
    
                e.CancelCommand();
                e.Handled = true;
            }
    
            public bool IsOK
            {
                get { return bOK; }
                set
                {
                    bOK = value;
                    OnPropertyChanged("IsRunning");
                }
    
            }
            public event PropertyChangedEventHandler PropertyChanged = (s, e) => { };
    
            private void OnPropertyChanged(string name)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
    
            // ペーストされる文字が数字か調べる
            public static bool IsNumeric(string stTarget)
            {
                double dNullable;
    
                return double.TryParse(stTarget, System.Globalization.NumberStyles.Any, null, out dNullable);
            }
        }
    
        public class AngleRangeRule : ValidationRule
        {
            public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
            {
                double result;
    
                if (!double.TryParse(value as string, out result))
                    return new ValidationResult(false, "文字列が不正です");
    
                if (result < 0 || result > 360)
                    return new ValidationResult(false, "値の範囲が不正です");
    
                return new ValidationResult(true, null);
            }
        }
    }

    長文で申し訳ありません。

    DataTrigger の部分は、ツールチップでテキスト「xxxxxx」を表示させようとしましたが、Binding IsOK が見つからず、呼ばれずで別の方法を模索しました。ViewModel しか呼べない?でしょうか?

    いずれの方法でもいいので教えてください。よろしくお願いします。
    2015年10月2日 2:26

すべての返信

  • こんにちは。

    先に挙げられた2点のURLは「数値以外は入力できないようにする」ための添付ビヘイビアではないのですか。
    それに対して文字列を入力した時のValidationRuleを設定しているのは何故でしょう。

    文字列の入力を許容して入力時にエラー表示するのか。
    文字列の入力を許容しないのか。

    どちらでしょうか。
    当然ながら、View側の添付ビヘイビア設定を外しValidationRuleのみにした場合はエラー表示はされると思います。

    2015年10月2日 2:53
    モデレータ
  • お世話になります。

    言葉足らずで申し訳ありません。

    > 「数値以外は入力できないようにする」ための添付ビヘイビアではないのですか

    その通りのことを実装したいのです。数字以外の文字は、確かに拒否されていますので問題ない様(問題の個所もありますが)に思えます。間違って文字列を入力した場合、文字列を含んだ数字を入力(コピペ)をした場合の処理について、現在、うまくいかずご教授いただきたく思います。

    ※問題の個所もありますが・・(以下、別件です)

    とは、Shift+数字キー(EscやTabキー近くのファンクションキーの下の横に並んでいる)を押すと、!”#$などが入力されてしまいます。

    static void OnKeyDown(object sender, KeyEventArgs e)
     {

                if ((Key.D0 <= e.Key && e.Key <= Key.D9) || (Key.NumPad0 <= e.Key && e.Key <= Key.NumPad9) || (Key.Delete == e.Key) || (Key.Back == e.Key) || (Key.Tab == e.Key))
                {

    ここで、Shiftキーと数字キーが押した順に別々に判定されるみたい。・・・・・現在調査中、必要なら別スレ立てる予定です。

    2015年10月2日 3:45
  • ここで、Shiftキーと数字キーが押した順に別々に判定されるみたい。・・・・・現在調査中、必要なら別スレ立てる予定です。

    これについては、OnKeyDownの判定なので別問題でしょうかね。

    さて、IsOKの件ですが、このViewのDataContextは何を設定しているのでしょうか。
    DataTrigger でバインドしようとしているIsOKプロパティはViewModelのプロパティパスではないでしょうか。

    。間違って文字列を入力した場合、文字列を含んだ数字を入力(コピペ)をした場合の処理について
    これが何を指しているのか私が理解できてないです…。
    2015年10月2日 5:09
    モデレータ
  • お世話になります。

    ViewModelが別にありますのでそちらに移動しました。フォーム表示時に一度呼ばれているようですが、それ以外では反応していません。

    <DataTrigger Binding="{Binding  IsOK}" Value="True">

    で、IsOKは見えていますが、DataTriggerに反応して呼ばれていないようです。

     > Shiftキーと数字キーが押した順に別々に判定されるみたい

    については、if (Keyboard.Modifiers == ModifierKeys.Shift)

    で、回避できました。

    tatic void OnKeyDown(object sender, KeyEventArgs e)
            {
                var textBox = sender as TextBox;
                if (textBox == null)
                    return;
    
    
                if (Keyboard.Modifiers == ModifierKeys.Shift)
                {
                    if (Key.D0 <= e.Key && e.Key <= Key.D9)
                    {
                        e.Handled = true;
                        return;
                    }
                }
    
                if ((Key.D0 <= e.Key && e.Key <= Key.D9) || (Key.NumPad0 <= e.Key && e.Key <= Key.NumPad9) || (Key.Delete == e.Key) || (Key.Back == e.Key) || (Key.Tab == e.Key))
                {
                    e.Handled = false;
                }
                else
                {
                    e.Handled = true;
                }
            }

    2015年10月2日 5:35
  • IsOKをViewModelに移動したという意味で宜しいですか?

    OnPropertyChanged("IsRunning");

    がIsRunningになっているのがちょっと気になってますけど、ここは大丈夫でしょうか。
    あとは、ViewModel上でIsOKは更新されているのですよね。

    全体像が見えないのでこちらからの質問が部分的になってしまいます。


    2015年10月2日 5:39
    モデレータ
  • とりあえず、現状このスレッドに記述されているコードを見る限り、IsOKが変更されている(正確には"IsOK"でのPropertyChangedイベントを発生させている)様子がありませんが。

    // IsOKのset内でIsRunningとやらになっているのは転載ミスかしら。

    2015年10月2日 5:41
  • お世話になります。

    NotifyPropertyChanged("KomokuAddTable");

    と訂正することで、呼び出していることが分かりました。

            public bool bOK = false;
            public bool IsOK
            {
                get { return bOK; }
                set
                {
                    bOK = value;
                    NotifyPropertyChanged("KomokuAddTable");    // IsOK
                }
            }
    public bool bOK = false;または、trueを入れ替えて動作テストすると、ツールチップで"XXXXXXXXXXX"を表示したりしなかったりします。

    しかし、目的は、コピペ時に文字を貼り付けようとするときに「メッセージを表示したい」(数字のみ可能です。などの)

    ですので、ビヘイビア(最初にコード提示、NumericOnlyBehaviors)から下記のDataTriggerを呼び出せますか?

    <DataTrigger Binding="{Binding  IsOK, Mode=TwoWay}" Value="True">
                     <Setter Property="Foreground" Value="Red" />
                      <Setter Property="ToolTip" Value="xxxxxxxxxxxxxxxxxxxxxxxxx" />
    </DataTrigger>

    呼び出し(定義部分)は、local:NumericOnlyBehaviors.IsNumeric="True"なので無理ですか?

    コード:<TextBox ・・・ local:NumericOnlyBehaviors.IsNumeric="True">

    なにか、コピペ文字に数字以外が含まれていることを表示させたいのです。今は、「何も表示しない。無視する」です。

    >> 間違って文字列を入力した場合、文字列を含んだ数字を入力(コピペ)をした場合の処理について

    > これが何を指しているのか私が理解できてないです…。

    字足らずですみません。数字専用のテキストボックスを作成しキーボードから数字・数字以外を入力する処理は、定時のコードでうまくいってます。これを、コピペで行おうとする場合、数字以外の文字を含んでいたときこのコードで貼り付けできません。TextBoxPastingEventHandler()が正しく動作しています。(でも、そのお知らせを作りたい!<-現在の質問です)

    2015年10月2日 7:17
  • KomokuAddTableですか。

    さて、当初のソースの

    /* ツールチップに自分のTextをバインドする
    textBox.SetBinding(TextBlock.ToolTipProperty, new Binding()
    {
        RelativeSource = new RelativeSource(RelativeSourceMode.Self),
        Path = new PropertyPath("Text"),
        Mode = BindingMode.OneWay
    });
    
    textBox.ToolTip = "数字以外の文字が含まれています。";
    //表示位置と表示待ち時間を調整
    textBox.SetValue(ToolTipService.HorizontalOffsetProperty, -6D);
    textBox.SetValue(ToolTipService.VerticalOffsetProperty, -2D);
    textBox.SetValue(ToolTipService.PlacementProperty, PlacementMode.Relative);
    textBox.SetValue(ToolTipService.InitialShowDelayProperty, 0);
    */
    
    上記をコメントアウトしているのは何故ですか。

    2015年10月2日 7:34
    モデレータ
  • お世話になります。

    文字を含んだ部分の処理でツールチップでその近く表示しようとしました。

    表示するのですが、マウスがそのtextBoxの上にいないと表示してくれません。

    警告メッセージとしては使えないのでコメントしてます。

    2015年10月2日 7:39