BlackCoin's Corner

In diesem Blog dreht es sich zu 90 % um den Themenbereich C# .Net

Einfache Input-Validation mit WPF

Auch in aktuellen Anwendungen passiert es leider immer wieder

  • nicht ausgefüllte Pflichtfelder
  • Textlänge zu kurz oder zu lang
  • es wurden ein Text anstatt einer Zahl eingegeben
  • PLZ, Email, Urls Fehlerhaft

 

Die Input-Validation ist also eine Thematik, die selbst in den kleinsten Anwendungen mit beachtet werden sollte, aus diesem Grund handelt das heutige Blog-post genau über diese Thematik.

 

Als Beispiel Applikation, nehme ich heute kleines ein Formular, um neue Kontakt Daten zu erfassen.

  

using System;   

namespace WpfApplication3    
{     
    public class Kontakt     
    {     
        public string Vorname { get; set; }     
        public string Nachnam { get; set; }     
        public string EMail { get; set; }     
        public string Tel { get; set; }     
        public string Street { get; set; }     
        public string Hausnummer { get; set; }     
        public string Plz { get; set; }     
        public string Ort { get; set; }     
    }     
}  

in dieser einfachen Datenstruktur, gibt es schon genug Stolpersteine, die eine Imput-Validation erfordern.

dazu noch ein einfaches aber funktionales Window

  

<Window x:Class="WpfApplication3.KontaktForm"    
        xmlns="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation"">http://schemas.microsoft.com/winfx/2006/xaml/presentation"</a     
        xmlns:x="<a href=">http://schemas.microsoft.com/winfx/2006/xaml"">http://schemas.microsoft.com/winfx/2006/xaml"</a     
        Title="Kontakt" Height="350" Width="525">   

    <Grid>    
        <Grid.RowDefinitions>     
            <RowDefinition Height="Auto"/>     
            <RowDefinition Height="Auto"/>     
            <RowDefinition Height="Auto"/>     
            <RowDefinition Height="Auto"/>     
            <RowDefinition Height="10"/>     
            <RowDefinition Height="Auto"/>     
            <RowDefinition Height="Auto"/>     
        </Grid.RowDefinitions>     
        <Grid.ColumnDefinitions>     
            <ColumnDefinition Width="Auto"/>     
            <ColumnDefinition Width="3*"/>     
            <ColumnDefinition Width="3*"/>     
        </Grid.ColumnDefinitions>     
        <TextBlock Grid.Row="0" Grid.ColumnSpan="3" HorizontalAlignment="Center">Kontakte</TextBlock>        
        <Label Grid.Row="1" Grid.Column="0">Vorname/Nachname</Label>     
        <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=Vorname}" Margin="3"/>     
        <TextBox Grid.Row="1" Grid.Column="2" Text="{Binding Path=Nachname}" Margin="3"/>     
        <Label Grid.Row="2" Grid.Column="0">Straße/HausNr.</Label>     
        <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=Strasse}" Margin="3"/>     
        <TextBox Grid.Row="2" Grid.Column="2" Text="{Binding Path=Hausnummer}" Margin="3"/>     
        <Label Grid.Row="3" Grid.Column="0">PLZ/Ort</Label>     
        <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Path=Plz}" Margin="3"/>     
        <TextBox Grid.Row="3" Grid.Column="2" Text="{Binding Path=Ort}" Margin="3"/>     
        <Label Grid.Row="5" Grid.Column="0">E-Mail</Label>     
        <TextBox Grid.Row="5" Grid.Column="1" Text="{Binding Path=EMail}" Margin="3"/>     
        <Label Grid.Row="6" Grid.Column="0">Tel</Label>     
        <TextBox Grid.Row="6" Grid.Column="1" Text="{Binding Path=Tel}" Margin="3"/>     
    </Grid>   

</Window>  

im jetzigen Zustand dieser Form, können an allen möglichen Stellen, falsche Daten eingegeben werden. (Und glaubt mir, Anwender machen das dann auch)

Was man an dieser Stelle braucht, wäre auf keinen Fall eine Methode wie zb

  

private bool CheckAllValues(){  

if  (!string.IsNullOrEmpty(source.Vorname)){  

       MessageBox.Show(“Fehler bei der Eingabe, das Feld Vorname ist ein Pflichtfeld! …”, “Fehler”);  

      return false;  

}  

…  

}  

die vor jedem speichern ausgeführt wird, und nur so mit MessageBoxen um sich schmeißt.

