Laden...

MVVM - Command und ValidationRule - Problem

Erstellt von ErikM vor 11 Jahren Letzter Beitrag vor 11 Jahren 4.416 Views
E
ErikM Themenstarter:in
39 Beiträge seit 2011
vor 11 Jahren
MVVM - Command und ValidationRule - Problem

Halöle,

ich habe hier ein Window


<Window x:Class="ValidationRuleBeispiel.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:ValidationRuleBeispiel"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel Orientation="Vertical">
        <StackPanel.Resources>
            <ControlTemplate x:Key="validationTemplate">
                <DockPanel DataContext="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors)/ErrorContent}">
                    <Ellipse x:Name="Ellipse" 
                            DockPanel.Dock="Left" 
                            Margin="2,0,2,0" 
                            Width="14" Height="14" 
                            VerticalAlignment="Center" 
                            Stroke="#40000000" StrokeThickness="2" Fill="Red">
                        <Ellipse.ToolTip>
                            <Border MaxWidth="500" MaxHeight="500">
                                <ContentControl FontSize="14" Content="{Binding}"/>
                            </Border>
                        </Ellipse.ToolTip>
                    </Ellipse>
                    <Border BorderBrush="#40FFAF00" BorderThickness="2" IsHitTestVisible="False">
                        <Border.Background>
                            <SolidColorBrush Color="Red" Opacity="0.2"/>
                        </Border.Background>
                        <AdornedElementPlaceholder Margin="-2" Name="adorner"/>
                    </Border>
                </DockPanel>
            </ControlTemplate>

            <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>
        </StackPanel.Resources>
        <TextBox Width="200">
            <Binding Path="Text"  UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <c:TextValidation />
                </Binding.ValidationRules>
            </Binding>
        </TextBox>
        
        <Button Command="{Binding DasCommand}" Height="25" Width="150">Der Button</Button>
    </StackPanel>
</Window>


Dazu folgendes ViewModel


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using ValidationRuleBeispiel.Annotations;

namespace ValidationRuleBeispiel
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        private string _text;

        public string Text
        {
            get { return _text; }
            set
            {
                if (value == _text) return;
                _text = value;
                OnPropertyChanged("Text");
            }
        }


        public Command DasCommand { get; set; }


        public MainWindowViewModel()
        {
            DasCommand = new Command(DoSomeThing);
        }

        public void DoSomeThing()
        {
            MessageBox.Show("Funktioniert !");
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}


Meine TextValidation-Klasse


class TextValidation : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            bool isValid = false;

            Regex regex = new Regex(@"^[A-Za-z(\-)?]+(( )?[A-Za-z(\-)?])*$");
            try
            {
                if (((string)value).Length > 2)
                {
                    if (regex.IsMatch((string)value))
                    {
                        isValid = !((string)value).Last().Equals('-');
                    }

                }


            }
            catch (Exception e)
            {
                return new ValidationResult(false, "Konnte den Wert > " + value + "\n\n" + e.Message);
            }

            ValidationResult result;

            if (isValid)
            {
                result = new ValidationResult(isValid, null);
            }
            else
            {
                result = new ValidationResult(isValid, "Konnte den Wert > " + value + "< " + "nicht validieren :<");
            }
            return result;
        }
    }

Wenn die Validierung fehlschlägt, wird mir das auch im Window angezeigt.
Wie kann ich das Command dazu bringen, dass CanExecute false zurückliefert, wenn die Validierung fehlgeschlagen ist, sodass der Button sich entsprechend ausgraut 🤔

Gegoogelt habe ich gestern danach, jedoch nicht das gefunden was ich brauchte. Ich stiess auf das Interface IDataErrorInfo -> funktioniert das was ich vorhabe nur mit diesem Interface oder kann ich auch anderweitig irgendwie abfrage ob die Validierung fehlgeschlagen ist.. ?

Ziel ist, dass ich bei einem größeren Window alle Textboxen von verschiedenen Validation-Klassen validieren lasse und erst wenn ALLE Validierungen korrekt waren das Command ausführbar wird... garnich so leicht :<

2.207 Beiträge seit 2011
vor 11 Jahren

