web-dev-qa-db-fra.com

Forcer la validation sur les contrôles liés dans WPF

J'ai une boîte de dialogue WPF avec quelques zones de texte dessus . Les zones de texte sont liées à mon objet métier et sont associées à des règles de validation WPF.

Le problème est que l'utilisateur peut parfaitement cliquer sur le bouton 'OK' et fermer la boîte de dialogue sans entrer les données dans les zones de texte. Les règles de validation ne se déclenchent jamais, car l'utilisateur n'a même pas tenté de saisir les informations dans les zones de texte.

Est-il possible de forcer les contrôles de validation et de déterminer si certaines règles de validation sont enfreintes?

Je serais en mesure de le faire lorsque l'utilisateur essaiera de fermer la boîte de dialogue en lui interdisant de le faire si des règles de validation sont enfreintes.

Je vous remercie.

55
Valentin

Nous avons également ce problème dans notre application. La validation ne se déclenche que lorsque les liaisons sont mises à jour, vous devez donc les mettre à jour manuellement. Nous faisons cela dans l'événement Loaded de la fenêtre:

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();
}

Cela fera apparaître le modèle d'erreur (contour rouge) et définira la propriété Validation.HasError , ce qui déclenche le bouton OK pour désactiver:

<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>
59
Robert Macnee

Dans 3.5SP1/3.0SP2, ils ont également ajouté une nouvelle propriété à la base ValidationRule, à savoir ValidatesOnTargetUpdated = "True" . Cela appellera la validation dès que l'objet source est lié, plutôt que seulement lorsque le contrôle cible est mis à jour. Ce n'est peut-être pas exactement ce que vous voulez, mais ce n'est pas mal de voir au début tout ce que vous devez réparer.

Fonctionne à peu près comme ceci:

<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>
67
Ken Smith

Voici une autre façon de ne pas appeler "UpdateSource ()" ou "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);
}
1
ryan

Utilisez la méthode ci-dessus proposée par Robert Macnee. Par exemple:

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

Mais, ASSUREZ-VOUS que les contrôles liés sont visibles avant que ce code ne soit exécuté!

0
Alex Pollan

Juste au cas où quelqu'un trouverait cette vieille question et chercherait une réponse qui tienne compte du commentaire de Monstieur à propos des directives de l'interface utilisateur, j'ai procédé comme suit:

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);
    }
}

Dans la classe à laquelle le Xaml se lie

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
}

Je cache ensuite mon bouton Enregistrer à l'aide d'une propriété liée, si l'un de mes objets a HasValidationError = true.

J'espère que cela est utile à quelqu'un.

0
lukep