Zu Hauptinhalt springen

 none
Validation in dynamisch erzeugter Page RRS feed

  • Frage

  • Hallo,

    möchte aus gegebenen Klassen im Code eine jeweilige Edit-Page erzeugen.
    Das geht soweit. Nur wie die Validation hinzufügen?

    // Binding. Binding myBinding = new Binding(); myBinding.Source = this; myBinding.Path = new PropertyPath(entry_UI_Prop.Name); myBinding.Mode = BindingMode.TwoWay; myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; if (pi.PropertyType == typeof(decimal)) { myBinding.Converter = new DecimalToStringConverter(); } else if (pi.PropertyType == typeof(DateTime?)) { myBinding.Converter = new Nullable_Date_To_String_Converter(); myBinding.UpdateSourceTrigger = UpdateSourceTrigger.LostFocus;

    // ValidationRules ist nicht bekannt? myBinding.ValidationRules } t.SetBinding(TextBox.TextProperty, myBinding); // Der RelativePanel in dem die TextBox hinzugefügt wird. Prop_RP.Children.Add(t); c_AddedLast = t;


    Montag, 23. September 2019 08:21

Antworten

  • Hi Markus,
    in WPF geht das so:

            myBinding.ValidationRules.Add(new MyRule());
    
    …
    
        class MyRule : ValidationRule
        {
          public override ValidationResult Validate(object o, CultureInfo ci)
          {
            bool testresult;
            //...
            return new ValidationResult(testresult, null);
          }
        }

    In UWP musst Du das entweder beim Schreiben in die Eigenschaft oder mittels Converter lösen.


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    • Als Antwort markiert Markus222 Mittwoch, 25. September 2019 07:29
    Montag, 23. September 2019 11:51
  • Hi Markus,
    hier mal eine Demo, wie man das machen kann. Dabei wird der Instanz des Converters ein Rückruf (Action) übergeben, der bei Validierungsfehlern aufgerufen wird.

    XAML:

    <Page
        x:Class="App1.Page17"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:App1"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
      <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBox x:Name="tb"/>
        <TextBlock Text="{Binding ErrInfo}"/>
        <TextBox Text="für Focuswechsel"/>
      </StackPanel>
    </Page>

    Der Code dazu:

    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Data;
    
    // The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
    
    namespace App1
    {
      /// <summary>
      /// An empty page that can be used on its own or navigated to within a Frame.
      /// </summary>
      public sealed partial class Page17 : Page
      {
        public Page17()
        {
          this.InitializeComponent();
          InitBinding();
        }
        private void InitBinding()
        {
          var vm = new Page17VM();
          this.DataContext = vm;
          var b = new Binding();
          b.Path = new PropertyPath("Number");
          b.Mode = BindingMode.TwoWay;
          var conv = new Page17conv();
          conv.ErrLog = (par) => { vm.ErrInfo = par; };
          b.Converter = conv;
          this.tb.SetBinding(TextBox.TextProperty, b);
        }
      }
    
      public class Page17VM : INotifyPropertyChanged
      {
        private int _number = 111;
        public int Number
        {
          get { return this._number; }
          set { this._number = value; OnPropertyChanged(); }
        }
    
        private string _errInfo = "no error";
        public string ErrInfo
        {
          get { return this._errInfo; }
          set { this._errInfo = value; OnPropertyChanged(); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }
      public class Page17conv : IValueConverter
      {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
          return ((int)value).ToString();
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
          int res=0;
          if (value == null || !int.TryParse(value.ToString(), out res)) ErrLog.Invoke("keine Ganzzahl");
          else ErrLog.Invoke("kein Fehler");
          return res;
        }
    
        public Action<string> ErrLog { get; set; }
      }
    }


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    • Als Antwort markiert Markus222 Mittwoch, 25. September 2019 07:29
    Montag, 23. September 2019 19:27

Alle Antworten

  • Hi Markus,
    in WPF geht das so:

            myBinding.ValidationRules.Add(new MyRule());
    
    …
    
        class MyRule : ValidationRule
        {
          public override ValidationResult Validate(object o, CultureInfo ci)
          {
            bool testresult;
            //...
            return new ValidationResult(testresult, null);
          }
        }

    In UWP musst Du das entweder beim Schreiben in die Eigenschaft oder mittels Converter lösen.


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    • Als Antwort markiert Markus222 Mittwoch, 25. September 2019 07:29
    Montag, 23. September 2019 11:51
  • Hallo Peter,

    wie ginge das mit dem Converter?

    Ist dieser nicht für Typkonvertierung zwischen Property und Control gedacht? Wird so ein Converter auch zuverlässig aufgerufen wenn des Property vom Typ String und das Control eine TextBox ist?

    Man hat im Converter zwar einen Parameter und kann dort wohl einen Delegate auf eine Validierungsprozedur mitgeben. Aber was tut man im Fehlerfall? So wie ich probiert habe könnte ich eine im Converter ausgelöste Exception nicht abfangen.

    Glaube ich würde da ich die Page eh generiere auf des Binding eher ganz verzichten und die Werte per Code hin und herschieben.

    Montag, 23. September 2019 13:54
  • Hi Markus,
    hier mal eine Demo, wie man das machen kann. Dabei wird der Instanz des Converters ein Rückruf (Action) übergeben, der bei Validierungsfehlern aufgerufen wird.

    XAML:

    <Page
        x:Class="App1.Page17"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:App1"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
      <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBox x:Name="tb"/>
        <TextBlock Text="{Binding ErrInfo}"/>
        <TextBox Text="für Focuswechsel"/>
      </StackPanel>
    </Page>

    Der Code dazu:

    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Data;
    
    // The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
    
    namespace App1
    {
      /// <summary>
      /// An empty page that can be used on its own or navigated to within a Frame.
      /// </summary>
      public sealed partial class Page17 : Page
      {
        public Page17()
        {
          this.InitializeComponent();
          InitBinding();
        }
        private void InitBinding()
        {
          var vm = new Page17VM();
          this.DataContext = vm;
          var b = new Binding();
          b.Path = new PropertyPath("Number");
          b.Mode = BindingMode.TwoWay;
          var conv = new Page17conv();
          conv.ErrLog = (par) => { vm.ErrInfo = par; };
          b.Converter = conv;
          this.tb.SetBinding(TextBox.TextProperty, b);
        }
      }
    
      public class Page17VM : INotifyPropertyChanged
      {
        private int _number = 111;
        public int Number
        {
          get { return this._number; }
          set { this._number = value; OnPropertyChanged(); }
        }
    
        private string _errInfo = "no error";
        public string ErrInfo
        {
          get { return this._errInfo; }
          set { this._errInfo = value; OnPropertyChanged(); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }
      public class Page17conv : IValueConverter
      {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
          return ((int)value).ToString();
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
          int res=0;
          if (value == null || !int.TryParse(value.ToString(), out res)) ErrLog.Invoke("keine Ganzzahl");
          else ErrLog.Invoke("kein Fehler");
          return res;
        }
    
        public Action<string> ErrLog { get; set; }
      }
    }


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    • Als Antwort markiert Markus222 Mittwoch, 25. September 2019 07:29
    Montag, 23. September 2019 19:27
  • Hallo Peter,
    vielen Dank für das Beispiel.
    Bei den Edit-Pages für das Editieren der Objekte werde ich nun dennoch den Weg ohne Binding mit dem automatischen Hin- und Herschieben der Daten zwischen Objekt und Page nehmen.

    Dann kann ich es so einreichten das der Focus im Fehlerfall im Eingabefeld bleibt und die Daten erst beim Speichern (Verlassen der Page) in das Objekt übertragen werden.

    So kann ich sicherstellen das sich nur valide Daten im Objekt befinden. Sollte man so in einem Feld nicht weiterkommen bleibt nur Abbruch zu klicken was das Objekt dann unverändert lässt,
    Mittwoch, 25. September 2019 07:31
  • Hi Markus,
    eine solche Bedientechnologie (Focus zwangsweise setzen) empfinde ich als Bevormundung des Anwenders, was zu einer geringeren Akzeptanz der Anwendung führen kann.

    Eine bessere Lösung ist, den Anwender die Daten erfassen zu lassen und im Problemfall mit einer Farbe oder einer Meldung (z.B. rotes Sternchen) die unpassende Eingabe zu signalisieren. Außerdem kann der "Save"-Button solange disabled werden, solange nicht alle Eingaben passend sind. Diese Lösung ist vor allem dann besser, wenn der Anwender Daten erfassen muss, die nicht in der Reihenfolge der Felder der Erfassungsmaske vorliegen (z.B. auf Papier). Bei Deiner Technologie muss der Anwender u.U. in den zu erfassenden Daten "springen", was eine Fehlerquelle sein kann und den Erfassungsprozess verlangsamen kann, was wiederum Frust erzeugen kann.

    An die Erfassung sollte aber ein selbständiges Objekt gebunden sein, welches erst persistiert wird, z.B. in den mit der Datenbank korrespondierenden Datenpuffer eingefügt oder aktualisiert wird, wenn der "Save"-Button erfolgreich geklickt wurde. 


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks


    Mittwoch, 25. September 2019 10:19