次の方法で共有


MVVMにおけるTextBoxの入力制限について

質問

2015年4月24日金曜日 3:29 | 1 票

WPF アプリケーションを開発しております。

ViewにあるTextBoxへの入力を制限したいのですが、つまずいております。

TextBoxに特定の文字(サンプルコードでは'S')が入力された際、その文字を入力させないor削除することは可能でしょうか。

私達は、この入力に制限を加えることはModelの責務と考え、入力不可の文字が入絵よくされた際、Modelのプロパティのsetにて値を修正するといった方法を考えました。

しかし現状では「入力した'S'がTextBoxに表示されてしまう」といった問題点がございます。TextBoxに表示させない方法はございますでしょうか。

よろしくお願いいたします。

以下にサンプルコードの抜粋を記載いたします。

■ MainWindowViewModel

<TextBox    Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"    Height="24" HorizontalAlignment="Left" Margin="135,30,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />

■ MainWindowViewModel : ModelBase  

//ViewModelBaseはINotifyPropertyChangedを実装しています。

public class MainWindowViewModel : ViewModelBase
   {
      private Person _person;

      /// <summary>
      /// 名前
      /// </summary>
      public string Name
      {
         get
         {
            return _person.Name;
         }
         set
         {
            if (_person.Name != value)
            {
               _person.Name = value;
               RaisePropertyChanged("");
               //RaisePropertyChanged("Name");
               //RaisePropertyChanged("UrNameIs");
            }
         }
      }

      public string UrNameIs
      {
         get
         {
            return "Ur Name Is " ; _person.Name;
         }
      }

      /// <summary>
      /// コンストラクタ
      /// </summary>
      public MainWindowViewModel()
      {
         _person = new Person();
      }
   }

■ Person

//ModelBaseはINotifyPropertyChangedを実装しています。

public class Person : ModelBase
   {
      private string _name;
      /// <summary>
      /// Name
      /// </summary>
      public string Name
      {
         get
         {
            return _name;
         }
         set
         {
            if (_name != value)
            {
              * if (value.Contains("S"))*
***               {***
***                  _name = value.Replace("S", "");***
***               }***
               else
               {
                  _name = value;
               }
               RaisePropertyChanged("Name");
            }
         }
      }
        
   }

すべての返信 (16)

2015年4月24日金曜日 17:56 ✅回答済み | 2 票

あれから少し考えてみました。
イベントを実装する以外どうにも挙動を解決できない、でも可能な限りコードビハインドに実装したくない、さらに .NET Framework 4.5 に上げることもできないというのであれば、ビヘイビアを使うというのはいかがでしょうか。これならコードビハインドにイベントハンドラを書かずに済みます。

以下、.NET Framework 4 で動作確認したサンプルを掲示します。なお参照設定に、Microsoft.Expression.Interactions と System.Windows.Interactivity を追加する必要があります。

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

namespace WpfApplication1 {
    public class TextBoxBehavior : Behavior<TextBox> {
        protected override void OnAttached() {
            base.OnAttached();
            this.AssociatedObject.TextChanged ;= TextBox_TextChanged;
       }

        protected override void OnDetaching() {
            base.OnDetaching();
            this.AssociatedObject.TextChanged -= TextBox_TextChanged;
        }

        private void TextBox_TextChanged(object sender, TextChangedEventArgs e) {
            TextBox textBox = sender as TextBox;
            textBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
        }
    }
}
<Window x:Class="WpfApplication1.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="180" Width="300">
    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>
    <Grid>
        <Label Content="{Binding Name}" VerticalAlignment="Top" HorizontalAlignment="Left" />
        <TextBox Text="{Binding Name, UpdateSourceTrigger=Explicit}" Height="26" Width="180" >
            <i:Interaction.Behaviors>
                <local:TextBoxBehavior />
            </i:Interaction.Behaviors>
        </TextBox>
    </Grid>
</Window>

ぜひ 「フォーラムでご質問頂くにあたっての注意点」 もご覧ください https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?forum=announceja


