2016-05-16 16 views
0

私は(MSサンプルから)オブジェクトが観測可能にするために(私も、このトピックの下部にあるポスト旧1)以下のNEW BindableViewModelクラスを使用します。WPF - SetProperty()がXAMLで{BindingableBindableObject.Text}を設定したときにCanExecuteをトリガーしないのはなぜですか?

public abstract class BindableBase : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged = delegate { }; 

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) 
    { 
     if (object.Equals(storage, value)) return false; 

     storage = value; 
     this.OnPropertyChanged(propertyName); 
     return true; 
    } 

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

私は順序でのViewModelを作成するためにこれを使用したいです

public class Page1ViewModel : BindableBase 
{ 
    private TextBoxModel _firstName = null; 
    public TextBoxModel FirstName { get { return _firstName; } set { 
     if (SetProperty(ref _firstName, value)) 
      { 
       SubmitCommand.RaiseCanExecuteChanged(); 
      } } } 

    private TextBoxModel _surname = null; 
    public TextBoxModel Surname { get { return _surname; } set { 
     if (SetProperty(ref _surname, value)) 
      { 
       SubmitCommand.RaiseCanExecuteChanged(); 
      } } } 

    private DelegateCommand _submitCommand = null; 
    public DelegateCommand SubmitCommand { get { return _submitCommand??(_submitCommand=new DelegateCommand(SubmitExecute, SubmitCanExecute)); } } 
    private void SubmitExecute() 
    { 
     MessageBox.Show($"{FirstName.Text} {Surname.Text}"); 
    } 
    private bool SubmitCanExecute() 
    { 
     if(string.IsNullOrEmpty(FirstName.Text)) 
      return false; 
     else if(string.IsNullOrEmpty(Surname.Text)) 
      return false; 
     else 
      return true; 
    } 
} 

メートルで:

public class TextBoxModel : BindableBase 
{ 
    private string _text = string.Empty; 
    // I'm going to bind this field to TextBox.Text property 
    public string Text { get { return _text; } set { SetProperty(ref _text, value); } } 

    private bool _isEnabled = true; 
    // I'm going to bind this field to TextBox.IsEnabled property 
    public bool IsEnabled { get { return _isEnabled; } set { SetProperty(ref _isEnabled, value); } } 
} 

その後、私のページのViewModelに、私はフィールドを定義するためにTextBoxModelを使用します。このようなものをTextBoxコントロールを、バインドしますyのXAMLビューでは、私はいつものように結合テキストボックスを設定します。

<TextBox Text="{Binding FirstName.Text, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding FirstName.IsEnabled}"/> 
<TextBox Text="{Binding Surname.Text, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding Surname.IsEnabled}"/> 
<Button Content="Submit" Command{Binding SubmitCommand} /> 

私はこれを実行すると、私はテキスト変更が動作していないことがわかりました。 setterの(SetProperty(ref _firstName、value))またはif(SetProperty(ref _surname、value))がトリガされませんでした。 TextBoxModelでプロパティを結合しないと、すべて正常に動作します。 OLD ViewModelBaseを使用すると、TextBoxModelも正常に機能しました。

新しいBindableBaseクラスを使用するときに何か忘れてしまったと思いますか?あなたの助けを探して、ありがとう!

OLD ViewModelBase:

[Obsolete("Please use BindableBase。")] 
public abstract class ObservableModel : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    public static string GetPropertyName<T>(System.Linq.Expressions.Expression<Func<T>> e) 
    { 
     var member = (System.Linq.Expressions.MemberExpression)e.Body; 
     return member.Member.Name; 
    } 

    protected virtual void RaisePropertyChanged<T> 
     (System.Linq.Expressions.Expression<Func<T>> propertyExpression) 
    { 
     RaisePropertyChanged(GetPropertyName(propertyExpression)); 
    } 

    protected void RaisePropertyChanged(String propertyName) 
    { 
     System.ComponentModel.PropertyChangedEventHandler temp = PropertyChanged; 
     if (temp != null) 
     { 
      temp(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 
+0

これはどのように機能するのかわかりません。テキストが変更されたときに 'FirstName'や' Surname'を設定していないので、 'SetProperty'は呼び出されません。 * TextBoxModel内のプロパティの変更を監視する必要があります。 –

+0

@CharlesMagerどういう意味ですか?私はFirstNameオブジェクトのTextプロパティを変更しましたが、(SetProperty(ref _firstName、value))の場合はトリガーしませんでした。しかし、FirstNameオブジェクトの内部でTextプロパティがトリガされました。なぜ(SetProperty(ref _firstName、value))トリガーしなかったのですか? –

+0

あなたのバインディングは 'Text'プロパティになります。これは' string'です。その値を設定しようとすると、魔法のように 'TextBoxModel'が新しく作成されず、' FirstName'も設定されます。 'FirstName'が' null'なので、ただ静かに失敗します。 –

答えて

1

あなたが結合はFirstNameまたはLastNameを設定することはありませんし、他には何も、あなたはどちらかん示さなかっました。これらはどちらもnullです。 Textへのバインディングはその値のみを設定し、新しいTextBoxModelを作成せず、それを設定しません。

この方法を整理しながら、あなたはこれが仕事をしたい場合は、あなたがこのような何かをする必要があります

public class Page1ViewModel : BindableBase 
{ 
    private readonly TextBoxModel _firstName = new TextBoxModel(); 

    public Page1ViewModel() 
    { 
     _firstName.PropertyChanged += 
      (sender, args) => SubmitCommand.RaiseCanExecuteChanged(); 
    } 

    public TextBoxModel FirstName 
    { 
     get { return _firstName; } 
    } 
} 
+0

こんにちは@CharlesMager、あなたのコードが働いて、ありがとうございます。あなたの答えを受け入れる前に、私は何かを理解したい: "_firstName.PropertyChanged + = (sender、args)=> SubmitCommand.RaiseCanExecuteChanged();"これは、_firstNameオブジェクトが変更されたときに、SubmitCommand.RaiseCanExecuteChanged()を呼び出すよりも、わかりました。しかし、私はsetterに同じコードを追加しました。また、_firstNameオブジェクトが変更されたときに、SubmitCommand.RaiseCanExecuteChanged()を呼び出すよりも意味があります。どうして私の仕事がうまくいかないの? (私はコードを_firstName = new TextBoxModel()に変更しました;開始時にnullを避けるため) –

+0

@OhMyDog '_firstName' *プロパティが変更されたときに、そのメソッドを呼び出すことを意味します。セッターを置くということは、オブジェクトが*置き換えられたときだけ呼び出されることを意味します。交換されることはありません(セッターを完全に取り外すことによって問題は発生しません)。 –

+0

もう一度、ありがとうございます。 –

1

何をしようとすることは理にかなっているが、あなたはそれをやっているかが不必要です複雑です。 TextBoxModelを実際にTextBoxに似せようとしていますが、あなたのアプローチは間違っていると思います。

最初に、モデルはデータを表し、UIは表しません。 TextBoxを表すTextBoxModelを作成する代わりに、代わりにPersonModelまたはUserModelなど、データ構造を表すモデルを作成する必要があります。ここで私が何を意味するかの例です:

public class PersonModel 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

注:モデルにINotifyPropertyChangedのものを置くことができるが、それは本当にUI問題ではない、それだけのデータです。プロパティ変更実装の最適な場所はView Modelです。これについてはさらに詳しく説明します。

これで、データレイヤーがソートされました。ビューモデルを介してこのモデルをこのモデルに公開するだけです。

public abstract class PropertyChangedBase : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    public void NotifyOfPropertyChange([CallerMemberName]string propertyName = null) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

このようなモデルのプロパティを公開:

public class PersonViewModel : PropertyChangedBase 
{ 
    private PersonModel _Person; 

    public string FirstName 
    { 
     get { return _Person.FirstName; } 
     set 
     { 
      _Person.FirstName = value; 
      NotifyOfPropertyChange(); 
     } 
    } 

    public string LastName 
    { 
     get { return _Person.LastName; } 
     set 
     { 
      _Person.LastName = value; 
      NotifyOfPropertyChange(); 
     } 
    } 

    //TODO: Your command goes here 

    public PersonViewModel() 
    { 
     //TODO: Get your model from somewhere. 
     _Person = new PersonModel(); 
    } 
} 

あなたは、単にあなたのTextBoxFirstNameLastNameと結合することができる。まず、ここで私は、この例で使用する単純なプロパティ変更されたベースがありますモデルのプロパティを表示:

<TextBox Text="{Binding FirstName}" ... /> 

これは本当に簡単です。 は、データレイヤーにTextBoxを再作成する必要はありません。実際、すべてのUIの懸念事項はモデルレイヤーから完全に分離する必要があります。

INotifyPropertyChangedによって既に処理されているので、この方法を使用すると、CanExecuteChangedメソッドを呼び出すことについても心配する必要はありません。

覚え書き、UIはUI、データはデータです。

+0

こんにちは@MikeEasonあなたの答えをありがとうが、それは私の問題を解決しませんでした(私は思う)。フォームには、UIに展開する必要があるフィールドが80個必要です。すべてのフィールドには、ViewModelでコントロールする必要があるIsEnabledプロパティとIsFocusedプロパティが必要です。値プロパティでは、少なくとも80x3 = 240のプロパティがViewModelにあります。だから私は簡単なコーディングのためにこれらのプロパティを同じコントロールで組み合わせることにしました。 –

関連する問題