Hallo ErikM,

mach dir eine eigene Klasse für das Command. Das leitest du von ICommand ab. Dann wirst du schon sehen, dass du eine "CanExecute"-Methode ausimplementieren musst, die genau das tut, was du willst.


public MyCommand : ICommand

dann kannst du im ViewModel im Konstruktor einer privaten Command-Variablen eine neue Instanz von MyCommand zuweisen und im Property zurückgeben.

Gruss

Coffeebean

E
ErikM Themenstarter:in
39 Beiträge seit 2011
vor 11 Jahren

Ja.. das Command hab ich schon implementiert..

Was müsste ich denn in der CanExecute-Methode schreiben um an die ValidationResults zu kommen..

Die Validierung wird ja in XAML angestoßen.. die Textboxen verfärben sich bei einem Fehler..
Aber was müsste ich tun, damit das Command davon erfährt = ?

2.207 Beiträge seit 2011
vor 11 Jahren

Hallo ErikM,

ob das Command ausgeführt werden kann oder nicht, hängt ja, im einfachsten Fall von einer boolschen Variablen ab. Also machst du diese dem Command bekannt. (Falls die Variable auf einem Validator implementiert ist, übergibst du den im Konstruktor, o.ä. etc.).

Wenn sich diese ändert gibt es das CanExecuteChanged-Event, was du schmeissen kannst, das Command schaut dann, ob sich im CanExecute etwas geändert hat und (De)Aktiviert sich. Grob gesagt.

Gruss

Coffeebean

E
ErikM Themenstarter:in
39 Beiträge seit 2011
vor 11 Jahren

Zu Testzwecken habe ich nachdem ich weiter herumgoogelte, meine Command-Klasse umgebaut



private Action _action;
        private MainWindowViewModel model;

        public Command(Action action, MainWindowViewModel error)
        {
            model = error;
            _action = action;
        }

        public void Execute(object parameter)
        {
            _action();
        }

        public bool CanExecute(object parameter)
        {
            return new TextValidation().Validate(model.Error, CultureInfo.CurrentCulture).IsValid;
        }
        public void RaiseCanExecuteChanged()
        {
            CommandManager.InvalidateRequerySuggested(); 
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }


Aber ich habe den Eindruck, dass "CommandManager.InvalidateRequerySuggested(); " nicht anspringt..

D.h. meine CanExecute-Methode arbeitet nicht.. was mache ich falsch = ? 🙁

5.658 Beiträge seit 2006
vor 11 Jahren

Aber ich habe den Eindruck, dass "CommandManager.InvalidateRequerySuggested(); " nicht anspringt..

Siehe auch Command enabled Button nicht

Weeks of programming can save you hours of planning

E
ErikM Themenstarter:in
39 Beiträge seit 2011
vor 11 Jahren

Naja.. okee :3... jetzt funktioniert es :3

ABER :<

Mein INotifyPropertyChanged verhält sich merkwürdig, wenn man innerhalb einer Textbox die "Zurück-Taste" (also die über der Eingabetaste).. wenn man damit einzelne Buchstaben quasi löscht, dann wird meine "OnPropertyChanged"-Methode nicht gestartet..

Is das ein gewolltes Verhalten von WPF ?

merkwürdig--

W
955 Beiträge seit 2010
vor 11 Jahren

Hi,

-schau Dir mal die UpdateSourceTrigger-Eigenschaft im Binding an, Textbox.Text bindet standardmäßig nicht auf PropertyChanged.-
Ups, sry, haste ja schon gemacht.

E
ErikM Themenstarter:in
39 Beiträge seit 2011
vor 11 Jahren

Ja ne.. es lag an der ValidationRule 😭

Ich musste ValidationStep="UpdatedValue" mit reinschreiben..tadaa



<TextBox Width="200">
            <Binding Path="Text" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True" NotifyOnSourceUpdated="True">
                <Binding.ValidationRules>
                    <c:TextValidation  [B]ValidationStep="UpdatedValue"[/B] />
                </Binding.ValidationRules>
            </Binding>
        </TextBox>


Jetzt ist alles schön 🙂