2015年4月24日金曜日 3:47 | 1 票

ViewModelのNameで受け入れたら_person.NameをViewに読み直させるのは?

public class MainWindowViewModel : ViewModelBase
{
    private Person _person;

    /// <summary> 名前 </summary>
    public string Name
    {
        get
        {
            return _person.Name;
        }
        set
        {
            if (_person.Name != value)
            {
                _person.Name = value;              
             }
            //受け付けなくても再度読ませるために
            RaisePropertyChanged("UrNameIs");
            RaisePropertyChanged("Name");
        }
    }

    public string UrNameIs
    {
        get
        {
            return "Ur Name Is " ; _person.Name;
        }
    }

    /// <summary> コンストラクタ </summary>
    public MainWindowViewModel()
    {
        _person = new Person();
    }
}

個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)


2015年4月24日金曜日 4:32

gekkaさま、ご返信ありがとうございます。

このやり方ですと、現象は変わらず、

step 入力 TextBoxの表示
step1 a a
step2 S aS
step3 a aa

となります。step2ではTextBoxにaと表示することは可能でしょうか。


2015年4月24日金曜日 5:19

試してみました。私は MVVMインフラに Livet を使用してますが、狙いどおり動作しています。Livetの使用有無は別として、基本的に Person.Name プロパティのセッターで RaisePropertyChanged メソッドをコールするよう実装すれば反映される筈ですが、 どこかコーディングミスされてませんか?

using Livet;

namespace LivetWPFApplication9.Models {
    public class Person : NotificationObject {
        private string _name;
        public string Name {
            get { return _name; }
            set {
                if (_name != value) {
                    if (value.Contains("S")) {
                        _name = value.Replace("S", "");
                    } else {
                        _name = value;
                    }
                    RaisePropertyChanged("Name");
                }
            }
        }
    }
}
using Livet;
using LivetWPFApplication9.Models;

namespace LivetWPFApplication9.ViewModels {
    public class MainWindowViewModel : ViewModel {
        private Person _person;

        /// <summary>名前</summary>
        public string Name {
            get { return _person.Name; }
            set {
                if (_person.Name != value) {
                    _person.Name = value;
                }
                // 受け付けなくても再度読ませるために
                RaisePropertyChanged("Name");
            }
        }

        /// <summary>コンストラクタ</summary>
        public MainWindowViewModel() {
            _person = new Person();
        }
    }
}
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Height="26" Width="180" />

ぜひ 「フォーラムでご質問頂くにあたっての注意点」 もご覧ください https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?forum=announceja


2015年4月24日金曜日 5:51

ひらぽんさま、ご返信ありがとうございます。

Livetを使用して確認してみましたが、残念ながら現象は変わりません。

Livetを使ったことがなかったのですが、ひらぽんさまの環境では上手くいっているとのことですので、こちらにて引き続き確認いたします。

Viewに

<Label Content="{Binding Name, UpdateSourceTrigger=PropertyChanged}"・・・>

を追加しての確認もしており、Labelには’S'が削除されたPerson.Nameが表示されますが、TextBoxに'S'が入力されると'S'が表示され、さらに入力が更新されると'S'が削除されます。

よろしくお願いいたします。


2015年4月24日金曜日 6:04

まったくもって謎です。ちなみにこちらは簡単なサンプルで試してますが、SeanKidd さんの方でも問題を絞って試されてるのですよね?あと動作環境に問題はないでしょうか?

ぜひ 「フォーラムでご質問頂くにあたっての注意点」 もご覧ください https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?forum=announceja


2015年4月24日金曜日 6:36

ひらぽんさま、

VisualStudio2010 .NetFramework4 を使用し、記載いたしましたサンプルにて確認しております。


2015年4月24日金曜日 6:44

私が言いました 「環境」 とは、Visual Studio や .NET Framework のみでなく、OS・ビデオカードおよびドライバをも含めた動作環境全般を意味します。

