Eingabefehler abfangen in Windows 8 Store APP’s

Anders als in WPF oder Silverlight, hat das XAML UI Framework von WInRT keine Templates oder Mechanismen um Fehler an den Benutzer zu melden oder ihn zu leiten. Also ist in WinRT Handarbeit angesagt für das Errorhandling.

Optisch erscheint es sinnvoll, über eine Textbox einen rot eingefärbten Textblock zu platzieren, die über das Visbility Attribut gesteuert wird. Um es einheitlich im Layout handhaben zu können, bietet sich eine Gruppierung mit Stackpanel an.

image

Nun stellt sich díe Frage, wie man die Logik unterbringt. VB.NET Entwickler werden aus jahrelanger Erfahrung gerne zum lostfocus Event greifen.

Das Problem ist, das man auch das Event GotFocus nutzen muss, um die Fehlermeldung wieder auszublenden, wenn der Benutzer den Cursor in die Textbox zurück setzt. Folgender VB.NET Code prüft auf ein X und meldet dann einen Fehler.

 Private Sub TextBox_LostFocus_1(sender As Object, e As RoutedEventArgs)
        If sender.text.contains("x") Then
            errorTextbox1.Visibility = Windows.UI.Xaml.Visibility.Visible
        End If
 End Sub

 Private Sub TextBox_GotFocus_1(sender As Object, e As RoutedEventArgs)
        errorTextbox1.Visibility = Windows.UI.Xaml.Visibility.Collapsed
 End Sub

Der Code sieht etwas seltsam aus (wo ist der Typcast) funktioniert aber so unter Windows 8.

Wenn man allerdings Databinding verwendet, ergeben sich neue Problemstellungen. Ich setze hier eine Klasse class1 mit einem Property prop vom Typ String.

 <TextBlock Foreground="Red" Text="Bitte die Eingabe überprüfen" Visibility="Collapsed"
x:Name="errorTextbox1"></TextBlock> <TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding prop,Mode=TwoWay}"
GotFocus="TextBox_GotFocus_1" LostFocus="TextBox_LostFocus_1" VerticalAlignment="Top"/>

Die Zuweisung der Datenquelle erfolgt über den DataContext

Private Sub MainPage_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
        Dim c As New Class1
        c.prop = "test"
        Me.DataContext = c
 End Sub

Um auf den Inhalt des Datacontext zugreifen zu können, wird man unter Umständen, die vorige Codezeile einfach umdrehen. Das klappt aber in Lostfocus nicht, bzw liefert den letzten Inhalt der Textbox und nicht den aktuellen.

Also folgender Code erzeugt ein falsches seltsames verzögertes Verhalten.

 Private Sub TextBox_LostFocus_1(sender As Object, e As RoutedEventArgs)
        If Me.DataContext.prop.Contains("x") Then
            errorTextbox1.Visibility = Windows.UI.Xaml.Visibility.Visible
        End If
 End Sub

In WPF lässt sich das mit der BindingExpression und UpdateSource umgehen. Ich habe aber keine WinRT .NET Entsprechung gefunden.

Ist auch nicht essentiell, da es wahrscheinlich sinniger ist, die mit zwei Events nötige komplexe Logik in die Hände des Class1 Objektes zu geben. Diese bekommt dazu eine Eigenschaft isValid, die dann folgend an die ErrorTextbox gebunden wird. Da allerdings die Textbox zwar in das Objekt schreiben kann, aber nichts davon mitbekommt, wenn sich der Inhalt des isValid Propertys nachträglich ändert, muss diese davon unterrichtet werden. Das wird über ein Nachrichtensystem implementiert, das das Interface InotifyPropertyChanged mitliefert. Wenn sich isValid verändert wird dann per Code das Event erzwungen (raisevent).

Ergänzend: in .NET 4.5 gibt es nun einen Methode CallMemberName, die etwas weniger Code benötigt.

Public Class Class1
    Implements INotifyPropertyChanged

    Private _prop1 As String
    Public Property prop() As String
        Get
            Return _prop1
        End Get
        Set(ByVal value As String)
            If value.Contains("x") Then
                isValid = Visibility.Visible
            Else
                _prop1 = value
                isValid = Visibility.Collapsed
            End If

        End Set
    End Property
    Private _isValid As Windows.UI.Xaml.Visibility
    Public Property isValid() As Windows.UI.Xaml.Visibility
        Get
            Return _isValid
        End Get
        Set(ByVal value As Windows.UI.Xaml.Visibility)
            _isValid = value
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("isValid"))
        End Set
    End Property

    Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) 
Implements INotifyPropertyChanged.PropertyChanged End Class

Über die Implementierung von Überprüfungslogik kann man trefflich diskutieren. Wahrscheinlich wird man in der Praxis noch ein Fehlerobjekt mit Details mitführen. In Silverlight musste man z.B. Exceptions auslösen. Wie man sehen kann, hat man nun nur mehr Code an einer Stelle (im Datenobjekt) und keinen Code mehr in der Seite (im View). Richtig, das deckt sich mit dem Design Pattern MVVM.

Die Bindung in XAML ist nun ein leichtes und beinhaltet keine Überraschungen.

<StackPanel>
   <TextBlock Foreground="Red" Text="Bitte die Eingabe überprüfen" Visibility="{Binding isValid}" 
/> <TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding prop,Mode=TwoWay}"

Als Steigerungsstufe (ab hier optionales weiterlesen) wird auch noch der Code aus dem Load Event des Views eliminiert. Auch das geht per Binding im XAML.

<Page.Resources>
       <local:Class1 x:Key="class1"/>
</Page.Resources>
<StackPanel DataContext="{StaticResource class1}"

Pingbacks and trackbacks (1)+

Kommentare sind geschlossen