none
How to implement validation in WPF using MVVM method, validate against the data in the database RRS feed

  • Question

  • Hi,

    I have earlier asked this question https://social.msdn.microsoft.com/Forums/vstudio/en-US/363e125d-00c5-4fea-be61-a0e902665bbf/more-information-on-type-of-validation-implemented-in-wpf-how-to?forum=wpf#363e125d-00c5-4fea-be61-a0e902665bbf and I have tested out the answer, it works fine.

    In relation to the above post, I would need to validate the data that the user input with the data from the database. I am also new to the MVVM pattern and I understand that ( been reading the net ) validation in wpf uses mvvm pattern. I am still in the beginner phase, trying to understand the whole logic behind this. 

    Anyway, I would need some advice on how to implement it, this is my code. I was trying out the bindvalidation from the msdn site, I would need to modify it to suit my project. I would need the workerid_txt value to be checked against the table in the database once the user has entered. If the user has entered a value NOT in the database, it should colour the background of the textbox to red - to inform the user that this is an error.  

    I would need advice on how to go about implementing a validation against the database to see if the value exist. 

    This is my code 

    - This is my view

    <Window x:Class="Sunum.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Sunum"
            mc:Ignorable="d"
            Title="SuNum" Height="450" Width="800"
            Name="Sunumwindow" WindowStartupLocation="CenterScreen">
        <Window.Resources>
            <local:MyDataSource x:Key="Ods"/>
            
            <Style x:Key="DefaultTextBoxStyle" TargetType="TextBox">
                <Setter Property="Margin" Value="5" />
                
           
            </Style>
    
            <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>
            <Style x:Key="ToolTipWithErrorMessageOnErrorStyle" TargetType="TextBox" BasedOn="{StaticResource DefaultTextBoxStyle}">
                <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>
            <Style x:Key="RedBackgroundOnErrorStyle" TargetType="TextBox" BasedOn="{StaticResource ToolTipWithErrorMessageOnErrorStyle}">
                <Style.Triggers>
                    <Trigger Property="Validation.HasError" Value="True">
                        <Setter Property="Background" Value="Red" />
                    </Trigger>
                </Style.Triggers>
            </Style>
    
    
          
    
            
        </Window.Resources>
        
        
        <Grid Margin="0,10,0,0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="1*" />
                <ColumnDefinition Width="1*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="1*" />
                <RowDefinition Height="0.2*" />
                <RowDefinition Height="2*" />
                <RowDefinition Height="0.3*" />
                <RowDefinition Height="0.3*" />
            </Grid.RowDefinitions>
           
            <Border Background="#1f3d7a" Grid.ColumnSpan="3" Grid.Row="0"/>
            <Border Background="#2f5cb6" Grid.Column="0"  Grid.Row="2"/>
            <Border Background='#CFB53B' Grid.Column="2"  Grid.Row="2"/>
            <Image Source="Images\GD_Logo_CT_neg_oSZ.png" Margin="20,0,0,0" Stretch="None" Grid.ColumnSpan="3"></Image>
            <Image Source="Images\GD_backvisual.png" Stretch="UniformToFill" Grid.Row="4" Grid.ColumnSpan="3" ></Image>
    
            <TextBlock x:Name="machine_sunumtxt" Grid.Column="0" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap" Foreground="Azure" FontSize="60"><Run Text="TextBlock"/><InlineUIContainer>
                    
                </InlineUIContainer></TextBlock>
    
            <StackPanel Grid.Column="1" Grid.Row="2" VerticalAlignment="Center">
                <StackPanel Orientation="Horizontal">
                    <Label Margin="3" VerticalAlignment="Center">Batch Number</Label>
                    <TextBox Margin="3" x:Name="batch_txt" FontSize="30" VerticalAlignment="Center" Width="150" Height="50" 
                             Text="0"
                             HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="tryvalidate" />
                    
                    
                    
                    
                    
                </StackPanel>
                <StackPanel Orientation="Horizontal">
                    <Label Margin="5" VerticalAlignment="Center" Target="{Binding ElementName=workerid_txt}">Worker ID</Label>
    
    
                    <TextBox x:Name="workerid_txt" Margin="20,7" FontSize="30" VerticalAlignment="Center" HorizontalContentAlignment="Center" Width="150" Height="50" Style="{StaticResource RedBackgroundOnErrorStyle}">
                        <Binding Path="Age2" Source="{StaticResource Ods}" UpdateSourceTrigger="PropertyChanged" >
                            <Binding.ValidationRules>
                                <local:AgeRangeRule Min="21" Max="130"/>
                            </Binding.ValidationRules>
                        </Binding>
                    </TextBox>
    
                </StackPanel>
            </StackPanel>
    
    
            <Label x:Name="lblTime" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="2" Grid.Row="3" Margin="0" />
            <Label x:Name="label" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="1" Grid.Row="3" Content="Label"/>
            <Label x:Name="label_count" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="2" Grid.Row="2" Content="0" FontSize="72" Foreground="#2f5cb6"/>
    
    
    
    
    
        </Grid>
    </Window>
    

    - I suppose this is the Model, please correct me if I am wrong

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Sunum
    {
        public class MyDataSource
        {
            public MyDataSource()
            {
                Age = 10;
                Age2 = 10;
            }
    
            public int Age { get; set; }
            public int Age2 { get; set; }
            public int Age3 { get; set; }
        }
    }

    - And this must be the ViewModel, again please correct me if I am wrong.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Globalization;
    using System.Windows.Controls;
    
    namespace Sunum
    {
        public class AgeRangeRule : ValidationRule
        {
            //properties
            public int Min { get; set; }
            public int Max { get; set; }
    
            //override method in ValidationRule
            public override ValidationResult Validate(object value, CultureInfo cultureInfo)
            {
                var age = 0;
    
                try
                {
                    if (((string)value).Length > 0)
                        age = int.Parse((string)value);
                }
                catch (Exception e)
                {
                    return new ValidationResult(false, "Illegal characters or " + e.Message);
                }
    
                if ((age < Min) || (age > Max))
                {
                    return new ValidationResult(false,
                        "Please enter an age in the range: " + Min + " - " + Max + ".");
                }
                return new ValidationResult(true, null);
            }
        }
    }
    
    

    Wednesday, November 6, 2019 4:02 AM

