Подтвердить что ты не робот

Проверка с использованием MVVM Light в универсальном приложении Windows

После настройки MVVM Light в приложении Universal Windows App у меня есть следующая структура, и мне интересно, какой самый чистый способ сделать проверку в 2017 году с помощью UWP и mvvmlight для уведомления пользователей с ошибками и, возможно, reset значение текстового поля, когда это необходимо. Единственный трюк в том, что текстовое поле является частью UserControl (для ясности очищено ненужный код xaml), поскольку он будет использоваться несколько раз. Также я добавил DataAnnotations и ValidationResult для демонстрации, а не для того, чтобы предположить, что это лучший способ сделать это или что он работает каким-либо образом до сих пор.

Код работает отлично, поскольку привязка и добавление и удаление значений

  • ViewModel

    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Command;
    using GalaSoft.MvvmLight.Views;
    using System;
    using System.ComponentModel.DataAnnotations;
    
    public class ValidationTestViewModel : ViewModelBase
     {
       private int _age;
    
      [Required(ErrorMessage = "Age is required")]
      [Range(1, 100, ErrorMessage = "Age should be between 1 to 100")]
      [CustomValidation(typeof(int), "ValidateAge")]
      public int Age
        {
          get { return _age; }
          set
          {
            if ((value > 1) && (value =< 100))
                _age= value;
          }
        }
    
      public static ValidationResult ValidateAge(object value, ValidationContext validationContext)
       {
          return null;
       }
    }
    
  • Просмотр

    <Page
     x:Class="ValidationTest.Views.ValidationTestPage"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:local="using:ValidationTest.Views"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     DataContext="{Binding ValidationTestPageInstance, Source={StaticResource  Locator}}" 
    xmlns:views="using:ValidationTest.Views">
    
         <views:NumberEdit TextInControl="{Binding Age, Mode=TwoWay}" />
    
    </Page>
    
  • UserControl

    <UserControl
     x:Class="ValidationTest.Views.Number"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:local="using:ValidationTest.Views"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     x:Name="userControl1">
      <Grid>
    
      <TextBox x:Name="content" Text="{Binding TextInControl, ElementName=userControl1, Mode=TwoWay}"></TextBox>
       </Grid>
    
     </UserControl>
    
  • Код UserControl Behind:

    public partial class NumberEdit : UserControl
    {
       public string TextInControl
          {
            get { return (string)GetValue(TextInControlProperty); }
            set {
                  SetValue(TextInControlProperty, value);
                }
    }
    
    public static readonly DependencyProperty TextInControlProperty =
        DependencyProperty.Register("TextInControl", typeof(string),
                                       typeof(NumberEdit), new PropertyMetadata(null));
    
     }
    
4b9b3361

Ответ 1

Обычно мы используем интерфейс IDialogService в MVVM Light для уведомления пользователей с ошибками, есть метод ShowError, метод ShowMessage и ShowMessageBox в IDialogService.

Мы должны иметь возможность создать экземпляр PropertyMetadata с PropertyChangedCallback, он будет вызываться, когда эффективное значение свойства свойства зависимостей изменения. Когда он вызывается, мы можем использовать в нем метод ShowMessage.

Например:

public sealed partial class NumberEdit : UserControl
{
    public NumberEdit()
    {
        this.InitializeComponent();
    }

    public static readonly DependencyProperty TextInControlProperty =
     DependencyProperty.Register("TextInControl", typeof(string),
                                    typeof(NumberEdit), new PropertyMetadata(null, new PropertyChangedCallback(StringChanged)));

    private static void StringChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        int value;
        Int32.TryParse(e.NewValue.ToString(), out value);
        if (0 < value && value < 99)
        {
        }
        else
        {
            var dialog = ServiceLocator.Current.GetInstance<IDialogService>();
            dialog.ShowMessage("Age should be between 1 to 100", "Error mesage");
        }
    }

    public string TextInControl
    {
        get { return (string)GetValue(TextInControlProperty); }
        set
        {
            SetValue(TextInControlProperty, value);
        }
    }
}

Также, если вы хотите reset значение TextBox, вы должны использовать RaisePropertyChanged в свойстве Age. Если мы не используем RaisePropertyChanged в свойстве Age, значение TextBox не изменится при изменении значения Age.

Подробнее о RaisePropertyChanged см. интерфейс INotifyPropertyChanged.

Например:

public int Age
{
    get { return _age; }
    set
    {
        if ((value > 1) && (value <= 100))
            _age = value;
        RaisePropertyChanged("Age");
    }
}

Update:

На вашей странице вы должны добавить для добавления DataContext в свой контроль.

<Page x:Class="Validation_Using_MVVM_Light_in_a.SecondPage"
      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"
      mc:Ignorable="d"
      xmlns:local="using:Validation_Using_MVVM_Light_in_a"
xmlns:VM="using:Validation_Using_MVVM_Light_in_a.ViewModel">

    <Page.Resources>
        <VM:ValidationTestViewModel x:Key="MyViewModel" />
    </Page.Resources>
    <Grid>
        <local:NumberEdit  DataContext="{StaticResource MyViewModel}"  TextInControl="{Binding Age, Mode=TwoWay}" />
    </Grid>
</Page>

Ответ 2

Здесь вам не хватает вызова функции Validator.ValidateObject, чтобы выполнить фактическую проверку. Это применит атрибуты проверки к данным и также вызовет IValidatableObject.Validate, если вы его внедрили (вы должны реализовать это вместо наличия специальных функций, таких как ValidateAge).

Вот так:

        // Validate using:
        // 1. ValidationAttributes attached to this validatable class, and
        // 2. ValidationAttributes attached to the properties of this validatable class, and 
        // 3. this.Validate( validationContext)
        // 
        // Note, for entities, a NotSupportedException will be thrown by TryValidateObject if any of 
        // the primary key fields are changed. Correspondingly the UI should not allow modifying 
        // primary key fields. 
        ValidationContext validationContext = new ValidationContext(this);
        List<ValidationResult> validationResults = new List<ValidationResult>(64);
        bool isValid = Validator.TryValidateObject( this, validationContext, validationResults, true);
        Debug.Assert(isValid == (validationResults.Count == 0));