Viel besser wäre doch eine Überprüfung, nach der Eingabe des jeweiligen wertes, mit einer sofortigen Visualisierung.

 

Welche Änderungen, müssen dafür an dem Vorhanden Code vorgenommen werden.

 

Als erstes sollte die Datenstruktur, das Interface IDataErrorInfo Implementieren.

zusätzlich benutze ich ein private Dictionary, um nach einer Prüfung die Fehlermeldungen zu speichern.

  

using System;   
using System.ComponentModel;    
using System.Collections.Generic;    
using System.Text.RegularExpressions;   

namespace WpfApplication3   
{    
    public class Kontakt : IDataErrorInfo    
    {    
        Dictionary<string, string> errorDict;   

        public Kontakt()   
        {    
            errorDict = new Dictionary<string, string>();   

            InitErrorDict();   
        }   

        //mit Initial Meldungen beim kreieren vorbelegen   
        private void InitErrorDict()    
        {    
            //diese 3 Felder sind Pflichtfelder    
            errorDict.Add("Vorname", "Pflichtfeld");    
            errorDict.Add("Nachname", "Pflichtfeld");    
            errorDict.Add("EMail", "Pflichtfeld");    
        }    
        private string _vorname;    
        private string _nachname;    
        private string _eMail;    
        private string _tel;    
        private string _plz;    
        public string Vorname { get { return _vorname; } set { if (IsNullOrEmpty(value, "Vorname")) _vorname = value; } }    
        public string Nachname { get { return _nachname; } set { if (IsNullOrEmpty(value, "Nachname")) _nachname = value; } }    
        public string EMail { get { return _eMail; } set { if (IsNullOrEmpty(value, "EMail") && IsEMail(value, "EMail")) _eMail = value; } }    
        public string Tel { get { return _tel; } set { if (IsPhone(value, "Tel")) _tel = value; } }    
        public string Strasse { get; set; }    
        public string Hausnummer { get; set; }    
        public string Plz { get { return _plz; } set { if (IsPLZ(value, "Plz")) _plz = value; } }    
        public string Ort { get; set; }   

        private bool IsPhone(string value, string propertyName)   
        {    
            Regex regex = new Regex(@"^\(\d{1,2}(\s\d{1,2}){1,2}\)\s(\d{1,2}(\s\d{1,2}){1,2})((-(\d{1,4})){0,1})$");   

            if (!regex.IsMatch(value))   
            {    
                errorDict.Add(propertyName, "Dies ist keine Telefonnummer!");    
                return false;    
            }    
            else    
                return true;    
        }    
        private bool IsPLZ(string value, string propertyName)    
        {    
            Regex regex = new Regex(@"^\d{5}$");   

            if (!regex.IsMatch(value))   
            {    
                errorDict.Add(propertyName, "Dies ist keine Postleitzahl!");    
                return false;    
            }    
            else    
                return true;    
        }   

        private bool IsEMail(string value, string propertyName)   
        {    
            Regex regex = new Regex(@"^[-_.\w]+@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.){1,300}([a-zA-Z]{2,9})$");   

            if (!regex.IsMatch(value))   
            {    
                errorDict.Add(propertyName, "Dies ist keine EMail Adresse!");    
                return false;    
            }    
            else    
                return true;    
        }   

        private bool IsNullOrEmpty(string value, string propertyName)   
        {    
            if (string.IsNullOrEmpty(value))    
            {    
                errorDict.Add(propertyName, "Dies ist ein Pflichtfeld!");    
                return false;    
            }    
            else    
                return true;    
        }   

        public string Error   
        {    
            get { throw new NotImplementedException(); }    
        }   

        public string this[string columnName]   
        {    
            get    
            {    
                if (errorDict.ContainsKey(columnName))    
                    return errorDict[columnName];    
                else    
                    return "";    
            }    
        }    
    }    
}  

  

 

Was jetzt noch zu tun wäre, ist die Anpassung der Oberfläche.

zur Visualisierung der Fehler dient, an dieser Stelle das ErrorTemplate, denn sobald die darunterliegende Datenklasse meldet, dass ein Fehler für ein bestimmtes Feld vorhanden ist wird dieses ErrorTemplate benutzt und Visualisiert dem Anwender somit sofort das ein Fehler vorliegt.


Wichtig für diese Art der Validation ist das Property ValidatesOnDataErrors, dass für die entsprechenden Felder auf wahr gesetzt werden muss und die Implementierung der Interfaces.


  

