Laden...

WPF: Validierung mit ValidationRule

Erstellt von flacker vor 9 Jahren Letzter Beitrag vor 9 Jahren 4.003 Views
F
flacker Themenstarter:in
33 Beiträge seit 2014
vor 9 Jahren
WPF: Validierung mit ValidationRule

Hallo,

ich möchte gern eine eigene ValidierungsRule erstellen. Dafür habe ich ein schönes Buch welches mir das anhand eines Beispiels erklärt.
Dies habe ich genau nachgeschrieben nur funktioniert ein bestimmter Teil nicht!

Erstmal der Xaml Teil:

<Window.Resources>
        <Style TargetType="TextBox">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <StackPanel Orientation="Horizontal">
                            <AdornedElementPlaceholder/>
                        </StackPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={ RelativeSource Self}, 
                        Path=(Validation.Errors)[0].ErrorContent}"/>
                </Trigger>
            </Style.Triggers>        
        </Style>
    </Window.Resources>
    <Grid>
        <TextBox
            HorizontalAlignment="Left"
            VerticalAlignment="Top">
            <Binding Path="States" UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <local:ValidierungTest/>
                </Binding.ValidationRules>
            </Binding>
        </TextBox>

Nun meine Validierungsklasse:

public class ValidierungTest :ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            int wert;
           if(int.TryParse(value.ToString(), out wert))
            {

            }
            else
            {
                return new ValidationResult(false, "Es werden nur positive Integer Akzeptiert!");
            }

            return ValidationResult.ValidResult;
        }
    }

Also die Textbox wird auch nicht mehr Rot umrandet... das heißt ja das es erstmal soweit erkannt wurde, jedoch wird der ToolTip nicht angezeigt.

C
80 Beiträge seit 2010
vor 9 Jahren

Hallo flacker,

in deinem XAML ist nirgendwo definiert, auf welche Property der TextBox dein Binding angewendet werden soll. Du musst hier die Text-Property angegeben.


....
 <TextBox
            HorizontalAlignment="Left"
            VerticalAlignment="Top">
<TextBox.Text>
            <Binding Path="States" UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <local:ValidierungTest/>
                </Binding.ValidationRules>
            </Binding>
</TextBox.Text>
        </TextBox>

F
flacker Themenstarter:in
33 Beiträge seit 2014
vor 9 Jahren

Funktioniert jetzt

Jetzt stellt sich nur eine andere Frage, wie kann ich von dem ToolTip zum Beispiel das StaysOpen Property auf true setzen?

F
flacker Themenstarter:in
33 Beiträge seit 2014
vor 9 Jahren

Ich hätte noch eine Frage. Das mit dem ToolTip hat sich erledigt!

Wie schaffe ich es von einem Button im selben Window wie der XamlCode da oben die Property IsEnabled auf false zu setzen, solange die Validation.HasError property auf true ist?

2.207 Beiträge seit 2011
vor 9 Jahren

Hallo flacker,

mit einem Trigger wie du ihn schon verwendest.

PSEUDECODE:


property="Validation.HasError" Value="true" 
Setter Property="IsEnabled" ElementName="myButton" Value="false"

So in etwa.

Gruss

Coffeebean

F
flacker Themenstarter:in
33 Beiträge seit 2014
vor 9 Jahren

das Problem ist, dass ich nicht weiß wo! Füge ich einfach noch ein neues Setter dazu und gebe bei TargetName meinen Button ein, kommt immer einer Fehlermeldung das er ihn nicht findet

2.207 Beiträge seit 2011
vor 9 Jahren

Hallo flacker,

im Button (oder dessen Style) einen Trigger einfügen. Dann auf dein Validation.HasError prüfen, wenn das true ist, dann setze das IsEnabled auf false.

Gruss

Coffeebean

F
flacker Themenstarter:in
33 Beiträge seit 2014
vor 9 Jahren

Sry das ich das jetzt sagen muss, aber das klappt so nicht!

Hier der Code:

<Style TargetType="Button">
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="Button.IsEnabled" Value="false"/>
                </Trigger>
            </Style.Triggers>
        </Style>

Das ganze befindet sich in Window.Resource
und hier mein Button:

<Button Name="HMMErstellen" 
                Grid.Row="3" 
                Grid.Column="0" 
                HorizontalAlignment="Right" 
                VerticalAlignment="Center" 
                Margin="5" 
                Content="HMM Erstellen" 
                Click="HmmErstellen_Click">
        </Button>

