web-dev-qa-db-ja.com

WPFのバインドされたコントロールの検証を強制する

テキストボックスがいくつかあるWPFダイアログがあります。テキストボックスはビジネスオブジェクトにバインドされ、WPF検証ルールが添付されています。

問題は、ユーザーがテキストボックスにデータを実際に入力しなくても、「OK」ボタンを完全にクリックしてダイアログを閉じることができることです。ユーザーは情報をテキストボックスに入力することさえしなかったため、検証ルールは実行されません。

検証チェックを強制し、いくつかの検証ルールが破られているかどうかを判断することは可能ですか?

ユーザーがダイアログを閉じて、検証ルールが破られた場合にダイアログを閉じることを禁止しようとすると、私はそれを行うことができます。

ありがとうございました。

55
Valentin

アプリケーションにもこの問題があります。検証はバインディングが更新されたときにのみ起動するため、手動で更新する必要があります。ウィンドウの Loaded イベントでこれを行います:

public void Window_Loaded(object sender, RoutedEventArgs e)
{
    // we manually fire the bindings so we get the validation initially
    txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
    txtCode.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

これにより、エラーテンプレート(赤いアウトライン)が表示され、 Validation.HasError プロパティが設定されます。これにより、[OK]ボタンをトリガーして無効にします。

<Button x:Name="btnOK" Content="OK" IsDefault="True" Click="btnOK_Click">
    <Button.Style>
        <Style TargetType="{x:Type Button}">
            <Setter Property="IsEnabled" Value="false" />
            <Style.Triggers>
                <!-- Require the controls to be valid in order to press OK -->
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding ElementName=txtName, Path=(Validation.HasError)}" Value="false" />
                        <Condition Binding="{Binding ElementName=txtCode, Path=(Validation.HasError)}" Value="false" />
                    </MultiDataTrigger.Conditions>
                    <Setter Property="IsEnabled" Value="true" />
                </MultiDataTrigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>
62
Robert Macnee

3.5SP1/3.0SP2では、ValidationRuleベースに新しいプロパティ、つまり ValidatesOnTargetUpdated = "True" も追加しました。これは、ターゲットコントロールが更新されたときだけでなく、ソースオブジェクトがバインドされるとすぐに検証を呼び出します。それはあなたが望むものとは正確に異なるかもしれませんが、最初に修正する必要があるすべてのものを見るのは悪くありません。

次のように機能します。

<TextBox.Text>
    <Binding Path="Amount" StringFormat="C">
        <Binding.ValidationRules>
            <validation:RequiredValidationRule 
                ErrorMessage="The pledge amount is required." 
                ValidatesOnTargetUpdated="True"  />
            <validation:IsNumericValidationRule 
                ErrorMessage="The pledge amount must be numeric." 
                ValidationStep="ConvertedProposedValue" 
                ValidatesOnTargetUpdated="True"  />
        </Binding.ValidationRules>
    </Binding>
</TextBox.Text>
68
Ken Smith

「UpdateSource()」または「UpdateTarget()」を呼び出す必要のない代替方法を次に示します。

var binding = thingToValidate.GetBinding(propertyToValidate);
foreach (var rule in binding.ValidationRules)
{
    var value = thingToValidate.GetValue(propertyToValidate);
    var result = rule.Validate(value, CultureInfo.CurrentCulture);
    if (result.IsValid) 
         continue;
    var expr = BindingOperations.GetBindingExpression(thingToValidate, propertyToValidate);
    if (expr == null)  
        continue;
    var validationError = new ValidationError(rule, expr);
    validationError.ErrorContent = result.ErrorContent;
    Validation.MarkInvalid(expr, validationError);
}
2
ryan

Robert Macneeが提案した上記の方法を使用します。例えば:

//force initial validation
foreach (FrameworkElement item in grid1.Children)
{
    if (item is TextBox)
    {
        TextBox txt = item as TextBox;
        txt.GetBindingExpression(TextBox.TextProperty).UpdateSource();
    }
}        

ただし、このコードを実行する前に、バインドされたコントロールがVisiblesであることを確認してください!

0
Alex Pollan

誰かがこの古い質問を見つけて、UIガイドラインに関するMonstieurのコメントに対応する回答を探している場合に備えて、次のことを行いました。

Xaml

<TextBox.Text>
    <Binding Path="TextValue" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
        <Binding.ValidationRules>
            <local:RequiredFieldValidationRule>
                    <local:RequiredFieldValidationRule.IsRequiredField>
                    <local:BoolValue Value="{Binding Data.Required, Source={StaticResource proxy}}" />
                </local:RequiredFieldValidationRule.IsRequiredField>
                <local:RequiredFieldValidationRule.ValidationFailed>
                    <local:BoolValue Value="{Binding Data.HasValidationError, Mode=TwoWay, Source={StaticResource proxy}}" />
                </local:RequiredFieldValidationRule.ValidationFailed>
            </local:RequiredFieldValidationRule>
        </Binding.ValidationRules>
    </Binding>
</TextBox.Text>

RequiredFieldValidationRule:

public class RequiredFieldValidationRule : ValidationRule
{
    private BoolValue _isRequiredField;
    public BoolValue IsRequiredField
    {
        get { return _isRequiredField; }
        set { _isRequiredField = value; }
    }
    private BoolValue _validationFailed;
    public BoolValue ValidationFailed
    {
        get { return _validationFailed; }
        set { _validationFailed = value; }
    }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        ValidationFailed.Value = IsRequiredField.Value && (value == null || value.ToString().Length == 0);
        return new ValidationResult(!ValidationFailed.Value, ValidationFailed.Value ? "This field is mandatory" : null);
    }
}

Xamlがバインドするクラス内

private bool _hasValidationError;
public bool HasValidationError
{
    get { return _hasValidationError; }
    set { _hasValidationError = value; NotifyPropertyChanged(nameof(HasValidationError)); }
}


public void InitialisationMethod() // Or could be done in a constructor
{
    _hasValidationError = Required; // Required is a property indicating whether the field is mandatory or not
}

次に、いずれかのオブジェクトにHasValidationError = trueがある場合、バインドされたプロパティを使用して[保存]ボタンを非表示にします。

これが誰かに役立つことを願っています。

0
lukep