0

MVVMのテキストボックスに簡単な検証を実装しようとしています

    public string Property
    {
        get
        {
            if (App.PropertyStorageContainer != null)
            {
                return App.PropertyStorageContainer.Property;
            }
            else
            {
                return null;
            }
        }
        set
        {
            App.PropertyStorageContainer.Property = value;
            RaisePropertyChanged("Property");
        }
    }

次に、PropertyStorageContainerクラスに

private string _property;
public string Property
    {
        get
        {               
            return App.PropertyStorageContainer.Property;
        }
        set
        {
            if(value meets some condition)
            {
                _property = value;
            }
            else
            {
               _property = someothervalue;
            }
        }
    }

   <TextBox Width="50" TextAlignment="Center" Text="{Binding Property, Mode=TwoWay, NotifyOnValidationError=True}" MaxLength="3"></TextBox>                          

これのポイントは、ボックスに何が入っているかを検証することです。この値をコードから直接設定すると、すべてが期待どおりに機能します。値を設定しようとし、次にRaiseProperyChangedを呼び出し、次に値をGETします(検証のため、最初に入力された値とは異なる場合があります)。取得された最終値はビューに表示されるので、TwoWayバインディングが機能していることがわかります。

私が抱えている問題は、SETの入力がバインドされたXAMLプロパティから/ユーザーから直接取得された場合です。この場合、SETメソッドが呼び出され、検証が実行されますが、GETは発生しません。これにより、未検証の値が画面のテキストボックスに残ります。

私の最初の質問は、これはバグですか、それとも予想される動作ですか?GETに新しいものは何もないはずなので、ユーザーから直接入力があったときに最後のGETを削除することで、パフォーマンスを節約しようとした可能性があります。しかし、そうでない場合は、おそらく私がすべてのセットアップを行っている方法が、呼び出されているGETに干渉しています。

2番目の質問はもちろんこれを回避するための提案です。検証を行う他の方法についていくつかの提案を読みましたが、私のプログラムはすでにPRODで公開されており、提案されている変更のほとんどは私にとって多くの手直しを伴うため、GETanyを呼び出す方法を見つけたいと思っています。プロパティがSETである時間。

4

2 に答える 2

1

I have made a couple of assumptions since I am not sure I understand you code completely but I think you could consider possibly implementing a custom validation rule. First off, since your custom ValidationRule will take care of the validation you could get the logic out of your model class's property definition and "dumb down" your poco:

class PropertyStorageContainer
{
    public string Property { get; set; }
}

It seems you desire your view model to act as a basic wrapper around your model class. Again, I will assume this is valid based on the description of your scenario:

class PropertyStorageContainerViewModel : INotifyPropertyChanged
{
    private PropertyStorageContainer model;

    public PropertyStorageContainerViewModel(PropertyStorageContainer model)
    {
        this.model = model;
    }

    public string Property
    {
        get
        {
            if (model != null)
            {
                return model.Property;
            }
            else
            {
                return null;
            }
        }
        set
        {
            if (model.Property != value)
            {
                model.Property = value;
                RaisePropertyChanged("Property");
            }
        }
    } 
    // INotifyPropertyChanged implementation...
}

Now create a new class that extends System.Windows.Controls.ValidationRule and override the abstract Validate method in order implement your validation logic. For the example, I created a rule that just checks if the string is null or empty (assuming that would be an invalid scenario):

class IsNullOrEmptyValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        string s = (value ?? string.Empty).ToString();
        if (string.IsNullOrEmpty(s))
        {
            // Invalid...
            return new ValidationResult(false, "Please enter a value.");
        }
        else
        {
            // Valid...
           return new ValidationResult(true, null);
        }
    }
}

Now for the XAML... Here is an example of a TextBox that adds the validation rule to its binding validation rules (can be multiple rules).

 <TextBox Name="textBox1" Width="50" FontSize="12"
        Validation.ErrorTemplate="{StaticResource validationTemplate}"
        Style="{StaticResource textBoxInError}">
    <TextBox.Text>
        <Binding Path="Property" UpdateSourceTrigger="PropertyChanged" >
            <Binding.ValidationRules>
                <local:IsNullOrEmptyValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox> 

Then define the following resources (referenced above) somewhere (e.g., Window.Resources). First a ControlTemplate to define how the TextBox should look when in invalid state:

<ControlTemplate x:Key="validationTemplate">
    <DockPanel>
        <TextBlock Foreground="Red" FontSize="15" Text="!!!" />
        <AdornedElementPlaceholder/>
    </DockPanel>
</ControlTemplate>

Additionally you could define a style trigger to display the error message. Here I just bind it to the ToolTip property of the TextBox:

<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip" 
                    Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                    Path=(Validation.Errors)[0].ErrorContent}"/>
        </Trigger>
    </Style.Triggers>
</Style>
于 2013-01-16T19:09:16.380 に答える
0

あなたは今、INPC 地獄に入っています。私はそこに行ったことがありますが、楽しくありません。
特に、そのようなクラスでマッピングが行われた場合、それらのゲッターとセッターは WPF バインディング コンテキストの外で呼び出され、地獄の休憩が失われるため、これは大きな問題です。

シンプルに保ちます。直接バインドしますApp.PropertyStorageContainer.Property

2 番目のケースでは、次のいずれかです。

  • データ検証を使用する
  • バインディングではなく、コマンドを使用してプロパティを設定できるようにします。このコマンドでは、値のスワップが可能です。

プロパティの get/set を乱用しないでください。

于 2013-01-16T17:03:36.653 に答える