< Summary

Class:ReactiveUI.Validation.Helpers.ReactiveValidationObject`1
Assembly:ReactiveUI.Validation
File(s):D:\a\1\s\src\ReactiveUI.Validation\Helpers\ReactiveValidationObject.cs
Covered lines:30
Uncovered lines:3
Coverable lines:33
Total lines:112
Line coverage:90.9% (30 of 33)
Covered branches:13
Total branches:18
Branch coverage:72.2% (13 of 18)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.ctor(...)-100%100%
get_HasErrors()-100%100%
set_HasErrors(...)-100%100%
get_ValidationContext()-100%100%
GetErrors(...)-87.5%50%
RaiseErrorsChanged(...)-100%100%
SelectInvalidPropertyValidations()-100%100%
OnValidationStatusChange(...)-71.43%50%

File(s)

D:\a\1\s\src\ReactiveUI.Validation\Helpers\ReactiveValidationObject.cs

#LineLine coverage
 1// Copyright (c) 2020 .NET Foundation and Contributors. All rights reserved.
 2// Licensed to the .NET Foundation under one or more agreements.
 3// The .NET Foundation licenses this file to you under the MIT license.
 4// See the LICENSE file in the project root for full license information.
 5
 6using System;
 7using System.Collections;
 8using System.Collections.Generic;
 9using System.ComponentModel;
 10using System.Linq;
 11using System.Reactive.Concurrency;
 12using System.Reactive.Linq;
 13using DynamicData;
 14using DynamicData.Binding;
 15using ReactiveUI.Validation.Abstractions;
 16using ReactiveUI.Validation.Components.Abstractions;
 17using ReactiveUI.Validation.Contexts;
 18using ReactiveUI.Validation.States;
 19
 20namespace ReactiveUI.Validation.Helpers
 21{
 22    /// <summary>
 23    /// Base class for ReactiveObjects that support INotifyDataErrorInfo validation.
 24    /// </summary>
 25    /// <typeparam name="TViewModel">The parent view model.</typeparam>
 26    public abstract class ReactiveValidationObject<TViewModel> : ReactiveObject, IValidatableViewModel, INotifyDataError
 27    {
 28        private bool _hasErrors;
 29
 30        /// <summary>
 31        /// Initializes a new instance of the <see cref="ReactiveValidationObject{TViewModel}"/> class.
 32        /// </summary>
 33        /// <param name="scheduler">Scheduler for OAPHs and for the the ValidationContext.</param>
 1234        protected ReactiveValidationObject(IScheduler? scheduler = null)
 35        {
 1236            ValidationContext = new ValidationContext(scheduler);
 1237            ValidationContext.Validations
 1238                .ToObservableChangeSet()
 1239                .ToCollection()
 2840                .Select(components => components
 4841                    .Select(component => component.ValidationStatusChange)
 2842                    .Merge())
 1243                .Switch()
 1244                .Subscribe(OnValidationStatusChange);
 1245        }
 46
 47        /// <inheritdoc />
 48        public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;
 49
 50        /// <inheritdoc />
 51        public bool HasErrors
 52        {
 2053            get => _hasErrors;
 3854            private set => this.RaiseAndSetIfChanged(ref _hasErrors, value);
 55        }
 56
 57        /// <inheritdoc />
 12058        public ValidationContext ValidationContext { get; }
 59
 60        /// <summary>
 61        /// Returns a collection of error messages, required by the INotifyDataErrorInfo interface.
 62        /// </summary>
 63        /// <param name="propertyName">Property to search error notifications for.</param>
 64        /// <returns>A list of error messages, usually strings.</returns>
 65        /// <inheritdoc />
 66        public virtual IEnumerable GetErrors(string propertyName) =>
 2467            string.IsNullOrEmpty(propertyName) ?
 2468                SelectInvalidPropertyValidations()
 069                    .SelectMany(validation => validation.Text)
 2470                    .ToArray() :
 2471                SelectInvalidPropertyValidations()
 4472                    .Where(validation => validation.ContainsPropertyName(propertyName))
 3873                    .SelectMany(validation => validation.Text)
 2474                    .ToArray();
 75
 76        /// <summary>
 77        /// Raises the <see cref="ErrorsChanged"/> event.
 78        /// </summary>
 79        /// <param name="propertyName">The name of the validated property.</param>
 80        protected void RaiseErrorsChanged(string propertyName = "") =>
 3881            ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
 82
 83        /// <summary>
 84        /// Selects validation components that are invalid.
 85        /// </summary>
 86        /// <returns>Returns the invalid property validations.</returns>
 87        private IEnumerable<IPropertyValidationComponent<TViewModel>> SelectInvalidPropertyValidations() =>
 2488            ValidationContext.Validations
 2489                .OfType<IPropertyValidationComponent<TViewModel>>()
 5290                .Where(validation => !validation.IsValid);
 91
 92        /// <summary>
 93        /// Updates the <see cref="HasErrors" /> property before raising the <see cref="ErrorsChanged" />
 94        /// event, and then raises the <see cref="ErrorsChanged" /> event. This behaviour is required by WPF, see:
 95        /// https://stackoverflow.com/questions/24518520/ui-not-calling-inotifydataerrorinfo-geterrors/24837028.
 96        /// </summary>
 97        private void OnValidationStatusChange(ValidationState state)
 98        {
 3899            HasErrors = !ValidationContext.GetIsValid();
 38100            if (state.Component is IPropertyValidationComponent<TViewModel> propertyValidationComponent &&
 38101                propertyValidationComponent.PropertyCount == 1)
 102            {
 38103                var propertyName = propertyValidationComponent.Properties.First();
 38104                RaiseErrorsChanged(propertyName);
 105            }
 106            else
 107            {
 0108                RaiseErrorsChanged();
 109            }
 0110        }
 111    }
 112}