実際、ビデオカードのドライバーが旧くて WPFアプリケーションの描画に不具合が発生した事例を見たことがあります。また、7 では正常に動いているアプリが、XP では描画遅延が発生したこともありました。この辺りはいかがでしょうか?

ぜひ 「フォーラムでご質問頂くにあたっての注意点」 もご覧ください https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?forum=announceja


2015年4月24日金曜日 7:04

試してみました。当初 .NET Framework 4.5 で試してましたが、.NET Framework 4 だと現象が再現するのを確認しました(汗) こちらの環境は

Windows 7 SP1 64bit
Visual Studio 2013 Update4
NVIDIA GeForce GT530 最新ドライバ更新済

です。もうちょっと調べてみますね。

ぜひ 「フォーラムでご質問頂くにあたっての注意点」 もご覧ください https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?forum=announceja


2015年4月24日金曜日 7:27 | 1 票

こんにちは。

この現象知りませんでした…。
.NET4.0までのTextBox同期バグのようですね。(ほんとか?)

下記リンク先では自前で更新してやることで解決しているようです。
もう少し調べてみます。

.NET 3.5 | WPF Textbox refuses to update itself while binded to a view model property
WPF - MVVM - Textbox getting out of sync with viewmodel property


2015年4月24日金曜日 7:55

ひらぽんさま、

「環境」の件、勘違いしておりました。

Thinkpad E520

Windows 7 Professional SP1 32bit

VisualStudio 2010 Professional SP1Rel

Intel HD Graphics 3000 

になります。

>試してみました。当初 .NET Framework 4.5 で試してましたが、.NET Framework 4 だと現象が再現するのを確認しました(汗)

ご確認ありがとうございます!


2015年4月24日金曜日 8:05

Tak1waさま、ご返信ありがとうございます。

お教え頂きましたページのように、コードビハインドでTextChengedイベント処理すれば問題は解決できました。この手しかないのでしょうかね・・・


2015年4月24日金曜日 9:04

> お教え頂きましたページのように、コードビハインドでTextChengedイベント処理すれば問題は解決できました。この手しかないのでしょうかね・・・

ですかねぇ・・・。とりあえずこちらも、二つ目のページどおり以下のコードで動作確認できましたが・・・

<TextBox Text="{Binding Name, UpdateSourceTrigger=Explicit}" Height="26" Width="180" TextChanged="TextBox_TextChanged" />
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) {
    TextBox textBox = sender as TextBox;
    textBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

ぜひ 「フォーラムでご質問頂くにあたっての注意点」 もご覧ください https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?forum=announceja


2015年4月24日金曜日 9:22 | 1 票

調べてみたところ、Connectにバグとして挙がっていました。

https://connect.microsoft.com/VisualStudio/feedback/details/612486/coercing-a-wpf-textbox-is-broken-in-wpf4

回避策としては、やはり自前でUpdateSourceを実行することになってしまいますね。
コメントの「次期リリースでの対応」というのが.NET4.5を指していると思いますので、.NET4.0では上記回避策になると思います。


2015年4月25日土曜日 1:55 | 1 票

>以下、.NET Framework 4 で動作確認したサンプルを掲示します。なお参照設定に、Microsoft.Expression.Interactions と System.Windows.Interactivity を追加する必要があります。

補足すると、Blendが入ってないとそれらのdllがありませんので、以下を参考にして下さい。

Visual Studioから使うExpression BlendのBehavior達
http://okazuki.hatenablog.com/entry/20100817/1282044737

また、私はTextChangedイベントでコマンドを実行することがありますが、以下のようにEventTriggerを使っています。

Handling events in an MVVM WPF application
http://blog.magnusmontin.net/2013/06/30/handling-events-in-an-mvvm-wpf-application/

★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/


2015年4月27日月曜日 0:15

みなさま、お世話になっております。

>調べてみたところ、Connectにバグとして挙がっていました。

今回はバグということでUpdateSourceを実行する方向でいこうと思います。

本件に関しまして、色々とお調べ頂きまことにありがとうございました。

SeanKidd