<Window x:Class="WpfApplication3.KontaktForm"   
        xmlns="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation"">http://schemas.microsoft.com/winfx/2006/xaml/presentation"</a    
        xmlns:x="<a href=">http://schemas.microsoft.com/winfx/2006/xaml"">http://schemas.microsoft.com/winfx/2006/xaml"</a    
        Title="Kontakt" Height="350" Width="525">   

    <Window.Resources>   
        <ControlTemplate x:Key="ErrorTemplate">    
            <DockPanel>    
                <Grid DockPanel.Dock="Left" Margin="0,0,2,0">    
                    <Ellipse Width="17" Height="17" Fill="Yellow"/>    
                    <TextBlock Text="!" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>    
                </Grid>    
                <Border DockPanel.Dock="Right" BorderBrush="Red" BorderThickness="1">    
                    <AdornedElementPlaceholder />    
                </Border>    
            </DockPanel>    
        </ControlTemplate>   

        <Style TargetType="{x:Type TextBox}">   
            <Setter Property="Validation.ErrorTemplate" Value="{StaticResource ErrorTemplate}"/>    
            <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>   

        </Style>   
    </Window.Resources>   

    <DockPanel>   
        <StatusBar DockPanel.Dock="Bottom">    
            <StatusBarItem Content="{Binding Path=Error}"/>    
        </StatusBar>    
        <Grid>    
            <Grid.RowDefinitions>    
                <RowDefinition Height="Auto"/>    
                <RowDefinition Height="Auto"/>    
                <RowDefinition Height="Auto"/>    
                <RowDefinition Height="Auto"/>    
                <RowDefinition Height="10"/>    
                <RowDefinition Height="Auto"/>    
                <RowDefinition Height="Auto"/>    
            </Grid.RowDefinitions>    
            <Grid.ColumnDefinitions>    
                <ColumnDefinition Width="Auto"/>    
                <ColumnDefinition Width="3*"/>    
                <ColumnDefinition Width="3*"/>    
            </Grid.ColumnDefinitions>    
            <TextBlock Grid.Row="0" Grid.ColumnSpan="3" HorizontalAlignment="Center" >Kontakte</TextBlock>    
            <Label Grid.Row="1" Grid.Column="0">Vorname/Nachname</Label>    
            <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=Vorname, ValidatesOnDataErrors=true}" Margin="5"/>    
            <TextBox Grid.Row="1" Grid.Column="2" Text="{Binding Path=Nachname, ValidatesOnDataErrors=true}" Margin="5"/>    
            <Label Grid.Row="2" Grid.Column="0">Straße/HausNr.</Label>    
            <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=Strasse, ValidatesOnDataErrors=true}" Margin="5"/>    
            <TextBox Grid.Row="2" Grid.Column="2" Text="{Binding Path=Hausnummer, ValidatesOnDataErrors=true}" Margin="5"/>    
            <Label Grid.Row="3" Grid.Column="0">PLZ/Ort</Label>    
            <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Path=Plz, ValidatesOnDataErrors=true}" Margin="5"/>    
            <TextBox Grid.Row="3" Grid.Column="2" Text="{Binding Path=Ort, ValidatesOnDataErrors=true}" Margin="5"/>    
            <Label Grid.Row="5" Grid.Column="0">E-Mail</Label>    
            <TextBox Grid.Row="5" Grid.Column="1" Text="{Binding Path=EMail, ValidatesOnDataErrors=true}" Margin="5"/>    
            <Label Grid.Row="6" Grid.Column="0">Tel</Label>    
            <TextBox Grid.Row="6" Grid.Column="1" Text="{Binding Path=Tel, ValidatesOnDataErrors=true}" Margin="5"/>    
        </Grid>    
    </DockPanel>    
</Window>  

 

Und abschließend wieder mal eine kleine Grafik!!

 

Validate

 

Und bevor Ihr es mir Mitteilen wollte, ich weiß das dies nicht die einzigsten Möglichkeiten sind.


Anregungen, Ideen, Fragen, … immer her damit, entweder als Kommentar oder übers Kontaktformular.


Posted: Nov 04 2009, 11:01 von Lars Schmitt | mit no comments
Abgelegt unter: ,

Kommentare

Keine Kommentare

Kommentar abgeben

(verpflichtend) 

(verpflichtend) 

(optional)

(verpflichtend)