All replies

  • Hi,
    you can use a wrapper inherited from DependencyObject. Properties of Wrapper you can bind to properties in ViewModel. This properties read the necessary data from database. Try this demo:

    XAML:

    <Window x:Class="Sunum.Window77"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Sunum"
            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
            mc:Ignorable="d"
            Title="Window77" Height="450" Width="800">
      <Window.Resources>
        <local:MyDataSource x:Key="Ods"/>
        <Style x:Key="DefaultTextBoxStyle" TargetType="TextBox">
          <Setter Property="Margin" Value="5" />
        </Style>
        <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>
        <Style x:Key="ToolTipWithErrorMessageOnErrorStyle" TargetType="TextBox" 
               BasedOn="{StaticResource DefaultTextBoxStyle}">
          <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>
        <Style x:Key="RedBackgroundOnErrorStyle" TargetType="TextBox" 
               BasedOn="{StaticResource ToolTipWithErrorMessageOnErrorStyle}">
          <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
              <Setter Property="Background" Value="Red" />
            </Trigger>
          </Style.Triggers>
        </Style>
      </Window.Resources>
      <Grid Margin="0,10,0,0">
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="1*"/>
          <ColumnDefinition Width="1*" />
          <ColumnDefinition Width="1*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition Height="1*" />
          <RowDefinition Height="0.2*" />
          <RowDefinition Height="2*" />
          <RowDefinition Height="0.3*" />
          <RowDefinition Height="0.3*" />
        </Grid.RowDefinitions>
        <Border Background="#1f3d7a" Grid.ColumnSpan="3" Grid.Row="0"/>
        <Border Background="#2f5cb6" Grid.Column="0"  Grid.Row="2"/>
        <Border Background='#CFB53B' Grid.Column="2"  Grid.Row="2"/>
        <!--<Image Source="Images\GD_Logo_CT_neg_oSZ.png" Margin="20,0,0,0" Stretch="None" Grid.ColumnSpan="3"></Image>
        <Image Source="Images\GD_backvisual.png" Stretch="UniformToFill" Grid.Row="4" Grid.ColumnSpan="3" ></Image>-->
        <TextBlock x:Name="machine_sunumtxt" Grid.Column="0" Grid.Row="2" HorizontalAlignment="Center" 
                   VerticalAlignment="Center" TextWrapping="Wrap" Foreground="Azure" FontSize="60">
          <Run Text="TextBlock"/>
          <InlineUIContainer></InlineUIContainer>
        </TextBlock>
        <StackPanel Grid.Column="1" Grid.Row="2" VerticalAlignment="Center">
          <StackPanel Orientation="Horizontal">
            <Label Margin="3" VerticalAlignment="Center">Batch Number</Label>
            <TextBox Margin="3" x:Name="batch_txt" FontSize="30" 
                     VerticalAlignment="Center" Width="150" Height="50" 
                     Text="0"
                     HorizontalContentAlignment="Center" VerticalContentAlignment="Center" />
            <!--PreviewTextInput="tryvalidate" />-->
          </StackPanel>
          <StackPanel Orientation="Horizontal">
            <Label Margin="5" VerticalAlignment="Center" Target="{Binding ElementName=workerid_txt}">Worker ID</Label>
            <TextBox x:Name="workerid_txt" Margin="20,7" FontSize="30" 
                     VerticalAlignment="Center" HorizontalContentAlignment="Center" Width="150" Height="50" 
                     Style="{StaticResource RedBackgroundOnErrorStyle}">
              <Binding Path="Age2" Source="{StaticResource Ods}" >
                <Binding.ValidationRules>
                  <local:AgeRangeRule>
                    <local:AgeRangeRule.Wrapper>
                      <local:RuleWrapper Min="{Binding MinAge, Source={StaticResource Ods}}" Max="{Binding MaxAge, Source={StaticResource Ods}}"/>
                    </local:AgeRangeRule.Wrapper>
                  </local:AgeRangeRule>
                </Binding.ValidationRules>
              </Binding>
               <i:Interaction.Behaviors>
                <local:TextBoxBehavior/>
              </i:Interaction.Behaviors>   
            </TextBox>
          </StackPanel>
        </StackPanel>
        <Label x:Name="lblTime" HorizontalAlignment="Center" VerticalAlignment="Center" 
               Grid.Column="2" Grid.Row="3" Margin="0" />
        <Label x:Name="label" HorizontalAlignment="Center" VerticalAlignment="Center" 
               Grid.Column="1" Grid.Row="3" Content="Label"/>
        <Label x:Name="label_count" HorizontalAlignment="Center" VerticalAlignment="Center" 
               Grid.Column="2" Grid.Row="2" Content="0" FontSize="72" Foreground="#2f5cb6"/>
      </Grid>
    </Window>

    and classes:

    using System.Globalization;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Interactivity;
    
    namespace Sunum
    {
      /// <summary>
      /// Interaction logic for Window77.xaml
      /// </summary>
      public partial class Window77 : Window
      {
        public Window77()
        {
          InitializeComponent();
        }
      }
      public class MyDataSource
      {
        public int Age { get; set; } = 10;
        public int Age2 { get; set; } = 10;
        public int Age3 { get; set; }
    
        public int MinAge { get; set; } = 21;
        public int MaxAge { get; set; } = 130;
      }
    
      public class RuleWrapper : DependencyObject
      {
        public static readonly DependencyProperty MinProperty =
             DependencyProperty.RegisterAttached("Min", typeof(int),
             typeof(RuleWrapper), new FrameworkPropertyMetadata(int.MinValue));
    
        public int Min
        {
          get { return (int)GetValue(MinProperty); }
          set { SetValue(MinProperty, value); }
        }
        public static readonly DependencyProperty MaxProperty =
             DependencyProperty.RegisterAttached("Max", typeof(int),
             typeof(RuleWrapper), new FrameworkPropertyMetadata(int.MaxValue));
    
        public int Max
        {
          get { return (int)GetValue(MaxProperty); }
          set { SetValue(MaxProperty, value); }
        }
      }
    
      public class AgeRangeRule : ValidationRule
      {
        //override method in ValidationRule
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
          var age = 0;
          if (!int.TryParse(value.ToString(), out age))
            return new ValidationResult(false, "Illegal characters or ");
          if ((age < this.Wrapper.Min) || (age > this.Wrapper.Max))
            return new ValidationResult(false, $"Please enter an age in the range: { this.Wrapper.Min} - {this.Wrapper.Max}.");
          return new ValidationResult(true, null);
        }
        public RuleWrapper Wrapper { get; set; }
      }
    
      public class TextBoxBehavior : Behavior<TextBox>
      {
        protected override void OnAttached() => AssociatedObject.KeyDown += AssociatedObject_KeyDown;
    
        protected override void OnDetaching() => AssociatedObject.KeyDown -= AssociatedObject_KeyDown;
    
        private void AssociatedObject_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
          if (e.Key == System.Windows.Input.Key.Enter)
          {
            TextBox tb = sender as TextBox;
            if (tb == null) return;
            var b = tb.GetBindingExpression(TextBox.TextProperty);
            b.UpdateSource();
          }
        }
      }
    }


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


    Wednesday, November 6, 2019 8:23 AM