leider passiert mit dem Button nichts wenn ich in der Textbox einen falschen Wert eingebe, dagehen reagiert die Textbox genauso wie ich es möchte... bin langsam echt Ratlos. Sitze schon ewig dran -.-

2.207 Beiträge seit 2011
vor 9 Jahren

Hallo flacker,

dann schau dir bitte die Triggers nochmal an. Auf den ersten Blick würde ich im Setter "Button." wegmachen. Du hast ja als TargetType schon "Button" angegeben.

Stelle sicher, dass er das "Validation.HasError" richtig liest. Eventuell musst du hier mit ElementName arbeiten. Ich kann es leider gerade nicht nachprogrammieren. Ausserdem arbeite erstmal mit dem Style direkt im Button udn schau, ob er das "Validation.HasError" richtig bekommt.

Du solltest auch das MVVM-Pattern verwenden. Dann hättest du dir viel Zeit sparen können und das ganze einfach auf dem ViewModel binden und es im Code behandeln können. (Was die nicht so schöne Alternative wäre, aber es hätte getan, zumal man im VM auch Services anbeiten kann).

Allgemein ist bei WPF das schwierigste zu sagen "Wo befinde ich mich gerade und was ist hier mein DataContext".

Nachtrag:


<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>

ist von Validation.HasError Attached Property

Gruss

Coffeebean

F
flacker Themenstarter:in
33 Beiträge seit 2014
vor 9 Jahren

Aber wie kann es sein, das dies hier funktioniert.

 <Style TargetType="TextBox">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <StackPanel Orientation="Horizontal">                            
                            <AdornedElementPlaceholder/>
                        </StackPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Trigger.Setters>
                        <Setter Property="ToolTip"
                                     Value="{Binding RelativeSource={RelativeSource Self},
                                     Path=(Validation.Errors)[0].ErrorContent}"/>
                        <Setter Property="BorderBrush" Value="Red"/>
                    </Trigger.Setters>
                </Trigger>
            </Style.Triggers>
        </Style>

Ja und das mit dem MVVM verwenden ist bissl spät... habe hier mittlerweile gut 2500 Zeilen Code... das geht nicht so schnell umzusteigen

Hab ein wenig rumprobiert.

<Style TargetType="Button">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=test, Path=Validation.HasError}" Value="true">
                    <Setter Property="IsEnabled" Value="false"></Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>

Dies geht in meinem kleinen Testprogramm, dabei entspricht ElementName = test der Name meiner Testtextbox.

Dies geht nur leider nicht in meinem Hauptprogramm, da in meinem richtigen Programm die Textboxen in einem Datatemplate sind. An dieses ist eine Collection gebunden.
Das heißt ich habe die Einträge der TextBoxen in meiner ObservableCollection. Elemente dieser Collection sind eine Klasse welche ein einfaches String Property besitzt und durch INotifyPropertyChanged wird der WErt sofort geändert wenn man etwas in der Textbox einträgt!
Wenn ich jetzt also einen Valid Wert eingebe, wird die Property meines Elements in der Collection gesetzt. Gebe ich danach einen falschen wert in die TextBox ein, wird dieser Wert nicht gesetzt durch meine ValidationRule. Und genau dort ist das Problem... jetzt steht in der TextBox ein falscher wert aber in die Property besitzt schon einen validen Wert. Damit kommt der Benutzer wenn er den Button drückt weiter obwohl in der TextBox gerade ein INvalider Wert angezeigt wird! Dies möchte ich gern verhindern... deswegen ist es auch wichtig, dass der Button disabled wird.

Oder denke ich viel zu kompliziert und es gibt eine einfachere Variante? Sry für den vielen Text aber ich denke, so versteht man was ich realisieren möchte!

F
flacker Themenstarter:in
33 Beiträge seit 2014
vor 9 Jahren

Hallo,

nachdem ich leider nie ein Antwort bekommen habe, möchte ich hier mal meine Lösung posten, für Leute die ein ähnliches Problem haben.

        public static bool IsValid(DependencyObject parent)
        {
            if (Validation.GetHasError(parent))
                return false;

            // Validate all the bindings on the children
            for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i)
            {
                DependencyObject child = VisualTreeHelper.GetChild(parent, i);
                if (!IsValid(child)) { return false; }
            }

            return true;
        }

Im Grunde macht die Methode nichts anderes, als sich am übergebenen Objekt runterzuhangeln und bei jedem Kind zu prüfen, ob ein Error bei einer Validation(Validation.GetHasError) vorliegt.