
*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Resources\Strings.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:data="clr-namespace:Updk7.Data.Entities.Metro;assembly=Updk7.Data">

    <!--MaritalStatus-->
    <sys:String x:Key="{x:Static data:MaritalStatus.NotSet}">Не задан</sys:String>
    <sys:String x:Key="{x:Static data:MaritalStatus.Single}">Холост</sys:String>
    <sys:String x:Key="{x:Static data:MaritalStatus.Married}">Женат (замужем)</sys:String>

    <!--EducationGrade-->
    <sys:String x:Key="{x:Static data:EducationGrade.NotSet}">Не задано</sys:String>
    <sys:String x:Key="{x:Static data:EducationGrade.MiddleProf}">Среднее профессиональное</sys:String>
    <sys:String x:Key="{x:Static data:EducationGrade.MiddleTech}">Среднее техническое</sys:String>
    <sys:String x:Key="{x:Static data:EducationGrade.MiddleCommon}">Среднее общее</sys:String>
    <sys:String x:Key="{x:Static data:EducationGrade.IncompleteHigh}">Незаконченное высшее</sys:String>
    <sys:String x:Key="{x:Static data:EducationGrade.High}">Высшее</sys:String>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\App.xaml

<prism:PrismApplication x:Class="Updk7.Customers.Metro.Master.App"
                        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                         xmlns:prism="http://prismlibrary.com/"
                         Startup="ApplicationStartup">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/Updk7.Wpf;component/Themes/Generic.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</prism:PrismApplication>

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\App.xaml.cs


using System.Windows;
using Prism.Ioc;

namespace Updk7.Customers.Metro.Master
{
    public partial class App
    {
        private MetroBootstrapper _bootstrapper;
        private Wpf.AppManager _appManager;

        protected override void OnStartup(StartupEventArgs e)
        {
            _appManager = new Wpf.AppManager(this, "Updk 7.0 - Master.Metro");
            if (_appManager.CanBoot)
                base.OnStartup(e);
            else
                _appManager.Shutdown();
        }

        private void ApplicationStartup(object sender, StartupEventArgs e)
        {
        }

        protected override Window CreateShell()
        {
            if (!_appManager.CanBoot)
                return null;

            _bootstrapper.ShellWindow.Title = "УПДК 7.5 - Метро";
            Container.Resolve<Wpf.Master.Assets.NavigationController>()
                .NavigateToPage(Wpf.Master.Assets.NavigationTargetPage.Login);

            return _bootstrapper.ShellWindow;
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            if (_appManager.CanBoot)
            {
                _bootstrapper = new MetroBootstrapper(_appManager, containerRegistry, Container);
                _bootstrapper.Run();
            }
        }

        protected override Prism.Modularity.IModuleCatalog CreateModuleCatalog()
        {
            return new Prism.Modularity.ConfigurationModuleCatalog();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\MetroBootstrapper.cs


using System;
using System.Windows;
using Prism.Ioc;

namespace Updk7.Customers.Metro.Master
{
    public class MetroBootstrapper : Wpf.Master.Assets.MasterBootstrapper
    {
        public MetroBootstrapper(Wpf.AppManager appManager, IContainerRegistry containerRegistry,
            IContainerProvider containerProvider)
            : base(appManager, containerRegistry, containerProvider)
        {
        }

        protected override void CreateDataLayer()
        {
            var dbDataPath = $@"{AppManager.Folders.Root}\{Configuration.DataConnection}";
            var database = new Data.Repositories.Sqlite.Metro.MetroDatabase(dbDataPath);
            database.Open();

            ContainerRegistry
                .RegisterInstance(database.Data)
                .RegisterSingleton<Data.Repositories.ITesteesRepository, Data.Repositories.Sqlite.Metro.MetroTesteesRepository>()
                .RegisterInstance<Data.Repositories.ITesteesPhotoRepository>(new Data.Repositories.Sqlite.TesteesPhotoRepository(database.Photos))
                .RegisterSingleton<Data.Repositories.IUsersRepository, Data.Repositories.Sqlite.UsersRepository>()
                .RegisterSingleton<Data.Repositories.ITestResultsRepository, Data.Repositories.Sqlite.TestResultsRepository>()
                .RegisterSingleton<Data.Repositories.IReportMessagesRepository, Data.Repositories.Sqlite.ReportMessagesRepository>()
                .RegisterSingleton<Data.Repositories.UpdkRepositories>();
        }

        protected override void CreateTestingTreeFactory()
        {
            ContainerRegistry.RegisterSingleton<Data.ITestingTreeFactory, MetroTestingTreeFactory>();
        }

        protected override void LoadStrings()
        {
            base.LoadStrings();

            var uri = new Uri("Updk7.Customers.Metro.Master;component/Resources/Strings.xaml", UriKind.Relative);
            var dictionary = new ResourceDictionary() { Source = uri };
            Wpf.AppStrings.AddMergedDictionary(dictionary);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\MetroPvkUpdater.cs


using System;

namespace Updk7.Customers.Metro.Master
{
    public class MetroPvkUpdater : Wpf.Master.Modules.Testees.ITesteeDataUpdater
    {
        private Wpf.Master.Assets.IAutorizationManager _autorizationManager;
        private Data.Repositories.UpdkRepositories _updkRepositories;
        private Data.Validation.Metro.PvkValidator _validator;

        public MetroPvkUpdater(Data.ITestingTreeFactory testingTreeFactory,
            Wpf.Master.Assets.IAutorizationManager autorizationManager,
            Data.Repositories.UpdkRepositories updkRepositories)
        {
            _validator = new Data.Validation.Metro.PvkValidator(testingTreeFactory);
            _updkRepositories = updkRepositories;
            _autorizationManager = autorizationManager;
        }

        public void UpdateTesteeData(Wpf.Master.Assets.TestGroupInfo savedGroupInfo)
        {
            try
            {
                var groupResults = savedGroupInfo.ToTestGroupResults();
                var testee = savedGroupInfo.Testee as Data.Entities.Metro.MetroTestee;
                var validationResults = _validator.Validate(groupResults, testee);
                if (validationResults == null || !validationResults.ProfessionalGroup.HasValue)
                    return;

                Wpf.AppLog.Current.Info($"Обновление группы ПВК. Испытуемый {savedGroupInfo.Testee.Id}, группа тестов {savedGroupInfo.TestGroup.GroupKey}");

                testee.ExaminationDate = savedGroupInfo.TestingDate;
                testee.ExaminationUserId = _autorizationManager.AutorizedUser.Id;
                testee.PVKGroup = validationResults.ProfessionalGroup;
                testee.NextExaminationDate = validationResults.NextExaminationDate;
                testee.VulnerablePVK = validationResults.GetFailedTestsString(Tests.TestType.Готовность_к_тестированию_ЭПФС);

                _updkRepositories.Testees.Update(testee);
            }
            catch (Exception ex)
            {
                Wpf.AppLog.Current.Error("MetroPvkUpdater.UpdateTesteeData");
                Wpf.AppLog.Current.Error(ex.Message);
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\MetroTesteesModule.cs


using Prism.Ioc;
using Assets = Updk7.Wpf.Master.Modules.Testees;

namespace Updk7.Customers.Metro.Master
{
    public class MetroTesteesModule : Wpf.Master.Modules.TesteesModule
    {
        public MetroTesteesModule()
        {
        }

        protected override void RegisterTesteeCustomEditor(Assets.ITesteeEditorController controller, 
            IContainerProvider containerProvider)
        {
            var testingTreeFactory = containerProvider.Resolve<Data.ITestingTreeFactory>();

            controller.EntitieType = typeof(Data.Entities.Metro.MetroTestee);

            controller.RegisterEditor(
                new ViewModels.MetroTesteePersonnalEditorViewModel(),
                new Views.MetroTesteePersonnalEditorView());

            controller.RegisterEditor(
                new ViewModels.MetroTesteeCustomEditorViewModel(testingTreeFactory),
                new Views.MetroTesteeCustomEditorView());
        }

        protected override void RegisterTesteeSlelectionView(IContainerRegistry containerRegistry, string regionKey)
        {
            containerRegistry.RegisterSingleton<Assets.ITesteeSelection, ViewModels.MetroTesteeSelectionViewModel>();
            Wpf.ViewModelLocator.Current
                .Register<Assets.ITesteeSelection, Views.MetroTesteeSelectionView>(regionKey);
        }

        protected override void RegisterTesteeDataUpdater(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<Assets.ITesteeDataUpdater, MetroPvkUpdater>();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\MetroTestingTreeFactory.cs


using System.Collections.Generic;
using System.Diagnostics;

namespace Updk7.Customers.Metro.Master
{
    public class MetroTestingTreeFactory : Data.ITestingTreeFactory
    {
        private Data.TestingTree _testingTree;
        private Data.TestingTreeMetadata _metadata;

        public MetroTestingTreeFactory()
        {
            _testingTree = createTestingTree();
            _metadata = createMetadata();
        }

        public Data.TestingTreeMetadata GetMetadata()
        {
            return _metadata;
        }

        public Data.TestingTree GetTestingTree()
        {
            return _testingTree;
        }

        public Data.TestingTreeGroupNode GetTestingTreeNode(Data.Entities.Testee testee)
        {
            var metroTestee = testee as Data.Entities.Metro.MetroTestee;
            Debug.Assert(metroTestee != null);

            Data.TestingTreeGroupNode node;
            if (metroTestee.ProfessionId == 5)
            {
                node = metroTestee.TrafficId == 1
                    ? _testingTree.GetNode("N1")
                    : _testingTree.GetNode("N2");
            }
            else if (metroTestee.TrafficId != 0)
            {
                node = metroTestee.TrafficId == 1
                    ? _testingTree.GetNode("N3")
                    : _testingTree.GetNode("N4");
            }
            else
                node = _testingTree.GetNode("Empty");

            return node;
        }

        private Data.TestingTree createTestingTree()
        {
            var a1 = new Data.TestingTreeGroupNode("A1", "Профессиональное обследование РЛБ")
                .AddTest(Tests.TestType.Готовность_к_тестированию_ЭПФС)
                .AddTest(Tests.TestType.Распределение_внимания_РВ)
                .AddTest(Tests.TestType.Уровень_восприятия_скорости_и_расстояния_УВСР)
                .AddTest(Tests.TestType.Оценка_склонности_к_риску_ОСР)
                .AddTest(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
                .AddTest(Tests.TestType.Оценка_бдительности_ОБ);

            var b1 = new Data.TestingTreeGroupNode("B1", "Профессиональное обследование РЛБ")
                .AddTest(Tests.TestType.Готовность_к_тестированию_ЭПФС)
                .AddTest(Tests.TestType.Распределение_внимания_РВ)
                .AddTest(Tests.TestType.Оценка_глазомера_ОГ)
                .AddTest(Tests.TestType.Оценка_склонности_к_риску_ОСР)
                .AddTest(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
                .AddTest(Tests.TestType.Оценка_бдительности_ОБ);

            var c1 = new Data.TestingTreeGroupNode("C1", "Профессиональное обследование РЛБ")
                .AddTest(Tests.TestType.Готовность_к_тестированию_ЭПФС)
                .AddTest(Tests.TestType.Распределение_внимания_РВ)
                .AddTest(Tests.TestType.Уровень_восприятия_скорости_и_расстояния_УВСР)
                .AddTest(Tests.TestType.Оценка_склонности_к_риску_ОСР)
                .AddTest(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
                .AddTest(Tests.TestType.Оценка_бдительности_ОБ);

            var d1 = new Data.TestingTreeGroupNode("D1", "Профессиональное обследование РЛБ")
                .AddTest(Tests.TestType.Готовность_к_тестированию_ЭПФС)
                .AddTest(Tests.TestType.Распределение_внимания_РВ)
                .AddTest(Tests.TestType.Оценка_глазомера_ОГ)
                .AddTest(Tests.TestType.Оценка_склонности_к_риску_ОСР)
                .AddTest(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
                .AddTest(Tests.TestType.Оценка_бдительности_ОБ);

            var a2 = new Data.TestingTreeGroupNode("A2", "Дополнительная диагностика")
                .AddTest(Tests.TestType.Готовность_к_тестированию_ЭПФС, isOptional: true)
                .AddTest(Tests.TestType.Концентрация_внимания_КВ, isOptional: true)
                .AddTest(Tests.TestType.Чувство_времени_ЧВ, isOptional: true)
                .AddTest(Tests.TestType.Тест_Люшера, isOptional: true)
                .AddTest(Tests.TestType.Тест_Спилберга, isOptional: true)
                .AddTest(Tests.TestType.Тест_Зунга, isOptional: true)
                .AddTest(Tests.TestType.Диагностики_мотивации_к_избеганию_неудач_Элерса_МИН, isOptional: true)
                .AddTest(Tests.TestType.Методика_диагностики_уровня_агрессии_Басс_Дарки, isOptional: true)
                .AddTest(Tests.TestType.Методика_ДОРС, isOptional: true)
                .AddTest(Tests.TestType.Комплексная_оценка_психологического_состояния_КОПС, isOptional: true)
                .AddTest(Tests.TestType.Оценка_нервно_психической_устойчивости_НПУ, isOptional: true)
                .AddTest(Tests.TestType.Тест_Уровень_субъективного_контроля_УСК, isOptional: true)
                .AddTest(Tests.TestType.Адаптивность, isOptional: true);

            var excludeTests = new Tests.TestType[]
            {
                Tests.TestType.Теппинг_тест,
                Tests.TestType.Тремор_3,
                Tests.TestType.Методика_оценки_оперативной_памяти_ОВ_М,
                Tests.TestType.Оценка_стрессоустойчивости_СТР_M2,
                Tests.TestType.Оценка_бдительности_ОБ_М,
                Tests.TestType.Оценка_монотоноустойчивости_ОМУ_М
            };

            var commonNodes = Data.TestingTree.CreateCommonNodes(excludeTests);

            var n1 = new Data.TestingTreeGroupNode("N1", "Первичная оценка ПВК")
               .AddNode(a1)
               .AddNodes(commonNodes);

            var n2 = new Data.TestingTreeGroupNode("N2", "Первичная оценка ПВК")
               .AddNode(b1)
               .AddNodes(commonNodes);

            var n3 = new Data.TestingTreeGroupNode("N3", "Периодическая оценка ПВК")
               .AddNode(c1)
               .AddNode(a2)
               .AddNodes(commonNodes);

            var n4 = new Data.TestingTreeGroupNode("N4", "Периодическая оценка ПВК")
                .AddNode(d1)
                .AddNode(a2)
                .AddNodes(commonNodes);

            var emptyNode = new Data.TestingTreeGroupNode("Empty", "Нет групп тестов для выбранной профессии");

            return new Data.TestingTree("Дерево тестирования РЖД")
               .AddNode(n1)
               .AddNode(n2)
               .AddNode(n3)
               .AddNode(n4)
               .AddNode(emptyNode);
        }

        private Data.TestingTreeMetadata createMetadata()
        {
            var professions = new Dictionary<int, string>()
            {
                [1] = "Машинист",
                [2] = "Помошник машиниста",
                [3] = "Машинист-наставник",
                [4] = "Машинист-инструктор",
                [5] = "Курсант"
            };

            var traffic = new Dictionary<int, string>()
            {
                [0] = "Не задано",
                [1] = "ЭПС",
                [2] = "ДДЭ",
                [3] = "МРТ"
            };

            return new Data.TestingTreeMetadata()
                .AddProperty("ProfessionTypes", professions)
                .AddProperty("TrafficTypes", traffic);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\ViewModels\MetroTesteeCustomEditorViewModel.cs


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;

namespace Updk7.Customers.Metro.Master.ViewModels
{
    public class MetroTesteeCustomEditorViewModel : Wpf.Master.Modules.Testees.ViewModels.TesteeRailroadEditorViewModel,
        Wpf.Master.Modules.Testees.ITesteeEditor
    {
        public MetroTesteeCustomEditorViewModel(Data.ITestingTreeFactory testingTreeFactory)
        {
            var metadata = testingTreeFactory.GetMetadata();
            var professions = metadata.GetProperty("ProfessionTypes") as Dictionary<int, string>;
            var traffic = metadata.GetProperty("TrafficTypes") as Dictionary<int, string>;

            SetConfiguration(professions, traffic);
        }

        private DateTime? _startExperienceDate = DateTime.Now;

        public DateTime? StartExperienceDate
        {
            get { return _startExperienceDate; }
            set { SetProperty(ref _startExperienceDate, value); }
        }

        private DateTime? _employmentDate = DateTime.Now;

        public DateTime? EmploymentDate
        {
            get { return _employmentDate; }
            set { SetProperty(ref _employmentDate, value); }
        }

        public override void SetData(Data.Entities.Testee testee)
        {
            base.SetData(testee);

            if (testee != null)
            {
                var metroTestee = testee as Data.Entities.Metro.MetroTestee;
                Debug.Assert(metroTestee != null);
                StartExperienceDate = metroTestee.StartExperienceDate;
                EmploymentDate = metroTestee.EmploymentDate;
            }
            else
            {
                StartExperienceDate = DateTime.Now;
                EmploymentDate = DateTime.Now;
            }

            Column = "n/a";
            ErrorsContainer.UpdateValidation();
        }

        protected override bool IsProfessionTrafficPairValid()
        {
            return SelectedTraffic != null && SelectedTraffic.Key != 0;
        }

        public override void UpdateEntitie(Data.Entities.Testee testee)
        {
            var metroTestee = testee as Data.Entities.Metro.MetroTestee;
            Debug.Assert(metroTestee != null);

            metroTestee.StartExperienceDate = StartExperienceDate;
            metroTestee.EmploymentDate = EmploymentDate;

            base.UpdateEntitie(testee);
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(SelectedTraffic))
                ErrorsContainer.UpdatePropertyValidation(nameof(SelectedProfession));
            else if (e.PropertyName == nameof(SelectedProfession))
                ErrorsContainer.UpdatePropertyValidation(nameof(SelectedTraffic));

            base.OnPropertyChanged(e);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\ViewModels\MetroTesteePersonnalEditorViewModel.cs


using System.Diagnostics;

namespace Updk7.Customers.Metro.Master.ViewModels
{

    public class MetroTesteePersonnalEditorViewModel : Wpf.Validation.ValidatedBindableBase,
        Wpf.Master.Modules.Testees.ITesteeEditor
    {
        public MetroTesteePersonnalEditorViewModel()
        {
        }

        public string Title => string.Empty;

        private Data.Entities.Metro.MaritalStatus _maritalStatus;

        public Data.Entities.Metro.MaritalStatus MaritalStatus
        {
            get { return _maritalStatus; }
            set { SetProperty(ref _maritalStatus, value); }
        }

        private Data.Entities.Metro.EducationGrade _educationGrade;

        public Data.Entities.Metro.EducationGrade EducationGrade
        {
            get { return _educationGrade; }
            set { SetProperty(ref _educationGrade, value); }
        }

        public void SetData(Data.Entities.Testee testee)
        {
            if (testee != null)
            {
                var metroTestee = testee as Data.Entities.Metro.MetroTestee;
                Debug.Assert(metroTestee != null);
                MaritalStatus = metroTestee.MartialStatus;
                EducationGrade = metroTestee.EducationGrade;
            }
            else
            {
                MaritalStatus = Data.Entities.Metro.MaritalStatus.NotSet;
                EducationGrade = Data.Entities.Metro.EducationGrade.NotSet;
            }
        }

        public void UpdateEntitie(Data.Entities.Testee testee)
        {
            var metroTestee = testee as Data.Entities.Metro.MetroTestee;
            Debug.Assert(metroTestee != null);

            metroTestee.EducationGrade = EducationGrade;
            metroTestee.MartialStatus = MaritalStatus;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\ViewModels\MetroTesteeSelectionRowViewModel.cs


using System;
using System.Collections.Generic;

namespace Updk7.Customers.Metro.Master.ViewModels
{
    public class MetroTesteeSelectionRowViewModel : Wpf.Master.Modules.Testees.ViewModels.TesteeSelectionRowViewModel
    {
        Data.Repositories.IUsersRepository _usersRepository;
        private Dictionary<int, string> _professionTypes;
        private Dictionary<int, string> _trafficTypes;

        public MetroTesteeSelectionRowViewModel(Dictionary<int, string> professionTypes,
            Dictionary<int, string> trafficTypes, Data.Repositories.IUsersRepository usersRepository)
        {
            _usersRepository = usersRepository;
            _professionTypes = professionTypes;
            _trafficTypes = trafficTypes;
        }

        private string _profession;

        public string Profession
        {
            get { return _profession; }
            private set { SetProperty(ref _profession, value); }
        }

        private string _traffic;

        public string Traffic
        {
            get { return _traffic; }
            private set { SetProperty(ref _traffic, value); }
        }

        private int? _pvkGroup;

        public int? PvkGroup
        {
            get { return _pvkGroup; }
            private set { SetProperty(ref _pvkGroup, value); }
        }

        private DateTime? _examination;

        public DateTime? Examination
        {
            get { return _examination; }
            private set { SetProperty(ref _examination, value); }
        }

        private DateTime? _nextExamination;

        public DateTime? NextExamination
        {
            get { return _nextExamination; }
            private set { SetProperty(ref _nextExamination, value); }
        }

        private string _examinationUser;

        public string ExaminationUser
        {
            get { return _examinationUser; }
            private set { SetProperty(ref _examinationUser, value); }
        }

        private string _upvk;

        public string Upvk
        {
            get { return _upvk; }
            private set { SetProperty(ref _upvk, value); }
        }

        private string _expirience;

        public string Expirience
        {
            get { return _expirience; }
            private set { SetProperty(ref _expirience, value); }
        }

        private bool _isExpired;

        public bool IsExpired
        {
            get { return _isExpired; }
            private set { SetProperty(ref _isExpired, value); }
        }

        private bool _isComing;

        public bool IsComing
        {
            get { return _isComing; }
            private set { SetProperty(ref _isComing, value); }
        }

        public override void Update(Data.Entities.Testee testee)
        {
            base.Update(testee);

            var metroTestee = testee as Data.Entities.Metro.MetroTestee;
            var traffic = _trafficTypes[metroTestee.TrafficId];
            if (metroTestee.TrafficId == 0)
                traffic = "-";

            Profession = _professionTypes[metroTestee.ProfessionId];
            Traffic = traffic;
            PvkGroup = metroTestee.PVKGroup;
            Examination = metroTestee.ExaminationDate;
            NextExamination = metroTestee.NextExaminationDate;
            Upvk = metroTestee.VulnerablePVK;

            if (metroTestee.StartExperienceDate.HasValue)
            {
                var years = DateTime.Now.Year - metroTestee.StartExperienceDate.Value.Year;
                Expirience = years >= 1
                    ? years.ToString()
                    : "менее года";
            }

            if (!metroTestee.ExaminationUserId.HasValue)
                ExaminationUser = string.Empty;
            else
            {
                var user = _usersRepository.GetById(metroTestee.ExaminationUserId.Value);
                ExaminationUser = user.GetShortName();
            }

            if (!metroTestee.NextExaminationDate.HasValue)
                IsComing = IsExpired = false;
            else
            {
                var nextExam = metroTestee.NextExaminationDate.Value.Date;
                if (nextExam < DateTime.Now.Date)
                {
                    IsComing = false;
                    IsExpired = true;
                }
                else if (nextExam.Subtract(DateTime.Now).TotalDays <= 30)
                {
                    IsComing = true;
                    IsExpired = false;
                }
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\ViewModels\MetroTesteeSelectionViewModel.cs


using System.Collections.Generic;
using ViewModelAssets = Updk7.Wpf.Master.Modules.Testees.ViewModels;

namespace Updk7.Customers.Metro.Master.ViewModels
{
    public class MetroTesteeSelectionViewModel : ViewModelAssets.TesteeSelectionViewModel
    {
        private Dictionary<int, string> _professionTypes;
        private Dictionary<int, string> _trafficTypes;
        private Data.Repositories.IUsersRepository _usersRepository;

        public MetroTesteeSelectionViewModel(Data.Repositories.ITesteesRepository testeesRepository,
            Data.Repositories.IUsersRepository usersRepository,
            Data.ITestingTreeFactory testingTreeFactory)
        {
            var metadata = testingTreeFactory.GetMetadata();
            _professionTypes = metadata.GetProperty("ProfessionTypes") as Dictionary<int, string>;
            _trafficTypes = metadata.GetProperty("TrafficTypes") as Dictionary<int, string>;
            _usersRepository = usersRepository;
            TesteesRepository = testeesRepository;
        }

        public override Wpf.Master.Assets.TesteeShortInfo GetSelectedTesteeShortInfo()
        {
            if (SelectedTestee == null || TesteesRepository == null)
                return null;

            var metroRow = SelectedRow as MetroTesteeSelectionRowViewModel;
            return new Wpf.Master.Assets.TesteeShortInfo()
            {
                Entitie = SelectedTestee,
                FullName = SelectedTestee.FullName,
                Photo = Wpf.BitmapImageHelper.CreateImage(TesteesRepository.Photos.GetImageData(metroRow.SourceData.Id)),
                Comment = $"{metroRow.Profession} ({metroRow.Traffic})"
            };
        }

        protected override ViewModelAssets.TesteeSelectionRowViewModel CreateTesteeViewModel(Data.Entities.Testee testee)
        {
            var viewModel = new MetroTesteeSelectionRowViewModel(_professionTypes, _trafficTypes, _usersRepository);
            viewModel.Update(testee);

            return viewModel;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\Views\MetroTesteeCustomEditorView.xaml

<UserControl x:Class="Updk7.Customers.Metro.Master.Views.MetroTesteeCustomEditorView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Customers.Metro.Master.Views"
             mc:Ignorable="d" 
             Width="550">
    <Grid
        Margin="0,3,0,0">
        <Grid.Resources>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="HorizontalAlignment" Value="Left" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <Setter Property="Margin" Value="5,3,5,3" />
            </Style>
            <Style TargetType="{x:Type TextBox}">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="FontWeight" Value="Normal" />
                <Setter Property="HorizontalAlignment" Value="Stretch" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <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>
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" SharedSizeGroup="TitleColumn" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!--Профессия-->
        <TextBlock Grid.Row="0" Grid.Column="0" Text="Должность"/>
        <ComboBox 
            Grid.Row="0"
            Grid.Column="1"
            Margin="0,0,0,3"
            VerticalAlignment="Center"
            HorizontalAlignment="Stretch"
            IsSynchronizedWithCurrentItem="True"
            ItemsSource="{Binding ProfessionTypes}"
            SelectedItem="{Binding SelectedProfession}"
            DisplayMemberPath="Title"
            />
        <!--Тип движения-->
        <TextBlock Grid.Row="1" Grid.Column="0" Text="Тип движения" />
        <ComboBox 
            Grid.Row="1"
            Grid.Column="1"
            Margin="0,0,0,3"
            VerticalAlignment="Center"
            HorizontalAlignment="Stretch"
            IsSynchronizedWithCurrentItem="True"
            ItemsSource="{Binding TrafficTypes}"
            SelectedItem="{Binding SelectedTraffic}"
            DisplayMemberPath="Title"
            />
        <!--Колонна-->
        <TextBlock Grid.Row="2" Grid.Column="0" Text="ТЧ (УПЧ)" />
        <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding TC, UpdateSourceTrigger=PropertyChanged}" />
        <!--Дата начала стажа-->
        <TextBlock Grid.Row="5" Grid.Column="0" Text="Начало стажа" />
        <DatePicker Grid.Row="5" Grid.Column="1" SelectedDate="{Binding StartExperienceDate}" VerticalAlignment="Center" />
        <!--Дата вступления в должность-->
        <TextBlock Grid.Row="6" Grid.Column="0" Text="Вступление в должность" />
        <DatePicker Grid.Row="6" Grid.Column="1" SelectedDate="{Binding EmploymentDate}" VerticalAlignment="Center" />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\Views\MetroTesteeCustomEditorView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Customers.Metro.Master.Views
{
    /// <summary>
    /// Interaction logic for MetroTesteeCustomEditorView.xaml
    /// </summary>
    public partial class MetroTesteeCustomEditorView : UserControl
    {
        public MetroTesteeCustomEditorView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\Views\MetroTesteePersonnalEditorView.xaml

<UserControl x:Class="Updk7.Customers.Metro.Master.Views.MetroTesteePersonnalEditorView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:entities="clr-namespace:Updk7.Data.Entities.Metro;assembly=Updk7.Data"
             xmlns:converters="clr-namespace:Updk7.Wpf.Converters;assembly=Updk7.Wpf"
             mc:Ignorable="d">
    <Grid
        Margin="0,3,0,0">
        <Grid.Resources>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="HorizontalAlignment" Value="Left" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <Setter Property="Margin" Value="5,3,5,3" />
            </Style>
            <Style TargetType="{x:Type TextBox}">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="FontWeight" Value="Normal" />
                <Setter Property="HorizontalAlignment" Value="Stretch" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <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>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" SharedSizeGroup="TitleColumn" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <!--Семейное положение-->
        <TextBlock Grid.Row="0" Grid.Column="0" Text="Семейное положение" />
        <ComboBox 
            Grid.Column="1"
            Grid.Row="0"
            Margin="5,0,4,3"
            FontSize="12"
            VerticalAlignment="Center"
            HorizontalAlignment="Stretch"
            ItemsSource="{converters:KeyToStringItemsSource EnumKeysSource={x:Type entities:MaritalStatus}}"
            SelectedItem="{Binding MaritalStatus, Converter={converters:KeyToStringConverter EnumKeySource={x:Type entities:MaritalStatus}}}"
            IsSynchronizedWithCurrentItem="True"
            />
        <!--Образование-->
        <TextBlock Grid.Row="1" Grid.Column="0" Text="Образование" />
        <ComboBox 
            Grid.Column="1"
            Grid.Row="1"
            Margin="5,0,4,3"
            FontSize="12"
            VerticalAlignment="Center"
            HorizontalAlignment="Stretch"
            ItemsSource="{converters:KeyToStringItemsSource EnumKeysSource={x:Type entities:EducationGrade}}"
            SelectedItem="{Binding EducationGrade, Converter={converters:KeyToStringConverter EnumKeySource={x:Type entities:EducationGrade}}}"
            IsSynchronizedWithCurrentItem="True"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\Views\MetroTesteePersonnalEditorView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Customers.Metro.Master.Views
{
    /// <summary>
    /// Interaction logic for MetroTesteePersonnalEditorView.xaml
    /// </summary>
    public partial class MetroTesteePersonnalEditorView : UserControl
    {
        public MetroTesteePersonnalEditorView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\Views\MetroTesteeSelectionView.xaml

<UserControl x:Class="Updk7.Customers.Metro.Master.Views.MetroTesteeSelectionView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Customers.Metro.Master.Views"
             mc:Ignorable="d" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             d:DesignWidth="800">
    <Grid>
        <DataGrid
            Grid.Row="0"
            Grid.Column="1"
            Margin="0,0,0,5"
            Style="{StaticResource FlatDataGridStyle}"
            ItemsSource="{Binding TesteesCollectionView}"
            IsSynchronizedWithCurrentItem="True"
            ColumnWidth="*"
            FontSize="12">
            <DataGrid.Resources>
                <Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource DataGridTextColumnElementStyle}" />
                <Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource DataGridCellStyle}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding IsComing}" Value="True">
                            <Setter Property="Background" Value="Blue" />
                            <Setter Property="Foreground" Value="Black" />
                        </DataTrigger>
                        <DataTrigger Binding="{Binding IsExpired}" Value="True">
                            <Setter Property="Background" Value="Red" />
                            <Setter Property="Foreground" Value="Black" />
                        </DataTrigger>
                        <DataTrigger Binding="{Binding IsUnderTest}" Value="True">
                            <Setter Property="Background" Value="Green" />
                            <Setter Property="Foreground" Value="Black" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </DataGrid.Resources>
            <DataGrid.Columns>
                <!--Ф.И.О.-->
                <DataGridTextColumn
                    Header="Ф.И.О."
                    IsReadOnly="True"
                    Binding="{Binding FullName, Mode=OneWay}"
                    FontSize="14"
                    Width="Auto"
                    />
                <!--Дата рождения-->
                <DataGridTextColumn 
                    Header="Дата рождения"
                    IsReadOnly="True"
                    Binding="{Binding BirthDate, StringFormat=\{0:yyyy.MM.dd\}, Mode=OneWay}"
                    Width="Auto"
                    />
                <!--Должность-->
                <DataGridTextColumn
                    Header="Должность"
                    IsReadOnly="True"
                    Binding="{Binding Profession, Mode=OneWay}"
                    FontSize="14"
                    Width="Auto"
                    />
                <!--Вид движения-->
                <DataGridTextColumn
                    Header="Вид движения"
                    IsReadOnly="True"
                    Binding="{Binding Traffic, Mode=OneWay}"
                    FontSize="14"
                    Width="140"
                    />
                <!--Стаж-->
                <DataGridTextColumn
                    Header="Стаж"
                    IsReadOnly="True"
                    Binding="{Binding Expirience, Mode=OneWay}"
                    FontSize="14"
                    Width="80"
                    />
                <!--Группа-->
                <DataGridTextColumn
                    Header="Группа"
                    IsReadOnly="True"
                    Binding="{Binding PvkGroup, Mode=OneWay}"
                    FontSize="14"
                    Width="Auto"
                    />
                <!--УПВК-->
                <DataGridTextColumn
                    Header="УПВК"
                    IsReadOnly="True"
                    Binding="{Binding Upvk, Mode=OneWay}"
                    FontSize="14"
                    Width="Auto"
                    />
                <!--Дата аттестации-->
                <DataGridTextColumn
                    Header="Аттестация"
                    IsReadOnly="True"
                    Binding="{Binding Examination, StringFormat=\{0:yyyy.MM.dd\}, Mode=OneWay}"
                    FontSize="14"
                    Width="Auto"
                    />
                <!--Дата следующего обследования-->
                <DataGridTextColumn
                    Header="След. аттестация."
                    IsReadOnly="True"
                    Binding="{Binding NextExamination, StringFormat=\{0:yyyy.MM.dd\}, Mode=OneWay}"
                    FontSize="14"
                    Width="Auto"
                    />
                <!--Психолог-->
                <DataGridTextColumn
                    Header="Психолог"
                    IsReadOnly="True"
                    Binding="{Binding ExaminationUser, Mode=OneWay}"
                    FontSize="14"
                    Width="*"
                    MinWidth="100"
                    />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Master\Source\Views\MetroTesteeSelectionView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Customers.Metro.Master.Views
{
    /// <summary>
    /// Interaction logic for MetroTesteeSelectionView.xaml
    /// </summary>
    public partial class MetroTesteeSelectionView : UserControl
    {
        public MetroTesteeSelectionView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\MetroReportsModule.cs


using Prism.Ioc;
using Assets = Updk7.Wpf.Master.Modules.Reports;

namespace Updk7.Customers.Metro.Reports
{
    public class MetroReportsModule : Wpf.Master.Modules.ReportsModule
    {
        public MetroReportsModule()
        {
        }

        protected override void RegisterCommands()
        {
            Wpf.Commands.CommandManager.Current
                .Register<Commands.SelectReportMessagesCommand>("SelectReportMessages")
                .Register<Commands.EditReportMessagesCommand>("EditReportMessages");
        }

        protected override void RegisterInteractions()
        {
            Wpf.Interactions.InteractionService.Current
                .Register<Interactions.SelectReportMessagesInteraction, Interactions.Views.SelectReportMessageView>()
                .Register<Interactions.EditReportMessagesInteraction, Interactions.Views.EditReportMessagesView>();
        }

        protected override void RegisterTestGroupReportsGenerator(IContainerRegistry containerRegistry)
        {
            containerRegistry
                .RegisterSingleton<ViewModels.EditReportMessagesViewModel>()
                .RegisterSingleton<Tests.Reports.Wpf.ITestsGroupReportGenerator, ReportGenerator.MetroReportGenerator>();
        }

        protected override void RegisterReportsTree(IContainerRegistry containerRegistry)
        {
            containerRegistry
                .RegisterSingleton<ViewModels.ReportSettingsViewModel>()
                .RegisterSingleton<ViewModels.ReportsTreeViewModel>()
                .RegisterSingleton<Assets.IReportsTree, Assets.ViewModels.ReportsTreeViewModel>();

            Wpf.ViewModelLocator.Current
                .Register<Assets.IReportsTree, Assets.Views.ReportsTreeView>()
                .Register<ViewModels.ReportSettingsViewModel, Views.ReportSettingsView>()
                .Register<ViewModels.ReportsTreeViewModel, Views.ReportsTreeView>(Assets.RegionKeys.ReportsTreeRegion);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Commands\AddReportMessageCommand.cs


using System;
using System.ComponentModel;

namespace Updk7.Customers.Metro.Reports.Commands
{
    public class AddReportMessageCommand : Wpf.Commands.CommandBase
    {
        private Data.Repositories.IReportMessagesRepository _messagesRepository;
        private ViewModels.EditReportMessagesViewModel _editorViewModel;

        public AddReportMessageCommand(Data.Repositories.IReportMessagesRepository messagesRepository,
            ViewModels.EditReportMessagesViewModel editorVieModel)
        {
            _messagesRepository = messagesRepository;
            _editorViewModel = editorVieModel;
            _editorViewModel.PropertyChanged += onEditorPropertyChanged;
            _editorViewModel.Goals.PropertyChanged += onMessagesEditorPropertyChanged;
            _editorViewModel.Conclusions.PropertyChanged += onMessagesEditorPropertyChanged;
        }

        public override bool CanExecute(object parameter)
        {
            return _editorViewModel.SelectedViewModel != null
                && !string.IsNullOrEmpty(_editorViewModel.SelectedViewModel.EnteredText);
        }

        public override void Execute(object parameter)
        {
            if (!CanExecute(null))
                return;

            try
            {
                var entitie = new Data.Entities.ReportMessage();
                entitie.MessageType = _editorViewModel.SelectedViewModel.MessageType;
                entitie.Text = _editorViewModel.SelectedViewModel.EnteredText;
                _messagesRepository.Add(entitie);

                _editorViewModel.SelectedViewModel.EnteredText = string.Empty;
            }
            catch (Exception ex)
            {
                Wpf.AppLog.Current.Error("AddReportMessageCommand");
                Wpf.AppLog.Current.Error(ex.Message);
            }
        }

        private void onEditorPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ViewModels.EditReportMessagesViewModel.SelectedViewModel))
                RaiseCanExecuteChanged();
        }

        private void onMessagesEditorPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ViewModels.EditMessagesViewModel.EnteredText))
                RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Commands\EditReportMessagesCommand.cs


using System;
using System.Linq;

namespace Updk7.Customers.Metro.Reports.Commands
{
    public class EditReportMessagesCommand : Wpf.Commands.CommandBase
    {
        private Data.Repositories.IReportMessagesRepository _messagesRepository;
        private Lazy<ViewModels.EditReportMessagesViewModel> _editorViewModel;

        public EditReportMessagesCommand(Data.Repositories.IReportMessagesRepository messagesRepository)
        {
            _messagesRepository = messagesRepository;
            _messagesRepository.DataChanged += onMessagesRepositoryDataChanged;
            _editorViewModel = new Lazy<ViewModels.EditReportMessagesViewModel>(createMessagesViewModel);
        }

        public override bool CanExecute(object parameter)
        {
            return true;
        }

        public override void Execute(object parameter)
        {
            var interaction = new Interactions.EditReportMessagesInteraction();
            interaction.EditorViewModel = _editorViewModel.Value;
            interaction.EditorViewModel.Clear();

            Wpf.Interactions.InteractionService.Current.ShowInteractionModel(interaction, () =>
            {
                interaction.EditorViewModel = null;
            });
        }

        private ViewModels.EditReportMessagesViewModel createMessagesViewModel()
        {
            var viewModel = new ViewModels.EditReportMessagesViewModel();
            viewModel.SetMessages(_messagesRepository.GetMessages().Where(e => e.CanBeSet));
            viewModel.AddReportMessageCommand = new AddReportMessageCommand(_messagesRepository, viewModel);
            viewModel.RemoveReportMessageCommand = new RemoveReportMessageCommand(_messagesRepository, viewModel);

            return viewModel;
        }

        private void onMessagesRepositoryDataChanged(object sender, Data.Repositories.RepositoryDataChangedEventArgs e)
        {
            var entitie = e.Entity as Data.Entities.ReportMessage;
            if (entitie == null)
                return;

            if (e.Operation == Data.Repositories.RepositoryDataChangedOperation.Create)
                _editorViewModel.Value.AddMessage(entitie);
            else
            {
                var isRemove = e.Operation == Data.Repositories.RepositoryDataChangedOperation.Remove;
                var isUpdateRemove = e.Operation == Data.Repositories.RepositoryDataChangedOperation.Update
                    && !entitie.CanBeSet;

                if (isRemove || isUpdateRemove)
                    _editorViewModel.Value.RemoveMessage(entitie.Id);
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Commands\RemoveReportMessageCommand.cs


using System;
using System.ComponentModel;

namespace Updk7.Customers.Metro.Reports.Commands
{
    public class RemoveReportMessageCommand : Wpf.Commands.CommandBase
    {
        private Data.Repositories.IReportMessagesRepository _messagesRepository;
        private ViewModels.EditReportMessagesViewModel _editorViewModel;

        public RemoveReportMessageCommand(Data.Repositories.IReportMessagesRepository messagesRepository,
            ViewModels.EditReportMessagesViewModel editorVieModel)
        {
            _messagesRepository = messagesRepository;
            _editorViewModel = editorVieModel;
            _editorViewModel.PropertyChanged += onEditorPropertyChanged;
            _editorViewModel.Goals.PropertyChanged += onMessagesEditorPropertyChanged;
            _editorViewModel.Conclusions.PropertyChanged += onMessagesEditorPropertyChanged;
        }

        public override bool CanExecute(object parameter)
        {
            return _editorViewModel.SelectedViewModel != null
                && _editorViewModel.SelectedViewModel.SelectedMessage != null
                && _editorViewModel.SelectedViewModel.SelectedMessage.IsValid;
        }

        public override void Execute(object parameter)
        {
            if (!CanExecute(null))
                return;

            try
            {
                var entitie = _editorViewModel.SelectedViewModel.SelectedMessage.Entite;
                if (!entitie.Locked)
                    _messagesRepository.Remove(entitie);
                else
                {
                    entitie.CanBeSet = false;
                    _messagesRepository.Update(entitie);
                }
            }
            catch (Exception ex)
            {
                Wpf.AppLog.Current.Error("RemoveReportMessageCommand");
                Wpf.AppLog.Current.Error(ex.Message);
            }
        }

        private void onEditorPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ViewModels.EditReportMessagesViewModel.SelectedViewModel))
                RaiseCanExecuteChanged();
        }

        private void onMessagesEditorPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ViewModels.EditMessagesViewModel.SelectedMessage))
                RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Commands\SelectReportMessagesCommand.cs


using System;
using System.Linq;
using System.ComponentModel;
using System.Diagnostics;

namespace Updk7.Customers.Metro.Reports.Commands
{
    public class SelectReportMessagesCommand : Wpf.Commands.CommandBase
    {
        private Wpf.Master.Modules.Reports.IReportsTree _reportsTree;
        private Data.Repositories.IReportMessagesRepository _messagesRepository;
        Wpf.Master.Modules.Reports.IReportsController _reportsController;

        public SelectReportMessagesCommand(Wpf.Master.Modules.Reports.IReportsTree reportsTree,
            Wpf.Master.Modules.Reports.IReportsController reportsController,
            Data.Repositories.IReportMessagesRepository messagesRepository)
        {
            _reportsController = reportsController;
            _messagesRepository = messagesRepository;
            _reportsTree = reportsTree;
            _reportsTree.PropertyChanged += onReportsTreePropertyChanged;
        }

        public override bool CanExecute(object parameter)
        {
            return _reportsTree.SelectedResults != null
                && _reportsTree.SelectedResults.Group != null;
        }

        public override void Execute(object parameter)
        {
            Debug.Assert(_reportsTree.SelectedResults.Group != null);

            var selectionViewModel = createSelectionViewModel();
            var selectionInteraction = new Interactions.SelectReportMessagesInteraction(selectionViewModel);

            Wpf.Interactions.InteractionService.Current.ShowInteractionModel(selectionInteraction, () =>
            {
                if (selectionInteraction.Confirmed)
                {
                    var testGroupId = _reportsTree.SelectedResults.Group.SourceEntitie.Id;
                    try
                    {
                        _messagesRepository.ClearMappings(testGroupId);
                        saveReportMessage(testGroupId, selectionViewModel.Goals.SelectedMessage);
                        saveReportMessage(testGroupId, selectionViewModel.Conclusions.SelectedMessage);
                        _reportsController.UpdateReport();
                    }
                    catch (Exception ex)
                    {
                        Wpf.AppLog.Current.Error("Set report message");
                        Wpf.AppLog.Current.Error(ex.Message);
                    }
                }
            });
        }

        private void onReportsTreePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(_reportsTree.SelectedResults))
                RaiseCanExecuteChanged();
        }

        private ViewModels.SelectReportMessagesViewModel createSelectionViewModel()
        {
            Debug.Assert(_reportsTree.SelectedResults.Group != null);

            var messages = _messagesRepository.GetMessages().ToArray();
            var viewModel = new ViewModels.SelectReportMessagesViewModel();
            viewModel.SetMessages(messages);

            var reportMessages = _messagesRepository.GetMessages(_reportsTree.SelectedResults.Group.SourceEntitie.Id);
            if (reportMessages != null)
                viewModel.SetSelectedMessages(reportMessages);

            return viewModel;
        }

        private void saveReportMessage(int testGroupId, ViewModels.MessageViewModel messageViewModel)
        {
            if (messageViewModel == null || !messageViewModel.IsValid)
                return;

            var message = messageViewModel.Entite;
            Wpf.AppLog.Current.Info($"Attach report message: TestGroup {testGroupId}, ReportMessage {message.Id}");

            try
            {
                if (!message.Locked)
                {
                    message.Locked = true;
                    _messagesRepository.Update(message);
                }

                var mapping = new Data.Entities.ReportMessageMapping();
                mapping.MessageId = message.Id;
                mapping.TestGroupId = testGroupId;
                _messagesRepository.Add(mapping);
            }
            catch (Exception ex)
            {
                Wpf.AppLog.Current.Error("Save report message");
                Wpf.AppLog.Current.Error(ex.Message);
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Interactions\EditReportMessagesInteraction.cs


namespace Updk7.Customers.Metro.Reports.Interactions
{
    public class EditReportMessagesInteraction : Wpf.Interactions.ConfirmationInteraction
    {
        public EditReportMessagesInteraction()
        {
        }

        private ViewModels.EditReportMessagesViewModel _editorViewModel;

        public ViewModels.EditReportMessagesViewModel EditorViewModel
        {
            get { return _editorViewModel; }
            set { SetProperty(ref _editorViewModel, value); }
        }

        protected override bool CanConfirm()
        {
            return true;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Interactions\SelectReportMessagesInteraction.cs


using System.ComponentModel;

namespace Updk7.Customers.Metro.Reports.Interactions
{
    public class SelectReportMessagesInteraction : Wpf.Interactions.ConfirmationInteraction
    {
        public SelectReportMessagesInteraction(ViewModels.SelectReportMessagesViewModel messageSelection)
        {
            MessagesSelection = messageSelection;
            MessagesSelection.PropertyChanged += onMessagesSelectionPropertyChanged;
        }

        public ViewModels.SelectReportMessagesViewModel MessagesSelection { get; }

        protected override bool CanConfirm()
        {
            return MessagesSelection.MessageSelected;
        }

        private void onMessagesSelectionPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ViewModels.SelectReportMessagesViewModel.MessageSelected))
                AcceptCommand.RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Interactions\Views\EditReportMessagesView.xaml

<UserControl x:Class="Updk7.Customers.Metro.Reports.Interactions.Views.EditReportMessagesView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Customers.Metro.Reports.Interactions.Views"
             xmlns:views="clr-namespace:Updk7.Customers.Metro.Reports.Views"
             mc:Ignorable="d" 
             Width="800"
             Height="600">
    <Grid
        Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!--Заголовок панели-->
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="40" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Image
                Grid.Column="0" 
                Margin="5"
                Source="{StaticResource ConclusionImage}" />
            <TextBlock 
                Grid.Column="1"
                FontSize="18"
                FontWeight="Bold"
                Margin="5,1,1,1"
                VerticalAlignment="Center"
                HorizontalAlignment="Left"
                Text="Пользовательские сообщения для групп тестирования"
                />
        </Grid>
        <!--Редактор заключений отчета-->
        <views:EditReportMessagesView 
            Grid.Row="1"
            Margin="2,0,0,0"
            DataContext="{Binding EditorViewModel}"
            />
        <!--Кнопки управления-->
        <Grid
            Grid.Row="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <!--Кнопка Применить-->
            <Button 
                Grid.Column="1"
                Width="120"
                Height="30"
                HorizontalAlignment="Right"
                FontSize="14"
                FontWeight="Bold"
                Content="Закрыть"
                Command="{Binding AcceptCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Interactions\Views\EditReportMessagesView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Customers.Metro.Reports.Interactions.Views
{
    /// <summary>
    /// Interaction logic for EditReportMessagesView.xaml
    /// </summary>
    public partial class EditReportMessagesView : UserControl
    {
        public EditReportMessagesView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Interactions\Views\SelectReportMessageView.xaml

<UserControl x:Class="Updk7.Customers.Metro.Reports.Interactions.Views.SelectReportMessageView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Customers.Metro.Reports.Interactions.Views"
             mc:Ignorable="d" 
             xmlns:views="clr-namespace:Updk7.Customers.Metro.Reports.Views"
             Width="600">
    <Grid
        Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!--Заголовок панели-->
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="40" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Image
                Grid.Column="0" 
                Margin="5"
                Source="{StaticResource ConclusionImage}" />
            <TextBlock 
                Grid.Column="1"
                FontSize="18"
                FontWeight="Bold"
                Margin="5,1,1,1"
                VerticalAlignment="Center"
                HorizontalAlignment="Left"
                Text="Выбор цели и заключения по группе тестов"
                />
        </Grid>
        <!--Выбор цели и заключения по обследованию-->
        <views:SelectReportMessagesView 
            Grid.Row="1"
            Margin="0,0,0,20"
            DataContext="{Binding MessagesSelection}"
            />
        <!--Кнопки управления-->
        <Grid
            Grid.Row="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <!--Кнопка Применить-->
            <Button 
                Grid.Column="1"
                Width="80"
                Height="30"
                HorizontalAlignment="Right"
                FontSize="14"
                FontWeight="Bold"
                Content="Задать"
                Command="{Binding AcceptCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
            <!--Кнопка Отменить-->
            <Button 
                Grid.Column="2"
                Width="80"
                Height="30"
                FontSize="14"
                FontWeight="Bold"
                HorizontalAlignment="Right"
                Content="Отменить"
                Command="{Binding CancelCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Interactions\Views\SelectReportMessageView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Customers.Metro.Reports.Interactions.Views
{
    /// <summary>
    /// Interaction logic for SelectReportMessageView.xaml
    /// </summary>
    public partial class SelectReportMessageView : UserControl
    {
        public SelectReportMessageView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\ReportGenerator\MetroFullReportGenerator.cs


using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;

namespace Updk7.Customers.Metro.Reports.ReportGenerator
{
    public class MetroFullReportGenerator
    {
        private Data.Validation.Metro.PvkValidator _validator;
        private Data.ITestingTreeFactory _testingTreeFactory;
        private Dictionary<int, string> _professions;
        private Dictionary<int, string> _traffic;

        public MetroFullReportGenerator(Data.ITestingTreeFactory testingTreeFactory)
        {
            var metadata = testingTreeFactory.GetMetadata();
            _professions = metadata.GetProperty("ProfessionTypes") as Dictionary<int, string>;
            _traffic = metadata.GetProperty("TrafficTypes") as Dictionary<int, string>;
            _validator = new Data.Validation.Metro.PvkValidator(testingTreeFactory);
            _testingTreeFactory = testingTreeFactory;
        }

        public FlowDocument CreateReport(MetroReportInfo reportInfo)
        {
            var testee = reportInfo.Header.Testee.Entitie as Data.Entities.Metro.MetroTestee;
            Debug.Assert(testee != null);

            var organization = Wpf.AppStrings.GetString("Organization");
            var validationResults = reportInfo.ValidationResults;

            var document = Tests.Reports.Wpf.ReportHelper.CreateBlankDocument();
            document.Blocks.Add(createHeader(organization, reportInfo.Goal, validationResults.GroupKey));
            document.Blocks.Add(createTesteeInfoTable(testee, validationResults.ExaminatinDate));
            document.Blocks.Add(createResultsTables(reportInfo.TestGroup, validationResults));
            document.Blocks.Add(createConclusionSection(validationResults, reportInfo.Conclusion));
            document.Blocks.Add(Tests.Reports.Wpf.ReportGenerators.ReportBlocks.Footer.Create(reportInfo.Header.UserName));

            return document;
        }

        private Block createHeader(string organization, string goal, string groupKey)
        {
            var title = "Протокол результатов психофизиологической диагностики профессионально-важных качеств РЛБ";
            if (groupKey == "A1" || groupKey == "B1")
                title = "Протокол результатов психофизиологической диагностики профессионально-важных качеств курсанта";
            else if (groupKey == "A1")
                title = "Протокол результатов дополнительной диагностики";

            var headerBlock = new BlockUIContainer()
            {
                Child = new Border()
                {
                    HorizontalAlignment = HorizontalAlignment.Stretch,
                    Background = Brushes.LightGray,
                    BorderBrush = Brushes.Gray,
                    BorderThickness = new Thickness(1.0),
                    Child = new TextBlock()
                    {
                        Text = title,
                        FontSize = 16.0,
                        FontWeight = FontWeights.Bold,
                        TextAlignment = TextAlignment.Center,
                        Margin = new Thickness(5.0),
                        TextWrapping = TextWrapping.Wrap
                        
                    }
                }
            };

            var organizationBlock = new Paragraph()
            {
                FontSize = 14.0,
                FontWeight = FontWeights.Medium,
                Margin = new Thickness(0.0, 7.0, 0.0, 7.0)
            };
            organizationBlock.Inlines.Add(new Run(organization));

            var goalBlock = new Paragraph() { FontSize = 14.0 };
            goalBlock.Inlines.Add(new Run("Цель обследования: ") { FontWeight = FontWeights.Bold });
            goalBlock.Inlines.Add(new Run(goal));

            var section = new Section();
            section.Blocks.Add(organizationBlock);
            section.Blocks.Add(headerBlock);
            section.Blocks.Add(goalBlock);

            return section;
        }

        private Block createTesteeInfoTable(Data.Entities.Metro.MetroTestee testee, DateTime testingDate)
        {
            var tableGenerator = new Tests.Reports.Wpf.Generic.TableGenerator(2);
            tableGenerator.Columns[0].FontSize = 14;
            tableGenerator.Columns[0].FontWeight = FontWeights.Medium;
            tableGenerator.Columns[1].FontSize = 14;
            tableGenerator.Columns[0].Width = new GridLength(1.0, GridUnitType.Star);
            tableGenerator.Columns[1].Width = new GridLength(3.0, GridUnitType.Star);

            var profession = _professions[testee.ProfessionId];
            var traffic = _traffic[testee.TrafficId];

            tableGenerator.AddRow("Ф.И.О.", testee.FullName);
            tableGenerator.AddRow("Дата рождения", testee.BirthDate.ToString("dd.MM.yyyy"));
            tableGenerator.AddRow("Стаж", getExpirience(testee.StartExperienceDate));
            tableGenerator.AddRow("Должность", profession);
            tableGenerator.AddRow("Вид движения", traffic);
            tableGenerator.AddRow("ТЧ (УПЧ)", testee.TC);
            tableGenerator.AddRow("Дата тестирования", testingDate.ToString("dd.MM.yyyy"));

            var section = new Section();
            section.Blocks.Add(tableGenerator.CreateTable());

            return section;
        }

        private string getExpirience(DateTime? startDate)
        {
            if (!startDate.HasValue)
                return string.Empty;

            var expirience = (DateTime.Now.Year - startDate.Value.Year);
            var result = string.Empty;
            if (expirience <= 0)
                result = "менее года";
            else if (expirience <= 4)
                result = $"{expirience} года";
            else
                result = $"{expirience} лет";

            return result;
        }

        private Block createResultsTables(Data.TestGroupResults group, Data.Validation.TestGroupValidationResults validation)
        {
            if (validation == null)
            {
                var paragraph = new Paragraph();
                paragraph.Inlines.Add("Нет результатов валидации группы тестов");
                return paragraph;
            }

            var groupValidator = _validator.GetValidator(group.GroupKey);
            var section = new Section();

            if (group.GroupKey != "A2" && !group.IsTestGroupClosed(_testingTreeFactory))
            {
                section.Blocks.Add(new Paragraph(new Run("Тестирование проведено не в полном объеме"))
                {
                    FontSize = 16.0,
                    FontWeight = FontWeights.Bold
                });
            }

            foreach (var testResults in group.TestResults.Where(r => r.Test != Tests.TestType.Готовность_к_тестированию_ЭПФС))
            {
                var testMetadata = Tests.Metadata.GetTestInfo(testResults.Test);
                var testValidator = groupValidator.GetTestValidationRules(testResults.Test);

                // Заголовок таблицы результатов
                var title = testMetadata.TestShortName;
                if (testValidator != null)
                {
                    var isTestFailed = validation.IsFailedTest(testResults.Test);
                    var conclusion = !isTestFailed ? "НОРМА" : "НИЖЕ НОРМЫ";
                    if (testResults.Values == null || testResults.Values.Count == 0)
                        conclusion = "НЕТ РЕЗУЛЬТАТОВ ТЕСТИРОВАНИЯ";
                    title += $" - {conclusion}";
                }

                var paragraphHeader = new Paragraph()
                {
                    FontSize = 16.0,
                    FontWeight = FontWeights.Bold
                };
                paragraphHeader.Inlines.Add(new Run(title));
                section.Blocks.Add(paragraphHeader);

                // Таблица результатов
                if (testValidator == null)
                {
                    // Если нет валидатора, выводим всю таблицу результатов
                    var resultsTable = new Tests.Reports.Wpf.ReportGenerators.ReportBlocks.ResultsTable();
                    resultsTable.Results = testResults;
                    section.Blocks.Add(resultsTable.CreateDocumentBlock());
                }
                else
                {
                    // Выводим только результаты для которых есть правила валидации
                    var tableGenerator = new Tests.Reports.Wpf.Generic.TableGenerator(2);
                    tableGenerator.Columns[0].TextAlignment = TextAlignment.Left;
                    tableGenerator.Columns[0].Width = new GridLength(5, GridUnitType.Star);
                    tableGenerator.Columns[1].Width = new GridLength(1.0, GridUnitType.Star);
                    tableGenerator.Columns[1].FontSize = 14;
                    tableGenerator.Columns[1].FontWeight = FontWeights.Medium;

                    foreach (var resultValue in testResults.Values)
                    {
                        if (testValidator.ContainsRule(resultValue.Key))
                        {
                            var resultTitle = resultValue.Key;
                            if (validation.IsFailedResult(testResults.Test, resultValue.Key))
                                title = $"(-) {title}";

                            tableGenerator.AddRow(resultTitle, formatResultValue(resultValue));
                        }
                    }

                    section.Blocks.Add(tableGenerator.CreateTable());
                }
            }

            var epfs = createEpfsBlock(group, validation);
            if (epfs != null)
                section.Blocks.Add(epfs);

            return section;
        }

        private Block createEpfsBlock(Data.TestGroupResults group, Data.Validation.TestGroupValidationResults validation)
        {
            var epfs = group.TestResults.FirstOrDefault(r => r.Test == Tests.TestType.Готовность_к_тестированию_ЭПФС);
            if (epfs == null)
                return null;

            var paragraph = new Paragraph()
            {
                FontSize = 16.0,
                FontWeight = FontWeights.Bold
            };

            if (!validation.IsFailedTest(Tests.TestType.Готовность_к_тестированию_ЭПФС))
                paragraph.Inlines.Add("Тест ГКТ-ЭПФС - норма. ФС в норме");
            else
            {
                var result = epfs.Values.FirstOrDefault(r => r.Key == "Общее количество ошибок");
                var text = "Тест ГКТ-ЭПФС - ниже нормы";
                if (result != null)
                    text += $" (количество ошибок: {result.Int}). ФС снижено.";
                else
                    text += ". ФС снижено.";

                paragraph.Inlines.Add(text);
            }

            return paragraph;
        }

        private string formatResultValue(Tests.TestResultValue resultValue)
        {
            var resultStr = string.Empty;
            if (resultValue.IsTypeOf<int>())
                resultStr = resultValue.Int.ToString();
            else if (resultValue.IsTypeOf<float>())
            {
                var formatStr = "f1";
                if (Math.Abs(resultValue.Float) <= 10.0)
                    formatStr = "f3";
                resultStr = resultValue.Float.ToString(formatStr);
            }

            return resultStr;
        }

        private Block createConclusionSection(Data.Validation.TestGroupValidationResults validation, string conclusion)
        {
            var section = new Section();

            if (validation.FailedTestsCount != 0)
            {
                var upvk = new Paragraph() { FontSize = 14.0 };
                upvk.Inlines.Add(new Run("Уязвимые качества: ") { FontWeight = FontWeights.Bold });
                upvk.Inlines.Add(new Run(validation.GetFailedTestsString(Tests.TestType.Готовность_к_тестированию_ЭПФС)));
                section.Blocks.Add(upvk);
            }

            var paragraphConclusion = new Paragraph() { FontSize = 14.0 };
            paragraphConclusion.Inlines.Add(new Run("Заключение: ") { FontWeight = FontWeights.Bold });
            paragraphConclusion.Inlines.Add(new Run(conclusion));
            section.Blocks.Add(paragraphConclusion);

            return section;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\ReportGenerator\MetroReportGenerator.cs


using System.Linq;
using System.Collections.Generic;
using System.Windows.Documents;

namespace Updk7.Customers.Metro.Reports.ReportGenerator
{
    public class MetroReportGenerator : Tests.Reports.Wpf.ITestsGroupReportGenerator
    {
        private Data.Repositories.IReportMessagesRepository _reportMessagesRepository;
        private Data.Validation.Metro.PvkValidator _validator;
        private ViewModels.ReportSettingsViewModel _reportSettings;

        private MetroTicketGenerator _ticketGenerator;
        private MetroFullReportGenerator _reportGenerator;

        public MetroReportGenerator(Data.ITestingTreeFactory testingTreeFactory,
            ViewModels.ReportSettingsViewModel reportSettings,
            Data.Repositories.IReportMessagesRepository reportMessagesRepository)
        {
            _validator = new Data.Validation.Metro.PvkValidator(testingTreeFactory);
            _reportMessagesRepository = reportMessagesRepository;
            _reportSettings = reportSettings;

            _ticketGenerator = new MetroTicketGenerator(testingTreeFactory);
            _reportGenerator = new MetroFullReportGenerator(testingTreeFactory);
        }

        public bool CanGenerate(Data.TestGroupResults results)
        {
            return results.GroupKey == "A1"
                || results.GroupKey == "B1"
                || results.GroupKey == "C1"
                || results.GroupKey == "D1"
                || results.GroupKey == "A2";
        }

        public FlowDocument CreateReport(Tests.Reports.Wpf.ReportInfo headerInfo, Data.TestGroupResults results)
        {
            if (!CanGenerate(results))
                return null;

            var reportInfo = createReportInfo(headerInfo, results);
            if (_reportSettings.ReportType == ViewModels.ReportType.Brief && results.GroupKey != "A2")
                return _ticketGenerator.CreateReport(reportInfo);
            else
                return _reportGenerator.CreateReport(reportInfo);
        }

        private MetroReportInfo createReportInfo(Tests.Reports.Wpf.ReportInfo headerInfo, Data.TestGroupResults results)
        {
            var testee = headerInfo.Testee.Entitie as Data.Entities.Metro.MetroTestee;
            var validationResults = _validator.Validate(results, testee);

            var defaultGoal = "первичная оценка ПВК";
            if (results.GroupKey == "C1" || results.GroupKey == "D1")
                defaultGoal = "периодическая оценка ПВК";
            else if (results.GroupKey == "A2")
                defaultGoal = "дополнительная диагностика";

            var defaultConclusion = validationResults?.Conclusion;
            if (string.IsNullOrEmpty(defaultConclusion))
                defaultConclusion = "обследование завершено";

            var reportMessages = _reportMessagesRepository.GetMessages(results.GroupId);
            var reportInfo = new MetroReportInfo();
            reportInfo.Header = headerInfo;
            reportInfo.ValidationResults = validationResults;
            reportInfo.TestGroup = results;
            reportInfo.Goal = getReportMessage(reportMessages, defaultGoal, Data.Entities.ReportMessageType.Goal);
            reportInfo.Conclusion = getReportMessage(reportMessages, defaultConclusion, Data.Entities.ReportMessageType.Conclusion);

            return reportInfo;
        }

        private string getReportMessage(IEnumerable<Data.Entities.ReportMessage> messages, string defaultValue,
            Data.Entities.ReportMessageType messageType)
        {
            if (messages == null)
                return defaultValue;

            var message = messages.FirstOrDefault(m => m.MessageType == messageType);
            return message != null
                ? message.Text
                : defaultValue;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\ReportGenerator\MetroReportInfo.cs


namespace Updk7.Customers.Metro.Reports.ReportGenerator
{
    public class MetroReportInfo
    {
        public Tests.Reports.Wpf.ReportInfo Header { get; set; }

        public string Goal { get; set; }

        public string Conclusion { get; set; }

        public Data.Validation.TestGroupValidationResults ValidationResults { get; set; }

        public Data.TestGroupResults TestGroup { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\ReportGenerator\MetroTicketGenerator.cs


using System.Collections.Generic;
using System.Windows.Documents;

namespace Updk7.Customers.Metro.Reports.ReportGenerator
{
    public class MetroTicketGenerator
    {
        private Tests.Reports.Wpf.Generic.TestGroupTicketGenerator _ticketGenerator =
            new Tests.Reports.Wpf.Generic.TestGroupTicketGenerator();

        private Data.ITestingTreeFactory _testingTreeFactory;
        private Dictionary<int, string> _professions;
        private Dictionary<int, string> _traffic;

        public MetroTicketGenerator(Data.ITestingTreeFactory testingTreeFactory)
        {
            var metadata = testingTreeFactory.GetMetadata();
            _professions = metadata.GetProperty("ProfessionTypes") as Dictionary<int, string>;
            _traffic = metadata.GetProperty("TrafficTypes") as Dictionary<int, string>;
            _testingTreeFactory = testingTreeFactory;
        }

        public FlowDocument CreateReport(MetroReportInfo reportInfo)
        {
            var metroTestee = reportInfo.Header.Testee.Entitie as Data.Entities.Metro.MetroTestee;
            var validationResults = reportInfo.ValidationResults;
            var pvkGroup = validationResults.ProfessionalGroup.HasValue
                ? validationResults.ProfessionalGroup.Value.ToString()
                : "-";

            var profession = _professions[metroTestee.ProfessionId];
            var traffic = _traffic[metroTestee.TrafficId];

            var groupKey = reportInfo.TestGroup.GroupKey;
            var title = "Талон результатов психофизиологической диагностики профессионально-важных качеств РЛБ";
            if (groupKey == "A1" || groupKey == "B1")
                title = "Талон результатов психофизиологической диагностики профессионально-важных качеств курсанта";
            else if (groupKey == "A1")
                title = "Талон результатов дополнительной диагностики";

            var conclusion = reportInfo.Conclusion;
            if (groupKey != "A2" && !reportInfo.TestGroup.IsTestGroupClosed(_testingTreeFactory))
                conclusion = "Тестирование проведено не в полном объеме";

            var ticketInfo = new Tests.Reports.Wpf.Generic.TestGroupTicketInfo()
            {
                Title = title,
                FullName = metroTestee.FullName,
                UserName = reportInfo.Header.UserName,
                ExaminationDate = reportInfo.TestGroup.TestingDate,
                Profession = profession,
                Traffic = traffic,
                FailedTests = validationResults.GetFailedTestsString(Tests.TestType.Готовность_к_тестированию_ЭПФС),
                AssignedGroup = pvkGroup,
                NextExamination = validationResults.NextExaminationDate,
                Goal = reportInfo.Goal,
                Conclusion = conclusion,
                Organization = Wpf.AppStrings.GetString("Organization")
            };

            return _ticketGenerator.CreateTicket(ticketInfo);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\ViewModels\EditMessagesViewModel.cs


using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Updk7.Customers.Metro.Reports.ViewModels
{
    public class EditMessagesViewModel : Prism.Mvvm.BindableBase
    {
        public EditMessagesViewModel(Data.Entities.ReportMessageType messageType)
        {
            MessageType = messageType;
        }

        public Data.Entities.ReportMessageType MessageType { get; set; }

        public ObservableCollection<MessageViewModel> Messages { get; } =
            new ObservableCollection<MessageViewModel>();

        private string _enteredText;

        public string EnteredText
        {
            get { return _enteredText; }
            set { SetProperty(ref _enteredText, value); }
        }

        private MessageViewModel _selectedMessage;

        public MessageViewModel SelectedMessage
        {
            get { return _selectedMessage; }
            set { SetProperty(ref _selectedMessage, value); }
        }

        private bool _isSelected;

        public bool IsSelected
        {
            get { return _isSelected; }
            set { SetProperty(ref _isSelected, value); }
        }

        public void Clear()
        {
            SelectedMessage = null;
            EnteredText = string.Empty;
        }

        public void SetMessages(IEnumerable<MessageViewModel> messages)
        {
            Messages.Clear();
            foreach (var message in messages)
                AddMessage(message);
        }

        public void AddMessage(MessageViewModel message)
        {
            Messages.Add(message);
        }

        public void RemoveMessage(int messageId)
        {
            var message = Messages.FirstOrDefault(vm => vm.IsValid && vm.Entite.Id == messageId);
            if (message != null)
            {
                Messages.Remove(message);
                if (SelectedMessage == message)
                    SelectedMessage = null;
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\ViewModels\EditReportMessagesViewModel.cs


using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Input;

namespace Updk7.Customers.Metro.Reports.ViewModels
{
    public class EditReportMessagesViewModel : Prism.Mvvm.BindableBase
    {
        public EditReportMessagesViewModel()
        {
            Goals.PropertyChanged += onEditMessagesViewPropertyChanged;
            Conclusions.PropertyChanged += onEditMessagesViewPropertyChanged;
        }

        public ICommand AddReportMessageCommand { get; set; }

        public ICommand RemoveReportMessageCommand { get; set; }

        public EditMessagesViewModel Goals { get; } = new EditMessagesViewModel(Data.Entities.ReportMessageType.Goal);

        public EditMessagesViewModel Conclusions { get; } = new EditMessagesViewModel(Data.Entities.ReportMessageType.Conclusion);

        private EditMessagesViewModel _selectedViewModel;

        public EditMessagesViewModel SelectedViewModel
        {
            get { return _selectedViewModel; }
            set { SetProperty(ref _selectedViewModel, value); }
        }

        public void Clear()
        {
            Goals.Clear();
            Conclusions.Clear();
        }

        public void SetMessages(IEnumerable<Data.Entities.ReportMessage> messages)
        {
            foreach (var message in messages)
                AddMessage(message);
        }

        public void AddMessage(Data.Entities.ReportMessage message)
        {
            var viewModel = new MessageViewModel(message);
            if (message.MessageType == Data.Entities.ReportMessageType.Goal)
                Goals.AddMessage(viewModel);
            else
                Conclusions.AddMessage(viewModel);
        }

        public void RemoveMessage(int messageId)
        {
            Goals.RemoveMessage(messageId);
            Conclusions.RemoveMessage(messageId);
        }

        private void onEditMessagesViewPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(EditMessagesViewModel.IsSelected))
                SelectedViewModel = Goals.IsSelected ? Goals : Conclusions;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\ViewModels\MessageSelectionViewModel.cs


using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Updk7.Customers.Metro.Reports.ViewModels
{
    public class MessageSelectionViewModel : Prism.Mvvm.BindableBase
    {
        public MessageSelectionViewModel()
        {
        }

        public MessageViewModel UnselectMessage { get; } = new MessageViewModel("Сформировать автоматически");

        public ObservableCollection<MessageViewModel> Messages { get; } =
            new ObservableCollection<MessageViewModel>();

        private MessageViewModel _selectedMessage;

        public MessageViewModel SelectedMessage
        {
            get { return _selectedMessage; }
            set { SetProperty(ref _selectedMessage, value); }
        }

        public bool IsSelectionValid => SelectedMessage != null && SelectedMessage != UnselectMessage;

        private bool _canChanged = true;

        public bool CanChanged
        {
            get { return _canChanged; }
            set { SetProperty(ref _canChanged, value); }
        }

        public void SetMessages(IEnumerable<Data.Entities.ReportMessage> entities)
        {
            var viewModels = entities.Select(e => new MessageViewModel(e)).ToArray();

            Messages.Clear();
            Messages.Add(UnselectMessage);
            Messages.AddRange(viewModels);
            SelectedMessage = UnselectMessage;
        }

        public void SetSelectedMessage(Data.Entities.ReportMessage entitie)
        {
            var viewModel = Messages.FirstOrDefault(m => m != UnselectMessage && m.Entite.Id == entitie.Id);
            if (viewModel == null)
            {
                viewModel = new MessageViewModel(entitie);
                Messages.Add(viewModel);
            }

            SelectedMessage = viewModel;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\ViewModels\MessageViewModel.cs


namespace Updk7.Customers.Metro.Reports.ViewModels
{
    public class MessageViewModel
    {
        public MessageViewModel(string text)
        {
            Text = text;
        }

        public MessageViewModel(Data.Entities.ReportMessage entitie)
        {
            Entite = entitie;
            Text = entitie.Text;
        }

        public Data.Entities.ReportMessage Entite { get; set; }

        public bool IsValid => Entite != null;

        public string Text { get; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\ViewModels\ReportSettingsViewModel.cs


namespace Updk7.Customers.Metro.Reports.ViewModels
{
    public enum ReportType
    {
        Full,
        Brief
    }

    public class ReportSettingsViewModel : Prism.Mvvm.BindableBase
    {
        public ReportSettingsViewModel()
        {
        }

        private ReportType _reportType;

        public ReportType ReportType
        {
            get { return _reportType; }
            set { SetProperty(ref _reportType, value); }
        }

        private bool _enabled;

        public bool Enabled
        {
            get { return _enabled; }
            set { SetProperty(ref _enabled, value); }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\ViewModels\ReportsTreeViewModel.cs


using System.ComponentModel;

namespace Updk7.Customers.Metro.Reports.ViewModels
{
    public class ReportsTreeViewModel : Prism.Mvvm.BindableBase
    {
        private Wpf.Master.Modules.Reports.IReportsController _reportsController;
        private Wpf.Master.Modules.Reports.IReportsTree _reportsTree;
        private ReportSettingsViewModel _reportSettings;

        public ReportsTreeViewModel(ReportSettingsViewModel reportSettings,
            Wpf.Master.Modules.Reports.IReportsController reportsController,
            Wpf.Master.Modules.Reports.IReportsTree reportsTree)
        {
            _reportsController = reportsController;

            _reportsTree = reportsTree;
            _reportsTree.PropertyChanged += onReportsTreePropertyChanged;

            _reportSettings = reportSettings;
            _reportSettings.PropertyChanged += onReportSettingsPropertyChanged;
        }

        private void onReportsTreePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(Wpf.Master.Modules.Reports.IReportsTree.SelectedResults))
                _reportSettings.Enabled = isTestGroupSelected();
        }

        private void onReportSettingsPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ReportSettingsViewModel.ReportType))
            {
                if (isTestGroupSelected())
                    _reportsController.UpdateReport();
            }
        }

        private bool isTestGroupSelected()
        {
            return _reportsTree.SelectedResults != null
                && _reportsTree.SelectedResults.Group != null
                && _reportsTree.SelectedResults.Test == null;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\ViewModels\SelectReportMessagesViewModel.cs


using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace Updk7.Customers.Metro.Reports.ViewModels
{
    public class SelectReportMessagesViewModel : Prism.Mvvm.BindableBase
    {
        public SelectReportMessagesViewModel()
        {
            Goals.PropertyChanged += onMessageSelectionPropertyChanged;
            Conclusions.PropertyChanged += onMessageSelectionPropertyChanged;
        }

        public MessageSelectionViewModel Goals { get; } = new MessageSelectionViewModel();

        public MessageSelectionViewModel Conclusions { get; } = new MessageSelectionViewModel();

        private bool _messageSelected;

        public bool MessageSelected
        {
            get { return _messageSelected; }
            private set { SetProperty(ref _messageSelected, value); }
        }

        public void SetMessages(IEnumerable<Data.Entities.ReportMessage> entities)
        {
            var validEntities = entities.Where(e => e.CanBeSet);

            Goals.SetMessages(validEntities.Where(e => e.MessageType == Data.Entities.ReportMessageType.Goal));
            Conclusions.SetMessages(validEntities.Where(e => e.MessageType == Data.Entities.ReportMessageType.Conclusion));
        }

        public void SetSelectedMessages(IEnumerable<Data.Entities.ReportMessage> entitiees)
        {
            if (entitiees == null)
                return;

            var goal = entitiees.FirstOrDefault(e => e.MessageType == Data.Entities.ReportMessageType.Goal);
            if (goal != null)
                Goals.SetSelectedMessage(goal);

            var conclusion = entitiees.FirstOrDefault(e => e.MessageType == Data.Entities.ReportMessageType.Conclusion);
            if (conclusion != null)
                Conclusions.SetSelectedMessage(conclusion);
        }

        private void onMessageSelectionPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(MessageSelectionViewModel.SelectedMessage))
            {
                MessageSelected = Goals.SelectedMessage != null
                    || Conclusions.SelectedMessage != null;
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Views\EditMessagesView.xaml

<UserControl x:Class="Updk7.Customers.Metro.Reports.Views.EditMessagesView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Customers.Metro.Reports.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="60" />
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <!--Текст нового сообщения-->
        <TextBlock 
            Grid.Row="0"
            Margin="0,3,0,3"
            FontWeight="Medium"
            FontSize="14">
            <Run Text="Добавление сообщения" />
            <LineBreak />
            <Run 
                FontSize="12" 
                FontWeight="Normal" 
                Text="Введите текст нового сообщения и нажмите  &quot;+&quot; на панеле инструментов для его добавления"
                />
        </TextBlock>
        <TextBox 
            Grid.Row="1"
            Text="{Binding EnteredText, UpdateSourceTrigger=PropertyChanged}"
            FontSize="14"
            FontWeight="Medium"
            TextWrapping="Wrap"
            Focusable="True"
            />
        <!--Список всех сообщений-->
        <TextBlock 
            Grid.Row="2"
            Margin="0,6,0,3"
            FontWeight="Medium"
            FontSize="14">
            <Run Text="Доступные сообщения" />
            <LineBreak />
            <Run 
                FontSize="12" 
                FontWeight="Normal"
                Text="Выберете сообщение из списка и нажмите &quot;-&quot; на панеле инструментов для его удаления" />
        </TextBlock>
        <ListView
            Grid.Row="3"
            Background="Transparent"
            BorderBrush="Black"
            BorderThickness="1"
            ItemsSource="{Binding Messages}"
            SelectedItem="{Binding SelectedMessage}"
            IsSynchronizedWithCurrentItem="True"
            ScrollViewer.VerticalScrollBarVisibility="Auto"
            ScrollViewer.HorizontalScrollBarVisibility="Disabled">
            <ListView.ItemContainerStyle>
                <Style TargetType="{x:Type ListViewItem}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ListViewItem}">
                                <RadioButton 
                                    Style="{StaticResource FlatRadioButtonStyle}"
                                    Margin="5,2,2,2"
                                    FontSize="16"
                                    Content="{Binding Text}"
                                    IsChecked="{Binding IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
                                    GroupName="ConclusionGroup"
                                    TextBlock.TextAlignment="Left"
                                    />
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListView.ItemContainerStyle>
        </ListView>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Views\EditMessagesView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Customers.Metro.Reports.Views
{
    /// <summary>
    /// Interaction logic for EditMessagesView.xaml
    /// </summary>
    public partial class EditMessagesView : UserControl
    {
        public EditMessagesView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Views\EditReportMessagesView.xaml

<UserControl x:Class="Updk7.Customers.Metro.Reports.Views.EditReportMessagesView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Customers.Metro.Reports.Views"
             mc:Ignorable="d">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="35" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <!--Кнопки панели управления-->
        <StackPanel
            Grid.Column="0"
            Margin="0,10,0,0">
            <Button
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Добавить заключение"
                BorderThickness="2"
                Command="{Binding AddReportMessageCommand}">
                <Image Margin="3" Source="{StaticResource BlackPlusImage}" />
            </Button>
            <Button 
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Удалить заключение"
                BorderThickness="2"
                Command="{Binding RemoveReportMessageCommand}">
                <Image Margin="3" Source="{StaticResource BlackMinusImage}" />
            </Button>
        </StackPanel>
        <!--Вкладки сообщений-->
        <TabControl
            Margin="10,0,0,0"
            Grid.Column="1"
            BorderBrush="Transparent"
            Background="Transparent"
            SelectedIndex="0">
            <!--Цели обследования-->
            <TabItem
                Style="{StaticResource FlatTabItemStyle}"
                Header="Цели обследования"
                FontWeight="Medium"
                FontSize="22"
                IsSelected="{Binding Goals.IsSelected, Mode=OneWayToSource}">
                <local:EditMessagesView
                    DataContext="{Binding Goals}"
                    />
            </TabItem>
            <!--Заключения по обследованиям-->
            <TabItem
                Style="{StaticResource FlatTabItemStyle}"
                Header="Заключения по обследованию"
                FontWeight="Medium"
                FontSize="22"
                IsSelected="{Binding Conclusions.IsSelected, Mode=OneWayToSource}">
                <local:EditMessagesView
                    DataContext="{Binding Conclusions}"
                    />
            </TabItem>
        </TabControl>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Views\EditReportMessagesView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Customers.Metro.Reports.Views
{
    /// <summary>
    /// Interaction logic for EditReportMessagesView.xaml
    /// </summary>
    public partial class EditReportMessagesView : UserControl
    {
        public EditReportMessagesView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Views\MessageSelectionView.xaml

<UserControl x:Class="Updk7.Customers.Metro.Reports.Views.MessageSelectionView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Customers.Metro.Reports.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <!--Текст выбранного сообщения-->
        <Border
            Grid.Column="0"
            BorderBrush="Black"
            BorderThickness="0.75"
            Margin="1">
            <TextBlock 
                VerticalAlignment="Center"
                HorizontalAlignment="Left"
                FontSize="14"
                FontWeight="Medium"
                Text="{Binding SelectedMessage.Text}"
                Margin="2,1,2,1"
                />
        </Border>
        <!--Кнопка выбора сообщения-->
        <ToggleButton
            Grid.Column="1"
            Height="25"
            Width="110"
            x:Name="MessagesButton"
            Margin="3,1,0,1"
            Style="{StaticResource FlatToggleButtonStyle}"
            HorizontalAlignment="Center">
            <Grid>
                <TextBlock
                    Margin="1"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Stretch"
                    Text="Изменить"
                    FontSize="14"
                    FontWeight="Medium"
                    TextAlignment="Center"
                    />
                <!--Панель со списком сообщений-->
                <Popup
                    Width="400"
                    HorizontalOffset="-20"
                    VerticalOffset="-10"
                    Placement="Top"
                    AllowsTransparency="True"
                    StaysOpen="False"
                    PlacementTarget="{Binding ElementName=MessagesButton}"
                    IsOpen="{Binding IsChecked, ElementName=MessagesButton}"
                    DataContext="{Binding}">
                    <Border
                        BorderBrush="Black"
                        BorderThickness="0.75"
                        Background="{StaticResource WindowBackground}">
                        <ListView
                            Background="Transparent"
                            BorderBrush="Transparent"
                            BorderThickness="0"
                            ItemsSource="{Binding Messages}"
                            SelectedItem="{Binding SelectedMessage}"
                            IsSynchronizedWithCurrentItem="True"
                            ScrollViewer.VerticalScrollBarVisibility="Auto"
                            ScrollViewer.HorizontalScrollBarVisibility="Disabled">
                            <ListView.ItemContainerStyle>
                                <Style TargetType="{x:Type ListViewItem}">
                                    <Setter Property="Template">
                                        <Setter.Value>
                                            <ControlTemplate TargetType="{x:Type ListViewItem}">
                                                <RadioButton 
                                                    Style="{StaticResource FlatRadioButtonStyle}"
                                                    Margin="5,2,2,2"
                                                    FontSize="16"
                                                    Content="{Binding Text}"
                                                    IsChecked="{Binding IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
                                                    GroupName="MessagesGroup"
                                                    />
                                            </ControlTemplate>
                                        </Setter.Value>
                                    </Setter>
                                </Style>
                            </ListView.ItemContainerStyle>
                        </ListView>
                    </Border>
                </Popup>
            </Grid>
        </ToggleButton>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Views\MessageSelectionView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Customers.Metro.Reports.Views
{
    /// <summary>
    /// Interaction logic for MessageSelectionView.xaml
    /// </summary>
    public partial class MessageSelectionView : UserControl
    {
        public MessageSelectionView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Views\ReportSettingsView.xaml

<UserControl x:Class="Updk7.Customers.Metro.Reports.Views.ReportSettingsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Customers.Metro.Reports.Views"
             mc:Ignorable="d" 
             xmlns:prism="http://prismlibrary.com/"
             xmlns:converters="clr-namespace:Updk7.Wpf.Converters;assembly=Updk7.Wpf"
             prism:ViewModelLocator.AutoWireViewModel="True"
             xmlns:commands="clr-namespace:Updk7.Wpf.Commands;assembly=Updk7.Wpf">
    <Grid
        Width="45">
        <Grid.RowDefinitions>
            <RowDefinition Height="45" />
            <RowDefinition Height="45" />
            <RowDefinition Height="45" />
            <RowDefinition Height="45" />
        </Grid.RowDefinitions>
        <RadioButton
            Grid.Row="0"
            Style="{StaticResource FlatRectRadioButtonStyle}"
            Content="О"
            FontSize="23"
            FontWeight="Bold"
            ToolTip="Общий отчет по группе тестов"
            IsChecked="{Binding ReportType, Converter={converters:StringToBoolMatchConverter MatchString=Full}}"
            IsEnabled="{Binding Enabled}"
            />
        <RadioButton
            Grid.Row="1"
            Style="{StaticResource FlatRectRadioButtonStyle}"
            Content="Т"
            FontSize="23"
            FontWeight="Bold"
            ToolTip="Талон по группе тестов"
            IsChecked="{Binding ReportType, Converter={converters:StringToBoolMatchConverter MatchString=Brief}}"
            IsEnabled="{Binding Enabled}"
            />
        <Button
            Grid.Row="2"
            Margin="0,2,0,0"
            Style="{StaticResource FlatButtonStyle}"
            FontSize="20"
            FontWeight="Bold"
            ToolTip="Заключение по результатам тестирования"
            Foreground="Black"
            BorderThickness="2"
            Command="{commands:CommandProvider CommandKey=SelectReportMessages}">
            <Image Source="{StaticResource ConclusionImage}" />
        </Button>
        <Button
            Grid.Row="3"
            Margin="0,2,0,0"
            Style="{StaticResource FlatButtonStyle}"
            FontSize="20"
            FontWeight="Bold"
            ToolTip="Редактирование целей и заключений отчетов"
            Foreground="Black"
            BorderThickness="2"
            Command="{commands:CommandProvider CommandKey=EditReportMessages}">
            <Image Source="{StaticResource BlackPencilImage}" />
        </Button>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Views\ReportSettingsView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Customers.Metro.Reports.Views
{
    /// <summary>
    /// Interaction logic for ReportSettingsView.xaml
    /// </summary>
    public partial class ReportSettingsView : UserControl
    {
        public ReportSettingsView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Views\ReportsTreeView.xaml

<UserControl x:Class="Updk7.Customers.Metro.Reports.Views.ReportsTreeView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Customers.Metro.Reports.Views"
             mc:Ignorable="d" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             xmlns:assets="clr-namespace:Updk7.Wpf.Master.Modules.Reports.Views;assembly=Updk7.Wpf.Master.Modules">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <local:ReportSettingsView 
            Grid.Column="0"
            />
        <assets:ReportsTreeView
            Grid.Column="1"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Views\ReportsTreeView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Customers.Metro.Reports.Views
{
    /// <summary>
    /// Interaction logic for ReportsTreeView.xaml
    /// </summary>
    public partial class ReportsTreeView : UserControl
    {
        public ReportsTreeView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Views\SelectReportMessagesView.xaml

<UserControl x:Class="Updk7.Customers.Metro.Reports.Views.SelectReportMessagesView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Customers.Metro.Reports.Views"
             mc:Ignorable="d">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <!--Выбор целей обследования-->
        <TextBlock
            Grid.Row="0"
            Margin="0,3,0,0"
            Text="Цель обследования"
            HorizontalAlignment="Left"
            FontSize="16"
            FontWeight="Medium"
            />
        <local:MessageSelectionView 
            Grid.Row="1"
            Margin="0,2,1,2"
            DataContext="{Binding Goals}"
            />
        <!--Выбор заключения по обследованию-->
        <TextBlock
            Grid.Row="2"
            Margin="0,5,0,0"
            Text="Заключение по обследованию"
            HorizontalAlignment="Left"
            FontSize="16"
            FontWeight="Medium"
            />
        <local:MessageSelectionView 
            Grid.Row="3"
            Margin="0,2,1,2"
            DataContext="{Binding Conclusions}"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Customers\Updk7.Customers.Metro\Updk7.Customers.Metro.Reports\Source\Views\SelectReportMessagesView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Customers.Metro.Reports.Views
{
    /// <summary>
    /// Interaction logic for SelectReportMessagesView.xaml
    /// </summary>
    public partial class SelectReportMessagesView : UserControl
    {
        public SelectReportMessagesView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\ITestingTreeFactory.cs


using System;
using System.Collections.Generic;

namespace Updk7.Data
{
    public interface ITestingTreeFactory
    {
        TestingTreeMetadata GetMetadata();

        TestingTree GetTestingTree();

        TestingTreeGroupNode GetTestingTreeNode(Entities.Testee testee);
    }

    public class TestingTreeFactoryHookPreviewArgs : EventArgs
    {
        public TestingTreeFactoryHookPreviewArgs(Entities.Testee testee, TestingTreeGroupNode node)
        {
            Testee = testee;
            Node = node;
        }

        public Entities.Testee Testee { get; }

        public TestingTreeGroupNode Node { get; }

        public string[] CustomGroupKeys { get; set; }
    }

    public interface ITestingTreeFactoryHook
    {
        event EventHandler<TestingTreeFactoryHookPreviewArgs> NodePreview;

        void SetCustomGroups(IEnumerable<TestingTreeGroupNode> customGroups);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\ITestParametersGenerator.cs


namespace Updk7.Data
{
    public interface ITestParametersGenerator
    {
        Tests.TestParameter[] CreateTestParameters(Entities.Testee testee, Tests.TestType test);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\ITestsParametersFactory.cs


namespace Updk7.Data
{
    public interface ITestsParametersFactory
    {
        Tests.TestParameter[] CreateTestParameters(Entities.Testee testee, Tests.TestType test);

        void Register(Tests.TestType test, ITestParametersGenerator generator);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\PvkTable.cs


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Updk7.Data
{
    public class PvkTestResultRule
    {
        public string Title { get; set; }

        public string ResultKey { get; set; }

        public double? LessThen { get; set; }

        public double? GreaterThen { get; set; }
    }

    public class PvkTestRules
    {
        public PvkTestRules()
        {
        }

        public PvkTestRules(Tests.TestType test, bool ignoreInPvk = false)
        {
            Test = test;
            IgnoreInPvk = ignoreInPvk;
        }

        public Tests.TestType Test { get; set; }

        public bool IgnoreInPvk { get; set; }

        public List<PvkTestResultRule> ResultsRules { get; set; } = new List<PvkTestResultRule>();

        public PvkTestRules AddGreaterThenRule(string title, string key, double value)
        {
            ResultsRules.Add(new PvkTestResultRule()
            {
                Title = title,
                ResultKey = key,
                GreaterThen = value
            });

            return this;
        }

        public PvkTestRules AddLessThenRule(string title, string key, double value)
        {
            ResultsRules.Add(new PvkTestResultRule()
            {
                Title = title,
                ResultKey = key,
                LessThen = value
            });

            return this;
        }
    }

    public class PvkGroupRules
    {
        public PvkGroupRules()
        {
        }

        public PvkGroupRules(string groupKey, List<PvkTestRules> rules)
        {
            TestGroupKey = groupKey;
            TestRules = rules;
        }

        public string TestGroupKey { get; set; }

        public List<PvkTestRules> TestRules { get; set; }
    }

    public class PvkSubnormalResult
    {
        public string RuleTitle { get; internal set; }

        public string ResultKey { get; internal set; }

        public string Value { get; internal set; }

        public bool IgnoreInPvk { get; internal set; }
    }

    public class PvkTestValidationResult
    {
        private List<PvkSubnormalResult> _subnormalResults = new List<PvkSubnormalResult>();

        public Tests.TestType Test { get; internal set; }

        public bool IgnoreInPvk { get; internal set; }

        public IEnumerable<PvkSubnormalResult> SubnormalResults => _subnormalResults;

        // Количество неудовлетворительных результатов в тесте
        public int SubnormalScore => _subnormalResults.Count;

        internal void AddSubnormalResult(PvkSubnormalResult result)
        {
            _subnormalResults.Add(result);
        }
    }

    public class PvkValidationResults
    {
        private List<PvkTestValidationResult> _testValidationResults = new List<PvkTestValidationResult>();

        public string GroupKey { get; internal set; }

        public int? ProfessionalGroup { get; set; }

        public IEnumerable<PvkTestValidationResult> TestValidationResults => _testValidationResults;

        // Количество неудовлетворительных тестов в группе
        public int SubnormalScore { get; private set; }

        public PvkTestValidationResult this[Tests.TestType test] => _testValidationResults
            .Where(t => t.Test == test)
            .FirstOrDefault();

        public bool ContainsResults(Tests.TestType test)
        {
            return _testValidationResults.Any(t => t.Test == test);
        }

        internal void AddValidationResult(PvkTestValidationResult validationResult)
        {
            _testValidationResults.Add(validationResult);
            if (!validationResult.IgnoreInPvk && validationResult.SubnormalScore != 0)
                SubnormalScore += 1;
        }
    }

    internal class SubnormalRange
    {
        public int? Minimum { get; set; }

        public int? Maximum { get; set; }

        public int GroupValue { get; set; }

        internal bool IsInRange(int value)
        {
            if (Minimum.HasValue && Maximum.HasValue)
                return (value >= Minimum.Value) && (value <= Maximum.Value);
            else if (Maximum.HasValue)
                return value <= Maximum.Value;
            else
                return value >= Minimum.Value;
        }
    }

    internal class SurveyExpiryPeriod
    {
        public int GroupValue { get; set; }

        public int DurationInYears { get; set; }
    }

    public class PvkRange
    {
        private List<SubnormalRange> _ranges = new List<SubnormalRange>();
        private List<string> _groupKeys = new List<string>();

        public bool CanAcceptGroup(string groupKey)
        {
            Debug.Assert(!string.IsNullOrEmpty(groupKey), nameof(groupKey));
            return _groupKeys.Any(k => k == groupKey);
        }

        public int GetGroupValue(int subnormalScore)
        {
            Debug.Assert(subnormalScore >= 0, nameof(subnormalScore));
            return _ranges.First(r => r.IsInRange(subnormalScore)).GroupValue;
        }

        internal void AddSubnormalRange(SubnormalRange subnormalRange)
        {
            Debug.Assert(subnormalRange != null, nameof(subnormalRange));
            _ranges.Add(subnormalRange);
        }

        internal void AddGroupKey(string groupKey)
        {
            Debug.Assert(!string.IsNullOrEmpty(groupKey), nameof(groupKey));
            _groupKeys.Add(groupKey);
        }

        internal void AddGroupKeys(IEnumerable<string> groupKeys)
        {
            foreach (var key in groupKeys)
                AddGroupKey(key);
        }
    }

    public class PvkTable
    {
        public static PvkTable Current { get; set; }

        public List<PvkGroupRules> GroupRules { get; set; }

        internal List<SurveyExpiryPeriod> ExpiryPeriods { get; set; }

        public bool CanValidateTestGroup(string groupKey)
        {
            return GroupRules != null
                ? GroupRules.Any(r => r.TestGroupKey == groupKey)
                : false;
        }

        public bool CanValidateTestGroup(TestGroupResults groupResults)
        {
            return CanValidateTestGroup(groupResults.GroupKey);
        }

        public int? GetValidPeriodInYears(int professionalGroup)
        {
            var period = ExpiryPeriods.FirstOrDefault(g => g.GroupValue == professionalGroup);
            return period != null
                ? period.DurationInYears
                : (int?)null;
        }

        public PvkValidationResults ValidateTestGroup(TestGroupResults groupResults)
        {
            if (!CanValidateTestGroup(groupResults))
                return null;

            var groupRules = GroupRules.First(g => g.TestGroupKey == groupResults.GroupKey);
            var groupValidation = new PvkValidationResults();
            groupValidation.GroupKey = groupResults.GroupKey;

            foreach (var testResults in groupResults.TestResults)
            {
                var testRules = groupRules.TestRules.FirstOrDefault(t => t.Test == testResults.Test);
                if (testRules == null)
                    continue;

                var testValidation = new PvkTestValidationResult() { Test = testResults.Test };
                testValidation.IgnoreInPvk = testRules.IgnoreInPvk;

                foreach (var rule in testRules.ResultsRules)
                {
                    var resultKey = rule.ResultKey;
                    var result = testResults[rule.ResultKey];
                    Debug.Assert(result != null, nameof(ValidateTestGroup));

                    var subnormalResult = validateTestResultValue(rule, result);
                    if (subnormalResult != null)
                        testValidation.AddSubnormalResult(subnormalResult);
                }

                groupValidation.AddValidationResult(testValidation);
            }

            return groupValidation;
        }

        private PvkSubnormalResult validateTestResultValue(PvkTestResultRule rule, Tests.TestResultValue value)
        {
            Debug.Assert(value.IsTypeOf<int>() || value.IsTypeOf<float>(), nameof(value));

            Func<string, PvkSubnormalResult> subnormalResult = vstr => new PvkSubnormalResult()
            {
                RuleTitle = rule.Title,
                ResultKey = value.Key,
                Value = vstr
            };

            var dblValue = value.IsTypeOf<int>() ? value.Int : value.Float;
            PvkSubnormalResult result = null;

            if (rule.LessThen.HasValue)
            {
                if (dblValue > rule.LessThen.Value)
                    result = subnormalResult(dblValue.ToString());
            }
            else if (rule.GreaterThen.HasValue)
            {
                if (dblValue < rule.GreaterThen.Value)
                    result = subnormalResult(dblValue.ToString());
            }

            return result;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\TestGroupResults.cs


using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;

namespace Updk7.Data
{
    public class TestGroupResults
    {
        public TestGroupResults()
        {
        }

        public int GroupId { get; set; }

        public string GroupKey { get; set; }

        public string GroupTitle { get; set; }

        public DateTime TestingDate { get; set; }

        public IEnumerable<Tests.TestResults> TestResults { get; set; }

        public bool IsTestGroupClosed(ITestingTreeFactory testingTreeFactory)
        {
            var node = testingTreeFactory.GetTestingTree().GetNode(GroupKey);
            Debug.Assert(node != null && node.TestsCount != 0);

            foreach (var testInfo in node.Tests)
            {
                var results = TestResults.FirstOrDefault(t => t.Test == testInfo.Test);
                if (results == null || results.Values.Count == 0)
                    return false;
            }

            return true;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\TestGroupRun.cs


using System.Collections.Generic;

namespace Updk7.Data
{
    public class TestGroupRun
    {
        private List<TestRun> _tests = new List<TestRun>();

        public TestGroupRun()
        {
        }

        public string GroupKey { get; set; }

        public string GroupTitle { get; set; }

        public IEnumerable<TestRun> Tests => _tests;

        public int TestsCount => _tests.Count;

        public TestGroupRun AddTest(TestRun testRunInfo)
        {
            _tests.Add(testRunInfo);
            return this;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\TestingPlan.cs


using System.Collections.Generic;

namespace Updk7.Data
{
    public class TestingPlan
    {
        private List<TestGroupRun> _testsGroups = new List<TestGroupRun>();

        public TestingPlan()
        {
        }

        public IEnumerable<TestGroupRun> TestsGroups => _testsGroups;

        public int GroupsCount => _testsGroups.Count;

        public TestingPlan AddTestsGroup(TestGroupRun testsGroup)
        {
            _testsGroups.Add(testsGroup);
            return this;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\TestingTree.cs


using System;
using System.Linq;
using System.Collections.Generic;

namespace Updk7.Data
{
    public class TestingTreeTestNode
    {
        public TestingTreeTestNode(Tests.TestType test, bool isOptional)
        {
            Test = test;
            IsOptional = isOptional;
        }

        public Tests.TestType Test { get; }

        public bool IsOptional { get; }
    }

    public class TestingTreeGroupNode
    {
        private List<TestingTreeGroupNode> _nodes = new List<TestingTreeGroupNode>();
        private List<TestingTreeTestNode> _tests = new List<TestingTreeTestNode>();

        public TestingTreeGroupNode(string key, string title)
        {
            Key = key;
            Title = title;
        }

        public string Key { get; }

        public string Title { get; }

        public IEnumerable<TestingTreeGroupNode> Nodes => _nodes;

        public IEnumerable<TestingTreeTestNode> Tests => _tests;

        public int TestsCount => _tests.Count;

        public int NodesCount => _nodes.Count;

        public TestingTreeGroupNode AddTest(Tests.TestType test, bool isOptional = false)
        {
            _tests.Add(new TestingTreeTestNode(test, isOptional));
            return this;
        }

        public TestingTreeGroupNode AddNode(TestingTreeGroupNode node)
        {
            _nodes.Add(node);
            return this;
        }

        public TestingTreeGroupNode AddNodes(IEnumerable<TestingTreeGroupNode> nodes)
        {
            foreach (var node in nodes)
                _nodes.Add(node);
            return this;
        }

        public TestingTreeGroupNode GetNode(string nodeKey)
        {
            if (Key == nodeKey)
                return this;

            return getNode(_nodes, nodeKey);
        }

        private TestingTreeGroupNode getNode(IEnumerable<TestingTreeGroupNode> nodes, string nodeKey)
        {
            foreach (var node in nodes)
            {
                if (node.Key == nodeKey)
                    return node;

                var internalNode = getNode(node.Nodes, nodeKey);
                if (internalNode != null)
                    return internalNode;
            }

            return null;
        }
    }

    public class TestingTree
    {
        private List<TestingTreeGroupNode> _nodes = new List<TestingTreeGroupNode>();
        private List<TestingTreeGroupNode> _oldNodes = new List<TestingTreeGroupNode>();

        public TestingTree(string title)
        {
            Title = title;
        }

        public string Title { get; }

        public IEnumerable<TestingTreeGroupNode> Nodes => _nodes;

        public int NodesCount => _nodes.Count;

        public TestingTree AddNode(TestingTreeGroupNode node)
        {
            _nodes.Add(node);
            return this;
        }

        public TestingTree AddNodes(IEnumerable<TestingTreeGroupNode> nodes)
        {
            foreach (var node in nodes)
                _nodes.Add(node);
            return this;
        }

        public TestingTreeGroupNode GetNode(string nodeKey)
        {
            var nodes = _nodes.Concat(_oldNodes);

            foreach (var node in nodes)
            {
                var result = node.GetNode(nodeKey);
                if (result != null)
                    return result;
            }

            return null;
        }

        public TestingTree AddOldNode(TestingTreeGroupNode node)
        {
            _oldNodes.Add(node);
            return this;
        }

        public static IEnumerable<TestingTreeGroupNode> CreateCommonNodes()
        {
            return CreateCommonNodes(null);
        }

        public static IEnumerable<TestingTreeGroupNode> CreateCommonNodes(IEnumerable<Tests.TestType> excludeTests)
        {
            var pt = new TestingTreeGroupNode("PT", "Психофизиологические тесты");
            var lt = new TestingTreeGroupNode("LT", "Личностные тесты");

            foreach (Tests.TestType test in Enum.GetValues(typeof(Tests.TestType)))
            {
                if (!Tests.Metadata.ContainsInfo(test))
                    continue;

                if (excludeTests != null && excludeTests.Contains(test))
                    continue;

                var metadata = Tests.Metadata.GetTestInfo(test);
                if (metadata.UseExternalDevices)
                    pt.AddTest(test, isOptional: true);
                else
                    lt.AddTest(test, isOptional: true);
            }

            yield return pt;
            yield return lt;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\TestingTreeMetadata.cs


using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json;

namespace Updk7.Data
{
    public class TestingTreeMetadata
    {
        private Dictionary<string, object> _properties = new Dictionary<string, object>();

        public TestingTreeMetadata()
        {
        }

        public TestingTreeMetadata AddProperty(string key, object value)
        {
            _properties[key] = value;
            return this;
        }

        public object GetProperty(string key)
        {
            return _properties[key];
        }

        public string[] GetPropertyKeys()
        {
            return _properties.Keys.ToArray();
        }

        public string ToJson()
        {
            var results = new Dictionary<string, Dictionary<string, string>>();
            foreach (var key in GetPropertyKeys())
            {
                var property = GetProperty(key);
                if (property is Dictionary<string, string>)
                    results[key] = property as Dictionary<string, string>;
                else if (property is Dictionary<int, string>)
                {
                    results[key] = (property as Dictionary<int, string>)
                        .ToDictionary(kvp => kvp.Key.ToString(), kvp => kvp.Value);
                }
                else
                    Debug.Assert(false);
            }

            return JsonSerializer.Serialize(results);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\TestRun.cs


namespace Updk7.Data
{
    public class TestRun
    {
        public TestRun()
        {
        }

        public TestRun(Tests.TestType test)
        {
            TestType = test;
        }

        public Tests.TestType TestType { get; set; }

        public Tests.TestParameter[] Parameters { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\TestsParametersFactory.cs


using System.Collections.Generic;

namespace Updk7.Data
{
    public class TestsParametersFactory : ITestsParametersFactory
    {
        private Dictionary<Tests.TestType, ITestParametersGenerator> _generators =
            new Dictionary<Tests.TestType, ITestParametersGenerator>();

        public TestsParametersFactory()
        {
        }

        public Tests.TestParameter[] CreateTestParameters(Entities.Testee testee, Tests.TestType test)
        {
            return _generators.ContainsKey(test)
                ? _generators[test].CreateTestParameters(testee, test)
                : null;
        }

        public void Register(Tests.TestType test, ITestParametersGenerator generator)
        {
            _generators[test] = generator;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Commands\CommandNames.cs


namespace Updk7.Data.Commands
{
    public static class CommandNames
    {
        public static readonly string EntitieCud = "EntitieCud";
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Commands\CommandSerializer.cs


using System.Text.Json;

namespace Updk7.Data.Commands
{
    public class CommandSerializer
    {
        public static byte[] Serialize<TCommand>(TCommand command) where TCommand : UpdkCommand
        {
            return JsonSerializer.SerializeToUtf8Bytes(command, command.GetType());
        }

        public static UpdkCommand Deserialize(byte[] buffer)
        {
            var deserializedCommand = JsonSerializer.Deserialize(buffer, typeof(UpdkCommand))
                as UpdkCommand;

            if (deserializedCommand.CommandName == CommandNames.EntitieCud)
            {
                return JsonSerializer.Deserialize(buffer, typeof(EntitieCudCommand)) 
                    as EntitieCudCommand;
            }

            return deserializedCommand;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Commands\EntitieCudCommand.cs


namespace Updk7.Data.Commands
{
    public class EntitieCudCommand : UpdkCommand
    {
        public EntitieCudCommand()
        {
        }

        public EntitieCudCommand(string operation)
        {
            CommandName = CommandNames.EntitieCud;
            Operation = operation;
        }

        public string Operation { get; set; }

        public static EntitieCudCommand Create(object entitie, string operation)
        {
            var command = new EntitieCudCommand(operation);
            command.Payload = Entities.EntitieSerializer.Serialize(entitie);
            return command;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Commands\UpdkCommand.cs


namespace Updk7.Data.Commands
{
    public class UpdkCommand
    {
        public string CommandName { get; set; }

        public byte[] Payload { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\EntitieSerializer.cs


using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json;

namespace Updk7.Data.Entities
{
    public static class EntitieSerializer
    {
        public class SerializedData
        {
            public SerializedData()
            {
            }

            public SerializedData(object entitie)
            {
                EntitieType = entitie.GetType().Name;
                Entite = entitie;
            }

            public string EntitieType { get; set; }

            public object Entite { get; set; }
        }

        private static Dictionary<string, Type> _entitieTypes = new Dictionary<string, Type>();

        static EntitieSerializer()
        {
            RegisterEntitie<SerializedData[]>();
            RegisterEntitie<User>();
            RegisterEntitie<TestGroup>();
            RegisterEntitie<TestResults>();
            RegisterEntitie<Photo>();
            RegisterEntitie<StorageInfo>();
            RegisterEntitie<Rzd.RzdTestee>();
            RegisterEntitie<AutoPro.AutoProTestee>();
        }

        /// <summary>
        /// Регистрация допустимого типа для сериализатора
        /// </summary>
        /// <remarks>
        /// Зарегистрированные типы используются для десериализации объектов из массива байт и 
        /// контроле сериализуемых типов в отладочном режиме
        /// </remarks>
        /// <typeparam name="TEntitie">Тип сериализуемого объекта</typeparam>
        public static void RegisterEntitie<TEntitie>() where TEntitie: class
        {
            var type = typeof(TEntitie);
            _entitieTypes[type.Name] = type;
        }

        public static byte[] Serialize(object[] entities)
        {
            var objects = entities.Select(e => new SerializedData(e)).ToArray();
            return Serialize(objects as object);
        }

        public static byte[] Serialize(object entitie)
        {
            var data = new SerializedData(entitie);
            Debug.Assert(_entitieTypes.ContainsKey(data.EntitieType));
            return JsonSerializer.SerializeToUtf8Bytes(data);
        }

        public static object Deserialize(byte[] data)
        {
            var rawData = JsonSerializer.Deserialize<SerializedData>(data);
            var deserialized = deserialize(rawData);
            if (rawData.EntitieType == typeof(SerializedData[]).Name)
            {
                var arrayData = deserialized as SerializedData[];
                return arrayData.Select(e => deserialize(e)).ToArray();
            }

            return deserialized;
        }

        private static object deserialize(SerializedData data)
        {
            Debug.Assert(_entitieTypes.ContainsKey(data.EntitieType));
            var jsonElement = (JsonElement)data.Entite;
            return JsonSerializer.Deserialize(jsonElement.GetRawText(), _entitieTypes[data.EntitieType]);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\GenderType.cs


namespace Updk7.Data.Entities
{
    public enum GenderType
    {
        Male = 1,
        Female
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\Person.cs


using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Updk7.Data.Entities
{
    public abstract class Person
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [Required(AllowEmptyStrings = false)]
        [StringLength(256)]
        public string FullName { get; set; }

        public bool Removed { get; set; }

        public DateTime RegistrationDate { get; set; }

        public DateTime? RemoveDate { get; set; }

        public string GetShortName()
        {
            if (string.IsNullOrEmpty(FullName))
                return string.Empty;

            var nameParts = FullName.Split(' ');
            var lastName = nameParts[0];
            var firstName = nameParts.Length > 1 ? nameParts[1] : string.Empty;
            var patronymic = nameParts.Length == 3 ? nameParts[2] : string.Empty;

            var name = $"{lastName} {firstName[0]}.";
            if (!string.IsNullOrEmpty(patronymic))
                name += $" {patronymic[0]}.";

            return name.Trim();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\Photo.cs


using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Updk7.Data.Entities
{
    [Table("TesteesPhoto")]
    public class Photo
    {
        [Key]
        public int Id { get; set; }

        public byte[] ImageData { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\ReportMessage.cs


using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Updk7.Data.Entities
{
    public enum ReportMessageType
    {
        Goal = 1,
        Conclusion
    }

    public class ReportMessage
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        public ReportMessageType MessageType { get; set; } = ReportMessageType.Goal;

        // Флаг возможности выбора сообщения пользователем (false - сообщение не показывается в списке)
        public bool CanBeSet { get; set; } = true;

        // Сообщение используется в отчетах и не может быть удалено
        public bool Locked { get; set; } = false;

        public DateTime RegistrationDate { get; set; } = DateTime.Now;

        [Required(AllowEmptyStrings = false)]
        public string Text { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\ReportMessageMapping.cs


using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Updk7.Data.Entities
{
    public class ReportMessageMapping
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [ForeignKey(nameof(Message))]
        public int MessageId { get; set; }

        [ForeignKey(nameof(TestGroup))]
        public int TestGroupId { get; set; }

        [System.Text.Json.Serialization.JsonIgnore()]
        public virtual ReportMessage Message { get; set; }

        [System.Text.Json.Serialization.JsonIgnore()]
        public virtual TestGroup TestGroup { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\StorageInfo.cs


using System;
using System.ComponentModel.DataAnnotations.Schema;

namespace Updk7.Data.Entities
{
    [Table("StorageInfo")]
    public class StorageInfo
    {
        public string Version { get; set; }

        public DateTime Date { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\Testee.cs


using System;

namespace Updk7.Data.Entities
{
    public abstract class Testee : Person
    {
        public GenderType Gender { get; set; } = GenderType.Male;

        public DateTime BirthDate { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\TesteePinPair.cs


using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Updk7.Data.Entities
{
    public class TesteePinPair
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        public int TesteeId1 { get; set; }

        public int TesteeId2 { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\TesteeRailroad.cs


using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Updk7.Data.Entities
{
    public abstract class TesteeRailroad : Testee
    {
        public int ProfessionId { get; set; }

        public int TrafficId { get; set; }

        [Required(AllowEmptyStrings = true)]
        public string Column { get; set; }

        [Required(AllowEmptyStrings = true)]
        public string TC { get; set; }

        // Дата вступления в должность
        public DateTime? EmploymentDate { get; set; }

        [Required(AllowEmptyStrings = true)]
        public string VulnerablePVK { get; set; }

        public int? PVKGroup { get; set; }

        public DateTime? ExaminationDate { get; set; }

        public DateTime? NextExaminationDate { get; set; }

        [ForeignKey(nameof(ExaminationUser))]
        public int? ExaminationUserId { get; set; }

        [System.Text.Json.Serialization.JsonIgnore()]
        public virtual User ExaminationUser { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\TestGroup.cs


using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Updk7.Data.Entities
{
    [Table("TestGroups")]
    public class TestGroup
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [Required]
        [DataType(DataType.DateTime)]
        public DateTime TestingDate { get; set; }

        [Required(AllowEmptyStrings = false)]
        [StringLength(8)]
        public string GroupKey { get; set; }

        [ForeignKey(nameof(Testee))]
        public int TesteeId { get; set; }

        [ForeignKey(nameof(User))]
        public int UserId { get; set; }

        [System.Text.Json.Serialization.JsonIgnore()]
        public virtual Testee Testee { get; set; }

        [System.Text.Json.Serialization.JsonIgnore()]
        public virtual User User { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\TestResults.cs


using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Updk7.Data.Entities
{
    [Table("TestResults")]
    public class TestResults
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [ForeignKey(nameof(TestGroup))]
        public int TestGroupId { get; set; }

        public Tests.TestType TestType { get; set; }

        public byte[] SerializedData { get; set; }

        [System.Text.Json.Serialization.JsonIgnore()]
        public virtual TestGroup TestGroup { get; set; }

        private Tests.TestResults _data;

        [NotMapped]
        [System.Text.Json.Serialization.JsonIgnore()]
        public Tests.TestResults Data
        {
            get
            {
                if (_data == null && SerializedData != null)
                    _data = Tests.TestResultsSerializer.Deserialize(SerializedData);
                return _data;
            }
            set
            { 
                if (value != null)
                    SerializedData = Tests.TestResultsSerializer.Serialize(value);
                _data = value;
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\User.cs


using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Updk7.Data.Entities
{
    [Table("Users")]
    public class User : Person
    {
        [Required(AllowEmptyStrings = false)]
        [StringLength(16)]
        public string Role { get; set; }

        [Required(AllowEmptyStrings = false)]
        public string Password { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\UserRoles.cs


using System.Collections.Generic;

namespace Updk7.Data.Entities
{
    public static class UserRoles
    {
        public static readonly string Admin = "admin";
        public static readonly string Psycho = "psycho";

        public static IEnumerable<string> AllRoles
        {
            get
            {
                yield return Admin;
                yield return Psycho;
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\AutoPro\AutoProTestee.cs


using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Updk7.Data.Entities.AutoPro
{
    [Table("Testees")]
    public class AutoProTestee : Testee
    {
        public AutoProTestee()
        {
        }

        public int ProfessionId { get; set; }

        public string TrafficType { get; set; }

        [Required(AllowEmptyStrings = false)]
        [StringLength(64)]
        public string DriveCategory { get; set; }

        public int? PvkGroup { get; set; }

        [Required(AllowEmptyStrings = true)]
        public string Column { get; set; }

        [DataType(DataType.Date)]
        public DateTime? ExaminationDate { get; set; }

        [ForeignKey(nameof(User))]
        public int? UserId { get; set; }

        [System.Text.Json.Serialization.JsonIgnore()]
        public virtual User User { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\Metro\MetroTestee.cs


using System;

namespace Updk7.Data.Entities.Metro
{
    public enum MaritalStatus
    {
        NotSet,
        Single,
        Married
    }

    public enum EducationGrade
    {
        NotSet,
        MiddleProf,     // Среднее профессиональное
        MiddleTech,     // Среднее техническое
        MiddleCommon,   // Среднее общее
        IncompleteHigh, // Незаконченное высшее
        High            // Высшее
    }

    public class MetroTestee : TesteeRailroad
    {
        // Начало трудового стажа
        public DateTime? StartExperienceDate { get; set; }

        public MaritalStatus MartialStatus { get; set; } = MaritalStatus.NotSet;

        public EducationGrade EducationGrade { get; set; } = EducationGrade.NotSet;
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Entities\Rzd\RzdTestee.cs


using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Updk7.Data.Entities.Rzd
{
    public enum MaritalStatus
    {
        NotSet,
        Single,
        Married
    }

    public enum EducationGrade
    {
        NotSet,
        MiddleProf,     // Среднее профессиональное
        MiddleTech,     // Среднее техническое
        MiddleCommon,   // Среднее общее
        IncompleteHigh, // Незаконченное высшее
        High            // Высшее
    }

    [Table("Testees")]
    public class RzdTestee : TesteeRailroad
    {
        public RzdTestee()
        {
        }

        // Начало трудового стажа
        public DateTime? StartExperienceDate { get; set; }

        [Required(AllowEmptyStrings = true)]
        public string PersonnalNumber { get; set; }

        public MaritalStatus MartialStatus { get; set; } = MaritalStatus.NotSet;

        public EducationGrade EducationGrade { get; set; } = EducationGrade.NotSet;
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Repositories\Backup.cs


using System;
using System.Linq;
using System.IO;

namespace Updk7.Data.Repositories
{
    public static class Backup
    {
        public static void Create(string dbFolder, string backupFolder)
        {
            copyfiles(dbFolder, backupFolder);
            cleanupFolder(backupFolder);
        }

        private static void copyfiles(string dbFolder, string backupFolder)
        {
            var dbFiles = Directory.GetFiles(dbFolder, "*.db");
            var timestamp = DateTime.Now.ToString("yyyy-MM-dd");

            foreach (var file in dbFiles)
            {
                var backupFile = $@"{backupFolder}\{Path.GetFileNameWithoutExtension(file)}-{timestamp}.db";
                if (File.Exists(backupFile))
                    continue;

                File.Copy(file, backupFile);
            }
        }

        private static void cleanupFolder(string backupFolder)
        {
            var allFiles = Directory.GetFiles(backupFolder, "*.db").OrderBy(f => f).ToArray();
            var dbPhoto = allFiles.Where(f => f.Contains("Photos")).ToArray();
            var dbData = allFiles.Except(dbPhoto).ToArray();

            deleteFiles(dbPhoto, 5);
            deleteFiles(dbData, 5);
        }

        private static void deleteFiles(string[] files, int reminingFiles)
        {
            for (int i = reminingFiles; i < files.Length; i++)
                File.Delete(files[i]);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Repositories\IPinnedTesteesRepository.cs


namespace Updk7.Data.Repositories
{
    public interface IPinnedTesteesRepository : IRepository
    {
        void SetPin(int testeeId1, int testeeId2);

        void RemovePin(int testeeId);

        int? GetTesteePin(int testeeId);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Repositories\IReportMessagesRepository.cs


using System.Collections.Generic;

namespace Updk7.Data.Repositories
{
    public interface IReportMessagesRepository : IRepository
    {
        void Add(Entities.ReportMessage message);

        void Add(Entities.ReportMessageMapping mapping);

        IEnumerable<Entities.ReportMessage> GetMessages(int testGroupId);

        IEnumerable<Entities.ReportMessage> GetMessages();

        IEnumerable<Entities.ReportMessageMapping> GetMappings(int testGroupId);

        void ClearMappings(int testGroupId);

        void Update(Entities.ReportMessage message);

        void Remove(Entities.ReportMessage message);

        void Remove(Entities.ReportMessageMapping mapping);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Repositories\IRepository.cs


using System;
using System.Collections.Generic;

namespace Updk7.Data.Repositories
{
    public interface IRepository
    {
        event EventHandler<RepositoryDataChangedEventArgs> DataChanged;

        IEnumerable<Type> EntitieTypes { get; }

        void Add(object entitie);

        void Update(object entitie);

        void Remove(object entitie);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Repositories\ITesteesPhotoRepository.cs


namespace Updk7.Data.Repositories
{
    public interface ITesteesPhotoRepository
    {
        byte[] GetImageData(int testeeId);

        void SetImageData(int testeeId, byte[] imageData);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Repositories\ITesteesRepository.cs


using System.Collections.Generic;

namespace Updk7.Data.Repositories
{
    public interface ITesteesRepository : IRepository
    {
        ITesteesPhotoRepository Photos { get; }

        void Add(Entities.Testee item);

        IEnumerable<Entities.Testee> Get();

        Entities.Testee GetById(int id);

        IEnumerable<Entities.Testee> Get(bool removed);

        void Remove(Entities.Testee item);

        void Update(Entities.Testee item);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Repositories\ITestResultsRepository.cs


using System.Collections.Generic;

namespace Updk7.Data.Repositories
{
    public interface ITestResultsRepository : IRepository
    {
        IEnumerable<Entities.TestGroup> GetGroups(int testeeId);

        Entities.TestGroup GetGroup(int groupId);

        IEnumerable<Entities.TestResults> GetGroupResults(int groupId);

        Entities.TestResults GetTestResults(int testId);

        void Add(Entities.TestGroup testGroup);

        void Add(Entities.TestResults testResults);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Repositories\IUsersRepository.cs


using System.Collections.Generic;

namespace Updk7.Data.Repositories
{
    public interface IUsersRepository : IRepository
    {
        void Add(Entities.User item);

        IEnumerable<Entities.User> Get();

        Entities.User GetById(int id);

        IEnumerable<Entities.User> Get(bool removed);

        void Remove(Entities.User item);

        void Update(Entities.User item);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Repositories\RepositoryAggregator.cs


using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;

namespace Updk7.Data.Repositories
{
    public class RepositoryAggregator : IRepository
    {
        private List<IRepository> _repositories = new List<IRepository>();
        private HashSet<Type> _entitieTypes = new HashSet<Type>();

        public RepositoryAggregator()
        {
        }

        public IEnumerable<Type> EntitieTypes { get; private set; }

        public event EventHandler<RepositoryDataChangedEventArgs> DataChanged;

        public RepositoryAggregator AddRepository(IRepository repository)
        {
            Debug.Assert(!repository.EntitieTypes.Any(t => _entitieTypes.Contains(t)));

            foreach (var type in repository.EntitieTypes)
                _entitieTypes.Add(type);

            EntitieTypes = _entitieTypes.ToArray();
            repository.DataChanged += (o, e) => DataChanged?.Invoke(this, e);
            _repositories.Add(repository);

            return this;
        }

        public void AddRepositories(IEnumerable<IRepository> repositories)
        {
            foreach (var repository in repositories)
                AddRepository(repository);
        }

        public void Add(object entitie)
        {
            getRepository(entitie.GetType()).Add(entitie);
        }

        public void Remove(object entitie)
        {
            getRepository(entitie.GetType()).Remove(entitie);
        }

        public void Update(object entitie)
        {
            getRepository(entitie.GetType()).Update(entitie);
        }

        private IRepository getRepository(Type entitieType)
        {
            return _repositories.First(r => r.EntitieTypes.Any(t => t == entitieType));
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Repositories\RepositoryDataChangedEventArgs.cs


namespace Updk7.Data.Repositories
{
    public enum RepositoryDataChangedOperation
    {
        Create,
        Update,
        Remove
    }

    public class RepositoryDataChangedEventArgs
    {
        public RepositoryDataChangedEventArgs(object entity, RepositoryDataChangedOperation operation)
        {
            Entity = entity;
            Operation = operation;
        }

        public object Entity { get; }

        public RepositoryDataChangedOperation Operation { get; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Repositories\UpdkRepositories.cs


using System.Collections.Generic;

namespace Updk7.Data.Repositories
{
    public class UpdkRepositories
    {
        public UpdkRepositories(ITesteesRepository testees, 
            ITestResultsRepository results, 
            IUsersRepository users)
        {
            Testees = testees;
            Results = results;
            Users = users;
        }

        public virtual IEnumerable<IRepository> All
        {
            get
            {
                yield return Users;
                yield return Testees;
                yield return Results;
            }
        }

        public ITesteesRepository Testees { get; }

        public ITestResultsRepository Results { get; }

        public IUsersRepository Users { get; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Validation\TestGroupValidation.cs


using System;
using System.Linq;
using System.Collections.Generic;

namespace Updk7.Data.Validation
{
    public class TestGroupValidationResults
    {
        public TestGroupValidationResults(TestGroupResults groupResults, 
            IEnumerable<TestValidationResult> failedTests)
        {
            ExaminatinDate = groupResults.TestingDate;
            GroupResults = groupResults;
            GroupKey = groupResults.GroupKey;
            FailedTests = failedTests;
        }

        public TestGroupResults GroupResults { get; }

        public string GroupKey { get; }

        public DateTime ExaminatinDate { get; internal set; }

        public DateTime? NextExaminationDate { get; internal set; }

        public int? ProfessionalGroup { get; internal set; }

        public string Conclusion { get; internal set; }

        public IEnumerable<TestValidationResult> FailedTests { get; }

        public int FailedTestsCount => FailedTests.Count();

        public bool IsFailedTest(Tests.TestType test)
        {
            return FailedTests.Any(ft => ft.Test == test);
        }

        public bool IsFailedResult(Tests.TestType test, string resultKey)
        {
            var failedTest = FailedTests.FirstOrDefault(ft => ft.Test == test);
            return failedTest != null && failedTest.IsFailedResult(resultKey);
        }

        public string GetFailedTestsString(Tests.TestType excludeTest)
        {
            var excludeSet = new HashSet<Tests.TestType>();
            excludeSet.Add(excludeTest);

            return GetFailedTestsString(excludeSet);
        }

        public string GetFailedTestsString(HashSet<Tests.TestType> excludeTests = null)
        {
            Func<TestValidationResult, string> short_name = vr => Tests.Metadata.GetTestShortName(vr.Test);
            var failedTests = FailedTests;
            if (excludeTests != null)
                failedTests = FailedTests.Where(t => !excludeTests.Contains(t.Test)).ToArray();

            return failedTests.Aggregate(string.Empty, (acc, v) =>
                string.IsNullOrEmpty(acc)
                    ? short_name(v)
                    : $"{acc}, {short_name(v)}");
        }
    }

    public class TestGroupValidation
    {
        private List<TestValidation> _validationRules = new List<TestValidation>();

        public TestGroupValidation(string groupKey)
        {
            GroupKey = groupKey;
        }

        public string GroupKey { get; }

        public IEnumerable<TestValidation> ValidationRules => _validationRules;

        public TestGroupValidation AddRule(TestValidation rule)
        {
            _validationRules.Add(rule);
            return this;
        }

        public TestGroupValidation AddRules(params TestValidation[] rules)
        {
            foreach (var rule in rules)
                AddRule(rule);

            return this;
        }

        public TestValidation GetTestValidationRules(Tests.TestType test)
        {
            return _validationRules.FirstOrDefault(vr => vr.Test == test);
        }

        public TestGroupValidationResults Validate(TestGroupResults groupResults)
        {
            var failedTests = new List<TestValidationResult>();

            foreach (var testResults in groupResults.TestResults)
            {
                var rule = GetTestValidationRules(testResults.Test);
                if (rule != null)
                {
                    var validationResults = rule.Validate(testResults);
                    if (validationResults.FailedResultsCount != 0)
                        failedTests.Add(validationResults);
                }
            }

            return new TestGroupValidationResults(groupResults, failedTests);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Validation\TestResultValueValidation.cs


using System;
using System.Diagnostics;

namespace Updk7.Data.Validation
{
    public class TestResultValueValidation
    {
        private double _boundaryValue;
        private string _normalRelation;

        public TestResultValueValidation(string resultKey, double boundaryValue, string normalRelation)
        {
            ResultKey = resultKey;
            _boundaryValue = boundaryValue;
            _normalRelation = normalRelation;
        }

        public string ResultKey { get; }

        public bool IsResultValid(Tests.TestResultValue resultValue)
        {
            Debug.Assert(resultValue.Key == ResultKey);
            Debug.Assert(resultValue.IsTypeOf<int>() || resultValue.IsTypeOf<float>());

            var value = 0.0;
            if (resultValue.ValueType == Tests.TestResultValueType.Int)
                value = resultValue.Int;
            else if (resultValue.ValueType == Tests.TestResultValueType.Float)
                value = resultValue.Float;
            else
                throw new InvalidOperationException();

            switch (_normalRelation)
            {
                case "<": return value < _boundaryValue;
                case "<=": return value <= _boundaryValue;
                case ">": return value > _boundaryValue;
                case ">=": return value >= _boundaryValue;
                default:
                    throw new InvalidOperationException();
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Validation\TestValidation.cs


using System.Linq;
using System.Collections.Generic;

namespace Updk7.Data.Validation
{
    public class TestValidationResult
    {
        public TestValidationResult(Tests.TestType test, IEnumerable<Tests.TestResultValue> failedResults)
        {
            Test = test;
            FailedResults = failedResults;
        }

        public Tests.TestType Test { get; }

        public IEnumerable<Tests.TestResultValue> FailedResults { get; }

        public int FailedResultsCount => FailedResults.Count();

        public bool IsFailedResult(string resultKey)
        {
            return FailedResults.Any(fr => fr.Key == resultKey);
        }
    }

    public class TestValidation
    {
        private List<TestResultValueValidation> _validationRules = new List<TestResultValueValidation>();

        public TestValidation(Tests.TestType test)
        {
            Test = test;
        }

        public Tests.TestType Test { get; }

        public IEnumerable<TestResultValueValidation> ValidationRules => _validationRules;

        public TestValidation AddRule(TestResultValueValidation resultValueValidation)
        {
            _validationRules.Add(resultValueValidation);
            return this;
        }

        public TestValidation AddRule(string resultKey, double boundaryValue, string normalRelation)
        {
            return AddRule(new TestResultValueValidation(resultKey, boundaryValue, normalRelation));
        }

        public bool ContainsRule(string resultKey)
        {
            return _validationRules.Any(r => r.ResultKey == resultKey);
        }

        public TestResultValueValidation GetValidationRule(string resultKey)
        {
            return _validationRules.FirstOrDefault(r => r.ResultKey == resultKey);
        }

        public TestValidationResult Validate(Tests.TestResults testResults)
        {
            var failedResults = new List<Tests.TestResultValue>();

            foreach (var result in testResults.Values)
            {
                var rule = _validationRules.FirstOrDefault(r => r.ResultKey == result.Key);
                if (rule != null && !rule.IsResultValid(result))
                    failedResults.Add(result);
            }

            return new TestValidationResult(testResults.Test, failedResults);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Validation\Metro\PvkValidator.cs


using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;

namespace Updk7.Data.Validation.Metro
{
    public class PvkValidator
    {
        private List<TestGroupValidation> _groupRules = new List<TestGroupValidation>();
        private ITestingTreeFactory _testingTreeFactory;

        public PvkValidator(ITestingTreeFactory testingTreeFactory)
        {
            _testingTreeFactory = testingTreeFactory;
            createValidationRules();
        }

        public bool CanValidate(string groupKey)
        {
            return _groupRules.Any(g => g.GroupKey == groupKey);
        }

        public TestGroupValidationResults Validate(TestGroupResults groupResults, Entities.Metro.MetroTestee testee)
        {
            Debug.Assert(groupResults != null);
            Debug.Assert(testee != null);

            if (!CanValidate(groupResults.GroupKey))
                return null;

            var validator = GetValidator(groupResults.GroupKey);
            var validationResults = validator.Validate(groupResults);
            var isClosed = groupResults.IsTestGroupClosed(_testingTreeFactory);
            updateProfessionalGroup(groupResults, validationResults, testee, isClosed);

            return validationResults;
        }

        private void updateProfessionalGroup(TestGroupResults groupResults,
            TestGroupValidationResults validationResults,
            Entities.Metro.MetroTestee testee,
            bool isClosed)
        {
            var group = validationResults.GroupKey;
            var failedTestsCount = validationResults.FailedTests
                .Where(r => r.Test != Tests.TestType.Готовность_к_тестированию_ЭПФС)
                .Count();

            if (group != "A2" && !isClosed)
            {
                validationResults.ProfessionalGroup = null;
                validationResults.NextExaminationDate = null;
                validationResults.Conclusion = "Тестирование проведено не в полном объеме";
            }
            else if (group == "A2")
            {
                // Дополнительная диагностика валидируется, но не используется для присвоения группы
                validationResults.ProfessionalGroup = null;
                validationResults.NextExaminationDate = null;
                validationResults.Conclusion = string.Empty;

                var containsGkt = groupResults.TestResults.Any(t => t.Test == Tests.TestType.Готовность_к_тестированию_ЭПФС);
                var gtkFailed = validationResults.IsFailedTest(Tests.TestType.Готовность_к_тестированию_ЭПФС);
                var containsKv = groupResults.TestResults.Any(t => t.Test == Tests.TestType.Концентрация_внимания_КВ);
                var kvFailed = validationResults.IsFailedTest(Tests.TestType.Концентрация_внимания_КВ);

                if (containsGkt && containsKv && !gtkFailed && !kvFailed)
                    validationResults.Conclusion = "Достаточные функциональные возможности организма";
                else if ((containsGkt || containsKv) && (gtkFailed || kvFailed))
                {
                    validationResults.Conclusion = "Тенденция к снижению функциональных возможностей организма." +
                        "Медицинскому психологу рекомендовано: контроль за психофизиологическими показателями РЛБ и его текущим ФС";
                }
            }
            else if (failedTestsCount == 0)
            {
                validationResults.ProfessionalGroup = 1;
                validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(2);
                validationResults.Conclusion = "Профессионально-важные качества в норме. Переосвидетельствование через 2 года.";
            }
            else
            {
                validationResults.ProfessionalGroup = 2;
                validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(1);
                validationResults.Conclusion = "Показатели профессионально-важных качеств ниже нормы. Переосвидетельствование через 1 год.";
            }
        }

        public TestGroupValidation GetValidator(string groupKey)
        {
            return _groupRules.FirstOrDefault(v => v.GroupKey == groupKey);
        }

        private void createValidationRules()
        {
            var epfs = new TestValidation(Tests.TestType.Готовность_к_тестированию_ЭПФС)
                .AddRule("Общее количество ошибок", 4, "<=");

            var kv = new TestValidation(Tests.TestType.Концентрация_внимания_КВ)
                 .AddRule("Количество просмотренных строк", 30, ">=")
                 .AddRule("Допущено ошибок", 5, "<=");

            var ob = new TestValidation(Tests.TestType.Оценка_бдительности_ОБ)
                .AddRule("Среднеарифметическое время реагирования на красные сигналы", 0.78, "<=")
                .AddRule("Количество пропусков сигналов с предупреждением", 1, "<=")
                .AddRule("Показатель бдительности", 0.13, "<=")
                .AddRule("Общее количество пропусков", 3, "<=")
                .AddRule("Общее количество неправильных нажатий", 4, "<=");

            var og = new TestValidation(Tests.TestType.Оценка_глазомера_ОГ)
                .AddRule("Средний процент соотношения линий", 16, "<=");

            var eu = new TestValidation(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
                .AddRule("Количество ошибок без помехи (N1)", 3, "<=")
                .AddRule("Количество пропусков без помехи", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования без помехи (ВР1)", 1.35, "<=")
                .AddRule("Количество ошибок с помехой (N2)", 6, "<=")
                .AddRule("Количество пропусков с помехой", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования с помехой (ВР2)", 1.7, "<=")
                .AddRule("Разница среднеарифметических времен реагирования (ВР2 - ВР1)", 0.35, "<=")
                .AddRule("Разница количества ошибок с помехой и без помехи (N2 - N1)", 4, "<=");

            var rv = new TestValidation(Tests.TestType.Распределение_внимания_РВ)
                .AddRule("Среднее время реагирования в задании №1", 0.65, "<=")
                .AddRule("Количество правильных ответов на зрительные стимулы в задании №1", 17, ">=")
                .AddRule("Разница средних времен реагирования между заданием №2 и заданием №1", 0.19, "<=")
                .AddRule("Количество правильных ответов на слуховые стимулы в задании №2", 9, ">=")
                .AddRule("Количество ошибочных ответов на слуховые стимулы", 3, "<=")
                .AddRule("Разница количества правильных ответов на зрительные стимулы (№1 - №2)", 3, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 1 на зрительные стимулы", 5, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 2 на зрительные стимулы", 5, "<=");

            var osr = new TestValidation(Tests.TestType.Оценка_склонности_к_риску_ОСР)
                .AddRule("Количество баллов", 7, "<=");

            var uvsr = new TestValidation(Tests.TestType.Уровень_восприятия_скорости_и_расстояния_УВСР)
                .AddRule("Количество точных попаданий", 6, ">=");

            _groupRules.Add(new TestGroupValidation("A1").AddRules(epfs, kv, ob, og, eu, rv, osr, uvsr));
            _groupRules.Add(new TestGroupValidation("B1").AddRules(epfs, kv, ob, og, eu, rv, osr, uvsr));
            _groupRules.Add(new TestGroupValidation("C1").AddRules(epfs, kv, ob, og, eu, rv, osr, uvsr));
            _groupRules.Add(new TestGroupValidation("D1").AddRules(epfs, kv, ob, og, eu, rv, osr, uvsr));
            _groupRules.Add(new TestGroupValidation("A2").AddRules(epfs, kv, ob, og, eu, rv, osr, uvsr));
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Validation\Rzd\IOL.cs


using System;
using System.Linq;
using System.Diagnostics;

namespace Updk7.Data.Validation.Rzd
{
    public static class IOL
    {
        public static bool ContainsIOL(TestGroupResults groupResults)
        {
            Func<Tests.TestType, bool> containsTest = t => groupResults.TestResults.Any(r => r.Test == t);
            return containsTest(Tests.TestType.Аддикция_и_аддиктивное_поведение)
                && containsTest(Tests.TestType.Адаптивность)
                && containsTest(Tests.TestType.Тест_Уровень_субъективного_контроля_УСК);
        }

        public static bool IsNorma(double value)
        {
            return value >= 1.3;
        }

        public static double? Calculate(TestGroupResults groupResults)
        {
            Debug.Assert(ContainsIOL(groupResults));

            try
            {
                var addiction = groupResults.TestResults.FirstOrDefault(t => t.Test == Tests.TestType.Аддикция_и_аддиктивное_поведение);
                var up = calculateUP(addiction);
                var sv = calculateSV(addiction);

                var adaptivity = groupResults.TestResults.FirstOrDefault(t => t.Test == Tests.TestType.Адаптивность);
                var asv = calculateAS(adaptivity);
                var npu = calculateNPU(adaptivity);
                var kot = calculateKOT(adaptivity);
                var mn = calculateMN(adaptivity);

                var usk = groupResults.TestResults.FirstOrDefault(t => t.Test == Tests.TestType.Тест_Уровень_субъективного_контроля_УСК);
                var io = calculateIO(usk);
                var id = calculateID(usk);
                var inv = calculateIN(usk);
                var ipo = calculateIPO(usk);

                return (up + sv + asv + npu + kot + mn + io + id + inv + ipo) / 10.0;
            }
            catch (Exception)
            {
                return null;
            }
        }

        private static double calculateIPO(Tests.TestResults results)
        {
            var value = results["Интернальность в области производственных отношений"].Int;
            if (value == 10)
                return 3.0;
            else if (value == 9)
                return 2.5;
            else if (value >= 7)
                return 2.0;
            else if (value >= 5)
                return 1.5;
            else if (value == 4)
                return 1.0;
            else if (value >= 2)
                return 0.5;
            else
                return 0.0;
        }

        private static double calculateIN(Tests.TestResults results)
        {
            var value = results["Интернальность в ситуации неудачи"].Int;
            if (value == 10)
                return 3.0;
            else if (value == 9)
                return 2.5;
            else if (value >= 7)
                return 2.0;
            else if (value >= 5)
                return 1.5;
            else if (value == 4)
                return 1.0;
            else if (value >= 2)
                return 0.5;
            else
                return 0.0;
        }

        private static double calculateID(Tests.TestResults results)
        {
            var value = results["Интернальность в области достижений"].Int;
            if (value == 10)
                return 3.0;
            else if (value == 9)
                return 2.5;
            else if (value >= 7)
                return 2.0;
            else if (value >= 5)
                return 1.5;
            else if (value == 4)
                return 1.0;
            else if (value >= 2)
                return 0.5;
            else
                return 0.0;
        }

        private static double calculateIO(Tests.TestResults results)
        {
            var value = results["Общая интернальность"].Int;
            if (value == 10)
                return 3.0;
            else if (value == 9)
                return 2.5;
            else if (value >= 7)
                return 2.0;
            else if (value >= 5)
                return 1.5;
            else if (value == 4)
                return 1.0;
            else if (value >= 2)
                return 0.5;
            else
                return 0.0;
        }

        private static double calculateMN(Tests.TestResults results)
        {
            var value = results["Моральная нормативность (в стэнах)"].Int;
            if (value == 10)
                return 3.0;
            else if (value == 9)
                return 2.5;
            else if (value == 8)
                return 2.0;
            else if (value >= 5)
                return 1.5;
            else if (value == 4)
                return 1.0;
            else if (value >= 2)
                return 0.5;
            else
                return 0.0;
        }

        private static double calculateKOT(Tests.TestResults results)
        {
            var value = results["Коммуникативные особенности (в стэнах)"].Int;
            if (value == 10)
                return 3.0;
            else if (value == 9)
                return 2.5;
            else if (value == 8)
                return 2.0;
            else if (value >= 5)
                return 1.5;
            else if (value == 4)
                return 1.0;
            else if (value >= 2)
                return 0.5;
            else
                return 0.0;
        }

        private static double calculateNPU(Tests.TestResults results)
        {
            var value = results["Нервно-психическая устойчивость (в стэнах)"].Int;
            if (value == 10)
                return 3.0;
            else if (value == 9)
                return 2.5;
            else if (value >= 7)
                return 2.0;
            else if (value == 6)
                return 1.5;
            else if (value == 5)
                return 1.0;
            else if (value >= 3)
                return 0.5;
            else
                return 0.0;
        }

        private static double calculateAS(Tests.TestResults results)
        {
            var value = results["Адаптивные способности (в стэнах)"].Int;
            if (value == 10)
                return 3.0;
            else if (value >= 8)
                return 2.5;
            else if (value >= 6)
                return 2.0;
            else if (value >= 4)
                return 1.5;
            else if (value == 3)
                return 1.0;
            else if (value == 2)
                return 0.5;
            else
                return 0.0;
        }

        private static double calculateUP(Tests.TestResults results)
        {
            var value = results["Уровень проявления аддикции (УП)"].Int;
            if (value <= 30)
                return 3.0;
            else if (value <= 40)
                return 2.5;
            else if (value <= 50)
                return 2.0;
            else if (value <= 60)
                return 1.5;
            else if (value <= 65)
                return 1.0;
            else if (value <= 70)
                return 0.5;
            else
                return 0.0;
        }

        private static double calculateSV(Tests.TestResults results)
        {
            var value = results["Степень выраженности аддикции (СВ)"].Int;
            if (value == 0)
                return 3.0;
            else if (value == 1)
                return 2.5;
            else if (value == 2)
                return 2.0;
            else if (value == 3)
                return 1.0;
            else if (value == 4)
                return 0.5;
            else
                return 0.0;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Validation\Rzd\PvkValidator.cs


using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;

namespace Updk7.Data.Validation.Rzd
{
    public class PvkValidator
    {
        private List<TestGroupValidation> _groupRules = new List<TestGroupValidation>();
        private ITestingTreeFactory _testingTreeFactory;

        public PvkValidator(ITestingTreeFactory testingTreeFactory)
        {
            _testingTreeFactory = testingTreeFactory;
            createValidationRules();
        }

        public bool CanValidate(string groupKey)
        {
            return _groupRules.Any(g => g.GroupKey == groupKey);
        }

        public RzdValidationResults Validate(TestGroupResults groupResults, Entities.Rzd.RzdTestee testee)
        {
            Debug.Assert(groupResults != null);
            Debug.Assert(testee != null);

            if (!CanValidate(groupResults.GroupKey))
                return null;

            var validator = GetValidator(groupResults.GroupKey);
            var validationResults = validator.Validate(groupResults);
            var rzdValidation = new RzdValidationResults(validationResults);
            updateProfessionalGroup(rzdValidation, testee);
            
            return rzdValidation;
        }

        public TestGroupValidation GetValidator(string groupKey)
        {
            return _groupRules.FirstOrDefault(v => v.GroupKey == groupKey);
        }

        private void updateProfessionalGroup(RzdValidationResults validationResults,
           Entities.Rzd.RzdTestee testee)
        {
            if (!validationResults.GroupValidation.GroupResults.IsTestGroupClosed(_testingTreeFactory))
            {
                validationResults.ProfessionalGroup = null;
                validationResults.NextExaminationDate = null;
                validationResults.Conclusion = "Тестирование проведено не в полном объеме";
                return;
            }

            if (validationResults.GroupKey.StartsWith("272"))
                updateProfessionalGroup272(validationResults, testee);
            else
                updateProfessionalGroup310(validationResults, testee);
        }

        private void updateProfessionalGroup272(RzdValidationResults validationResults,
            Entities.Rzd.RzdTestee testee)
        {
            var failedTests = validationResults.FailedTestsCount;
            var group = failedTests + 1;

            if (!validationResults.IsIolNorma.HasValue)
            {
                validationResults.ProfessionalGroup = null;
                validationResults.NextExaminationDate = null;
                validationResults.Conclusion = "Нет оценки коэффициента надежности (КН) по методике ИОЛ";
                return;
            }

            if (validationResults.GroupKey == "272G2")
            {
                if (failedTests == 0)
                {
                    validationResults.ProfessionalGroup = group;
                    validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(3);
                    validationResults.Conclusion = getConclusion272(group, testee);
                }
                else
                {
                    validationResults.ProfessionalGroup = null;
                    validationResults.NextExaminationDate = null;
                    validationResults.Conclusion = getConclusion272(0, testee);
                }
            }
            else
            {
                if (failedTests == 0)
                {
                    validationResults.ProfessionalGroup = group;
                    validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(3);
                    validationResults.Conclusion = getConclusion272(group, testee);
                }
                else if (failedTests == 1)
                {
                    validationResults.ProfessionalGroup = group;
                    validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(2);
                    validationResults.Conclusion = getConclusion272(group, testee);
                }
                else if (failedTests == 2)
                {
                    validationResults.ProfessionalGroup = group;
                    validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(1);
                    validationResults.Conclusion = getConclusion272(group, testee);
                }
                else
                {
                    validationResults.ProfessionalGroup = null;
                    validationResults.NextExaminationDate = null;
                    validationResults.Conclusion = getConclusion272(0, testee);
                }
            }
        }

        private static Dictionary<int, string> _professionsMappint272 = new Dictionary<int, string>()
        {
            [1] = "машиниста",
            [2] = "помошника машиниста",
            [3] = "машиниста-инструктора локомотивных бригад",
            [4] = "кандидата"
        };

        private string getConclusion272(int group, Entities.Rzd.RzdTestee testee)
        {
            Debug.Assert(_professionsMappint272.ContainsKey(testee.ProfessionId));
            var profession = _professionsMappint272[testee.ProfessionId];

            var traffic = _testingTreeFactory.GetMetadata().GetProperty("TrafficTypes")
                as Dictionary<int, string>;

            var driver = $"{profession} ({traffic[testee.TrafficId].ToLower()})";
            var prefix = "Годен для работы, связанной с движением поездов и маневровой работой, сроком на";

            if (group == 1)
                return $"{prefix} 3 года (I группа) в качестве {driver}";
            else if (group == 2)
                return $"{prefix} 2 года (II группа) в качестве {driver}";
            else if (group == 3)
                return $"{prefix} 1 год (III группа) в качестве {driver}";
            else
            {
                return "Не годен для работы, связанной с движением поездов и маневровой работой " +
                    $"в качестве {driver}";
            }
        }

        private void updateProfessionalGroup310(RzdValidationResults validationResults, 
            Entities.Rzd.RzdTestee testee)
        {
            var group = validationResults.GroupKey;

            if (testee.ProfessionId == 4)
            {
                // Кандидат в машинисты
                if (validationResults.FailedTestsCount == 0)
                {
                    validationResults.ProfessionalGroup = 1;
                    validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(3);
                    validationResults.Conclusion = $"Годен для поездной работы {getTrafficDescription(validationResults.GroupKey)} сроком на 3 года";
                }
                else if (validationResults.FailedTestsCount == 1)
                {
                    validationResults.ProfessionalGroup = 2;
                    validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(2);
                    validationResults.Conclusion = $"Ограниченно годен для поездной работы {getTrafficDescription(validationResults.GroupKey)} сроком на 2 года";
                }
                else
                {
                    validationResults.ProfessionalGroup = null;
                    validationResults.NextExaminationDate = null;
                    validationResults.Conclusion = $"НЕ ГОДЕН";
                }
            }
            else if (group == "G31" || group == "G32")
            {
                // Энергодиспетчеры и кандидаты
                if (validationResults.FailedTestsCount == 0)
                {
                    validationResults.ProfessionalGroup = 1;
                    validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(3);
                    validationResults.Conclusion = "Рекомендован для работы в должности энергодиспетчера железнодорожного транспорта. Рекомендованный срок переосвидетельствования - через 3 года.";
                }
                else if (validationResults.FailedTestsCount == 1)
                {
                    validationResults.ProfessionalGroup = 2;
                    validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(2);
                    validationResults.Conclusion = "Рекомендован для работы в должности энергодиспетчера железнодорожного транспорта. Рекомендованный срок переосвидетельствования - через 2 года";
                }
                else
                {
                    validationResults.ProfessionalGroup = null;
                    validationResults.NextExaminationDate = null;
                    validationResults.Conclusion = "Не желателен для работы в должности энергодиспетчера железнодорожного транспорта.";
                }
            }
            else if (group == "G1" || group == "G2" || group == "G7" || group == "G8" || group == "G27" || group == "G28")
            {
                // Скоростное движение и движение без помошника
                if (validationResults.FailedTestsCount == 0)
                {
                    validationResults.ProfessionalGroup = 1;
                    validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(2);
                    validationResults.Conclusion = "ГОДЕН к вождению поездов без помощника, скоростных поездов и для работы по интенсивным технологиям.";
                }
                else
                {
                    validationResults.ProfessionalGroup = null;
                    validationResults.NextExaminationDate = null;
                    validationResults.Conclusion = "НЕ ГОДЕН к вождению поездов без помощника, скоростных поездов и для работы по интенсивным технологиям.";
                }
            }
            else
            {
                // Прочие типы движения
                if (validationResults.FailedTestsCount == 0)
                {
                    validationResults.ProfessionalGroup = 1;
                    validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(3);
                    validationResults.Conclusion = $"Годен для поездной работы {getTrafficDescription(validationResults.GroupKey)} сроком на 3 года";
                }
                else if (validationResults.FailedTestsCount == 1)
                {
                    validationResults.ProfessionalGroup = 2;
                    validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(2);
                    validationResults.Conclusion = $"Годен для поездной работы {getTrafficDescription(validationResults.GroupKey)} сроком на 2 года";
                }
                else
                {
                    validationResults.ProfessionalGroup = 3;
                    validationResults.NextExaminationDate = validationResults.ExaminatinDate.AddYears(1);
                    validationResults.Conclusion = $"Годен для поездной работы {getTrafficDescription(validationResults.GroupKey)} сроком на 1 год";
                }
            }
        }

        private string getTrafficDescription(string groupKey)
        {
            var prefixCommon = "машиниста/помошника машиниста ";
            var prefixSingle = "машиниста ";
            switch (groupKey)
            {
                case "G1":
                case "G7":
                    return prefixCommon + "(скоростное/высокоскоростное движение)";
                case "G2":
                case "G8":
                    return prefixSingle + "(магистральное движение без помошника)";
                case "G3":
                case "G9":
                    return prefixCommon + "(пассажирское движение)";
                case "G5":
                case "G11":
                    return prefixCommon + "(пригородное движение (МВПС))";
                case "G6":
                case "G12":
                    return prefixCommon + "(маневровое движение)";
                case "G27":
                case "G28":
                    return prefixSingle + "(маневровое движение без помошника)";
                case "G4":
                case "G10":
                    return prefixCommon + "(грузовое движение)";
                default:
                    return $"{groupKey} - нет описания типа движения";
            }
        }

        private void createValidationRules()
        {
            createValidationRules310();
            createValidationRules272();
        }

        private void createValidationRules272()
        {
            // === 272G1 ===
            var ged_1 = new TestValidation(Tests.TestType.Готовность_к_экстренному_действию_ГЭД)
                .AddRule("Готовность", 0.25, "<=")
                .AddRule("Число пропущенных сигналов без предупреждения", 2, "<=")
                .AddRule("Число пропущенных сигналов с предупреждением", 0, "<=")
                .AddRule("Кол-во реагирований на сигналы с предупреждением большие 1 с.", 3, "<=");

            var pvpu_1 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
                .AddRule("Время выполнения 1-го задания", 90.0, "<=")
                .AddRule("Время выполнения 2-го задания", 90.0, "<=")
                .AddRule("Время выполнения 3-го задания", 330.0, "<=")
                .AddRule("Время переключения внимания", 150.0, "<=")
                .AddRule("Количество ошибок в 3 задании", 7, "<=")
                .AddRule("Время выполнения 4-го задания", 430, "<=")
                .AddRule("Помехоустойчивость", 100.0, "<=")
                .AddRule("Количество ошибок в 4 задании", 8, "<=")
                .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            var str_1 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_СТР)
               .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
               .AddRule("Разница времён реагирования (Т3 - Т4)", 0, ">=")
               .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=");

            _groupRules.Add(new TestGroupValidation("272G1").AddRules(ged_1, pvpu_1, str_1));

            // === 272G2 ===
            var ged2_2 = new TestValidation(Tests.TestType.Готовность_к_экстренному_действию_2_ГЭД_2)
                .AddRule("ГЭД-1 Готовность", 0.25, "<=")
                .AddRule("ГЭД-1 Число пропущенных сигналов без предупреждения", 2, "<=")
                .AddRule("ГЭД-1 Число пропущенных сигналов с предупреждением", 0, "<=")
                .AddRule("ГЭД-1 Кол-во реагирований на сигналы с предупреждением большие 1 с", 3, "<=")
                .AddRule("ГЭД-2 Показатель  надёжности  работы в состоянии утомления", 0.25, "<=")
                .AddRule("ГЭД-2 Отношение пропущенных скачков к поданным", 0.4, "<=");

            var pvpu_2 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
               .AddRule("Время выполнения 1-го задания", 90.0, "<=")
               .AddRule("Время выполнения 2-го задания", 90.0, "<=")
               .AddRule("Время выполнения 3-го задания", 330.0, "<=")
               .AddRule("Время переключения внимания", 150.0, "<=")
               .AddRule("Количество ошибок в 3 задании", 7, "<=")
               .AddRule("Время выполнения 4-го задания", 430, "<=")
               .AddRule("Помехоустойчивость", 100.0, "<=")
               .AddRule("Количество ошибок в 4 задании", 8, "<=")
               .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            var str_2 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_СТР)
               .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
               .AddRule("Разница времён реагирования (Т3 - Т4)", 0, ">=")
               .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=");

            _groupRules.Add(new TestGroupValidation("272G2").AddRules(ged2_2, pvpu_2, str_2));

            // === 272G3 ===
            var ob_3 = new TestValidation(Tests.TestType.Оценка_бдительности_ОБ)
                .AddRule("Показатель бдительности", 0.18, "<=")
                .AddRule("Среднеарифметическое время реагирования на красные сигналы", 0.75, "<=")
                .AddRule("Общее количество пропусков", 4, "<=")
                .AddRule("Общее количество неправильных нажатий", 6, "<=");

            var str_3 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_СТР)
               .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
               .AddRule("Разница времён реагирования (Т3 - Т4)", 0, ">=")
               .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=");

            var pvpu_3 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
               .AddRule("Время выполнения 1-го задания", 90.0, "<=")
               .AddRule("Время выполнения 2-го задания", 90.0, "<=")
               .AddRule("Время выполнения 3-го задания", 280.0, "<=")
               .AddRule("Время переключения внимания", 130.0, "<=")
               .AddRule("Количество ошибок в 3 задании", 7, "<=")
               .AddRule("Время выполнения 4-го задания", 380, "<=")
               .AddRule("Помехоустойчивость", 100.0, "<=")
               .AddRule("Количество ошибок в 4 задании", 8, "<=")
               .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            var kp_3 = new TestValidation(Tests.TestType.Корректурная_проба_КП)
                .AddRule("Количество ошибок", 5, "<=")
                .AddRule("Количество просмотренных строк", 28, ">=");

            _groupRules.Add(new TestGroupValidation("272G3").AddRules(ob_3, str_3, pvpu_3, kp_3));

            // === 272G3-1 ===
            var ob_31 = new TestValidation(Tests.TestType.Оценка_бдительности_ОБ)
                .AddRule("Показатель бдительности", 0.18, "<=")
                .AddRule("Среднеарифметическое время реагирования на красные сигналы", 0.75, "<=")
                .AddRule("Общее количество пропусков", 4, "<=")
                .AddRule("Общее количество неправильных нажатий", 6, "<=");

            var str_31 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_СТР)
               .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
               .AddRule("Разница времён реагирования (Т3 - Т4)", 0, ">=")
               .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=");

            var pvpu_31 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
               .AddRule("Время выполнения 1-го задания", 90.0, "<=")
               .AddRule("Время выполнения 2-го задания", 90.0, "<=")
               .AddRule("Время выполнения 3-го задания", 280.0, "<=")
               .AddRule("Время переключения внимания", 130.0, "<=")
               .AddRule("Количество ошибок в 3 задании", 7, "<=")
               .AddRule("Время выполнения 4-го задания", 380, "<=")
               .AddRule("Помехоустойчивость", 100.0, "<=")
               .AddRule("Количество ошибок в 4 задании", 8, "<=")
               .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            var kp_31 = new TestValidation(Tests.TestType.Корректурная_проба_КП)
                .AddRule("Количество ошибок", 5, "<=")
                .AddRule("Количество просмотренных строк", 28, ">=");

            _groupRules.Add(new TestGroupValidation("272G3-1").AddRules(ob_31, str_31, pvpu_31, kp_31));

            // === 272G4 ===
            var sdr_4 = new TestValidation(Tests.TestType.Сложная_двигательная_реакция_30_СДР_30)
                .AddRule("Среднее время реагирования", 0.6, "<=")
                .AddRule("Количество ошибок", 5, "<=");

            var str_4 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_СТР)
               .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
               .AddRule("Разница времён реагирования (Т3 - Т4)", 0, ">=")
               .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=");

            var pvpu_4 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
               .AddRule("Время выполнения 1-го задания", 90.0, "<=")
               .AddRule("Время выполнения 2-го задания", 90.0, "<=")
               .AddRule("Время выполнения 3-го задания", 330.0, "<=")
               .AddRule("Время переключения внимания", 150.0, "<=")
               .AddRule("Количество ошибок в 3 задании", 7, "<=")
               .AddRule("Время выполнения 4-го задания", 430, "<=")
               .AddRule("Помехоустойчивость", 100.0, "<=")
               .AddRule("Количество ошибок в 4 задании", 8, "<=")
               .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            var ov_4 = new TestValidation(Tests.TestType.Оценка_объема_внимания_ОВ)
                .AddRule("Максимальный результат обследуемого", 10, ">=");

            _groupRules.Add(new TestGroupValidation("272G4").AddRules(sdr_4, str_4, pvpu_4, ov_4));

            // === 272G4-1 ===
            var sdr_41 = new TestValidation(Tests.TestType.Сложная_двигательная_реакция_СДР_100)
                .AddRule("Среднее время реагирования", 0.6, "<=")
                .AddRule("Количество ошибок", 5, "<=");

            var str_41 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_СТР)
               .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
               .AddRule("Разница времён реагирования (Т3 - Т4)", 0, ">=")
               .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=");

            var pvpu_41 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
               .AddRule("Время выполнения 1-го задания", 90.0, "<=")
               .AddRule("Время выполнения 2-го задания", 90.0, "<=")
               .AddRule("Время выполнения 3-го задания", 330.0, "<=")
               .AddRule("Время переключения внимания", 150.0, "<=")
               .AddRule("Количество ошибок в 3 задании", 7, "<=")
               .AddRule("Время выполнения 4-го задания", 430, "<=")
               .AddRule("Помехоустойчивость", 100.0, "<=")
               .AddRule("Количество ошибок в 4 задании", 8, "<=")
               .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            var ov_41 = new TestValidation(Tests.TestType.Оценка_объема_внимания_ОВ)
                .AddRule("Максимальный результат обследуемого", 10, ">=");

            _groupRules.Add(new TestGroupValidation("272G4-1").AddRules(sdr_41, str_41, pvpu_41, ov_41));
        }

        private void createValidationRules310()
        {
            var epfs = new TestValidation(Tests.TestType.Готовность_к_тестированию_ЭПФС)
                .AddRule("Общее количество ошибок", 3, "<=");

            // ***
            // Скоростное (высокоскоростное) движение
            var pvpu_1 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
                .AddRule("Время выполнения 1-го задания", 90.0, "<=")
                .AddRule("Время выполнения 2-го задания", 90.0, "<=")
                .AddRule("Время выполнения 3-го задания", 330.0, "<=")
                .AddRule("Время переключения внимания", 150.0, "<=")
                .AddRule("Количество ошибок в 3 задании", 7, "<=")
                .AddRule("Помехоустойчивость", 100.0, "<=")
                .AddRule("Количество ошибок в 4 задании", 8, "<=")
                .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            var str_1 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_СТР)
               .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
               .AddRule("Разница времён реагирования (Т3 - Т4)", 0, ">=") 
               .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=");

            var ged2_1 = new TestValidation(Tests.TestType.Готовность_к_экстренному_действию_2_ГЭД_2)
                .AddRule("ГЭД-1 Готовность", 0.25, "<=")
                .AddRule("ГЭД-1 Число пропущенных сигналов без предупреждения", 3, "<=")
                .AddRule("ГЭД-1 Среднеарифметическое время реагирования на сигналы без предупреждения", 1.0, "<=")
                .AddRule("ГЭД-1 Число пропущенных сигналов с предупреждением", 0, "<=")
                .AddRule("ГЭД-1 Кол-во реагирований на сигналы с предупреждением большие 1 с", 4, "<=")
                .AddRule("ГЭД-2 Показатель  надёжности  работы в состоянии утомления", 0.25, "<=")
                .AddRule("ГЭД-2 Отношение пропущенных скачков к поданным", 0.4, "<=")
                .AddRule("ГЭД-2 Количество пропусков скачков подряд", 3, "<=");

            _groupRules.Add(new TestGroupValidation("G1").AddRules(pvpu_1, str_1, ged2_1));

            var rv_7 = new TestValidation(Tests.TestType.Распределение_внимания_РВ)
                .AddRule("Среднее время реагирования в задании №1", 0.6, "<=")
                .AddRule("Количество правильных ответов на зрительные стимулы в задании №1", 18, ">=")
                .AddRule("Разница средних времен реагирования между заданием №2 и заданием №1", 0.2, "<=")
                .AddRule("Количество правильных ответов на слуховые стимулы в задании №2", 10, ">=")
                .AddRule("Разница количества правильных ответов на зрительные стимулы (№1 - №2)", 2, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 1 на зрительные стимулы", 3, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 2 на зрительные стимулы", 3, "<=")
                .AddRule("Количество ошибочных ответов на слуховые стимулы", 3, "<=");

            var spn_7 = new TestValidation(Tests.TestType.Оценка_скорости_переделки_навыков_СПН)
                .AddRule("Среднее время реагирования на зелёные сигналы", 0.45, "<=")
                .AddRule("Количество неправильных нажатий (нажатие не на ту кнопку)", 1, "<=")
                .AddRule("Количество ошибок (нажатие на красно – зелёный сигнал)", 3, "<=");

            var osr_7 = new TestValidation(Tests.TestType.Оценка_склонности_к_риску_ОСР)
                .AddRule("Количество баллов", 6, "<=");

            var eu_7 = new TestValidation(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
                .AddRule("Количество ошибок без помехи (N1)", 2, "<=")
                .AddRule("Количество пропусков без помехи", 1, "<=")
                .AddRule("Среднеарифметическое время реагирования без помехи (ВР1)", 1, "<=")
                .AddRule("Количество ошибок с помехой (N2)", 4, "<=")
                .AddRule("Количество пропусков с помехой", 1, "<=")
                .AddRule("Среднеарифметическое время реагирования с помехой (ВР2)", 1.2, "<=")
                .AddRule("Разница среднеарифметических времен реагирования (ВР2 - ВР1)", 0.25, "<=")
                .AddRule("Разница количества ошибок с помехой и без помехи (N2 - N1)", 3, "<=");

            var uv_7 = new TestValidation(Tests.TestType.Оценка_устойчивости_внимания_УВ)
                .AddRule("Среднеарифметическое время реагирования", 0.7, "<=")
                .AddRule("Количество ошибок", 5, "<=")
                .AddRule("Количество пропусков", 1, "<=");

            var omu_7 = new TestValidation(Tests.TestType.Оценка_монотоноустойчивости_ОМУ)
                .AddRule("Среднее время реагирования в 1-й части теста", 0.55, "<=")
                .AddRule("Коэффициент монотоноустойчивости < 50%", 1, "<=")
                .AddRule("Количество пропусков сигналов", 1, "<=");

            _groupRules.Add(new TestGroupValidation("G7").AddRules(epfs, rv_7, spn_7, osr_7, eu_7, uv_7, omu_7));

            // ***
            // Магистральное движение без помошника
            var pvpu_2 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
                .AddRule("Время выполнения 1-го задания", 90.0, "<=")
                .AddRule("Время выполнения 2-го задания", 90.0, "<=")
                .AddRule("Время выполнения 3-го задания", 330.0, "<=")
                .AddRule("Время переключения внимания", 150.0, "<=")
                .AddRule("Количество ошибок в 3 задании", 7, "<=")
                .AddRule("Помехоустойчивость", 100.0, "<=")
                .AddRule("Количество ошибок в 4 задании", 8, "<=")
                .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            var str_2 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_СТР)
                .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
                .AddRule("Разница времён реагирования (Т3 - Т4)", 0, ">=")
                .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<="); 

            var ged2_2 = new TestValidation(Tests.TestType.Готовность_к_экстренному_действию_2_ГЭД_2)
                .AddRule("ГЭД-1 Готовность", 0.25, "<=")
                .AddRule("ГЭД-1 Число пропущенных сигналов без предупреждения", 3, "<=")
                .AddRule("ГЭД-1 Среднеарифметическое время реагирования на сигналы без предупреждения", 1.0, "<=")
                .AddRule("ГЭД-1 Число пропущенных сигналов с предупреждением", 0, "<=")
                .AddRule("ГЭД-2 Показатель  надёжности  работы в состоянии утомления", 0.25, "<=")
                .AddRule("ГЭД-2 Отношение пропущенных скачков к поданным", 0.4, "<=")
                .AddRule("ГЭД-2 Количество пропусков скачков подряд", 3, "<=");

            _groupRules.Add(new TestGroupValidation("G2").AddRules(pvpu_2, str_2, ged2_2));

            var rv_8 = new TestValidation(Tests.TestType.Распределение_внимания_РВ)
               .AddRule("Среднее время реагирования в задании №1", 0.6, "<=")
               .AddRule("Количество правильных ответов на зрительные стимулы в задании №1", 18, ">=")
               .AddRule("Разница средних времен реагирования между заданием №2 и заданием №1", 0.2, "<=")
               .AddRule("Количество правильных ответов на слуховые стимулы в задании №2", 10, ">=")
               .AddRule("Разница количества правильных ответов на зрительные стимулы (№1 - №2)", 2, "<=")
               .AddRule("Количество реагирований при отсутствии сигнала в задании № 1 на зрительные стимулы", 3, "<=")
               .AddRule("Количество реагирований при отсутствии сигнала в задании № 2 на зрительные стимулы", 3, "<=")
               .AddRule("Количество ошибочных ответов на слуховые стимулы", 3, "<=");

            var spn_8 = new TestValidation(Tests.TestType.Оценка_скорости_переделки_навыков_СПН)
                .AddRule("Среднее время реагирования на зелёные сигналы", 0.45, "<=")
                .AddRule("Количество неправильных нажатий (нажатие не на ту кнопку)", 1, "<=")
                .AddRule("Количество ошибок (нажатие на красно – зелёный сигнал)", 3, "<=");

            var osr_8 = new TestValidation(Tests.TestType.Оценка_склонности_к_риску_ОСР)
                .AddRule("Количество баллов", 6, "<=");

            var eu_8 = new TestValidation(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
                .AddRule("Количество ошибок без помехи (N1)", 2, "<=")
                .AddRule("Количество пропусков без помехи", 1, "<=")
                .AddRule("Среднеарифметическое время реагирования без помехи (ВР1)", 1, "<=")
                .AddRule("Количество ошибок с помехой (N2)", 4, "<=")
                .AddRule("Количество пропусков с помехой", 1, "<=")
                .AddRule("Среднеарифметическое время реагирования с помехой (ВР2)", 1.2, "<=")
                .AddRule("Разница среднеарифметических времен реагирования (ВР2 - ВР1)", 0.25, "<=")
                .AddRule("Разница количества ошибок с помехой и без помехи (N2 - N1)", 3, "<=");

            var uv_8 = new TestValidation(Tests.TestType.Оценка_устойчивости_внимания_УВ)
               .AddRule("Среднеарифметическое время реагирования", 0.7, "<=")
               .AddRule("Количество ошибок", 5, "<=")
               .AddRule("Количество пропусков", 1, "<=");

            var omu_8 = new TestValidation(Tests.TestType.Оценка_монотоноустойчивости_ОМУ)
                .AddRule("Среднее время реагирования в 1-й части теста", 0.55, "<=")
                .AddRule("Коэффициент монотоноустойчивости < 50%", 1, "<=")
                .AddRule("Количество пропусков сигналов", 1, "<=");

            _groupRules.Add(new TestGroupValidation("G8").AddRules(epfs, rv_8, spn_8, osr_8, eu_8, uv_8, omu_8));

            // ***
            // Пассажирское движение
            var pvpu_3 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
               .AddRule("Время выполнения 1-го задания", 90.0, "<=")
               .AddRule("Время выполнения 2-го задания", 90.0, "<=")
               .AddRule("Время выполнения 3-го задания", 330.0, "<=")
               .AddRule("Время переключения внимания", 150.0, "<=")
               .AddRule("Количество ошибок в 3 задании", 7, "<=")
               .AddRule("Помехоустойчивость", 100.0, "<=")
               .AddRule("Количество ошибок в 4 задании", 8, "<=")
               .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            var str_3 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_СТР)
                .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
                .AddRule("Разница времён реагирования (Т3 - Т4)", 0, ">=")
                .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=");

            var ged_3 = new TestValidation(Tests.TestType.Готовность_к_экстренному_действию_ГЭД)
                .AddRule("Готовность", 0.25, "<=")
                .AddRule("Число пропущенных сигналов без предупреждения", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования на сигналы без предупреждения", 1.0, "<=")
                .AddRule("Число пропущенных сигналов с предупреждением", 0, "<=");

            _groupRules.Add(new TestGroupValidation("G3").AddRules(pvpu_3, str_3, ged_3));

            var rv_9 = new TestValidation(Tests.TestType.Распределение_внимания_РВ)
               .AddRule("Среднее время реагирования в задании №1", 0.65, "<=")
               .AddRule("Количество правильных ответов на зрительные стимулы в задании №1", 17, ">=")
               .AddRule("Разница средних времен реагирования между заданием №2 и заданием №1", 0.25, "<=")
               .AddRule("Количество правильных ответов на слуховые стимулы в задании №2", 9, ">=")
               .AddRule("Разница количества правильных ответов на зрительные стимулы (№1 - №2)", 3, "<=")
               .AddRule("Количество реагирований при отсутствии сигнала в задании № 1 на зрительные стимулы", 3, "<=")
               .AddRule("Количество реагирований при отсутствии сигнала в задании № 2 на зрительные стимулы", 3, "<=")
               .AddRule("Количество ошибочных ответов на слуховые стимулы", 3, "<=");

            var osr_9 = new TestValidation(Tests.TestType.Оценка_склонности_к_риску_ОСР)
                .AddRule("Количество баллов", 6, "<=");

            var eu_9 = new TestValidation(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
               .AddRule("Количество ошибок без помехи (N1)", 3, "<=")
               .AddRule("Количество пропусков без помехи", 2, "<=")
               .AddRule("Среднеарифметическое время реагирования без помехи (ВР1)", 1.2, "<=")
               .AddRule("Количество ошибок с помехой (N2)", 5, "<=")
               .AddRule("Количество пропусков с помехой", 2, "<=")
               .AddRule("Среднеарифметическое время реагирования с помехой (ВР2)", 1.35, "<=")
               .AddRule("Разница среднеарифметических времен реагирования (ВР2 - ВР1)", 0.35, "<=")
               .AddRule("Разница количества ошибок с помехой и без помехи (N2 - N1)", 4, "<=");

            var uv_9 = new TestValidation(Tests.TestType.Оценка_устойчивости_внимания_УВ)
               .AddRule("Среднеарифметическое время реагирования", 0.75, "<=")
               .AddRule("Количество ошибок", 7, "<=")
               .AddRule("Количество пропусков", 2, "<=");

            var omu_9 = new TestValidation(Tests.TestType.Оценка_монотоноустойчивости_ОМУ)
                .AddRule("Среднее время реагирования в 1-й части теста", 0.6, "<=")
                .AddRule("Коэффициент монотоноустойчивости < 50%", 2, "<=")
                .AddRule("Количество пропусков сигналов", 1, "<=");

            _groupRules.Add(new TestGroupValidation("G9").AddRules(epfs, rv_9, osr_9, eu_9, uv_9, omu_9));

            // ***
            // Пригородное движение (МВПС)
            var pvpu_5 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
               .AddRule("Время выполнения 1-го задания", 90.0, "<=")
               .AddRule("Время выполнения 2-го задания", 90.0, "<=")
               .AddRule("Время выполнения 3-го задания", 280.0, "<=")
               .AddRule("Время переключения внимания", 130.0, "<=")
               .AddRule("Количество ошибок в 3 задании", 7, "<=")
               .AddRule("Помехоустойчивость", 80.0, "<=")
               .AddRule("Количество ошибок в 4 задании", 8, "<=")
               .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            var str_5 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_СТР)
                .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
                .AddRule("Разница времён реагирования (Т3 - Т4)", 0, ">=")
                .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=");

            var ob_5 = new TestValidation(Tests.TestType.Оценка_бдительности_ОБ)
                .AddRule("Показатель бдительности", 0.18, "<=")
                .AddRule("Среднеарифметическое время реагирования на красные сигналы", 0.75, "<=")
                .AddRule("Общее количество пропусков", 4, "<=")
                .AddRule("Общее количество неправильных нажатий", 6, "<=");

            _groupRules.Add(new TestGroupValidation("G5").AddRules(pvpu_5, str_5, ob_5));

            var rv_11 = new TestValidation(Tests.TestType.Распределение_внимания_РВ)
                .AddRule("Среднее время реагирования в задании №1", 0.65, "<=")
                .AddRule("Количество правильных ответов на зрительные стимулы в задании №1", 17, ">=")
                .AddRule("Разница средних времен реагирования между заданием №2 и заданием №1", 0.25, "<=")
                .AddRule("Количество правильных ответов на слуховые стимулы в задании №2", 9, ">=")
                .AddRule("Разница количества правильных ответов на зрительные стимулы (№1 - №2)", 3, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 1 на зрительные стимулы", 3, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 2 на зрительные стимулы", 3, "<=")
                .AddRule("Количество ошибочных ответов на слуховые стимулы", 3, "<=");

            var osr_11 = new TestValidation(Tests.TestType.Оценка_склонности_к_риску_ОСР)
                .AddRule("Количество баллов", 7, "<=");

            var eu_11 = new TestValidation(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
                .AddRule("Количество ошибок без помехи (N1)", 3, "<=")
                .AddRule("Количество пропусков без помехи", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования без помехи (ВР1)", 1.2, "<=")
                .AddRule("Количество ошибок с помехой (N2)", 5, "<=")
                .AddRule("Количество пропусков с помехой", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования с помехой (ВР2)", 1.35, "<=")
                .AddRule("Разница среднеарифметических времен реагирования (ВР2 - ВР1)", 0.35, "<=")
                .AddRule("Разница количества ошибок с помехой и без помехи (N2 - N1)", 4, "<=");

            var uv_11 = new TestValidation(Tests.TestType.Оценка_устойчивости_внимания_УВ)
                .AddRule("Среднеарифметическое время реагирования", 0.75, "<=")
                .AddRule("Количество ошибок", 7, "<=")
                .AddRule("Количество пропусков", 2, "<=");

            var ob_11 = new TestValidation(Tests.TestType.Оценка_бдительности_ОБ)
                .AddRule("Показатель бдительности", 0.18, "<=")
                .AddRule("Количество пропусков сигналов с предупреждением", 0, "<=")
                .AddRule("Среднеарифметическое время реагирования на красные сигналы", 0.8, "<=")
                .AddRule("Общее количество пропусков", 4, "<=")
                .AddRule("Общее количество неправильных нажатий", 5, "<=");

            _groupRules.Add(new TestGroupValidation("G11").AddRules(epfs, rv_11, osr_11, eu_11, uv_11, ob_11));

            // ***
            // Маневровое движение
            var pvpu_6 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
                .AddRule("Время выполнения 1-го задания", 90.0, "<=")
                .AddRule("Время выполнения 2-го задания", 90.0, "<=")
                .AddRule("Время выполнения 3-го задания", 330.0, "<=")
                .AddRule("Время переключения внимания", 150.0, "<=")
                .AddRule("Количество ошибок в 3 задании", 7, "<=")
                .AddRule("Помехоустойчивость", 100.0, "<=")
                .AddRule("Количество ошибок в 4 задании", 8, "<=")
                .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            var sdr_6 = new TestValidation(Tests.TestType.Сложная_двигательная_реакция_СДР_100)
                .AddRule("Среднее время реагирования", 0.6, "<=")
                .AddRule("Количество ошибок", 5, "<=");

            var str_6 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_СТР)
                .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
                .AddRule("Разница времён реагирования (Т3 - Т4)", 0, ">=")
                .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=");

            var ov_6 = new TestValidation(Tests.TestType.Оценка_объема_внимания_ОВ)
                .AddRule("Максимальный результат обследуемого", 9, ">=");

            _groupRules.Add(new TestGroupValidation("G6").AddRules(pvpu_6, sdr_6, str_6, ov_6));

            var rv_12 = new TestValidation(Tests.TestType.Распределение_внимания_РВ)
                .AddRule("Среднее время реагирования в задании №1", 0.65, "<=")
                .AddRule("Количество правильных ответов на зрительные стимулы в задании №1", 17, ">=")
                .AddRule("Разница средних времен реагирования между заданием №2 и заданием №1", 0.25, "<=")
                .AddRule("Количество правильных ответов на слуховые стимулы в задании №2", 9, ">=")
                .AddRule("Разница количества правильных ответов на зрительные стимулы (№1 - №2)", 3, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 1 на зрительные стимулы", 3, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 2 на зрительные стимулы", 3, "<=")
                .AddRule("Количество ошибочных ответов на слуховые стимулы", 3, "<=");

            var osr_12 = new TestValidation(Tests.TestType.Оценка_склонности_к_риску_ОСР)
                .AddRule("Количество баллов", 7, "<=");

            var eu_12 = new TestValidation(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
                .AddRule("Количество ошибок без помехи (N1)", 3, "<=")
                .AddRule("Количество пропусков без помехи", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования без помехи (ВР1)", 1.2, "<=")
                .AddRule("Количество ошибок с помехой (N2)", 5, "<=")
                .AddRule("Количество пропусков с помехой", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования с помехой (ВР2)", 1.35, "<=")
                .AddRule("Разница среднеарифметических времен реагирования (ВР2 - ВР1)", 0.35, "<=")
                .AddRule("Разница количества ошибок с помехой и без помехи (N2 - N1)", 4, "<=");

            var og_12 = new TestValidation(Tests.TestType.Оценка_глазомера_ОГ)
                .AddRule("Средний процент соотношения линий", 14, "<=");

            var uv_12 = new TestValidation(Tests.TestType.Оценка_устойчивости_внимания_УВ)
                .AddRule("Среднеарифметическое время реагирования", 0.75, "<=")
                .AddRule("Количество ошибок", 7, "<=")
                .AddRule("Количество пропусков", 2, "<=");

            var ob_12 = new TestValidation(Tests.TestType.Оценка_бдительности_ОБ)
                .AddRule("Показатель бдительности", 0.18, "<=")
                .AddRule("Количество пропусков сигналов с предупреждением", 0, "<=")
                .AddRule("Среднеарифметическое время реагирования на красные сигналы", 0.8, "<=")
                .AddRule("Общее количество пропусков", 4, "<=")
                .AddRule("Общее количество неправильных нажатий", 5, "<=");

            _groupRules.Add(new TestGroupValidation("G12").AddRules(epfs, rv_12, osr_12, eu_12, og_12, uv_12, ob_12));

            // ***
            // Маневровое движение без помошника
            var pvpu_27 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
                .AddRule("Время выполнения 1-го задания", 90.0, "<=")
                .AddRule("Время выполнения 2-го задания", 90.0, "<=")
                .AddRule("Время выполнения 3-го задания", 330.0, "<=")
                .AddRule("Время переключения внимания", 150.0, "<=")
                .AddRule("Количество ошибок в 3 задании", 7, "<=")
                .AddRule("Помехоустойчивость", 100.0, "<=")
                .AddRule("Количество ошибок в 4 задании", 8, "<=")
                .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            var str_27 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_СТР)
                .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
                .AddRule("Разница времён реагирования (Т3 - Т4)", 0, ">=")
                .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=");

            var ov_27 = new TestValidation(Tests.TestType.Оценка_объема_внимания_ОВ)
                .AddRule("Максимальный результат обследуемого", 9, ">=");

            var sdr_27 = new TestValidation(Tests.TestType.Сложная_двигательная_реакция_СДР_100)
                .AddRule("Среднее время реагирования", 0.6, "<=")
                .AddRule("Количество ошибок", 5, "<=");

            _groupRules.Add(new TestGroupValidation("G27").AddRules(pvpu_27, str_27, ov_27, sdr_27));

            var rv_28 = new TestValidation(Tests.TestType.Распределение_внимания_РВ)
                .AddRule("Среднее время реагирования в задании №1", 0.65, "<=")
                .AddRule("Количество правильных ответов на зрительные стимулы в задании №1", 17, ">=")
                .AddRule("Разница средних времен реагирования между заданием №2 и заданием №1", 0.25, "<=")
                .AddRule("Количество правильных ответов на слуховые стимулы в задании №2", 9, ">=")
                .AddRule("Разница количества правильных ответов на зрительные стимулы (№1 - №2)", 3, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 1 на зрительные стимулы", 3, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 2 на зрительные стимулы", 3, "<=")
                .AddRule("Количество ошибочных ответов на слуховые стимулы", 3, "<=");

            var osr_28 = new TestValidation(Tests.TestType.Оценка_склонности_к_риску_ОСР)
                .AddRule("Количество баллов", 7, "<=");

            var eu_28 = new TestValidation(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
                .AddRule("Количество ошибок без помехи (N1)", 3, "<=")
                .AddRule("Количество пропусков без помехи", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования без помехи (ВР1)", 1.2, "<=")
                .AddRule("Количество ошибок с помехой (N2)", 5, "<=")
                .AddRule("Количество пропусков с помехой", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования с помехой (ВР2)", 1.35, "<=")
                .AddRule("Разница среднеарифметических времен реагирования (ВР2 - ВР1)", 0.35, "<=")
                .AddRule("Разница количества ошибок с помехой и без помехи (N2 - N1)", 4, "<=");

            var og_28 = new TestValidation(Tests.TestType.Оценка_глазомера_ОГ)
                .AddRule("Средний процент соотношения линий", 14, "<=");

            var uv_28 = new TestValidation(Tests.TestType.Оценка_устойчивости_внимания_УВ)
                .AddRule("Среднеарифметическое время реагирования", 0.75, "<=")
                .AddRule("Количество ошибок", 7, "<=")
                .AddRule("Количество пропусков", 2, "<=");

            var ob_28 = new TestValidation(Tests.TestType.Оценка_бдительности_ОБ)
                .AddRule("Показатель бдительности", 0.18, "<=")
                .AddRule("Среднеарифметическое время реагирования на красные сигналы", 0.8, "<=")
                .AddRule("Общее количество пропусков", 4, "<=")
                .AddRule("Общее количество неправильных нажатий", 5, "<=");

            _groupRules.Add(new TestGroupValidation("G28").AddRules(epfs, rv_28, osr_28, eu_28, og_28, uv_28, ob_28));

            // ***
            // Грузовое движение
            var pvpu_4 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
                .AddRule("Время выполнения 1-го задания", 90.0, "<=")
                .AddRule("Время выполнения 2-го задания", 90.0, "<=")
                .AddRule("Время выполнения 3-го задания", 330.0, "<=")
                .AddRule("Время переключения внимания", 150.0, "<=")
                .AddRule("Количество ошибок в 3 задании", 7, "<=")
                .AddRule("Помехоустойчивость", 100.0, "<=")
                .AddRule("Количество ошибок в 4 задании", 8, "<=")
                .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            var str_4 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_СТР)
                .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
                .AddRule("Разница времён реагирования (Т3 - Т4)", 0, ">=")
                .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=");

            var ged_4 = new TestValidation(Tests.TestType.Готовность_к_экстренному_действию_ГЭД)
                .AddRule("Готовность", 0.25, "<=")
                .AddRule("Число пропущенных сигналов без предупреждения", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования на сигналы без предупреждения", 1.0, "<=")
                .AddRule("Число пропущенных сигналов с предупреждением", 0, "<=");

            _groupRules.Add(new TestGroupValidation("G4").AddRules(pvpu_4, str_4, ged_4));

            var rv_10 = new TestValidation(Tests.TestType.Распределение_внимания_РВ)
                .AddRule("Среднее время реагирования в задании №1", 0.65, "<=")
                .AddRule("Количество правильных ответов на зрительные стимулы в задании №1", 17, ">=")
                .AddRule("Разница средних времен реагирования между заданием №2 и заданием №1", 0.25, "<=")
                .AddRule("Количество правильных ответов на слуховые стимулы в задании №2", 9, ">=")
                .AddRule("Разница количества правильных ответов на зрительные стимулы (№1 - №2)", 3, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 1 на зрительные стимулы", 3, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 2 на зрительные стимулы", 3, "<=")
                .AddRule("Количество ошибочных ответов на слуховые стимулы", 3, "<=");

            var osr_10 = new TestValidation(Tests.TestType.Оценка_склонности_к_риску_ОСР)
                .AddRule("Количество баллов", 7, "<=");

            var eu_10 = new TestValidation(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
                .AddRule("Количество ошибок без помехи (N1)", 3, "<=")
                .AddRule("Количество пропусков без помехи", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования без помехи (ВР1)", 1.2, "<=")
                .AddRule("Количество ошибок с помехой (N2)", 5, "<=")
                .AddRule("Количество пропусков с помехой", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования с помехой (ВР2)", 1.35, "<=")
                .AddRule("Разница среднеарифметических времен реагирования (ВР2 - ВР1)", 0.35, "<=")
                .AddRule("Разница количества ошибок с помехой и без помехи (N2 - N1)", 4, "<=");

            var uv_10 = new TestValidation(Tests.TestType.Оценка_устойчивости_внимания_УВ)
                .AddRule("Среднеарифметическое время реагирования", 0.75, "<=")
                .AddRule("Количество ошибок", 7, "<=")
                .AddRule("Количество пропусков", 2, "<=");

            var omu_10 = new TestValidation(Tests.TestType.Оценка_монотоноустойчивости_ОМУ)
                .AddRule("Среднее время реагирования в 1-й части теста", 0.6, "<=")
                .AddRule("Коэффициент монотоноустойчивости < 50%", 2, "<=")
                .AddRule("Количество пропусков сигналов", 1, "<=");

            _groupRules.Add(new TestGroupValidation("G10").AddRules(epfs, rv_10, osr_10, eu_10, uv_10, omu_10));

            // ***
            // Энергодиспетчер
            var rv_31 = new TestValidation(Tests.TestType.Распределение_внимания_РВ)
                .AddRule("Среднее время реагирования в задании №1", 0.65, "<=")
                .AddRule("Количество правильных ответов на зрительные стимулы в задании №1", 17, ">=")
                .AddRule("Количество правильных реагирований на зрительные стимулы в задании № 2", 15, ">=")
                .AddRule("Разница средних времен реагирования между заданием №2 и заданием №1", 0.25, "<=")
                .AddRule("Количество правильных ответов на слуховые стимулы в задании №2", 9, ">=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 1 на зрительные стимулы", 3, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 2 на зрительные стимулы", 3, "<=")
                .AddRule("Количество ошибочных ответов на слуховые стимулы", 3, "<=");

            var eu_31 = new TestValidation(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
                .AddRule("Количество ошибок без помехи (N1)", 3, "<=")
                .AddRule("Количество пропусков без помехи", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования без помехи (ВР1)", 1.2, "<=")
                .AddRule("Количество ошибок с помехой (N2)", 5, "<=")
                .AddRule("Количество пропусков с помехой", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования с помехой (ВР2)", 1.35, "<=")
                .AddRule("Разница среднеарифметических времен реагирования (ВР2 - ВР1)", 0.35, "<=")
                .AddRule("Разница количества ошибок с помехой и без помехи (N2 - N1)", 4, "<=");

            var uv_31 = new TestValidation(Tests.TestType.Оценка_устойчивости_внимания_УВ)
                .AddRule("Среднеарифметическое время реагирования", 0.75, "<=")
                .AddRule("Количество ошибок", 5, "<=")
                .AddRule("Количество пропусков", 2, "<=");

            var str_m_31 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_М_СТР_М)
                .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
                .AddRule("Разница времён реагирования (Т3 - Т4)", 0.0, ">=")
                .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=")
                .AddRule("Задание 4. Количество дополнительных пар сигналов", 3, "<=");

            var pvpu_31 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
                .AddRule("Время выполнения 1-го задания", 90.0, "<=")
                .AddRule("Время выполнения 2-го задания", 90.0, "<=")
                .AddRule("Время выполнения 3-го задания", 280.0, "<=")
                .AddRule("Время переключения внимания", 130.0, "<=")
                .AddRule("Время выполнения 4-го задания", 380, "<=")
                .AddRule("Количество ошибок в 3 задании", 7, "<=")
                .AddRule("Помехоустойчивость", 100.0, "<=")
                .AddRule("Количество ошибок в 4 задании", 8, "<=")
                .AddRule("Количество ошибок в 3 задании", 7, "<=")
                .AddRule("Разница числа ошибок в 4-ом и 3-ем заданиях", 7, "<=");

            _groupRules.Add(new TestGroupValidation("G31").AddRules(epfs, rv_31, eu_31, uv_31, str_m_31, pvpu_31));

            // ***
            // Кандидат в энергодиспетчеры
            var str_m_32 = new TestValidation(Tests.TestType.Оценка_стрессоустойчивости_М_СТР_М)
                .AddRule("Задание 4. Среднее время реагирования (Т4)", 0.6, "<=")
                .AddRule("Разница времён реагирования (Т3 - Т4)", 0.0, ">=")
                .AddRule("Разница количества ошибок между 4-ым и 3-им заданиями", 2, "<=")
                .AddRule("Задание 4. Количество дополнительных пар сигналов", 3, "<=");

            var pvpu_32 = new TestValidation(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ)
                .AddRule("Время выполнения 1-го задания", 90.0, "<=")
                .AddRule("Время выполнения 2-го задания", 90.0, "<=")
                .AddRule("Время выполнения 3-го задания", 280.0, "<=")
                .AddRule("Время переключения внимания", 130.0, "<=")
                .AddRule("Время выполнения 4-го задания", 380, "<=")
                .AddRule("Количество ошибок в 3 задании", 7, "<=")
                .AddRule("Помехоустойчивость", 100.0, "<=")
                .AddRule("Количество ошибок в 4 задании", 8, "<=")
                .AddRule("Количество ошибок в 3 задании", 7, "<=");

            var rv_32 = new TestValidation(Tests.TestType.Распределение_внимания_РВ)
                .AddRule("Среднее время реагирования в задании №1", 0.65, "<=")
                .AddRule("Количество правильных ответов на зрительные стимулы в задании №1", 17, ">=")
                .AddRule("Количество правильных реагирований на зрительные стимулы в задании № 2", 15, ">=")
                .AddRule("Разница средних времен реагирования между заданием №2 и заданием №1", 0.25, "<=")
                .AddRule("Количество правильных ответов на слуховые стимулы в задании №2", 9, ">=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 1 на зрительные стимулы", 3, "<=")
                .AddRule("Количество реагирований при отсутствии сигнала в задании № 2 на зрительные стимулы", 3, "<=")
                .AddRule("Количество ошибочных ответов на слуховые стимулы", 3, "<=");

            var eu_32 = new TestValidation(Tests.TestType.Эмоциональная_устойчивость_ЭУ)
                .AddRule("Количество ошибок без помехи (N1)", 3, "<=")
                .AddRule("Количество пропусков без помехи", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования без помехи (ВР1)", 1.2, "<=")
                .AddRule("Количество ошибок с помехой (N2)", 5, "<=")
                .AddRule("Количество пропусков с помехой", 2, "<=")
                .AddRule("Среднеарифметическое время реагирования с помехой (ВР2)", 1.35, "<=")
                .AddRule("Разница среднеарифметических времен реагирования (ВР2 - ВР1)", 0.35, "<=")
                .AddRule("Разница количества ошибок с помехой и без помехи (N2 - N1)", 4, "<=");

            var uv_32 = new TestValidation(Tests.TestType.Оценка_устойчивости_внимания_УВ)
                .AddRule("Среднеарифметическое время реагирования", 0.75, "<=")
                .AddRule("Количество ошибок", 5, "<=")
                .AddRule("Количество пропусков", 2, "<=");

            _groupRules.Add(new TestGroupValidation("G32").AddRules(epfs, str_m_32, pvpu_32, rv_32, eu_32, uv_32));
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data\Source\Validation\Rzd\RzdValidationResults.cs


using System;
using System.Linq;

namespace Updk7.Data.Validation.Rzd
{
    public class RzdValidationResults
    {
        public RzdValidationResults(TestGroupValidationResults results)
        {
            GroupValidation = results;
            GroupKey = results.GroupKey;
            ExaminatinDate = results.ExaminatinDate;
            updateValidationResults(results);
        }

        public TestGroupValidationResults GroupValidation { get; }

        public string GroupKey { get; }

        public DateTime ExaminatinDate { get; internal set; }

        public DateTime? NextExaminationDate { get; internal set; }

        public int? ProfessionalGroup { get; internal set; }

        public string Conclusion { get; internal set; }

        public bool ContainsPvPu { get; private set; }

        public bool? IsPuNorma { get; private set; }

        public bool? IsPvNorma { get; private set; }

        public double? IolValue { get; private set; }

        public bool? IsIolNorma { get; private set; }

        public int FailedTestsCount { get; private set; }

        public string FailedTestsString { get; private set; }

        private void updateValidationResults(TestGroupValidationResults validation)
        {
            updateFailedCount(validation);
            updatePvPu(validation);
            updateIol(validation);
        }

        private void updateFailedCount(TestGroupValidationResults validation)
        {
            var pvpu = Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ;
            FailedTestsString = validation.GetFailedTestsString(pvpu);
            FailedTestsCount = validation.FailedTests
                .Where(t => t.Test != pvpu).Count();
        }

        private void updateIol(TestGroupValidationResults validation)
        {
            if (!IOL.ContainsIOL(validation.GroupResults))
                return;

            IolValue = IOL.Calculate(validation.GroupResults);
            if (IolValue.HasValue && !double.IsNaN(IolValue.Value))
            {
                IsIolNorma = IOL.IsNorma(IolValue.Value);
                if (!IsIolNorma.Value)
                {
                    FailedTestsCount += 1;
                    appendToFailedTestString("КН");
                }
            }
        }

        private void updatePvPu(TestGroupValidationResults validation)
        {
            var pvpu = Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ;
            var pvpuResults = validation.GroupResults.TestResults.FirstOrDefault(t => t.Test == pvpu);

            ContainsPvPu = pvpuResults != null && pvpuResults.Values != null && pvpuResults.Values.Count != 0;
            if (!ContainsPvPu)
                return;

            updatePvPuFlags(validation);
            if (!IsPvNorma.Value)
            {
                FailedTestsCount += 1;
                appendToFailedTestString("ПВ");
            }

            if (!IsPuNorma.Value)
            {
                FailedTestsCount += 1;
                appendToFailedTestString("ПУ");
            }
        }

        private void appendToFailedTestString(string test)
        {
            if (!string.IsNullOrEmpty(FailedTestsString))
                FailedTestsString += ", ";

            FailedTestsString += test;
        }

        private void updatePvPuFlags(TestGroupValidationResults validation)
        {
            Func<string, bool> isNormal = rk => 
                !validation.IsFailedResult(Tests.TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ, rk);

            if (validation.GroupKey.StartsWith("272"))
            {
                IsPvNorma = isNormal("Время выполнения 1-го задания")
                    && isNormal("Время выполнения 2-го задания")
                    && isNormal("Время выполнения 3-го задания")
                    && isNormal("Время переключения внимания")
                    && isNormal("Количество ошибок в 3 задании");

                IsPuNorma = isNormal("Время выполнения 4-го задания")
                    && isNormal("Помехоустойчивость")
                    && isNormal("Количество ошибок в 4 задании")
                    && isNormal("Разница числа ошибок в 4-ом и 3-ем заданиях");
            }
            else
            {
                IsPvNorma = isNormal("Время выполнения 1-го задания")
                    && isNormal("Время выполнения 2-го задания")
                    && isNormal("Количество ошибок в 1 задании")
                    && isNormal("Количество ошибок вo 2 задании")
                    && isNormal("Время выполнения 3-го задания")
                    && isNormal("Количество ошибок в 3 задании")
                    && isNormal("Время переключения внимания");

                IsPuNorma = isNormal("Время выполнения 4-го задания")
                    && isNormal("Количество ошибок в 4 задании")
                    && isNormal("Помехоустойчивость");
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\Database.cs


using System.IO;

namespace Updk7.Data.Repositories.Sqlite
{
    public abstract class Database
    {
        public Database(string dbPath)
        {
            DataPath = dbPath;
            PhotosPath = $@"{Path.GetDirectoryName(dbPath)}\{Path.GetFileNameWithoutExtension(dbPath)}.Photos.db";
        }

        public string DataPath { get; }

        public string PhotosPath { get; }

        public bool AutoinitializeDatabase { get; set; } = true;

        public SqliteMapper.ISqliteDatabaseInitializer DatabaseInitializer { get; set; }

        public SqliteMapper.SqliteDatabase Data { get; private set; }

        public SqliteMapper.SqliteDatabase Photos { get; private set; }

        public virtual void Open()
        {
            Data = new SqliteMapper.SqliteDatabase(CreateDataSchema());
            if (AutoinitializeDatabase)
                Data.Initializer = DatabaseInitializer;

            Photos = new SqliteMapper.SqliteDatabase(CreatePhotosSchema());

            Data.Open();
            Photos.Open();
        }

        protected abstract SqliteMapper.SqliteSchema CreateDataSchema();

        protected virtual SqliteMapper.SqliteSchema CreatePhotosSchema()
        {
            return new SqliteMapper.SqliteSchema(PhotosPath)
                .AddEntitie<Entities.Photo>();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\GenericTesteesRepository.cs


using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Updk7.Data.Repositories.Sqlite
{
    public class GenericTesteesRepository<TTestee> : ITesteesRepository
        where TTestee : Entities.Testee, new()
    {
        private SqliteMapper.SqliteDatabase _database;

        public GenericTesteesRepository(SqliteMapper.SqliteDatabase database, 
            ITesteesPhotoRepository photoRepository)
        {
            _database = database;
            Photos = photoRepository;
        }

        public ITesteesPhotoRepository Photos { get; }

        public event EventHandler<RepositoryDataChangedEventArgs> DataChanged;

        IEnumerable<Type> IRepository.EntitieTypes
        {
            get
            {
                yield return typeof(TTestee);
            }
        }

        public void Add(Entities.Testee item)
        {
            _database.Insert(item as TTestee);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(item, RepositoryDataChangedOperation.Create));
        }

        public IEnumerable<Entities.Testee> Get()
        {
            return _database.Get<TTestee>();
        }

        public IEnumerable<Entities.Testee> Get(bool removed)
        {
            var filter = removed ? "1" : "0";
            return _database.Get<TTestee>($"Removed = {filter}");
        }

        public Entities.Testee GetById(int id)
        {
            return _database.GetById<TTestee>(id);
        }

        public void Remove(Entities.Testee item)
        {
            throw new InvalidOperationException();
        }

        public void Update(Entities.Testee item)
        {
            _database.Update(item as TTestee);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(item, RepositoryDataChangedOperation.Update));
        }

        void IRepository.Add(object entitie)
        {
            Debug.Assert(entitie is TTestee);
            Add(entitie as TTestee);
        }

        void IRepository.Update(object entitie)
        {
            Debug.Assert(entitie is TTestee);
            Update(entitie as TTestee);
        }

        void IRepository.Remove(object entitie)
        {
            Debug.Assert(entitie is TTestee);
            Remove(entitie as TTestee);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\PinnedTesteesRepository.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace Updk7.Data.Repositories.Sqlite
{
    public class PinnedTesteesRepository : IPinnedTesteesRepository
    {
        private SqliteMapper.SqliteDatabase _database;
        private string _dbPath;

        public PinnedTesteesRepository(string dbPath)
        {
            _dbPath = dbPath;
        }

        public event EventHandler<RepositoryDataChangedEventArgs> DataChanged;

        public IEnumerable<Type> EntitieTypes
        {
            get
            {
                yield return typeof(Entities.TesteePinPair);
            }
        }

        public void Open()
        {
            var schema = new SqliteMapper.SqliteSchema(_dbPath)
                .AddEntitie<Entities.TesteePinPair>();

            _database = new SqliteMapper.SqliteDatabase(schema);
            _database.Open();
        }

        public void SetPin(int testeeId1, int testeeId2)
        {
            RemovePin(testeeId1);
            RemovePin(testeeId2);

            var pin = new Entities.TesteePinPair()
            { 
                TesteeId1 = testeeId1,
                TesteeId2 = testeeId2
            };

            ((IRepository)this).Add(pin);
        }

        public void RemovePin(int testeeId)
        {
            var pinEntitie = getPinPair(testeeId);
            if (pinEntitie != null)
                ((IRepository)this).Remove(pinEntitie);
        }

        public int? GetTesteePin(int testeeId)
        {
            var pin = getPinPair(testeeId);
            if (pin != null)
            {
                return pin.TesteeId1 == testeeId
                    ? pin.TesteeId2
                    : pin.TesteeId1;
            }

            return null;
        }

        void IRepository.Add(object entitie)
        {
            Debug.Assert(entitie is Entities.TesteePinPair);

            _database.Insert(entitie as Entities.TesteePinPair);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(entitie, 
                RepositoryDataChangedOperation.Create));
        }

        void IRepository.Remove(object entitie)
        {
            Debug.Assert(entitie is Entities.TesteePinPair);

            _database.Delete(entitie as Entities.TesteePinPair);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(entitie,
                RepositoryDataChangedOperation.Remove));
        }

        void IRepository.Update(object entitie)
        {
            Debug.Assert(entitie is Entities.TesteePinPair);

            _database.Update(entitie as Entities.TesteePinPair);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(entitie,
                RepositoryDataChangedOperation.Update));
        }

        public Entities.TesteePinPair getPinPair(int testeeId)
        {
            var condition = $"TesteeId1 = {testeeId} or TesteeId2 = {testeeId}";
            return _database.Get<Entities.TesteePinPair>(condition).FirstOrDefault();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\ReportMessagesRepository.cs


using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Updk7.Data.Repositories.Sqlite
{
    public class ReportMessagesRepository : IReportMessagesRepository
    {
        private SqliteMapper.SqliteDatabase _database;

        public ReportMessagesRepository(SqliteMapper.SqliteDatabase database)
        {
            _database = database;
        }

        public event EventHandler<RepositoryDataChangedEventArgs> DataChanged;

        public IEnumerable<Type> EntitieTypes
        {
            get
            {
                yield return typeof(Entities.ReportMessage);
                yield return typeof(Entities.ReportMessageMapping);
            }
        }
        
        public void Add(Entities.ReportMessage message)
        {
            Debug.Assert(message != null);

            _database.Insert(message);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(message, 
                RepositoryDataChangedOperation.Create));
        }

        public void Add(Entities.ReportMessageMapping mapping)
        {
            Debug.Assert(mapping != null);

            _database.Insert(mapping);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(mapping, 
                RepositoryDataChangedOperation.Create));
        }

        public IEnumerable<Entities.ReportMessage> GetMessages(int testGroupId)
        {
            var mappings = GetMappings(testGroupId);
            if (mappings != null)
            {
                foreach (var mapping in mappings)
                    yield return _database.GetById<Entities.ReportMessage>(mapping.MessageId);
            }

            yield break;
        }

        public IEnumerable<Entities.ReportMessage> GetMessages()
        {
            return _database.Get<Entities.ReportMessage>();
        }

        public IEnumerable<Entities.ReportMessageMapping> GetMappings(int testGroupId)
        {
            return _database.Get<Entities.ReportMessageMapping>($"TestGroupId = {testGroupId}");
        }

        public void ClearMappings(int testGroupId)
        {
            var mappings = GetMappings(testGroupId);
            if (mappings != null)
            {
                foreach (var mapping in mappings)
                    Remove(mapping);
            }
        }

        public void Update(Entities.ReportMessage message)
        {
            Debug.Assert(message != null);

            _database.Update(message);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(message, 
                RepositoryDataChangedOperation.Update));
        }

        public void Remove(Entities.ReportMessage message)
        {
            Debug.Assert(message != null);
            Debug.Assert(!message.Locked);

            _database.Delete(message);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(message, 
                RepositoryDataChangedOperation.Remove));
        }

        public void Remove(Entities.ReportMessageMapping mapping)
        {
            Debug.Assert(mapping != null);

            _database.Delete(mapping);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(mapping,
                RepositoryDataChangedOperation.Remove));
        }

        void IRepository.Add(object entitie)
        {
            if (entitie is Entities.ReportMessage)
                Add(entitie as Entities.ReportMessage);
            else if (entitie is Entities.ReportMessageMapping)
                Add(entitie as Entities.ReportMessageMapping);
            else
                Debug.Assert(false);
        }

        void IRepository.Remove(object entitie)
        {
            if (entitie is Entities.ReportMessage)
                Remove(entitie as Entities.ReportMessage);
            else if (entitie is Entities.ReportMessageMapping)
                Remove(entitie as Entities.ReportMessageMapping);
            else
                Debug.Assert(false);
        }

        void IRepository.Update(object entitie)
        {
            Debug.Assert(entitie is Entities.ReportMessage);
            Update(entitie as Entities.ReportMessage);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\TesteesPhotoRepository.cs


using System;

namespace Updk7.Data.Repositories.Sqlite
{
    public class TesteesPhotoRepository : ITesteesPhotoRepository
    {
        private SqliteMapper.SqliteDatabase _database;

        public TesteesPhotoRepository(SqliteMapper.SqliteDatabase database)
        {
            _database = database;
        }

        public event EventHandler<RepositoryDataChangedEventArgs> DataChanged;

        public byte[] GetImageData(int testeeId)
        {
            return _database.GetById<Entities.Photo>(testeeId)?.ImageData;
        }

        public void SetImageData(int testeeId, byte[] imageData)
        {
            var photo = _database.GetById<Entities.Photo>(testeeId);
            var dbOperation = RepositoryDataChangedOperation.Create;

            if (photo == null)
            {
                photo = new Entities.Photo();
                photo.Id = testeeId;
                photo.ImageData = imageData;
                _database.Insert(photo);
            }
            else
            {
                photo.ImageData = imageData;
                _database.Update(photo);
                dbOperation = RepositoryDataChangedOperation.Update;
            }

            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(photo, dbOperation));
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\TestResultsRepository.cs


using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Updk7.Data.Repositories.Sqlite
{
    public class TestResultsRepository : ITestResultsRepository
    {
        private SqliteMapper.SqliteDatabase _database;

        public TestResultsRepository(SqliteMapper.SqliteDatabase database)
        {
            _database = database;
        }

        public event EventHandler<RepositoryDataChangedEventArgs> DataChanged;

        IEnumerable<Type> IRepository.EntitieTypes
        {
            get
            {
                yield return typeof(Entities.TestGroup);
                yield return typeof(Entities.TestResults);
            }
        }

        public void Add(Entities.TestGroup testGroup)
        {
            _database.Insert(testGroup);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(testGroup,
                RepositoryDataChangedOperation.Create));
        }

        public void Add(Entities.TestResults testResults)
        {
            _database.Insert(testResults);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(testResults,
                RepositoryDataChangedOperation.Create));
        }

        public IEnumerable<Entities.TestGroup> GetGroups(int testeeId)
        {
            return _database.Get<Entities.TestGroup>($"TesteeId = {testeeId}");
        }

        public Entities.TestGroup GetGroup(int groupId)
        {
            return _database.GetById<Entities.TestGroup>(groupId);
        }

        public IEnumerable<Entities.TestResults> GetGroupResults(int groupId)
        {
            return _database.Get<Entities.TestResults>($"TestGroupId = {groupId}");
        }

        public Entities.TestResults GetTestResults(int testId)
        {
            return _database.GetById<Entities.TestResults>(testId);
        }

        void IRepository.Add(object entitie)
        {
            if (entitie is Entities.TestGroup)
                Add(entitie as Entities.TestGroup);
            else if (entitie is Entities.TestResults)
                Add(entitie as Entities.TestResults);
            else
                Debug.Assert(false);
        }

        void IRepository.Update(object entitie)
        {
            throw new NotImplementedException();
        }

        void IRepository.Remove(object entitie)
        {
            throw new NotImplementedException();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\UsersRepository.cs


using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Updk7.Data.Repositories.Sqlite
{
    public class UsersRepository : IUsersRepository
    {
        private SqliteMapper.SqliteDatabase _database;

        public UsersRepository(SqliteMapper.SqliteDatabase database)
        {
            _database = database;
        }

        public event EventHandler<RepositoryDataChangedEventArgs> DataChanged;

        IEnumerable<Type> IRepository.EntitieTypes
        {
            get
            {
                yield return typeof(Entities.User);
            }
        }

        public void Add(Entities.User item)
        {
            _database.Insert(item);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(item, RepositoryDataChangedOperation.Create));
        }

        public IEnumerable<Entities.User> Get()
        {
            return _database.Get<Entities.User>();
        }

        public IEnumerable<Entities.User> Get(bool removed)
        {
            var filter = removed ? "1" : "0";
            return _database.Get<Entities.User>($"Removed = {filter}");
        }

        public Entities.User GetById(int id)
        {
            return _database.GetById<Entities.User>(id);
        }

        public void Remove(Entities.User item)
        {
            throw new InvalidOperationException();
        }

        public void Update(Entities.User item)
        {
            _database.Update(item);
            DataChanged?.Invoke(this, new RepositoryDataChangedEventArgs(item, RepositoryDataChangedOperation.Update));
        }

        void IRepository.Add(object entitie)
        {
            Debug.Assert(entitie is Entities.User);
            Add(entitie as Entities.User);
        }

        void IRepository.Update(object entitie)
        {
            Debug.Assert(entitie is Entities.User);
            Update(entitie as Entities.User);
        }

        void IRepository.Remove(object entitie)
        {
            Debug.Assert(entitie is Entities.User);
            Remove(entitie as Entities.User);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\AutoPro\AutoProDatabase.cs


using Updk7.Data.SqliteMapper;

namespace Updk7.Data.Repositories.Sqlite.AutoPro
{
    public class AutoProDatabase : Database
    {
        public AutoProDatabase(string dbPath) : base(dbPath)
        {
            DatabaseInitializer = new AutoProDatabaseInitializer();
        }

        protected override SqliteSchema CreateDataSchema()
        {
            return new SqliteSchema(DataPath)
                .AddEntitie<Entities.StorageInfo>()
                .AddEntitie<Entities.AutoPro.AutoProTestee>()
                .AddEntitie<Entities.User>()
                .AddEntitie<Entities.TestGroup>()
                .AddEntitie<Entities.TestResults>()
                .AddIndex<Entities.TestGroup>(nameof(Entities.TestGroup.TesteeId))
                .AddIndex<Entities.TestGroup>(nameof(Entities.TestGroup.UserId))
                .AddIndex<Entities.TestResults>(nameof(Entities.TestResults.TestGroupId));
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\AutoPro\AutoProDatabaseInitializer.cs


using System;

namespace Updk7.Data.Repositories.Sqlite.AutoPro
{
    public class AutoProDatabaseInitializer : SqliteMapper.ISqliteDatabaseInitializer
    {
        private SqliteMapper.SqliteDatabase _database;

        public AutoProDatabaseInitializer()
        {
        }

        public void SeedData(SqliteMapper.SqliteDatabase database)
        {
            _database = database;

            _database.Insert(new Entities.StorageInfo()
            {
                Version = "AUTO-PRO-7.0-2021",
                Date = DateTime.Now
            });

            _database.Insert(new Entities.User()
            {
                FullName = "Администратор",
                Role = Entities.UserRoles.Admin,
                RegistrationDate = DateTime.Now,
                Password = "123"
            });

#if DEBUG
            _database.Insert(new Entities.User()
            {
                FullName = "Карл Густав Юнг",
                Role = Entities.UserRoles.Psycho,
                RegistrationDate = DateTime.Now,
                Password = "123"
            });

            addTestee("Неруль Иван Петрович", "M", "Г");
            addTestee("Непедаль Петр Иванович", "A", "МГП");
            addTestee("Тормозов Гаврила Филимонович", "A1", "МГГ");

            addTestee("Торпеда Вилианор Формалинович", "B", "МН");
            addTestee("Сосиска Магомед Аркадьевич", "B1", "ОГ");
            addTestee("Кардан Кузьма Пруткович", "C", "Г");
            addTestee("Фара Махрепяк Саулович", "C1", "Г");
            addTestee("Габаритов Казмагоры Пупыркович", "D", "Г");
            addTestee("Асисяй Гуглеме Ибн'Шмякнога", "D1", "МГП");
            addTestee("Балалаев Педалий Маркович", "BE", "МН");

            addTestee("Поршнев Виктор Михайлович", "CE", "МН");
            addTestee("Ололоев Гриборий Нажимович", "DE", "ОГ");
            addTestee("Рычагидзе Вантуз Боржомович", "C1E", "МГГ");
            addTestee("Омывайко Панас Тарасович", "D1E", "МГГ");
            addTestee("Чебурек Константин Вицлипуцкович", "Tm", "Г");
            addTestee("Шпрота Шелялис Гавсович", "Tb", "Г");
#endif
        }

        private void addTestee(string fullName, string driveCategory, string trafficType)
        {
#if DEBUG
            var nameParts = fullName.Split(' ');
            var testee = new Entities.AutoPro.AutoProTestee()
            {
                FullName = fullName,
                BirthDate = DateTime.Parse("11 апреля 1987"),
                Gender = Entities.GenderType.Male,
                DriveCategory = driveCategory,
                ProfessionId = 0,
                TrafficType = trafficType,
                Column = "1A"
            };

            _database.Insert(testee);
#endif
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\AutoPro\AutoProTesteesRepository.cs


namespace Updk7.Data.Repositories.Sqlite.AutoPro
{
    public class AutoProTesteesRepository : GenericTesteesRepository<Entities.AutoPro.AutoProTestee>
    {
        public AutoProTesteesRepository(SqliteMapper.SqliteDatabase database,
            ITesteesPhotoRepository photoRepository) : base(database, photoRepository)
        {
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\Metro\MetroDatabase.cs


using Updk7.Data.SqliteMapper;

namespace Updk7.Data.Repositories.Sqlite.Metro
{
    public class MetroDatabase : Database
    {
        public MetroDatabase(string dbPath) : base(dbPath)
        {
            DatabaseInitializer = new MetroDatabaseInitializer();
        }

        protected override SqliteSchema CreateDataSchema()
        {
            return new SqliteSchema(DataPath)
                .AddEntitie<Entities.StorageInfo>()
                .AddEntitie<Entities.Metro.MetroTestee>()
                .AddEntitie<Entities.User>()
                .AddEntitie<Entities.TestGroup>()
                .AddEntitie<Entities.TestResults>()
                .AddEntitie<Entities.ReportMessage>()
                .AddEntitie<Entities.ReportMessageMapping>()
                .AddIndex<Entities.TestGroup>(nameof(Entities.TestGroup.TesteeId))
                .AddIndex<Entities.TestGroup>(nameof(Entities.TestGroup.UserId))
                .AddIndex<Entities.TestResults>(nameof(Entities.TestResults.TestGroupId))
                .AddIndex<Entities.ReportMessage>(nameof(Entities.ReportMessage.Id))
                .AddIndex<Entities.ReportMessageMapping>(nameof(Entities.ReportMessageMapping.TestGroupId));
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\Metro\MetroDatabaseInitializer.cs


using System;

namespace Updk7.Data.Repositories.Sqlite.Metro
{
    public class MetroDatabaseInitializer : SqliteMapper.ISqliteDatabaseInitializer
    {
        private SqliteMapper.SqliteDatabase _database;

        public MetroDatabaseInitializer()
        {
        }

        public void SeedData(SqliteMapper.SqliteDatabase database)
        {
            _database = database;

            _database.Insert(new Entities.StorageInfo()
            {
                Version = "METRO-2022",
                Date = DateTime.Now
            });

            _database.Insert(new Entities.User()
            {
                FullName = "Администратор",
                Role = Entities.UserRoles.Admin,
                RegistrationDate = DateTime.Now,
                Password = "123"
            });

#if DEBUG
            _database.Insert(new Entities.User()
            {
                FullName = "Карл Густав Юнг",
                Role = Entities.UserRoles.Psycho,
                RegistrationDate = DateTime.Now,
                Password = "123"
            });

            addTestee("Никифоров Галактион Григорьевич", "24 июня 1980", 1, 1);
            addTestee("Охота Станислава Артемовна", "17 июня 1982", 1, 1);
            addTestee("Воронин Аскольд Максимович", "8 августа 1970", 1, 1);

            addTestee("Воронина Прасковья Сергеевна", "6 декабря 1978", 1, 2, removed: true);
            addTestee("Карпова Лилия Игоревна", "17 июня 1980", 1, 2);
            addTestee("Киселёва Алина Ефимовна", "5 февраля 1988", 1, 2);

            addTestee("Попов Тихон Леонидович", "6 января 1974", 1, 3, removed: true);
            addTestee("Гусев Наум Аркадьевич", "7 июня 1976", 1, 3);
            addTestee("Егоров Артемий Святославович", "1 мая 1974", 1, 3);

            addTestee("Андронов Фадей Егорович", "27 февраля 1972", 2, 1);
            addTestee("Тимофеев Амаяк Анатольевич", "12 июля 1985", 2, 2);
            addTestee("Абрамова Пелагея Иосифовна", "28 февраля 1993", 2, 3, removed: true);

            addTestee("Прокофьев Амаяк Евгеньевич", "22 февраля 1983", 2, 1);
            addTestee("Кочетков Флорентин Романович", "16 апреля 1977", 2, 2);
            addTestee("Конягин Мартьян Степанович", "11 апреля 1987", 2, 3);
#endif
        }

        private void addTestee(string fullName, string birthDate, int professionId, int traficId,
            bool removed = false)
        {
            var testee = new Entities.Metro.MetroTestee()
            {
                FullName = fullName,
                BirthDate = DateTime.Parse(birthDate),
                ProfessionId = professionId,
                TrafficId = traficId,
                RegistrationDate = DateTime.Parse("2000.01.01"),
                Column = "6А",
                TC = "2"
            };

            if (removed)
            {
                testee.Removed = true;
                testee.RemoveDate = DateTime.Now;
            }

            _database.Insert(testee);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\Metro\MetroTesteesRepository.cs


namespace Updk7.Data.Repositories.Sqlite.Metro
{
    public class MetroTesteesRepository : GenericTesteesRepository<Entities.Metro.MetroTestee>
    {
        public MetroTesteesRepository(SqliteMapper.SqliteDatabase database,
            ITesteesPhotoRepository photoRepository) : base(database, photoRepository)
        {
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\Rzd\RzdDatabase.cs


using Updk7.Data.SqliteMapper;

namespace Updk7.Data.Repositories.Sqlite.Rzd
{
    public class RzdDatabase : Database
    {
        public RzdDatabase(string dbPath) : base(dbPath)
        {
            DatabaseInitializer = new RzdDatabaseInitializer();
        }

        protected override SqliteSchema CreateDataSchema()
        {
            return new SqliteSchema(DataPath)
                .AddEntitie<Entities.StorageInfo>()
                .AddEntitie<Entities.Rzd.RzdTestee>()
                .AddEntitie<Entities.User>()
                .AddEntitie<Entities.TestGroup>()
                .AddEntitie<Entities.TestResults>()
                .AddEntitie<Entities.ReportMessage>()
                .AddEntitie<Entities.ReportMessageMapping>()
                .AddIndex<Entities.TestGroup>(nameof(Entities.TestGroup.TesteeId))
                .AddIndex<Entities.TestGroup>(nameof(Entities.TestGroup.UserId))
                .AddIndex<Entities.TestResults>(nameof(Entities.TestResults.TestGroupId))
                .AddIndex<Entities.ReportMessage>(nameof(Entities.ReportMessage.Id))
                .AddIndex<Entities.ReportMessageMapping>(nameof(Entities.ReportMessageMapping.TestGroupId));
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\Rzd\RzdDatabaseInitializer.cs


using System;

namespace Updk7.Data.Repositories.Sqlite.Rzd
{
    public class RzdDatabaseInitializer : SqliteMapper.ISqliteDatabaseInitializer
    {
        private SqliteMapper.SqliteDatabase _database;

        public RzdDatabaseInitializer()
        {
        }

        public void SeedData(SqliteMapper.SqliteDatabase database)
        {
            _database = database;

            _database.Insert(new Entities.StorageInfo()
            {
                Version = "RZD-2020",
                Date = DateTime.Now
            });

            _database.Insert(new Entities.User()
            {
                FullName = "Администратор",
                Role = Entities.UserRoles.Admin,
                RegistrationDate = DateTime.Now,
                Password = "123"
            });

#if DEBUG
            _database.Insert(new Entities.User()
            {
                FullName = "Карл Густав Юнг",
                Role = Entities.UserRoles.Psycho,
                RegistrationDate = DateTime.Now,
                Password = "123"
            });

            addTestee("Никифоров Галактион Григорьевич", "24 июня 1980", 1, 1);
            addTestee("Охота Станислава Артемовна", "17 июня 1982", 1, 1);
            addTestee("Воронин Аскольд Максимович", "8 августа 1970", 1, 1);

            addTestee("Воронина Прасковья Сергеевна", "6 декабря 1978", 1, 2, removed: true);
            addTestee("Карпова Лилия Игоревна", "17 июня 1980", 1, 2);
            addTestee("Киселёва Алина Ефимовна", "5 февраля 1988", 1, 2);

            addTestee("Попов Тихон Леонидович", "6 января 1974", 1, 3, removed: true);
            addTestee("Гусев Наум Аркадьевич", "7 июня 1976", 1, 3);
            addTestee("Егоров Артемий Святославович", "1 мая 1974", 1, 3);

            addTestee("Андронов Фадей Егорович", "27 февраля 1972", 2, 1);
            addTestee("Тимофеев Амаяк Анатольевич", "12 июля 1985", 2, 2);
            addTestee("Абрамова Пелагея Иосифовна", "28 февраля 1993", 2, 3, removed: true);

            addTestee("Прокофьев Амаяк Евгеньевич", "22 февраля 1983", 2, 1);
            addTestee("Кочетков Флорентин Романович", "16 апреля 1977", 2, 2);
            addTestee("Конягин Мартьян Степанович", "11 апреля 1987", 2, 3);

            addReportMessage("Цель обследования 1", Entities.ReportMessageType.Goal);
            addReportMessage("Цель обследования 2", Entities.ReportMessageType.Goal);

            addReportMessage("Заключение по обследованию 1", Entities.ReportMessageType.Conclusion);
            addReportMessage("Заключение по обследованию 2", Entities.ReportMessageType.Conclusion);
            addReportMessage("Заключение по обследованию 3", Entities.ReportMessageType.Conclusion);
#endif
        }

        private void addTestee(string fullName, string birthDate, int professionId, int traficId,
            bool removed = false)
        {
            var testee = new Entities.Rzd.RzdTestee()
            {
                FullName = fullName,
                BirthDate = DateTime.Parse(birthDate),
                ProfessionId = professionId,
                TrafficId = traficId,
                RegistrationDate = DateTime.Parse("2000.01.01"),
                Column = "6А",
                TC = "2"
            };

            if (removed)
            {
                testee.Removed = true;
                testee.RemoveDate = DateTime.Now;
            }

            _database.Insert(testee);
        }

        private void addReportMessage(string message, Entities.ReportMessageType messageType)
        {
            var entitie = new Entities.ReportMessage()
            {
                MessageType = messageType,
                Text = message,
                RegistrationDate = DateTime.Now
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.Repositories.Sqlite\Source\Rzd\RzdTesteesRepository.cs


namespace Updk7.Data.Repositories.Sqlite.Rzd
{
    public class RzdTesteesRepository : GenericTesteesRepository<Entities.Rzd.RzdTestee>
    {
        public RzdTesteesRepository(SqliteMapper.SqliteDatabase database,
            ITesteesPhotoRepository photoRepository) : base(database, photoRepository)
        {
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.SqliteMapper\Source\IPropertyConverter.cs


namespace Updk7.Data.SqliteMapper
{
    public interface IPropertyConverter
    {
        string Convert(object value);

        object ConvertBack(object dbValue);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.SqliteMapper\Source\ISqliteDatabaseInitializer.cs


namespace Updk7.Data.SqliteMapper
{
    public interface ISqliteDatabaseInitializer
    {
        void SeedData(SqliteDatabase database);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.SqliteMapper\Source\SqliteConvertersFactory.cs


using System;
using System.Collections.Generic;
using System.Text;

namespace Updk7.Data.SqliteMapper
{
    public class SqliteConvertersFactory
    {
        private class IntConverter : IPropertyConverter
        {
            public string Convert(object value) => value.ToString();
            public object ConvertBack(object dbValue) => (int)((long)dbValue);
        }

        private class StringConverter : IPropertyConverter
        {
            public string Convert(object value) => value != null ? $"\"{value}\"" : "null";
            public object ConvertBack(object dbValue) => dbValue is DBNull ? null : (string)dbValue;
        }

        private class DateTimeConverter : IPropertyConverter
        {
            private static readonly string Format = "yyyy-MM-dd HH.mm.ss";

            public string Convert(object value) => $"\"{((DateTime)value).ToString(Format)}\"";
            public object ConvertBack(object dbValue) => DateTime.ParseExact((string)dbValue, Format, System.Globalization.CultureInfo.InvariantCulture);
        }

        private class BoolConverter : IPropertyConverter
        {
            public string Convert(object value) => ((bool)value) ? "1" : "0";
            public object ConvertBack(object dbValue) => (long)dbValue == 1 ? true : false;
        }

        private class ByteArrayConverter : IPropertyConverter
        {
            public string Convert(object value) => $"x'{byteArrayToString((byte[])value)}'";
            public object ConvertBack(object dbValue) => (byte[])dbValue;

            private static string byteArrayToString(byte[] data)
            {
                if (data == null)
                    return "null";

                var hex = new StringBuilder(data.Length * 2);
                foreach (byte b in data)
                    hex.AppendFormat("{0:x2}", b);

                return hex.ToString();
            }
        }

        private class NullableValueConverter : IPropertyConverter
        {
            private IPropertyConverter _basePropertyConverter;

            public NullableValueConverter(IPropertyConverter basePropertyConverter)
            {
                _basePropertyConverter = basePropertyConverter;
            }

            public string Convert(object value) => value == null ? "null" : _basePropertyConverter.Convert(value);
            public object ConvertBack(object dbValue) => dbValue is DBNull ? null : _basePropertyConverter.ConvertBack(dbValue);
        }

        private class EnumConverter : IPropertyConverter
        {
            private IPropertyConverter _intConverter = new IntConverter();
            private Type _enumType;

            public EnumConverter(Type enumType)
            {
                _enumType = enumType;
            }

            public string Convert(object value) => _intConverter.Convert((int)value);
            public object ConvertBack(object dbValue) => Enum.ToObject(_enumType, _intConverter.ConvertBack(dbValue));
        }

        private Dictionary<Type, IPropertyConverter> _convertersDictionary = new Dictionary<Type, IPropertyConverter>()
        {
            [typeof(int)] = new IntConverter(),
            [typeof(string)] = new StringConverter(),
            [typeof(DateTime)] = new DateTimeConverter(),
            [typeof(bool)] = new BoolConverter(),
            [typeof(byte[])] = new ByteArrayConverter()
        };

        public SqliteConvertersFactory()
        {
        }

        public IPropertyConverter CreateConverter(SqliteTableColumn column)
        {
            var baseConverter = column.DataType.IsEnum
                ? new EnumConverter(column.DataType)
                : _convertersDictionary[column.DataType];

            return column.Nullable
                ? new NullableValueConverter(baseConverter)
                : baseConverter;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.SqliteMapper\Source\SqliteDatabase.cs


using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Data.Sqlite;

namespace Updk7.Data.SqliteMapper
{
    public class SqliteDatabase : IDisposable
    {
        private SqliteScriptsFactory _scriptsFactory = new SqliteScriptsFactory();

        public SqliteDatabase(SqliteSchema schema)
        {
            Schema = schema;
            MasterTable = new SqliteDatabaseMasterTable(this);
            SequenceTable = new SqliteDatabaseSequenceTable(this);
        }

        public SqliteSchema Schema { get; }

        public SqliteConnection Connection { get; private set; }

        public SqliteDatabaseMasterTable MasterTable { get; }

        public SqliteDatabaseSequenceTable SequenceTable { get; }

        public ISqliteDatabaseInitializer Initializer { get; set; }

        public void Open()
        {
            if (Connection != null)
                return;

            Connection = new SqliteConnection(Schema.ConnectionString);
            Connection.Open();

            Schema.Build();
            MasterTable.Update();
            SequenceTable.Update();

            if (MasterTable.TablesCount == 0)
            {
                CreateSchema();
                if (Initializer != null)
                    Initializer.SeedData(this);
            }
        }

        public void Close()
        {
            Dispose();
        }

        public void Dispose()
        {
            if (Connection != null)
            {
                Connection.Dispose();
                Connection = null;
            }
        }

        public virtual void Insert<TEntitie>(TEntitie entitie) where TEntitie : class
        {
            var table = Schema.GetTable<TEntitie>();
            var script = _scriptsFactory.CreateInsertScript(table, entitie);

            ExecuteScript(script);

            if (table.IsAutogeneratedKey)
            {
                SequenceTable.Update();
                var key = table.KeyColumn.Converter.ConvertBack(SequenceTable.SequenceValue(table.Name));
                table.KeyColumn.DataPropertyInfo.SetValue(entitie, key);
            }
        }

        public virtual void Update<TEntitie>(TEntitie entitie) where TEntitie : class
        {
            var script = _scriptsFactory.CreateUpdateScript(Schema.GetTable<TEntitie>(), entitie);
            ExecuteScript(script);
        }

        public virtual IEnumerable<TEntitie> Get<TEntitie>(string searchCondition = null) where TEntitie : class, new()
        {
            var table = Schema.GetTable<TEntitie>();
            var script = _scriptsFactory.CreateSelectScript(table, searchCondition);

            var command = Connection.CreateCommand();
            command.CommandText = script;

            var results = new List<TEntitie>();
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    var entitie = new TEntitie();
                    foreach (var column in table.Columns)
                    {
                        var value = column.Converter.ConvertBack(reader[column.Name]);
                        column.DataPropertyInfo.SetValue(entitie, value);
                    }

                    results.Add(entitie);
                }
            }

            return results;
        }

        public virtual TEntitie GetById<TEntitie>(int id) where TEntitie : class, new()
        {
            var table = Schema.GetTable<TEntitie>();
            var searchCondition = $"{table.KeyColumn.Name} = {id}";
            return Get<TEntitie>(searchCondition).FirstOrDefault();
        }

        public virtual void Delete<TEntitie>(TEntitie entitie) where TEntitie : class
        {
            var script = _scriptsFactory.CreateDeleteScript(Schema.GetTable<TEntitie>(), entitie);
            ExecuteScript(script);
        }

        public void ExecuteScript(string script)
        {
            var command = Connection.CreateCommand();
            command.CommandText = script;
            command.ExecuteNonQuery();
        }

        protected virtual void CreateSchema()
        {
            var createScript = "PRAGMA foreign_keys = on;\n";
            foreach (var table in Schema.Tables)
            {
                createScript += _scriptsFactory.CreateTableScript(table);
                var indexScript = _scriptsFactory.CreateTableIndexScript(table);
                if (!string.IsNullOrEmpty(indexScript))
                    createScript += indexScript;
            }

            ExecuteScript(createScript);
            MasterTable.Update();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.SqliteMapper\Source\SqliteDatabaseMasterTable.cs


using System.Linq;
using System.Collections.Generic;

namespace Updk7.Data.SqliteMapper
{
    public class SqliteDatabaseMasterTable
    {
        public class MasterTableRow
        {
            public string Type { get; set; }

            public string Name { get; set; }

            public string TableName { get; set; }

            public string Sql { get; set; }
        }

        private static readonly string SqliteName = "sqlite_master";

        private SqliteDatabase _database;

        public SqliteDatabaseMasterTable(SqliteDatabase database)
        {
            _database = database;
        }

        public IEnumerable<MasterTableRow> Tables { get; private set; }

        public int TablesCount => Tables.Count();

        public bool ContainsTable(string tableName) => Tables.Any(t => t.TableName == tableName);

        public void Update()
        {
            var tables = new List<MasterTableRow>();

            var command = _database.Connection.CreateCommand();
            command.CommandText = $"select * from {SqliteName}";
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                    tables.Add(new MasterTableRow()
                    {
                        Type = reader["type"] as string,
                        Name = reader["name"] as string,
                        TableName = reader["tbl_name"] as string,
                        Sql = reader["sql"] as string
                    });
            }

            Tables = tables;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.SqliteMapper\Source\SqliteDatabaseSequenceTable.cs


using System.Collections.Generic;

namespace Updk7.Data.SqliteMapper
{
    public class SqliteDatabaseSequenceTable
    {
        private static readonly string SqliteName = "sqlite_sequence";

        private Dictionary<string, long> _seqTable = new Dictionary<string, long>();
        private SqliteDatabase _database;

        public SqliteDatabaseSequenceTable(SqliteDatabase database)
        {
            _database = database;
        }

        public long SequenceValue(string tableName) => _seqTable[tableName];

        public bool ContainsTable(string tableName) => _seqTable.ContainsKey(tableName);

        public void Update()
        {
            if (!_database.MasterTable.ContainsTable(SqliteName))
                return;

            var command = _database.Connection.CreateCommand();
            command.CommandText = $"select * from {SqliteName}";
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                    _seqTable[reader["name"] as string] = (long)reader["seq"];
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.SqliteMapper\Source\SqliteSchema.cs


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Updk7.Data.SqliteMapper
{
    public class SqliteSchema
    {
        private SqliteConvertersFactory _convertersFactory = new SqliteConvertersFactory();
        private List<SqliteTableIndex> _indexes = new List<SqliteTableIndex>();
        private List<Type> _entitieTypes = new List<Type>();

        public SqliteSchema(string database)
        {
            Database = database;
            ConnectionString = $"Data Source = {database}";
        }

        public string Database { get; }

        public string ConnectionString { get; }

        public IEnumerable<Type> EntitieTypes => _entitieTypes;

        public IEnumerable<SqliteTable> Tables { get; private set; }

        public SqliteTable GetTable<TEntitie>()
        {
            return Tables?.FirstOrDefault(t => t.EntitieType == typeof(TEntitie));
        }

        public SqliteSchema AddEntitie<TEntitie>()
            where TEntitie : class
        {
            Debug.Assert(_entitieTypes.All(t => t != typeof(TEntitie)));
            _entitieTypes.Add(typeof(TEntitie));
            Tables = null;
            return this;
        }

        public SqliteSchema AddIndex<TEntitie>(string propertyName)
            where TEntitie : class
        {
            var index = _indexes.FirstOrDefault(idx => idx.Entitie == typeof(TEntitie));
            if (index == null)
                index = new SqliteTableIndex(typeof(TEntitie));

            index.AppendIndexProperty(propertyName);
            _indexes.Add(index);
            return this;
        }

        public void Build()
        {
            if (_entitieTypes.Count == 0 || Tables != null)
                return;

            var tables = _entitieTypes.Select(t => new SqliteTable(t, _convertersFactory)).ToArray();
            updateForeignKeysAndIndexes(tables);

            var sortedTables = sortTablesByForeignKeys(tables);
            if (hasCycles(sortedTables))
                throw new ArgumentOutOfRangeException("Schema contains cycles");

            Tables = sortedTables;
        }

        private void updateForeignKeysAndIndexes(IEnumerable<SqliteTable> tables)
        {
            Func<Type, Type, bool> isSubclassOf = (parent, child) => parent == child || child.IsSubclassOf(parent);

            foreach (var table in tables)
            {
                table.Indexes = _indexes.FirstOrDefault(idx => idx.Entitie == table.EntitieType);
                if (table.ForeignKeys == null)
                    continue;

                foreach (var key in table.ForeignKeys)
                {
                    var foreignTable = tables.First(t => isSubclassOf(key.ForeignEntitieType, t.EntitieType));
                    key.ForeignTable = foreignTable.Name;
                    key.ForeignColumn = foreignTable.KeyColumn.Name;
                }
            }
        }
        
        private IEnumerable<SqliteTable> sortTablesByForeignKeys(IEnumerable<SqliteTable> tables)
        {
            Func<SqliteTable, SqliteTable, bool> isForeignKeyForTable = (table, foreignTable) =>
                table.ForeignKeys != null && 
                table.ForeignKeys.Any(fk => fk.ForeignTable == foreignTable.Name);

            var sequence = new List<SqliteTable>();

            foreach (var table in tables)
            {
                int? sortedPosition = null;
                for (int i = sequence.Count - 1; i >= 0; i--)
                {
                    if (isForeignKeyForTable(sequence[i], table))
                        sortedPosition = i;
                }

                if (sortedPosition.HasValue)
                    sequence.Insert(sortedPosition.Value, table);
                else
                    sequence.Add(table);
            }

            return sequence.ToArray();
        }

        private bool hasCycles(IEnumerable<SqliteTable> tables)
        {
            var resolvedTypes = new HashSet<string>();

            Func<SqliteTable, bool> notResolved = table =>
                table.ForeignKeys != null &&
                table.ForeignKeys.Any(fk => !resolvedTypes.Contains(fk.ForeignTable));

            foreach (var table in tables)
            {
                if (notResolved(table))
                    return true;

                resolvedTypes.Add(table.Name);
            }

            return false;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.SqliteMapper\Source\SqliteScriptsFactory.cs


using System;
using System.Diagnostics;
using System.Linq;

namespace Updk7.Data.SqliteMapper
{
    public class SqliteScriptsFactory
    {
        public string CreateTableScript(SqliteTable tableInfo)
        {
            Func<SqliteTableColumn, string> createColumnDefinition = ci =>
            {
                var result = $"{ci.Name} {ci.DatabaseType}";
                if (!ci.Nullable)
                    result += " not null";
                if (ci.IsPrimaryKey)
                {
                    result += ci.IsAutogenerategPrimaryKey
                        ? " primary key autoincrement"
                        : " primary key";
                }

                return result;
            };

            Func<SqliteTableForeignKey, string> createForeignKeyDefinition = fk =>
                $"foreign key ({fk.ColumnName}) references {fk.ForeignTable} ({fk.ForeignColumn}) on update no action on delete no action";

            return $"create table {tableInfo.Name}(\n" +
                tableInfo.Columns.Select(createColumnDefinition)
                .Concat(tableInfo.ForeignKeys.Select(createForeignKeyDefinition))
                .Aggregate(string.Empty, (acc, v) => !string.IsNullOrEmpty(acc) ? $"{acc},\n{v}" : $"{v}")
                + ");\n";
        }

        public string CreateTableIndexScript(SqliteTable table)
        {
            if (table.Indexes == null)
                return null;

            var columnNames = table.Indexes.IndexProperties
                .Select(pName => table.Columns.First(c => c.DataPropertyInfo.Name == pName).Name);

            return $"create index idx_{table.Name} \non {table.Name} (" +
                columnNames.Aggregate(string.Empty, (acc, v) => !string.IsNullOrEmpty(acc) ? $"{acc}, {v}" : v) 
                + ");\n";
        }

        public string CreateInsertScript(SqliteTable table, object entitie)
        {
            Func<SqliteTableColumn, bool> ignoreInInsert = ci =>
            {
                // Игнорируем столбец ключа таблицы с автогенерацией, если он не задан. При этом 
                // значение ключа будет сгенерировано автоматически БД. Ключ при автогенерации всегда int
                if (table.IsAutogeneratedKey && table.KeyColumn == ci)
                {
                    var keyValue = (int)ci.DataPropertyInfo.GetValue(entitie);
                    return keyValue == 0;
                }

                return false;
            };

            var insertedColumns = table.Columns.Where(ci => !ignoreInInsert(ci));
            var insertedValues = insertedColumns.Select(ci => ci.Convert(entitie));

            return $"insert into {table.Name} (" +
                insertedColumns.Select(c => c.Name)
                .Aggregate(string.Empty, (acc, v) => !string.IsNullOrEmpty(acc) ? $"{acc}, {v}" : v) +
                ")\nvalues(" +
                insertedValues.Aggregate(string.Empty, (acc, v) => !string.IsNullOrEmpty(acc) ? $"{acc}, {v}" : v) +
                ");\n";
        }

        public string CreateUpdateScript(SqliteTable table, object entitie)
        {
            Debug.Assert(table.KeyColumn != null);
            var searchCondition = $"{table.KeyColumn.Name} = {table.KeyColumn.Convert(entitie)}";
            return CreateUpdateScript(table, entitie, searchCondition);
        }

        public string CreateUpdateScript(SqliteTable table, object entitie, string searchCondition)
        {
            Func<SqliteTableColumn, bool> ignoreInUpdate = ci =>
                table.IsAutogeneratedKey && table.KeyColumn == ci;

            var setValues = table.Columns
                .Where(ci => !ignoreInUpdate(ci))
                .Select(ci => $"{ci.Name} = {ci.Convert(entitie)}");

            return $"update {table.Name}\nset\n" +
                setValues.Aggregate(string.Empty, (acc, v) => !string.IsNullOrEmpty(acc) ? $"{acc},\n{v}" : v) +
                $"\nwhere\n{searchCondition};";
        }

        public string CreateDeleteScript(SqliteTable table, object entitie)
        {
            Debug.Assert(table.KeyColumn != null);
            var searchCondition = $"{table.KeyColumn.Name} = {table.KeyColumn.Convert(entitie)}";
            return CreateDeleteScript(table, searchCondition);
        }

        public string CreateDeleteScript(SqliteTable table, string searchCondition)
        {
            return $"delete from {table.Name} where {searchCondition};";
        }

        public string CreateSelectScript(SqliteTable table, string searchCondition = null)
        {
            var script = $"select * from {table.Name}";
            if (!string.IsNullOrEmpty(searchCondition))
                script += $"\nwhere {searchCondition}";
            return $"{script};";
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.SqliteMapper\Source\SqliteTable.cs


using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Updk7.Data.SqliteMapper
{
    public class SqliteTable
    {
        private List<SqliteTableColumn> _columns = new List<SqliteTableColumn>();
        private List<SqliteTableForeignKey> _foreignKeys = new List<SqliteTableForeignKey>();

        public SqliteTable(Type entitieType, SqliteConvertersFactory convertersFactory)
        {
            createDescription(entitieType, convertersFactory);
        }

        public string Name { get; private set; }

        public Type EntitieType { get; private set; }

        public IEnumerable<SqliteTableColumn> Columns => _columns;

        public IEnumerable<SqliteTableForeignKey> ForeignKeys => _foreignKeys;

        public SqliteTableIndex Indexes { get; set; }

        public SqliteTableColumn KeyColumn { get; private set; }

        public bool IsAutogeneratedKey => KeyColumn != null && KeyColumn.IsAutogenerategPrimaryKey;

        public SqliteTableColumn this[string columnName] => _columns.FirstOrDefault(c => c.Name == columnName);

        private void createDescription(Type entitieType, SqliteConvertersFactory convertersFactory)
        {
            var tableAttribute = entitieType.GetCustomAttribute<TableAttribute>();
            Name = tableAttribute != null ? tableAttribute.Name : $"{entitieType.Name}s";
            EntitieType = entitieType;

            var entitieProperties = entitieType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .OrderBy(p => p.MetadataToken)
                .ToArray();

            foreach (var dataPropertyInfo in entitieProperties.Where(canMapPropertie))
            {
                var column = createTableColumn(dataPropertyInfo);
                column.Converter = convertersFactory.CreateConverter(column);

                if (column.IsPrimaryKey)
                {
                    Debug.Assert(KeyColumn == null);
                    KeyColumn = column;
                }

                if (column.IsForeignKey)
                {
                    column.NavigationPropertyType = getNavigationPropertyType(column.NavigationPropertyName,
                        entitieProperties);

                    _foreignKeys.Add(new SqliteTableForeignKey(column.NavigationPropertyType, column.Name));
                }

                _columns.Add(column);
            }
        }

        private SqliteTableColumn createTableColumn(PropertyInfo dataPropertyInfo)
        {
            var column = new SqliteTableColumn();
            column.Name = getColumnName(dataPropertyInfo);
            column.DataPropertyInfo = dataPropertyInfo;

            // Primary key
            if (dataPropertyInfo.GetCustomAttribute<KeyAttribute>() != null)
            {
                column.IsPrimaryKey = true;
                var generated = dataPropertyInfo.GetCustomAttribute<DatabaseGeneratedAttribute>();
                if (generated != null)
                    column.PrimaryKeyGeneratedOptions = generated.DatabaseGeneratedOption;
            }

            // Foreign key
            var foreignKeyAttribute = dataPropertyInfo.GetCustomAttribute<ForeignKeyAttribute>();
            if (foreignKeyAttribute != null)
            {
                column.IsForeignKey = true;
                column.NavigationPropertyName = foreignKeyAttribute.Name;
            }

            // Nullable value
            if (dataPropertyInfo.PropertyType.IsValueType && dataPropertyInfo.PropertyType.IsGenericType)
            {
                column.DataType = dataPropertyInfo.PropertyType.GenericTypeArguments[0];
                column.Nullable = true;
            }
            else
                column.DataType = dataPropertyInfo.PropertyType;

            // Array (as blob)
            if (column.DataType.IsArray)
                column.Nullable = true;

            // String
            if (column.DataType == typeof(string))
            {
                var requiredAttribute = dataPropertyInfo.GetCustomAttribute<RequiredAttribute>();
                if (requiredAttribute != null)
                {
                    column.AllowEmptyString = requiredAttribute.AllowEmptyStrings;
                    column.Nullable = requiredAttribute.AllowEmptyStrings;
                }
                else
                    column.Nullable = true;
            }

            // Sqlite type
            column.DatabaseType = SqliteTypes.GetDbType(column.DataType);
            Debug.Assert(!string.IsNullOrEmpty(column.DatabaseType));

            return column;
        }

        private bool canMapPropertie(PropertyInfo info)
        {
            // Not mapped -> false
            if (info.GetCustomAttribute<NotMappedAttribute>() != null)
                return false;

            // Nullable -> true
            if (info.PropertyType.IsValueType && info.PropertyType.IsGenericType)
                return true;

            // Composite types -> false
            if (string.IsNullOrEmpty(SqliteTypes.GetDbType(info.PropertyType)))
                return false;

            return true;
        }

        private Type getNavigationPropertyType(string foreignKeyProperty, PropertyInfo[] entitieProperties)
        {
            var keyProperty = entitieProperties.First(p => p.Name == foreignKeyProperty);
            return keyProperty.PropertyType.IsGenericType
                ? keyProperty.PropertyType.GenericTypeArguments[0]
                : keyProperty.PropertyType;
        }

        private string getColumnName(PropertyInfo info)
        {
            var columnAttribute = info.GetCustomAttribute<ColumnAttribute>();
            return columnAttribute != null
                ? columnAttribute.Name
                : info.Name;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.SqliteMapper\Source\SqliteTableColumn.cs


using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;

namespace Updk7.Data.SqliteMapper
{
    public class SqliteTableColumn
    {
        public SqliteTableColumn()
        {
        }

        public string Name { get; internal set; }

        public bool IsPrimaryKey { get; internal set; }

        public DatabaseGeneratedOption? PrimaryKeyGeneratedOptions { get; internal set; }

        public bool IsAutogenerategPrimaryKey => IsPrimaryKey
            && PrimaryKeyGeneratedOptions.HasValue
            && PrimaryKeyGeneratedOptions.Value == DatabaseGeneratedOption.Identity;

        public bool IsForeignKey { get; internal set; }

        public string NavigationPropertyName { get; internal set; }

        public Type NavigationPropertyType { get; internal set; }

        public bool Nullable { get; internal set; }

        public bool? AllowEmptyString { get; internal set; }

        public Type DataType { get; internal set; }

        public PropertyInfo DataPropertyInfo { get; internal set; }

        public string DatabaseType { get; internal set; }

        public IPropertyConverter Converter { get; internal set; }

        public string Convert(object entitie)
        {
            var propertyValue = DataPropertyInfo.GetValue(entitie);

            if (DataType == typeof(string))
            {
                if (AllowEmptyString.HasValue && !AllowEmptyString.Value)
                {
                    if (string.IsNullOrEmpty(propertyValue as string))
                        throw new ArgumentException("AllowEmptyString = false", DataPropertyInfo.Name);
                }
            }

            return Converter.Convert(propertyValue);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.SqliteMapper\Source\SqliteTableForeignKey.cs


using System;

namespace Updk7.Data.SqliteMapper
{
    public class SqliteTableForeignKey
    {
        public SqliteTableForeignKey()
        {
        }

        public SqliteTableForeignKey(Type entitieType)
        {
            ForeignEntitieType = entitieType;
        }

        public SqliteTableForeignKey(Type entitieType, string columnName)
        {
            ForeignEntitieType = entitieType;
            ColumnName = columnName;
        }

        public string ColumnName { get; internal set; }

        public Type ForeignEntitieType { get; internal set; }

        public string ForeignTable { get; internal set; }

        public string ForeignColumn { get; internal set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.SqliteMapper\Source\SqliteTableIndex.cs


using System;
using System.Collections.Generic;

namespace Updk7.Data.SqliteMapper
{
    public class SqliteTableIndex
    {
        private List<string> _indexProperties = new List<string>();

        public SqliteTableIndex(Type entitie)
        {
            Entitie = entitie;
        }

        public Type Entitie { get; }

        public IEnumerable<string> IndexProperties => _indexProperties;

        public void AppendIndexProperty(string propertyName)
        {
            _indexProperties.Add(propertyName);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Data\Updk7.Data.SqliteMapper\Source\SqliteTypes.cs


using System;

namespace Updk7.Data.SqliteMapper
{
    public class SqliteTypes
    {
        public static readonly string Int = "Integer";
        public static readonly string Text = "Text";
        public static readonly string Real = "Real";
        public static readonly string Blob = "Blob";

        public static string GetDbType(Type sourceType)
        {
            if (sourceType == typeof(long)
                || sourceType == typeof(int)
                || sourceType == typeof(short)
                || sourceType == typeof(bool)
                || sourceType.IsEnum)
            {
                return Int;
            }
            else if (sourceType == typeof(float)
                || sourceType == typeof(double))
            {
                return Real;
            }
            else if (sourceType == typeof(string)
                || sourceType == typeof(DateTime))
            {
                return Text;
            }
            else if (sourceType == typeof(byte[]))
            {
                return Blob;
            }
            else
                return null;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\ErrorFlags.cs


using System;

namespace Updk7.Network.Local
{
    [Flags]
    public enum ErrorFlags
    {
        NoErrors = 0,
        HardwareError = 1 << 1,
        ActivationKey = 1 << 2,
        TestInternalError = 1 << 3
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Master\IUpdkServiceContract.cs


using System.ServiceModel;

namespace Updk7.Network.Local.Master
{
    [ServiceContract(CallbackContract = typeof(Slave.IUpdkServiceClientContract))]
    public interface IUpdkServiceContract
    {
        [OperationContract()]
        void Ping();

        [OperationContract(IsInitiating = true)]
        void RegisterClient(string clientName);

        [OperationContract(IsOneWay = true)]
        void SetTestProgress(int progress);

        [OperationContract(IsOneWay = true)]
        void SetTestResults(byte[] data);

        [OperationContract(IsOneWay = true)]
        void SetError(ErrorFlags flag, bool flagValue);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Master\UpdkClientProxy.cs


using System;

namespace Updk7.Network.Local.Master
{
    public class UpdkClientProxy : Prism.Mvvm.BindableBase, Slave.IUpdkServiceClientContract
    {
        public UpdkClientProxy(Slave.IUpdkServiceClientContract clientChannel)
        {
            ClientChannel = clientChannel;
        }

        public event EventHandler<Tests.TestResults> TestComplete;

        internal Slave.IUpdkServiceClientContract ClientChannel { get; set; }

        private string _address;

        public string Address
        {
            get { return _address; }
            internal set { SetProperty(ref _address, value); }
        }

        private string _title;

        public string Title
        {
            get { return _title; }
            internal set { SetProperty(ref _title, value); }
        }

        private int _testProgress;

        public int TestProgress
        {
            get { return _testProgress; }
            private set { SetProperty(ref _testProgress, value); }
        }

        private ErrorFlags _errors = ErrorFlags.NoErrors;

        public ErrorFlags Errors
        {
            get { return _errors; }
            private set { SetProperty(ref _errors, value); }
        } 

        private bool _isTestRunning;

        public bool IsTestRunning
        {
            get { return _isTestRunning; }
            set { SetProperty(ref _isTestRunning, value); }
        }

        public void StartTest(Tests.TestType test, Tests.TestParameter[] parameters)
        {
            ClientChannel.StartTest(test, parameters);
            IsTestRunning = true;
        }

        public void BreakTest()
        {
            ClientChannel.BreakTest();
            IsTestRunning = false;
        }

        public void ShowWelcome()
        {
            ClientChannel.ShowWelcome();
        }

        void Slave.IUpdkServiceClientContract.Ping()
        {
            ClientChannel.Ping();
        }

        internal void SetTestProgress(int progress)
        {
            TestProgress = progress;
        }

        internal void SetTestResults(byte[] data)
        {
            Tests.TestResults results = null;
            try
            {
                results = Tests.TestResultsSerializer.Deserialize(data);
            }
            catch (Exception)
            {
            }

            IsTestRunning = false;
            TestComplete?.Invoke(this, results);
        }

        internal void SetError(ErrorFlags flag, bool value)
        {
            if (value)
                Errors |= flag;
            else
                Errors &= ~flag;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Master\UpdkService.cs


using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.ServiceModel;
using System.Threading;

namespace Updk7.Network.Local.Master
{
    [ServiceBehavior(
        InstanceContextMode = InstanceContextMode.Single,
        ConcurrencyMode = ConcurrencyMode.Multiple,
        UseSynchronizationContext = false)]
    public class UpdkService : IUpdkServiceContract, IDisposable
    {
        private UpdkServiceClientsMonitor _clientsMonitor = new UpdkServiceClientsMonitor();
        private SynchronizationContext _syncContext;
        private UpdkServiceHost _host;
        private int _port;

        public UpdkService(int port)
        {
            _port = port;
            _syncContext = SynchronizationContext.Current;
            _clientsMonitor.ObservedClients = Clients;
            _clientsMonitor.DisconnectedClientsChanged += onDisconnectedClientsChanged;
        }

        public ObservableCollection<UpdkClientProxy> Clients { get; } =
            new ObservableCollection<UpdkClientProxy>();

        public bool IsBusy => Clients.Any(c => c.IsTestRunning);

        public void Dispose()
        {
            _clientsMonitor.Dispose();
        }

        public void Run()
        {
            if (_host == null)
            {
                _host = new UpdkServiceHost(this, _port);
                _host.Open();
                _clientsMonitor.Run();
            }
        }

        void IUpdkServiceContract.Ping()
        {
        }

        void IUpdkServiceContract.RegisterClient(string clientName)
        {
            System.Diagnostics.Debug.WriteLine($"register client");

            var callbackChannel = getCallbackChannel();
            var clientProxy = new UpdkClientProxy(callbackChannel);
            clientProxy.Address = Wcf.ServiceAddress.GetServiceClientAddress(OperationContext.Current);
            clientProxy.Title = clientName;

            _syncContext.Post(_ => Clients.Add(clientProxy), null);
        }

        void IUpdkServiceContract.SetTestProgress(int progress)
        {
            var proxy = getCurrentProxy();
            if (proxy != null)
                _syncContext.Post(_ => proxy.SetTestProgress(progress), null);
        }

        void IUpdkServiceContract.SetTestResults(byte[] data)
        {
            var proxy = getCurrentProxy();
            if (proxy != null)
                _syncContext.Post(_ => proxy.SetTestResults(data), null);
        }

        void IUpdkServiceContract.SetError(ErrorFlags flag, bool flagValue)
        {
            // Флаг ошибки приходит сразу после подключения ведомого, когда прокси в коллекцию Clients
            // еще не была добавлена в контексте _syncContext, поэтому поиск прокси переносим в тот же 
            // поток, что и его добавление. В других методах это не нужно, так как работа с тестами
            // управляется через пользовательский интерфейс, чем гарантируется наличие прокси в
            // коллекции Clients
            var callbackChannel = getCallbackChannel();
            _syncContext.Post(_ =>
            {
                var proxy = Clients.FirstOrDefault(c => c.ClientChannel == callbackChannel);
                if (proxy != null)
                    proxy.SetError(flag, flagValue);
            }, null);
        }

        private void onDisconnectedClientsChanged(object sender, EventArgs e)
        {
            var disconnectedClients = _clientsMonitor.DisconnectedClients;
            foreach (var proxy in disconnectedClients)
                Clients.Remove(proxy);
        }

        private UpdkClientProxy getCurrentProxy()
        {
            var callbackChannel = getCallbackChannel();
            return Clients.FirstOrDefault(c => c.ClientChannel == callbackChannel);
        }

        private Slave.IUpdkServiceClientContract getCallbackChannel()
        {
            return OperationContext.Current.GetCallbackChannel<Slave.IUpdkServiceClientContract>();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Master\UpdkServiceClientsMonitor.cs


using System.Collections.Generic;

namespace Updk7.Network.Local.Master
{
    public class UpdkServiceClientsMonitor : Wcf.ServiceClientsMonitor<UpdkClientProxy>
    {
        public UpdkServiceClientsMonitor(IReadOnlyCollection<UpdkClientProxy> observedClients = null)
            : base(observedClients)
        {
        }

        protected override void PingClient(UpdkClientProxy proxy)
        {
            (proxy as Slave.IUpdkServiceClientContract).Ping();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Master\UpdkServiceHost.cs


namespace Updk7.Network.Local.Master
{
    public class UpdkServiceHost : Wcf.ServiceHost<IUpdkServiceContract, UpdkService>
    {
        public UpdkServiceHost(UpdkService service, int port)
            : base(service, Wcf.ServiceAddress.CreateHostAddress<IUpdkServiceContract>(port))
        {
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Slave\IUpdkServiceClientContract.cs


using System.ServiceModel;

namespace Updk7.Network.Local.Slave
{
    public interface IUpdkServiceClientContract
    {
        [OperationContract]
        void Ping();

        [OperationContract(IsOneWay = true)]
        void StartTest(Tests.TestType test, Tests.TestParameter[] parameters);

        [OperationContract(IsOneWay = true)]
        void BreakTest();

        [OperationContract(IsOneWay = true)]
        void ShowWelcome();
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Slave\StartTestEventArgs.cs


using System;

namespace Updk7.Network.Local.Slave
{
    public class StartTestEventArgs : EventArgs
    {
        public StartTestEventArgs(Tests.TestType test, Tests.TestParameter[] parameters)
        {
            Test = test;
            Parameters = parameters;
        }

        public Tests.TestType Test { get; }

        public Tests.TestParameter[] Parameters { get; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Slave\UpdkMasterChannel.cs


using System.ServiceModel;
using System.ServiceModel.Channels;

namespace Updk7.Network.Local.Slave
{
    public class UpdkMasterChannel : DuplexClientBase<Master.IUpdkServiceContract>
    {
        public UpdkMasterChannel(InstanceContext instanceContext) : base(instanceContext)
        {
        }

        public UpdkMasterChannel(InstanceContext instanceContext, Binding binding, string endpoint)
            : base(instanceContext, binding, new EndpointAddress(endpoint))
        {
        }

        public Master.IUpdkServiceContract Service => Channel;
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Slave\UpdkServiceMonitor.cs


using System;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace Updk7.Network.Local.Slave
{
    public class UpdkServiceMonitor : Wcf.ServiceMonitor<UpdkMasterChannel>
    {
        private string _instanceName;

        public UpdkServiceMonitor(string endpoint, string instanceName,
            InstanceContext instanceContext) : base(endpoint, instanceContext)
        {
            _instanceName = instanceName;
        }

        protected override UpdkMasterChannel CreateServiceProxy(InstanceContext instanceContext,
            Binding binding, string endpoint)
        {
            return new UpdkMasterChannel(instanceContext, binding, endpoint);
        }

        protected override void OpenClient()
        {
            ServiceProxy.Open();
            ServiceProxy.Service.RegisterClient(_instanceName);
        }

        protected override bool IsServiceConnected()
        {
            try
            {
                ServiceProxy.Service.Ping();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Slave\UpdkServiceProxy.cs


using System;
using System.ServiceModel;
using System.Threading;

namespace Updk7.Network.Local.Slave
{
    [CallbackBehavior(
        ConcurrencyMode = ConcurrencyMode.Reentrant,
        UseSynchronizationContext = false)]
    public class UpdkServiceProxy : Prism.Mvvm.BindableBase, 
        IUpdkServiceClientContract, 
        IDisposable
    {
        private SynchronizationContext _syncContext;
        private UpdkServiceMonitor _serviceMonitor;

        public UpdkServiceProxy(string ipAddress, string instanceName)
        {
            var serviceAddress = Wcf.ServiceAddress.CreateServiceAddress<Master.IUpdkServiceContract>(ipAddress);
            _syncContext = SynchronizationContext.Current;
            _serviceMonitor = new UpdkServiceMonitor(serviceAddress, instanceName, new InstanceContext(this));
            _serviceMonitor.IsConnectedChanged += (o, e) => IsConnected = _serviceMonitor.IsConnected;

            ServiceAddress = ipAddress;
            InstanceName = instanceName;
        }

        public event EventHandler<StartTestEventArgs> StartTest;

        public event EventHandler BreakTest;

        public event EventHandler ShowWelcome;

        public string ServiceAddress { get; private set; }

        public string InstanceName { get; private set; }

        public UpdkMasterChannel ServiceProxy => _serviceMonitor.ServiceProxy;

        private bool _isConnected;

        public bool IsConnected
        {
            get { return _isConnected; }
            private set { SetProperty(ref _isConnected, value); }
        }

        public void Dispose()
        {
            _serviceMonitor.Dispose();
        }

        public void Run()
        {
            _serviceMonitor.Run();
        }

        public void SetTestProgress(int progress)
        {
            try
            {
                if (_serviceMonitor.IsConnected)
                    ServiceProxy.Service.SetTestProgress(progress);
            }
            catch
            {
            }
        }

        public void SetTestResults(Tests.TestResults results)
        {
            try
            {
                if (_serviceMonitor.IsConnected)
                {
                    var data = Tests.TestResultsSerializer.Serialize(results);
                    ServiceProxy.Service.SetTestResults(data);
                }
            }
            catch
            {
            }
        }

        public void SetError(ErrorFlags flag, bool flagValue)
        {
            try
            {
                if (_serviceMonitor.IsConnected)
                    ServiceProxy.Service.SetError(flag, flagValue);
            }
            catch
            {
            }
        }

        void IUpdkServiceClientContract.Ping()
        {
        }

        void IUpdkServiceClientContract.StartTest(Tests.TestType test, Tests.TestParameter[] parameters)
        {
            _syncContext.Post(_ => StartTest?.Invoke(this, new StartTestEventArgs(test, parameters)), null);
        }

        void IUpdkServiceClientContract.BreakTest()
        {
            _syncContext.Post(_ => BreakTest?.Invoke(this, EventArgs.Empty), null);
        }

        void IUpdkServiceClientContract.ShowWelcome()
        {
            _syncContext.Post(_ => ShowWelcome?.Invoke(this, EventArgs.Empty), null);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Wcf\ServiceAddress.cs


using System;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace Updk7.Network.Local.Wcf
{
    public static class ServiceAddress
    {
        private static Lazy<string> _localIp = new Lazy<string>(() => GetHostIp(Dns.GetHostName()));

        public static string CreateHostAddress<TService>(int port)
        {
            Debug.Assert(port > 0);
            return $"net.tcp://localhost:{port}/{typeof(TService).Name}";
        }

        public static string CreateServiceAddress<TService>(string ipAddress, int port)
        {
            Debug.Assert(port > 0);
            return $"net.tcp://{ipAddress}:{port}/{typeof(TService).Name}";
        }

        public static string CreateServiceAddress<TService>(string ipAddress)
        {
            return $"net.tcp://{ipAddress}/{typeof(TService).Name}";
        }

        public static string GetServiceClientAddress(OperationContext context)
        {
            var prop = OperationContext.Current.IncomingMessageProperties;
            var endpoint = prop[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
            var ip = endpoint.Address;

            return ((ip == "::1") || (ip == "127.0.0.1"))
                ? GetLocalIp()
                : ip;
        }

        public static string GetLocalIp()
        {
            return _localIp.Value;
        }

        public static string GetHostIp(string hostName)
        {
            try
            {
                var host = Dns.GetHostEntry(hostName);

                foreach (var ip in host.AddressList)
                {
                    if (ip.AddressFamily == AddressFamily.InterNetwork)
                        return ip.ToString();
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"*** Excteption {nameof(ServiceAddress)}.{nameof(GetHostIp)}");
                Debug.WriteLine(ex.Message);
            }

            return "127.0.0.1";
        }

        public static bool IsLocalIp(string address)
        {
            var splitAddress = address.Split(':');
            return splitAddress[0] == _localIp.Value
                || splitAddress[0] == "localhost"
                || splitAddress[0] == "127.0.0.1";
        }

        public static string ResolveHostName(string address)
        {
            try
            {
                IPHostEntry entry = Dns.GetHostEntry(address);
                if (entry != null)
                    return entry.HostName;
            }
            catch (SocketException)
            {
            }

            return address;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Wcf\ServiceClientsMonitor.cs


using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Updk7.Network.Local.Wcf
{
    public abstract class ServiceClientsMonitor<TClientProxy> : IDisposable
        where TClientProxy : class
    {
        protected TimeSpan PollPeriod = TimeSpan.FromSeconds(1);

        private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
        private Task _pollTask;
        private bool _disposed = false;
        private bool _isRunning = false;

        public ServiceClientsMonitor(IReadOnlyCollection<TClientProxy> observedClients = null)
        {
            ObservedClients = observedClients;
        }

        public event EventHandler DisconnectedClientsChanged;

        public bool IsRunning => _isRunning;

        public IReadOnlyCollection<TClientProxy> ObservedClients { get; set; }

        private IReadOnlyCollection<TClientProxy> _disconnectedClients;

        public IReadOnlyCollection<TClientProxy> DisconnectedClients
        {
            get { return _disconnectedClients; }
            private set
            {
                if (_disconnectedClients != value)
                {
                    _disconnectedClients = value;
                    DisconnectedClientsChanged?.Invoke(this, EventArgs.Empty);
                }
            }
        }

        public void Run()
        {
            if (!_isRunning)
            {
                var progress = new Progress<List<TClientProxy>>(cl => DisconnectedClients = cl);
                _pollTask = Task.Run(() => pollLoop(_cancellationTokenSource.Token, progress));
                _isRunning = true;
            }
        }

        public void Dispose()
        {
            if (!_disposed)
            {
                _cancellationTokenSource.Cancel();
                _disposed = true;
            }
        }

        protected abstract void PingClient(TClientProxy proxy);

        private void pollLoop(CancellationToken ct, IProgress<List<TClientProxy>> progress)
        {
            while (true)
            {
                try
                {
                    Task.Delay(PollPeriod, ct).Wait(ct);
                    var disconnectedClients = getDisconnectedClients();
                    if (disconnectedClients != null && disconnectedClients.Count != 0)
                        progress.Report(disconnectedClients);
                }
                catch (OperationCanceledException)
                {
                    break;
                }
            }
        }

        private List<TClientProxy> getDisconnectedClients()
        {
            List<TClientProxy> disconnected = null;

            try
            {
                if (ObservedClients != null && ObservedClients.Count != 0)
                {
                    var clientsProxy = new List<TClientProxy>();
                    clientsProxy.AddRange(ObservedClients);

                    foreach (var proxy in clientsProxy)
                    {
                        try
                        {
                            PingClient(proxy);
                        }
                        catch (Exception)
                        {
                            if (disconnected == null)
                                disconnected = new List<TClientProxy>();

                            disconnected.Add(proxy);
                        }
                    }
                }
            }
            catch (Exception)
            {
            }

            return disconnected;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Wcf\ServiceHost.cs


using System.ServiceModel;

namespace Updk7.Network.Local.Wcf
{
    public class ServiceHost<TContract, TImplementation> : ServiceHost
    {
        public ServiceHost(TImplementation service, string baseAddress)
            : base(service)
        {
            Service = service;
            addEndpoint(baseAddress);
        }

        public TImplementation Service { get; private set; }

        private void addEndpoint(string baseAddress)
        {
            var binding = new NetTcpBinding();
            binding.HostNameComparisonMode = HostNameComparisonMode.WeakWildcard;
            binding.Security.Mode = SecurityMode.None;

            AddServiceEndpoint(typeof(TContract), binding, baseAddress);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Network\Updk7.Network.Local\Source\Wcf\ServiceMonitor.cs


using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;
using System.Threading.Tasks;

namespace Updk7.Network.Local.Wcf
{
    public abstract class ServiceMonitor<TServiceProxy> : IDisposable
        where TServiceProxy : class, ICommunicationObject
    {
        protected TimeSpan Timeout = TimeSpan.FromSeconds(3);
        protected TimeSpan PollPeriod = TimeSpan.FromSeconds(1);

        private CancellationTokenSource _cancellationSource = new CancellationTokenSource();
        private Task _pollTask;
        private TServiceProxy _serviceProxy;
        private InstanceContext _instanceContext;
        private bool _disposed = false;
        private string _endpoint;

        public ServiceMonitor(string endpoint, InstanceContext instanceContext)
        {
            _endpoint = endpoint;
            _instanceContext = instanceContext;
        }

        public event EventHandler IsConnectedChanged;

        public TServiceProxy ServiceProxy => _serviceProxy;

        private bool _isConnected;

        public bool IsConnected
        {
            get { return _isConnected; }
            private set
            {
                if (value != _isConnected)
                {
                    _isConnected = value;
                    IsConnectedChanged?.Invoke(this, EventArgs.Empty);
                }
            }
        }

        public void Run()
        {
            if (_pollTask == null)
            {
                var progress = new Progress<bool>(cs => IsConnected = cs);
                _pollTask = Task.Run(() => pollLoop(_cancellationSource.Token, progress));
            }
        }

        public void Dispose()
        {
            if (!_disposed)
            {
                _cancellationSource.Cancel();
                _disposed = true;
            }
        }

        protected virtual Binding CreateBinding()
        {
            var binding = new NetTcpBinding();
            binding.OpenTimeout = Timeout;
            binding.SendTimeout = Timeout;
            binding.HostNameComparisonMode = HostNameComparisonMode.WeakWildcard;
            binding.Security.Mode = SecurityMode.None;

            return binding;
        }

        protected abstract TServiceProxy CreateServiceProxy(InstanceContext instanceContext,
            Binding binding, string endpoint);

        protected abstract bool IsServiceConnected();

        protected virtual void OpenClient()
        {
            ServiceProxy.Open();
        }

        private void pollLoop(CancellationToken ct, IProgress<bool> progress)
        {
            var binding = CreateBinding();

            while (true)
            {
                try
                {
                    Task.Delay(PollPeriod, ct).Wait(ct);
                    if (_serviceProxy == null)
                    {
                        _serviceProxy = CreateServiceProxy(_instanceContext, binding, _endpoint);
                        openClient();
                    }

                    var isConnected = pingService();
                    if (!isConnected)
                        freeServiceClient();

                    progress.Report(isConnected);
                }
                catch (OperationCanceledException)
                {
                    break;
                }
            }

            freeServiceClient();
        }

        private void freeServiceClient()
        {
            if (_serviceProxy != null)
            {
                _serviceProxy.Abort();
                _serviceProxy = null;
            }
        }

        private void openClient()
        {
            try
            {
                OpenClient();
            }
            catch (Exception)
            {
            }
        }

        private bool pingService()
        {
            var isConnected = false;

            try
            {
                if (_serviceProxy != null && _serviceProxy.State == CommunicationState.Opened)
                    isConnected = IsServiceConnected();
            }
            catch (Exception)
            {
            }

            return isConnected;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\App.xaml

<Application x:Class="Updk7.Rutoken.Admin.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:Updk7.Rutoken.Admin"
             Startup="ApplicationStartup">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/Updk7.Wpf;component/Themes/Generic.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\App.xaml.cs


using System.Windows;

namespace Updk7.Rutoken.Admin
{
    public partial class App : Application
    {
        private void ApplicationStartup(object sender, StartupEventArgs e)
        {
            var shell = new Shell();
            shell.DataContext = new ViewModels.ShellViewModel();
            shell.Show();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\Shell.xaml

<Window x:Class="Updk7.Rutoken.Admin.Shell"
        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:Updk7.Rutoken.Admin"
        xmlns:s="clr-namespace:Updk7.Wpf.Styles;assembly=Updk7.Wpf"
        xmlns:views="clr-namespace:Updk7.Rutoken.Admin.Views"
        WindowStartupLocation="CenterScreen"
        Background="{StaticResource WindowBackground}"
        Style="{StaticResource WindowStyle}"
        Title="Rutoken - Admin 1.0"
        ResizeMode="NoResize"
        s:WindowHeader.CanBeMaximized="False"
        Height="350"
        Width="635">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="270" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!--Левая панель-->
        <Grid
            Grid.Row="0"
            Grid.Column="0">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <TextBlock
                Grid.Row="0"
                Margin="5"
                FontFamily="Comic Sans MS"
                FontSize="14"
                Text="Параметры ключа после активации"
                TextWrapping="Wrap"
                />
            <views:CommonInfoView
                Grid.Row="1"
                Margin="3,0,5,0"
                VerticalAlignment="Top"
                />
            <views:RutokenInfoView
                Grid.Row="2"
                Margin="3,10,3,3"
                DataContext="{Binding RutokenInfo}"
                />
        </Grid>
        <!--Разделитель-->
        <Border 
            Grid.Row="0"
            Grid.Column="1"
            Width="2"
            BorderThickness="0"
            BorderBrush="Black"
            Background="DarkGray"
            />
        <!--Правая половина-->
        <Grid
            Grid.Row="0"
            Grid.Column="2">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <TextBlock
                Grid.Row="0"
                Margin="5"
                FontFamily="Comic Sans MS"
                FontSize="14"
                Text="Выберет ПО, доступное по активируемому ключу"
                />
            <views:AppSelectionView
                Grid.Row="1"
                Margin="5"
                Grid.Column="2"
                DataContext="{Binding AppSelection}"
                />
        </Grid>
        <!--Строка состояния и кнопки-->
        <views:ControlBarView 
            Grid.Row="1"
            Grid.Column="0"
            Grid.ColumnSpan="3"
            DataContext="{Binding ControlBar}"
            />
    </Grid>
</Window>

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\Shell.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Rutoken.Admin
{
    /// <summary>
    /// Interaction logic for Shell.xaml
    /// </summary>
    public partial class Shell : Window
    {
        public Shell()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\ViewModels\AppSelectionViewModel.cs


using System;
using System.Collections.Generic;
using System.Linq;

namespace Updk7.Rutoken.Admin.ViewModels
{
    public class AppSelectionViewModel : Prism.Mvvm.BindableBase
    {
        private class AppSelectionAttribute : Attribute
        {
            public AppSelectionAttribute(string appTag)
            {
                AppTag = appTag;
            }

            public string AppTag { get; }
        }

        public AppSelectionViewModel()
        {
        }

        private bool _updkSlave;

        [AppSelection("Updk 7.0 - Slave")]
        public bool UpdkSlave
        {
            get { return _updkSlave; }
            set { SetProperty(ref _updkSlave, value); }
        }

        private bool _updkMasterRzd;

        [AppSelection("Updk 7.0 - Master.Rzd")]
        public bool UpdkMasterRzd
        {
            get { return _updkMasterRzd; }
            set { SetProperty(ref _updkMasterRzd, value); }
        }

        private bool _updkMasterAutoPro;

        [AppSelection("Updk 7.0 - Master.AutoPro")]
        public bool UpdkMasterAutoPro
        {
            get { return _updkMasterAutoPro; }
            set { SetProperty(ref _updkMasterAutoPro, value); }
        }

        private bool _updkMasterMetro;

        [AppSelection("Updk 7.0 - Master.Metro")]
        public bool UpdkMasterMetro
        {
            get { return _updkMasterMetro; }
            set { SetProperty(ref _updkMasterMetro, value); }
        }

        private bool _horizonSlave;

        [AppSelection("Horizon 2 - Slave")]
        public bool HorizonSlave
        {
            get { return _horizonSlave; }
            set { SetProperty(ref _horizonSlave, value); }
        }

        private bool _horizonMaster;

        [AppSelection("Horizon 2 - Master")]
        public bool HorizonMaster
        {
            get { return _horizonMaster; }
            set { SetProperty(ref _horizonMaster, value); }
        }

        public string GetSelectedApps()
        {
            var selectedApps = new List<string>();
            var propertiesInfo = GetType().GetProperties(
                System.Reflection.BindingFlags.Public | 
                System.Reflection.BindingFlags.Instance);

            foreach (var property in propertiesInfo)
            {
                var attributes = property.GetCustomAttributes(typeof(AppSelectionAttribute), false);
                if (attributes == null || attributes.Length == 0)
                    continue;

                var selected = (bool)property.GetValue(this);
                if (selected)
                    selectedApps.Add((attributes[0] as AppSelectionAttribute).AppTag);
            }

            return string.Join(", ", selectedApps);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\ViewModels\ControlBarViewModel.cs


using System;

namespace Updk7.Rutoken.Admin.ViewModels
{
    public class ControlBarViewModel : Prism.Mvvm.BindableBase
    {
        public ControlBarViewModel(Action flashDevice)
        {
            FlashDeviceCommand = new Prism.Commands.DelegateCommand(flashDevice);
        }

        public Prism.Commands.DelegateCommand FlashDeviceCommand { get; }

        private string _message;

        public string Message
        {
            get { return _message; }
            private set { SetProperty(ref _message, value); }
        }

        private bool _isErrorMessage;

        public bool IsErrorMessage
        {
            get { return _isErrorMessage; }
            private set { SetProperty(ref _isErrorMessage, value); }
        }

        public void SetMessage(string message)
        {
            Message = message;
            IsErrorMessage = false;
        }

        public void SetError(string error)
        {
            Message = error;
            IsErrorMessage = true;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\ViewModels\RutokenInfoViewModel.cs


using System.ComponentModel;

namespace Updk7.Rutoken.Admin.ViewModels
{
    public class RutokenInfoViewModel : Prism.Mvvm.BindableBase
    {
        public RutokenInfoViewModel()
        {
        }

        private string _appLit;

        public string AppList
        {
            get { return _appLit; }
            set { SetProperty(ref _appLit, value); }
        }

        private bool _containsData;

        public bool ContainsData
        {
            get { return _containsData; }
            private set { SetProperty(ref _containsData, value); }
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(AppList))
                ContainsData = !string.IsNullOrEmpty(AppList);

            base.OnPropertyChanged(args);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\ViewModels\ShellViewModel.cs


using System;
using System.ComponentModel;

namespace Updk7.Rutoken.Admin.ViewModels
{
    public class ShellViewModel : Prism.Mvvm.BindableBase
    {
        private Common.RutokenApi _rutokenApi;
        private Common.RutokenSlot[] _slots;

        public ShellViewModel()
        {
            ControlBar = new ControlBarViewModel(flashDevice);
            RutokenInfo = new RutokenInfoViewModel();
            AppSelection = new AppSelectionViewModel();
            AppSelection.PropertyChanged += onAppSelectionPropertyChanged;

            setupRutokenApi();
            validateSetup();
            updateRutokenInfo();
        }

        public ControlBarViewModel ControlBar { get; }

        public AppSelectionViewModel AppSelection { get; }

        public RutokenInfoViewModel RutokenInfo { get; }

        private void flashDevice()
        {
            if (_rutokenApi == null)
                return;

            if (validateSetup())
            {
                try
                {
                    var rutokenSlot = _slots[0];
                    if (!rutokenSlot.IsUpdkToken)
                        rutokenSlot.FormatToken();

                    var appList = AppSelection.GetSelectedApps();
                    rutokenSlot.WriteAppList(appList);
                    updateRutokenInfo();

                    ControlBar.SetMessage("Данные записаны!");
                }
                catch (Exception ex)
                {
                    ControlBar.SetError("Ошибка: " + ex.Message);
                }
            }
        }

        private void onRutokenSlotEvent(object sender, EventArgs e)
        {
            _slots = _rutokenApi.GetSlots();
            validateSetup();
            updateRutokenInfo();
        }

        private void onAppSelectionPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            validateSetup();
        }

        private void  setupRutokenApi()
        {
            try
            {
                _rutokenApi = new Common.RutokenApi();
                _rutokenApi.SlotEvent += onRutokenSlotEvent;
                _rutokenApi.RunSlotEventMonitoring();

                _slots = _rutokenApi.GetSlots();
            }
            catch (Exception)
            {
                _rutokenApi = null;
            }
        }

        private void updateRutokenInfo()
        {
            if (_slots == null || _slots.Length == 0)
            {
                RutokenInfo.AppList = string.Empty;
                return;
            }

            RutokenInfo.AppList = _slots[0].IsUpdkToken
                ? RutokenInfo.AppList = _slots[0].ReadAppList()
                : string.Empty;
        }

        private bool validateSetup()
        {
            if (_rutokenApi == null)
            {
                ControlBar.SetError("Не удалось загрузить rtPKCS11.dll. Нет драйверов?");
                return false;
            }

            var appList = AppSelection.GetSelectedApps();
            if (string.IsNullOrEmpty(appList))
            {
                ControlBar.SetError("Не выбрано ПО для активации");
                return false;
            }

            if (_slots == null || _slots.Length == 0)
                ControlBar.SetError("Rutoken не подключен");
            else if (_slots.Length != 1)
                ControlBar.SetError("Подключено более 1 устройства");
            else
            {
                ControlBar.SetMessage("Выберете ПО для активации");
                return true;
            }

            return false;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\Views\AppSelectionCheckBoxStyle.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style x:Key="AppSelectionCekBoxStyle" TargetType="{x:Type CheckBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type CheckBox}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Border
                            x:Name="PART_Border"
                            Grid.Column="0"
                            BorderThickness="2"
                            BorderBrush="LimeGreen"
                            Width="15"
                            Height="20"
                            Background="Transparent"
                            />
                        <TextBlock 
                            x:Name="PART_Text"
                            Margin="5,0,0,0"
                            Grid.Column="1"
                            FontSize="16"
                            FontWeight="Medium"
                            FontFamily="Comic Sans MS"
                            Text="{TemplateBinding Content}"
                            Foreground="{TemplateBinding Foreground}"
                            TextWrapping="Wrap"
                            VerticalAlignment="Center"
                            HorizontalAlignment="Left"
                            />
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsChecked" Value="True">
                            <Setter TargetName="PART_Border" Property="Background" Value="LimeGreen" />
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="PART_Border" Property="BorderBrush" Value="LimeGreen" />
                            <Setter TargetName="PART_Text" Property="FontWeight" Value="Bold" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\Views\AppSelectionView.xaml

<UserControl x:Class="Updk7.Rutoken.Admin.Views.AppSelectionView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Rutoken.Admin.Views"
             mc:Ignorable="d" >
    <Grid>
        <Grid.Resources>
            <ResourceDictionary Source="AppSelectionCheckBoxStyle.xaml" />
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!--Updk-->
        <Grid
            Grid.Row="0">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" SharedSizeGroup="A" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <TextBlock 
                Grid.Row="0"
                Grid.Column="0"
                Text="УПДК"
                FontFamily="Comic Sans MS"
                FontSize="20"
                FontWeight="Bold"
                Margin="5"
                Grid.ColumnSpan="2"
                />
            <StackPanel
                Grid.Row="1"
                Grid.Column="0"
                Margin="5,0,10,0">
                <CheckBox
                    Style="{StaticResource AppSelectionCekBoxStyle}"
                    Content="Updk-Slave"
                    IsChecked="{Binding UpdkSlave}"
                    />
            </StackPanel>
            <StackPanel
                Grid.Column="1"
                Grid.Row="1"
                Margin="0,0,10,0">
                <CheckBox
                    Style="{StaticResource AppSelectionCekBoxStyle}"
                    Content="Updk-Rzd"
                    IsChecked="{Binding UpdkMasterRzd}"
                    />
                <CheckBox
                    Style="{StaticResource AppSelectionCekBoxStyle}"
                    Content="Updk-Auto-Pro"
                    Margin="0,5,0,5"
                    IsChecked="{Binding UpdkMasterAutoPro}"
                    />
                <CheckBox
                    Style="{StaticResource AppSelectionCekBoxStyle}"
                    Content="Updk-Metro"
                    IsChecked="{Binding UpdkMasterMetro}"
                    />
            </StackPanel>
        </Grid>
        <!--Horizon-->
        <Grid
            Grid.Row="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="A" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBlock 
                Grid.Row="0"
                Grid.Column="0"
                Text="Горизонт-2"
                FontFamily="Comic Sans MS"
                FontSize="20"
                FontWeight="Bold"
                Margin="5"
                Grid.ColumnSpan="2"
                />
            <StackPanel
                Grid.Row="1"
                Grid.Column="0"
                Margin="5,0,10,0">
                <CheckBox
                    Style="{StaticResource AppSelectionCekBoxStyle}"
                    Content="Horizon2-Slave"
                    IsChecked="{Binding HorizonSlave}"
                    />
            </StackPanel>
            <StackPanel
                Grid.Column="1"
                Grid.Row="1"
                Margin="0,0,10,0">
                <CheckBox
                    Style="{StaticResource AppSelectionCekBoxStyle}"
                    Content="Horizon2 - Master"
                    IsChecked="{Binding HorizonMaster}"
                    />
            </StackPanel>
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\Views\AppSelectionView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Rutoken.Admin.Views
{
    /// <summary>
    /// Interaction logic for AppSelectionView.xaml
    /// </summary>
    public partial class AppSelectionView : UserControl
    {
        public AppSelectionView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\Views\CommonInfoView.xaml

<UserControl x:Class="Updk7.Rutoken.Admin.Views.CommonInfoView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Rutoken.Admin.Views"
             mc:Ignorable="d" >
    <Grid>
        <Grid.Resources>
            <Style x:Key="TextBlockNameStyle" TargetType="{x:Type TextBlock}">
                <Setter Property="Margin" Value="5,3,5,3" />
                <Setter Property="FontFamily" Value="Comic Sans MS" />
                <Setter Property="FontSize" Value="16" />
                <Setter Property="Foreground" Value="DarkGray" />
            </Style>
            <Style x:Key="TextBlockValueStyle" TargetType="{x:Type TextBlock}"
                   BasedOn="{StaticResource TextBlockNameStyle}">
                <Setter Property="FontWeight" Value="Bold" />
                <Setter Property="Foreground" Value="Black" />
            </Style>
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock
            Grid.Row="0"
            Grid.Column="0"
            Text="Key"
            Style="{StaticResource TextBlockNameStyle}"
            />
        <TextBlock
            Grid.Row="0"
            Grid.Column="1"
            Text="Neurocom-Updk-Key"
            Style="{StaticResource TextBlockValueStyle}"
            />
        <TextBlock
            Grid.Row="1"
            Grid.Column="0"
            Text="SO pswd"
            Style="{StaticResource TextBlockNameStyle}"
            />
        <TextBlock
            Grid.Row="1"
            Grid.Column="1"
            Text="2718281jik"
            Style="{StaticResource TextBlockValueStyle}"
            />
        <TextBlock
            Grid.Row="2"
            Grid.Column="0"
            Text="User pswd"
            Style="{StaticResource TextBlockNameStyle}"
            />
        <TextBlock
            Grid.Row="2"
            Grid.Column="1"
            Text="31415926zza"
            Style="{StaticResource TextBlockValueStyle}"
            />

    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\Views\CommonInfoView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Rutoken.Admin.Views
{
    /// <summary>
    /// Interaction logic for CommonInfoView.xaml
    /// </summary>
    public partial class CommonInfoView : UserControl
    {
        public CommonInfoView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\Views\ControlBarView.xaml

<UserControl x:Class="Updk7.Rutoken.Admin.Views.ControlBarView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" >
    <Grid>
        <Grid.Resources>
            <Style x:Key="MessageTextBlockStyle" TargetType="{x:Type TextBlock}">
                <Setter Property="FontFamily" Value="Comic Sans MS" />
                <Setter Property="FontSize" Value="14" />
                <Setter Property="FontWeight" Value="Medium" />
                <Setter Property="Foreground" Value="LimeGreen" />
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsErrorMessage}" Value="True">
                        <Setter Property="Foreground" Value="Red" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
            <Style x:Key="ControlButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource FlatButtonStyle}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsErrorMessage}" Value="True">
                        <Setter Property="IsEnabled" Value="False" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <TextBlock
            Grid.Column="0"
            Margin="5,3,3,3"
            VerticalAlignment="Center"
            Style="{StaticResource MessageTextBlockStyle}"
            Text="{Binding Message}"
            />
        <Button
            Grid.Column="1"
            Margin="3"
            Width="120"
            BorderThickness="2"
            Height="35"
            Style="{StaticResource ControlButtonStyle}"
            FontSize="16"
            FontFamily="Comic Sans MS"
            FontWeight="Bold"
            Content="Записать"
            Command="{Binding FlashDeviceCommand}"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\Views\ControlBarView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Rutoken.Admin.Views
{
    /// <summary>
    /// Interaction logic for ControlBarView.xaml
    /// </summary>
    public partial class ControlBarView : UserControl
    {
        public ControlBarView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\Views\RutokenInfoView.xaml

<UserControl x:Class="Updk7.Rutoken.Admin.Views.RutokenInfoView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Rutoken.Admin.Views"
             mc:Ignorable="d" >
    <Grid>
        <Grid.Resources>
            <Style x:Key="InfoTextBlockStyle" TargetType="{x:Type TextBlock}">
                <Setter Property="FontFamily" Value="Comic Sans MS" />
                <Setter Property="FontSize" Value="14" />
                <Setter Property="FontWeight" Value="Medium" />
                <Style.Triggers>
                    <DataTrigger Binding="{Binding ContainsData}" Value="False">
                        <Setter Property="Visibility" Value="Hidden" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock 
            Grid.Row="0"
            Text="Подключен Rutoken:"
            Style="{StaticResource InfoTextBlockStyle}"
            />
        <TextBlock 
            Grid.Row="1"
            Style="{StaticResource InfoTextBlockStyle}"
            Text="{Binding AppList}"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            TextWrapping="Wrap"
            Margin="3,5,0,0"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Admin\Source\Views\RutokenInfoView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Rutoken.Admin.Views
{
    /// <summary>
    /// Interaction logic for RutokenInfoView.xaml
    /// </summary>
    public partial class RutokenInfoView : UserControl
    {
        public RutokenInfoView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Common\Source\RutokenApi.cs


using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Net.Pkcs11Interop.Common;
using Net.Pkcs11Interop.HighLevelAPI;
using RutokenPkcs11Interop;

namespace Updk7.Rutoken.Common
{
    public class RutokenApi : IDisposable
    {
        private CancellationTokenSource _cancelSource = new CancellationTokenSource();
        private SynchronizationContext _syncContext;
        private object _syncRoot = new object();

        private Pkcs11 _pkcs11;
        private bool _disposed;

        public RutokenApi()
        {
            var libPath = Settings.RutokenEcpDllDefaultPath.Replace("rtPKCS11ECP", "rtPKCS11");
            _pkcs11 = new Pkcs11(libPath, AppType.MultiThreaded);
        }

        public event EventHandler SlotEvent;

        public void Dispose()
        {
            if (!_disposed)
            {
                _cancelSource.Cancel();
                lock (_syncRoot)
                {
                    _pkcs11.Dispose();
                    _disposed = true;
                }
            }
        }

        public RutokenSlot[] GetSlots()
        {
            return _pkcs11.GetSlotList(SlotsType.WithTokenPresent)
                .Select(s => new RutokenSlot(s))
                .ToArray();
        }

        public void RunSlotEventMonitoring()
        {
            if (_syncContext == null)
            {
                _syncContext = SynchronizationContext.Current;
                Task.Run(() => monitoringTask(_cancelSource.Token));
            }
        }

        private void monitoringTask(CancellationToken cancelToken)
        { 
            while (!cancelToken.IsCancellationRequested)
            {
                try
                {
                    lock (_syncRoot)
                    {
                        bool eventOccured;
                        ulong slotId;

                        _pkcs11.WaitForSlotEvent(WaitType.NonBlocking, out eventOccured, out slotId);
                        if (eventOccured)
                            _syncContext.Post(_ => SlotEvent?.Invoke(this, EventArgs.Empty), null);
                    }
                }
                catch (Exception)
                {
                }

                Task.Delay(TimeSpan.FromSeconds(1), cancelToken).Wait(cancelToken);
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Common\Source\RutokenMonitor.cs


using System;
using System.Linq;

namespace Updk7.Rutoken.Common
{
    public class RutokenMonitor : IDisposable
    {
        private RutokenApi _rutokenApi;
        private string _monitoringFeature;

        public RutokenMonitor(string monitoringFeature = null)
        {
            _monitoringFeature = monitoringFeature;
        }

        public void Dispose()
        {
            if (_rutokenApi != null)
            {
                _rutokenApi.SlotEvent -= onRutokenApiSlotEvent;
                _rutokenApi.Dispose();
                _rutokenApi = null;
            }
        }

        public event EventHandler FeaturesChanged;

        private string _features = string.Empty;

        public string Features
        {
            get { return _features; }
            private set
            {
                if (_features != value)
                {
                    _features = value;
                    FeaturesChanged?.Invoke(this, EventArgs.Empty);
                }
            }
        }

        public bool ContainsFeature(string feature = null)
        {
            if (!string.IsNullOrEmpty(feature))
                return _features.Contains(feature);
            else if (!string.IsNullOrEmpty(_monitoringFeature))
                return _features.Contains(_monitoringFeature);
            else
                return false;
        }

        public void Run()
        { 
            if (_rutokenApi == null)
            {
                try
                {
                    _rutokenApi = new RutokenApi();
                    _rutokenApi.SlotEvent += onRutokenApiSlotEvent;
                    _rutokenApi.RunSlotEventMonitoring();
                    updateFeatures();
                }
                catch (Exception)
                {
                    _rutokenApi = null;
                }
            }
        }

        private void onRutokenApiSlotEvent(object sender, EventArgs e)
        {
            updateFeatures();
        }

        private void updateFeatures()
        {
            try
            {
                Features = _rutokenApi.GetSlots()
                    .Where(s => s.IsUpdkToken)
                    .Select(s => s.ReadAppList())
                    .Where(l => !string.IsNullOrEmpty(l))
                    .ToArray()
                    .Aggregate(string.Empty, (acc, v) => string.IsNullOrEmpty(acc) ? v : $"{acc}, {v}");
            }
            catch (Exception)
            {
                Features = string.Empty;
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Rutoken\Updk7.Rutoken.Common\Source\RutokenSlot.cs


using System.Linq;
using System.Diagnostics;
using System.Collections.Generic;
using Net.Pkcs11Interop.Common;
using Net.Pkcs11Interop.HighLevelAPI;
using RutokenPkcs11Interop.Common;
using RutokenPkcs11Interop.HighLevelAPI;

namespace Updk7.Rutoken.Common
{
    public class RutokenSlot
    {
        protected static class Pins
        {
            public static string DefaultSO = @"87654321";
            public static string DefaultUser = @"12345678";
            public static string UpdkSO = @"2718281jik";
            public static string UpdkUser = @"31415926zza";
            public static string UpdkName = "Neurocom-Updk-Key";
            public static string UpdkAppsList = "updk-apps-list";
        }

        private Slot _slot;

        internal RutokenSlot(Slot slot)
        {
            _slot = slot;
            Info = _slot.GetTokenInfo();
        }

        public TokenInfo Info { get; private set; }

        public bool IsUpdkToken => Info.Label == Pins.UpdkName;

        public void FormatToken()
        {
            var initParams = new RutokenInitParam(
                Pins.UpdkSO,
                Pins.UpdkUser,
                Pins.UpdkName,
                new List<RutokenFlag>
                {
                    RutokenFlag.AdminChangeUserPin,
                    RutokenFlag.UserChangeUserPin
                },
                6,
                6,
                10,
                10,
                smMode: 0);
            
            var soPin = IsUpdkToken ? Pins.UpdkSO : Pins.DefaultSO;
            _slot.InitTokenExtended(soPin, initParams);

            Info = _slot.GetTokenInfo();
        }

        public void WriteAppList(string appsList)
        {
            Debug.Assert(IsUpdkToken);

            using (var session = _slot.OpenSession(SessionType.ReadWrite))
            {
                session.Login(CKU.CKU_USER, Pins.UpdkUser);

                var objectHandle = getUpdkObjectHandle(session);
                if (objectHandle == null)
                {
                    var attributes = new List<ObjectAttribute>()
                    {
                        new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_DATA),
                        new ObjectAttribute(CKA.CKA_TOKEN, true),
                        new ObjectAttribute(CKA.CKA_PRIVATE, true),
                        new ObjectAttribute(CKA.CKA_LABEL, Pins.UpdkAppsList),
                        new ObjectAttribute(CKA.CKA_VALUE, appsList)
                    };

                    session.CreateObject(attributes);
                }
                else
                {
                    var attributes = new List<ObjectAttribute>() 
                    {
                        new ObjectAttribute(CKA.CKA_VALUE, appsList)
                    };

                    session.SetAttributeValue(objectHandle, attributes);
                }
            }
        }

        public string ReadAppList()
        {
            Debug.Assert(IsUpdkToken);

            using (var session = _slot.OpenSession(SessionType.ReadWrite))
            {
                session.Login(CKU.CKU_USER, Pins.UpdkUser);

                var objectHandle = getUpdkObjectHandle(session);
                if (objectHandle != null)
                {
                    var objectProperties = new List<CKA>() { CKA.CKA_VALUE };
                    return session.GetAttributeValue(objectHandle, objectProperties)
                        .First()
                        .GetValueAsString();
                }
            }

            return null;
        }

        private ObjectHandle getUpdkObjectHandle(Session session)
        {
            var seekAttributes = new List<ObjectAttribute>() 
            { 
                new ObjectAttribute(CKA.CKA_LABEL, Pins.UpdkAppsList) 
            };

            var tokenObjects = session.FindAllObjects(seekAttributes);
            return tokenObjects.FirstOrDefault();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests\Source\ITest.cs


using System;
using System.ComponentModel;

namespace Updk7.Tests
{
    public class TestCompleteEventArgs : EventArgs
    {
        public TestCompleteEventArgs(TestResults results)
        {
            Results = results;
        }

        public TestResults Results { get; }

        public Exception Exception { get; set; }
    }

    public interface ITest : INotifyPropertyChanged
    {
        event EventHandler<TestCompleteEventArgs> TestComplete;

        string Title { get; }

        TestParameter[] Parameters { get; }

        int TestProgress { get; }

        void Start(TestParameter[] parameters);

        void Break();
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests\Source\Metadata.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Updk7.Tests
{
    public static partial class Metadata
    {
        private static readonly Dictionary<TestType, string> _shortNames =
            new Dictionary<TestType, string>()
            {
                [TestType.Готовность_к_тестированию_ЭПФС] = "ЭПФС",
                [TestType.Оценка_склонности_к_риску_ОСР] = "СК",
                [TestType.Распределение_внимания_РВ] = "ПРВ",
                [TestType.Оценка_монотоноустойчивости_ОМУ] = "ОМУ",
                [TestType.Оценка_бдительности_ОБ] = "ОБ",
                [TestType.Оценка_устойчивости_внимания_УВ] = "УВДР",
                [TestType.Оценка_объема_внимания_ОВ] = "ОВ",
                [TestType.Эмоциональная_устойчивость_ЭУ] = "ЭУ",
                [TestType.Оценка_глазомера_ОГ] = "ОГ-1",
                [TestType.Корректурная_проба_КП] = "КП",
                [TestType.Оценка_стрессоустойчивости_СТР] = "СТР",
                [TestType.Оценка_стрессоустойчивости_М_СТР_М] = "СТР-М",
                [TestType.Сложная_двигательная_реакция_30_СДР_30] = "СДР-30",
                [TestType.Сложная_двигательная_реакция_СДР_100] = "СДР-100",
                [TestType.Простая_двигательная_реакция_ПДР] = "ПДР-30",
                [TestType.Концентрация_внимания_КВ] = "КВ",
                [TestType.Критическая_частота_слияния_миганий_КЧСМ] = "КЧССМ",
                [TestType.Теппинг_тест_310_ТЕП_310] = "ТЕП-310",
                [TestType.Готовность_к_экстренному_действию_ГЭД] = "ГЭД-1",
                [TestType.Готовность_к_экстренному_действию_2_ГЭД_2] = "ГЭД-2",
                [TestType.Экспресс_проба_бдительности_УРБ] = "УРБ",
                [TestType.Реакция_на_движущийся_объект_РДО] = "РДО",
                [TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ] = "ПВ-ПУ",
                [TestType.Переключение_внимания_2_ПВ_2] = "ПВ-2",
                [TestType.Уровень_восприятия_скорости_и_расстояния_УВСР] = "ВПВ",
                [TestType.Сложная_двигательная_реакция_СДР_М] = "ПМ",
                [TestType.Проба_на_моторную_согласованность_МС] = "МС-1",
                [TestType.Оценка_стрессоустойчивости_СТР_M2] = "СТР-М2",
                [TestType.Оценка_монотоноустойчивости_ОМУ_М] = "ОМУ-М",
                [TestType.Оценка_монотоноустойчивости_ОМУ] = "ОМУ",
                [TestType.Готовность_к_экстренным_действиям_ГЭД_1М] = "ГЭД-1М",
                [TestType.Готовность_к_экстренным_действиям_ГЭД_2М] = "ГЭД-2М",
                [TestType.Оценка_глазомера_М] = "ОГ-2",
                [TestType.Оценка_моторной_согласованности_М_МС_м] = "МС-2",
                [TestType.Чувство_времени_ЧВ] = "ЧВ",
                [TestType.Тест_Люшера] = "Люшер",
                [TestType.Диагностики_мотивации_к_избеганию_неудач_Элерса_МИН] = "Элерс",
                [TestType.Тест_Зунга] = "Зунг",
                [TestType.Оценка_скорости_переделки_навыков_СПН] = "СПН"
            };

        private static Dictionary<TestType, TestMetadata> _testsInfo = new Dictionary<TestType, TestMetadata>();
        private static Func<TestType, string> _testNameFactory = t => t.ToString();
        private static Func<TestType, string> _testShortNameFactory = t =>
            _shortNames.ContainsKey(t) ? _shortNames[t] : _testNameFactory(t);

        static Metadata()
        {
            var nestedTypes = typeof(Tests).GetNestedTypes();
            var testTypes = getTestTypes();
             
            foreach (var type in nestedTypes)
            {
                if (!type.IsSubclassOf(typeof(TestMetadata)))
                    continue;
                 
                var info = Activator.CreateInstance(type) as TestMetadata;
                info.TestType = testTypes[type.Name];
                info.ResultKeys = getResultKeys(type);

                _testsInfo[info.TestType] = info;
            }
        }

        public static bool ContainsInfo(TestType test)
        {
            return _testsInfo.ContainsKey(test);
        }

        public static TestMetadata GetTestInfo(TestType test)
        {
            var info = _testsInfo[test];
            info.TestName = _testNameFactory(test);
            info.TestShortName = _testShortNameFactory(test);

            return info;
        }

        public static string GetTestName(TestType test)
        {
            return GetTestInfo(test).TestName;
        }

        public static string GetTestShortName(TestType test)
        {
            return GetTestInfo(test).TestShortName;
        }

        public static void SetTestNameFactory(Func<TestType, string> factory)
        {
            _testNameFactory = factory;
        }

        public static void SetTestShortNameFactory(Func<TestType, string> factory)
        {
            _testShortNameFactory = factory;
        }

        private static string[] getResultKeys(Type type)
        {
            var keysType = type.GetNestedType("Keys");

            if (keysType != null)
            {
                var fields = keysType.GetFields(BindingFlags.Public | BindingFlags.Static);
                if (fields != null)
                    return fields.Select(f => f.GetValue(null) as string).ToArray();
            }

            return new string[] { };
        }

        private static Dictionary<string, TestType> getTestTypes()
        {
            var result = new Dictionary<string, TestType>();
            foreach (TestType value in Enum.GetValues(typeof(TestType)))
            {
                var name = Enum.GetName(typeof(TestType), value);
                result[name] = value;
            }

            return result;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests\Source\Metadata.TestMetadata.cs


using System;

namespace Updk7.Tests
{
    public static partial class Metadata
    {
        public class TestMetadata
        {
            public const byte ErrorId = 255;

            public TestMetadata()
            {
            }

            public TestType TestType { get; internal set; }

            /// <summary>
            /// Флаг использования внешних устройств для проведения теста (пульта)
            /// </summary>
            public bool UseExternalDevices { get; internal set; } = true;

            /// <summary>
            /// Массив ключей результатов тестирования
            /// </summary>
            public string[] ResultKeys { get; internal set; }

            public string TestName { get; internal set; }

            public string TestShortName { get; internal set; }

            public int GetResultKeyId(string resultKey)
            {
                return resultKey == TestResults.ErrorKey
                    ? ErrorId
                    : Array.FindIndex(ResultKeys, k => k == resultKey);
            }

            public string GetResultKeyById(int keyId)
            {
                return keyId == ErrorId
                    ? TestResults.ErrorKey
                    : ResultKeys[keyId];
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests\Source\Metadata.Tests.cs


namespace Updk7.Tests
{
    public static partial class Metadata
    {
        public static class Tests
        {
            // =============================================================================================
            // Опросники
            // =============================================================================================

            // Новые ключи результатов добавлять в конец списка ключей! Старые ключи не удалять!
            // Порядковые номера ключей используются при сериализации результатов!

            public class Q_Сортировка : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Зависимость = "Зависимость";
                    public static readonly string Независимость = "Независимость";
                    public static readonly string Общительность = "Общительность";
                    public static readonly string Необщительность = "Необщительность";
                    public static readonly string Боротьба = "Принятие «борьбы»";
                    public static readonly string Неборотьба = "Избегание «борьбы»";
                    public static readonly string Флаги = "Внутренняя конфликтность и нерешительность";
                }

                public Q_Сортировка()
                {
                    UseExternalDevices = false;
                }
            }

            public class Адаптивность : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Veracity = "Достоверность";
                    public static readonly string Neuropsychic_resistance = "Нервно-психическая устойчивость";
                    public static readonly string Communicative_features = "Коммуникативные особенности";
                    public static readonly string Moral_normativity = "Моральная нормативность";
                    public static readonly string Adaptive_abilities = "Адаптивные способности";

                    public static readonly string Neuropsychic_resistance_stens = "Нервно-психическая устойчивость (в стэнах)";
                    public static readonly string Communicative_features_stens = "Коммуникативные особенности (в стэнах)";
                    public static readonly string Moral_normativity_stens = "Моральная нормативность (в стэнах)";
                    public static readonly string Adaptive_abilities_stens = "Адаптивные способности (в стэнах)";
                }

                public Адаптивность()
                {
                    UseExternalDevices = false;
                }
            }

            public class Методика_Аналогии : TestMetadata
            {
                public class Keys
                {
                    public static readonly string VerbalIntelligence = "Уровень развития вербального интеллекта";
                }

                public Методика_Аналогии()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_антиципационной_состоятельности_АС : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Common = "Общая АС";
                    public static readonly string Personal = "Личностно-ситуативная АС";
                    public static readonly string Spatial = "Пространственная АС";
                    public static readonly string Temporary = "Временная АС";
                }

                public Тест_антиципационной_состоятельности_АС()
                {
                    UseExternalDevices = false;
                }
            }

            public class Опроснок_профессиональных_предпочтений_Голланд : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Type1 = "Реалистический тип";
                    public static readonly string Type2 = "Интеллектуальный тип";
                    public static readonly string Type3 = "Социальный тип";
                    public static readonly string Type4 = "Конвенциальный тип";
                    public static readonly string Type5 = "Предприимчивый тип";
                    public static readonly string Type6 = "Артистический тип";
                }

                public Опроснок_профессиональных_предпочтений_Голланд()
                {
                    UseExternalDevices = false;
                }
            }

            public class Методика_диагностики_уровня_агрессии_Басс_Дарки : TestMetadata
            {
                public class Keys
                {
                    public static readonly string AgressionIndex = "Индекс агрессии";
                    public static readonly string HostilityIndex = "Индекс враждебности";
                    public static readonly string PhysicalAgression = "Физическая агрессия";
                    public static readonly string IndirectAgression = "Косвенная агрессия";
                    public static readonly string Irritation = "Раздражение";
                    public static readonly string Negativism = "Негативизм";
                    public static readonly string Offense = "Обида";
                    public static readonly string Suspicion = "Подозрительность";
                    public static readonly string VerbalAgrgression = "Вербальная агрессия";
                    public static readonly string Guilt = "Чувство вины";
                }

                public Методика_диагностики_уровня_агрессии_Басс_Дарки()
                {
                    UseExternalDevices = false;
                }
            }

            public class Методика_ДОРС : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Fatigue = "Утомление";
                    public static readonly string Monotonically = "Монотония";
                    public static readonly string Satiation = "Пресыщение";
                    public static readonly string Stress = "Стресс";
                }

                public Методика_ДОРС()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_Зунга : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Score = "Баллы";
                }

                public Тест_Зунга()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_опросник_Индекс_жизненного_стиля_ИЖС : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Negation = "Отрицание";
                    public static readonly string Extrusion = "Вытеснение";
                    public static readonly string Regression = "Самообладание";
                    public static readonly string Compensation = "Компенсация";
                    public static readonly string Projection = "Проекция";
                    public static readonly string Replacement = "Замещение";
                    public static readonly string Intellectualization = "Интеллектуализация";
                    public static readonly string Reactive = "Реактивные образования";
                }

                public Тест_опросник_Индекс_жизненного_стиля_ИЖС()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_опросник_на_исследование_волевой_саморегуляции_ИВС : TestMetadata
            {
                public class Keys
                {
                    public static readonly string CommonScale = "Общая шкала";
                    public static readonly string Perseverance = "Настойчивость";
                    public static readonly string SelfControl = "Самообладание";
                }

                public Тест_опросник_на_исследование_волевой_саморегуляции_ИВС()
                {
                    UseExternalDevices = false;
                }
            }


            public class Комплексная_оценка_психологического_состояния_КОПС : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Operability = "Работоспособность";
                    public static readonly string SelfConcept = "Самооценка";
                    public static readonly string SocialDesirability = "Социальная желательность";
                    public static readonly string Atypically = "Атипичность ответов";
                }

                public Комплексная_оценка_психологического_состояния_КОПС()
                {
                    UseExternalDevices = false;
                }
            }

            public class Опросник_Личностный_профиль_ЛП : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Activity = "Активность";
                    public static readonly string Responsibility = "Ответственность";
                    public static readonly string EmotionalEndurance = "Эмоциональная выносливость";
                    public static readonly string SocialDesirability = "Социальная желательность";
                    public static readonly string Atypically = "Атипичность ответов";
                }

                public Опросник_Личностный_профиль_ЛП()
                {
                    UseExternalDevices = false;
                }
            }

            public class Диагностики_мотивации_к_достижению_успеха_Элерса_МДУ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Motivation = "Мотивация к достижению успеха";
                }

                public Диагностики_мотивации_к_достижению_успеха_Элерса_МДУ()
                {
                    UseExternalDevices = false;
                }
            }

            public class Диагностики_мотивации_к_избеганию_неудач_Элерса_МИН : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Scores = "Баллы";
                }

                public Диагностики_мотивации_к_избеганию_неудач_Элерса_МИН()
                {
                    UseExternalDevices = false;
                }
            }

            public class Опросник_САН : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Самочувствие = "Самочувствие";
                    public static readonly string Активность = "Активность";
                    public static readonly string Настроение = "Настроение";
                    public static readonly string Гистограмма = "Гистограмма";
                }

                public Опросник_САН()
                {
                    UseExternalDevices = false;
                }
            }

            public class Оценка_нервно_психической_устойчивости_НПУ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Falsity = "Неискренность";
                    public static readonly string Stability = "Нервно-психическая устойчивость";
                }

                public Оценка_нервно_психической_устойчивости_НПУ()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_Потребность_в_достижении_ПД : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Key = "Потребность в достижении";
                }

                public Тест_Потребность_в_достижении_ПД()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_опросник_Склонность_к_зависимому_поведению_ЗП : TestMetadata
            {
                public class Keys
                {
                    public static readonly string DrugAddiction = "Наркозависимость";
                    public static readonly string AlcoholAddiction = "Алкогольная зависимость";
                }

                public Тест_опросник_Склонность_к_зависимому_поведению_ЗП()
                {
                    UseExternalDevices = false;
                }
            }

            public class Диагностика_степени_готовности_склонности_к_риску_Шуберт : TestMetadata
            {
                public class Keys
                {
                    public static readonly string RiskAppetite = "Склонность к риску";
                }

                public Диагностика_степени_готовности_склонности_к_риску_Шуберт()
                {
                    UseExternalDevices = false;
                }
            }

            public class Сокращенный_многопрофильный_опросник_личности_СМОЛ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string L = "L";
                    public static readonly string F = "F";
                    public static readonly string K = "K";
                    public static readonly string S1 = "1 (Hs)";
                    public static readonly string S2 = "2 (D)";
                    public static readonly string S3 = "3 (Hy)";
                    public static readonly string S4 = "4 (Pd)";
                    public static readonly string S6 = "6 (Pa)";
                    public static readonly string S7 = "7 (Pt)";
                    public static readonly string S8 = "8 (Se)";
                    public static readonly string S9 = "9 (Ma)";
                }

                public Сокращенный_многопрофильный_опросник_личности_СМОЛ()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_Стиль_руководства_СР : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Style1 = "Авторитарный стиль руководства";
                    public static readonly string Style2 = "Демократический стиль руководства";
                    public static readonly string Style3 = "Либеральный стиль руководства";
                }

                public Тест_Стиль_руководства_СР()
                {
                    UseExternalDevices = false;
                }
            }

            public class Структура_темперамента_Смирнов : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Extraversion_Introversion = "Экстраверсия-интроверсия";
                    public static readonly string Rigidity_Plasticity = "Ригидность-пластичность";
                    public static readonly string Emotional_excitability_Poise = "Эмоциональная возбудимость-уравновешенность";
                    public static readonly string Reaction_tempo = "Темп реакции";
                    public static readonly string Activity = "Активность";
                    public static readonly string Sincerity = "Искренность";

                    public static readonly string Extraversion_Introversion_Level = "Экстраверсия-интроверсия (уровень)";
                    public static readonly string Rigidity_Plasticity_Level = "Ригидность-пластичность (уровень)";
                    public static readonly string Emotional_excitability_Poise_Level = "Эмоциональная возбудимость-уравновешенность (уровень)";
                    public static readonly string Reaction_tempo_Level = "Темп реакции (уровень)";
                    public static readonly string Activity_Level = "Активность (уровень)";
                }

                public Структура_темперамента_Смирнов()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_MMPI_Березина : TestMetadata
            {
                public class Keys
                {
                    public static readonly string L = "Шкала L";
                    public static readonly string F = "Шкала F";
                    public static readonly string K = "Шкала K";
                    public static readonly string S1 = "Шкала 1";
                    public static readonly string S2 = "Шкала 2";
                    public static readonly string S3 = "Шкала 3";
                    public static readonly string S4 = "Шкала 4";
                    public static readonly string S5 = "Шкала 5";
                    public static readonly string S6 = "Шкала 6";
                    public static readonly string S7 = "Шкала 7";
                    public static readonly string S8 = "Шкала 8";
                    public static readonly string S9 = "Шкала 9";
                    public static readonly string S0 = "Шкала 0";
                    public static readonly string Reliability = "Достоверность";
                    public static readonly string Combination = "Сочетание";
                }

                public Тест_MMPI_Березина()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_Айзенка_EPQ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Психотизм = "Психотизм";
                    public static readonly string Экстраверсия = "Экстраверсия/Интроверсия";
                    public static readonly string Нейротизм = "Нейротизм";
                    public static readonly string Искренность = "Искренность";
                }

                public Тест_Айзенка_EPQ()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_Кетелла : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Высокие = "Высокие";
                    public static readonly string Низкие = "Низкие";
                    public static readonly string Баллы = "Баллы";
                    public static readonly string Стены = "Стены";
                }

                public Тест_Кетелла()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_Лири : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Factor1 = "Фактор \"Авторитарный\"";
                    public static readonly string Factor2 = "Фактор \"Независимо - доминирующий\"";
                    public static readonly string Factor3 = "Фактор \"Агрессивный\"";
                    public static readonly string Factor4 = "Фактор \"Недоверчивый - скептический\"";
                    public static readonly string Factor5 = "Фактор \"Покорно - застенчивый\"";
                    public static readonly string Factor6 = "Фактор \"Зависимый\"";
                    public static readonly string Factor7 = "Фактор \"Сотрудничающий\"";
                    public static readonly string Factor8 = "Фактор \"Альтруистический\"";
                }

                public Тест_Лири()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_Люшера : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Select1 = "Первый выбор";
                    public static readonly string Intensity1 = "Интенсивность тревоги в первом выборе";
                    public static readonly string Select2 = "Второй выбор";
                    public static readonly string Intensity2 = "Интенсивность тревоги во втором выборе";
                    public static readonly string Pairs = "Пары";
                    public static readonly string Coefficient = "Коэффициент вегетативного баланса";
                    public static readonly string Index = "Индекс суммарного отклонения";
                }

                public Тест_Люшера()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_Спилберга : TestMetadata
            {
                public class Keys
                {
                    public static readonly string СитуативнаяТревожность = "Ситуативная тревожность";
                    public static readonly string ЛичностнаяТревожность = "Личностная тревожность";
                }

                public Тест_Спилберга()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_Томаса_Конфликтность : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Rivalry = "Соперничество";
                    public static readonly string Cooperation = "Сотрудничество";
                    public static readonly string Compromise = "Компромисс";
                    public static readonly string Avoidance = "Избегание";
                    public static readonly string Adaptation = "Приспособление";
                }

                public Тест_Томаса_Конфликтность()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_Уровень_субъективного_контроля_УСК : TestMetadata
            {
                public class Keys
                {
                    public static readonly string General_internality_Stens = "Общая интернальность";
                    public static readonly string Internality_in_arhievement_field_Stens = "Интернальность в области достижений";
                    public static readonly string Internality_in_a_situation_of_failure_Stens = "Интернальность в ситуации неудачи";
                    public static readonly string Internality_in_family_relations_Stens = "Интернальность в семейных отношениях";
                    public static readonly string Internality_in_industrial_relations_Stens = "Интернальность в области производственных отношений";
                    public static readonly string Internality_in_interpersonal_relations_Stens = "Интернальность в межличностных отношениях";
                    public static readonly string Health_internality_Stens = "Интернальность в отношении здоровья";
                    public static readonly string Selfreflection = "Саморефлексия";
                }

                public Тест_Уровень_субъективного_контроля_УСК()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тест_уровня_тревожности_Тейлор : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Тревожность = "Степень тревожности";
                }

                public Тест_уровня_тревожности_Тейлор()
                {
                    UseExternalDevices = false;
                }
            }

            public class Ценностные_ориентации_Рокич : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Шкала1 = "Приоритетные терминальные ценности";
                    public static readonly string Шкала2 = "Неприоритетные терминальные ценности";
                    public static readonly string Шкала3 = "Приоритетные инструментальные ценности";
                    public static readonly string Шкала4 = "Неприоритетные инструментальные ценности";
                    public static readonly string Таблица1 = "Таблица 1";
                    public static readonly string Таблица2 = "Таблица 2";
                }

                public Ценностные_ориентации_Рокич()
                {
                    UseExternalDevices = false;
                }
            }

            public class Тип_акцентуации_личности_Шмишека_Леонгарда : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Type1 = "Демонстративность";
                    public static readonly string Type2 = "Застревание";
                    public static readonly string Type3 = "Педантичность";
                    public static readonly string Type4 = "Возбудимость";
                    public static readonly string Type5 = "Гипертимность";
                    public static readonly string Type6 = "Дистимность";
                    public static readonly string Type7 = "Тревожность";
                    public static readonly string Type8 = "Экзальтированность";
                    public static readonly string Type9 = "Эмотивность";
                    public static readonly string Type10 = "Циклотимность";
                    public static readonly string Type11 = "Шкала_лжи";
                }

                public Тип_акцентуации_личности_Шмишека_Леонгарда()
                {
                    UseExternalDevices = false;
                }
            }

            // =============================================================================================
            // Психофизиологические тесты
            // =============================================================================================

            public class Оценка_глазомера_ОГ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Средний процент соотношения линий";
                    public static readonly string Result2 = "Длины линий";
                    public static readonly string Result3 = "Длины эталонов";
                }

                public Оценка_глазомера_ОГ()
                {
                }
            }

            public class Оценка_объема_внимания_ОВ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Максимальный результат обследуемого";
                }

                public Оценка_объема_внимания_ОВ()
                {
                }
            }

            public class Оценка_склонности_к_риску_ОСР : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Количество баллов";
                    public static readonly string Result2 = "Количество недостоверных кругов";
                    public static readonly string Result3 = "Время прохождения круга";
                    public static readonly string Result4 = "Средняя скорость движения по кругу";
                    public static readonly string Result5 = "Ускорение торможения";
                    public static readonly string Result6 = "Расстояние до финиша";
                    public static readonly string Result7 = "Точность остановки на месте старта";
                    public static readonly string Result8 = "Время торможения";
                    public static readonly string Result9 = "Число пиков максимума скорости";
                }

                public Оценка_склонности_к_риску_ОСР()
                {
                }
            }

            public class Распределение_внимания_РВ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднее время реагирования в задании №1";
                    public static readonly string Result2 = "Количество правильных ответов на зрительные стимулы в задании №1";
                    public static readonly string Result3 = "Разница средних времен реагирования между заданием №2 и заданием №1";
                    public static readonly string Result4 = "Количество правильных ответов на слуховые стимулы в задании №2";
                    public static readonly string Result5 = "Разница количества правильных ответов на зрительные стимулы (№1 - №2)";
                    public static readonly string Result6 = "Количество ошибочных ответов на слуховые стимулы";
                    public static readonly string Result7 = "Среднее время реагирования в задании №2 на зрительные стимулы";
                    public static readonly string Result8 = "Количество правильных реагирований на зрительные стимулы в задании № 2";
                    public static readonly string Result9 = "Количество реагирований при отсутствии сигнала в задании № 1 на зрительные стимулы";
                    public static readonly string Result10 = "Количество реагирований при отсутствии сигнала в задании № 2 на зрительные стимулы";
                }

                public Распределение_внимания_РВ()
                {
                }
            }

            public class Сложная_двигательная_реакция_СДР_100 : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднее время реагирования";
                    public static readonly string Result2 = "Количество ошибок";
                    public static readonly string Result3 = "Количество пропусков";
                    public static readonly string Result4 = "Количество пустых нажатий";
                    public static readonly string Result5 = "Все значения времён реагирования на сигналы";
                }

                public Сложная_двигательная_реакция_СДР_100()
                {
                }
            }

            public class Сложная_двигательная_реакция_30_СДР_30 : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднее время реагирования";
                    public static readonly string Result2 = "Количество ошибок";
                    public static readonly string Result3 = "Количество пропусков";
                    public static readonly string Result4 = "Количество пустых нажатий";
                    public static readonly string Result5 = "Все значения времён реагирования на сигналы";
                }

                public Сложная_двигательная_реакция_30_СДР_30()
                {
                }
            }

            public class Сложная_двигательная_реакция_СДР_М : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднее время реагирования в задании №1";
                    public static readonly string Result2 = "Количество ошибок в задании №2";
                    public static readonly string Result3 = "Время выбора";
                    public static readonly string Result4 = "Количество нажатий на кнопку при отсутствии сигнала";
                    public static readonly string Result5 = "Среднее время реагирования в задании №2";
                }

                public Сложная_двигательная_реакция_СДР_М()
                {
                }
            }

            public class Концентрация_внимания_КВ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Время выполнения теста";
                    public static readonly string Result2 = "Количество просмотренных строк";
                    public static readonly string Result3 = "Время просмотра каждой строки";
                    public static readonly string Result4 = "Количество ошибок в каждой строке";
                    public static readonly string Result5 = "Допущено ошибок";
                }

                public Концентрация_внимания_КВ()
                {
                }
            }

            public class Корректурная_проба_КП : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Время выполнения теста";
                    public static readonly string Result2 = "Количество просмотренных строк";
                    public static readonly string Result3 = "Количество ошибок";
                    public static readonly string Result4 = "Время просмотра каждой строки";
                    public static readonly string Result5 = "Количество ошибок в каждой строке";
                }

                public Корректурная_проба_КП()
                {
                }
            }

            public class Эмоциональная_устойчивость_ЭУ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Количество ошибок без помехи (N1)";
                    public static readonly string Result2 = "Количество пропусков без помехи";
                    public static readonly string Result3 = "Среднеарифметическое время реагирования без помехи (ВР1)";
                    public static readonly string Result4 = "Количество ошибок с помехой (N2)";
                    public static readonly string Result5 = "Количество пропусков с помехой";
                    public static readonly string Result6 = "Среднеарифметическое время реагирования с помехой (ВР2)";
                    public static readonly string Result7 = "Разница среднеарифметических времен реагирования (ВР2 - ВР1)";
                    public static readonly string Result8 = "Разница количества ошибок с помехой и без помехи (N2 - N1)";
                }

                public Эмоциональная_устойчивость_ЭУ()
                {
                }
            }

            public class Оценка_устойчивости_внимания_УВ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднеарифметическое время реагирования";
                    public static readonly string Result2 = "Количество ошибок";
                    public static readonly string Result3 = "Количество пропусков";
                    public static readonly string Result4 = "Среднеарифметические времена реагирования по блокам";
                    public static readonly string Result5 = "Количество ошибок по блокам";
                    public static readonly string Result6 = "Количество пропусков по блокам";
                }

                public Оценка_устойчивости_внимания_УВ()
                {
                }
            }

            public class Оценка_монотоноустойчивости_ОМУ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднее время реагирования в 1-й части теста";
                    public static readonly string Result2 = "Коэффициент монотоноустойчивости < 50%";
                    public static readonly string Result3 = "Количество пропусков сигналов";
                    public static readonly string Result4 = "Времена реагирования";
                }

                public Оценка_монотоноустойчивости_ОМУ()
                {
                }
            }

            public class Оценка_монотоноустойчивости_ОМУ_М : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднее время реагирования в 1-й части теста";
                    public static readonly string Result2 = "Коэффициент монотоноустойчивости < 50%";
                    public static readonly string Result3 = "Количество пропусков сигналов";
                    public static readonly string Result4 = "Времена реагирования";
                }

                public Оценка_монотоноустойчивости_ОМУ_М()
                {
                }
            }

            public class Экспресс_проба_бдительности_УРБ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднеарифметическое время реагирования на сигналы без предупреждения";
                    public static readonly string Result2 = "Среднеарифметическое время реагирования на сигналы с предупреждением";
                    public static readonly string Result3 = "Число пропущенных сигналов без предупреждения";
                    public static readonly string Result4 = "Готовность";
                    public static readonly string Result5 = "Число пропущенных сигналов с предупреждением";
                    public static readonly string Result6 = "Времена реагирования на перескоки без предупреждения";
                    public static readonly string Result7 = "Времена реагирования на перескоки с предупреждением";
                    public static readonly string Result8 = "Число реагирований при отсутствии сигналов";
                }

                public Экспресс_проба_бдительности_УРБ()
                {
                }
            }

            public class Чувство_времени_ЧВ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Количество точных интервалов времени";
                    public static readonly string Result2 = "Количество переоцененных интервалов";
                    public static readonly string Result3 = "Количество недооцененных интервалов";
                    public static readonly string Result4 = "Среднее время переоцененных интервалов";
                    public static readonly string Result5 = "Среднее время недооцененных интервалов";
                    public static readonly string Result6 = "Среднее время общее";
                    public static readonly string Result7 = "Среднеквадратичное отклонение общее";
                }

                public Чувство_времени_ЧВ()
                {
                }
            }

            public class Тест_Игра_5 : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Вариант прямоугольника";
                    public static readonly string Result2 = "Минимальное число ходов";
                    public static readonly string Result3 = "Сделанное число ходов";
                    public static readonly string Result4 = "Время выполнения";
                    public static readonly string Result5 = "Общее количество повторяющихся позиций";
                }

                public Тест_Игра_5()
                {
                }
            }

            public class Уровень_восприятия_скорости_и_расстояния_УВСР : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Количество точных попаданий";
                }

                public Уровень_восприятия_скорости_и_расстояния_УВСР()
                {
                }
            }

            public class Методика_оценки_оперативной_памяти_ОВ_М : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Максимальный результат обследуемого";
                }

                public Методика_оценки_оперативной_памяти_ОВ_М()
                {
                }
            }

            public class Критическая_частота_слияния_миганий_КЧСМ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Частота нарастающих мельканий на 1-ом цикле";
                    public static readonly string Result2 = "Частота нарастающих мельканий на 2-ом цикле";
                    public static readonly string Result3 = "Частота нарастающих мельканий на 3-ом цикле";
                    public static readonly string Result4 = "Частота убывающих мельканий на 1-ом цикле";
                    public static readonly string Result5 = "Частота убывающих мельканий на 2-ом цикле";
                    public static readonly string Result6 = "Частота убывающих мельканий на 3-ом цикле";
                    public static readonly string Result7 = "Средняя частота по всем нарастающим мельканиям";
                    public static readonly string Result8 = "Средняя частота по всем убывающим мельканиям";
                    public static readonly string Result9 = "Средняя частота по всем циклам";
                }

                public Критическая_частота_слияния_миганий_КЧСМ()
                {
                }
            }

            public class Реакция_на_движущийся_объект_РДО : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Cреднеарифметическое время реагирования";
                    public static readonly string Result2 = "Cреднеквадратическое отклонение времени реагирования";
                    public static readonly string Result3 = "Количество точных нажатий";
                    public static readonly string Result4 = "Количество опережающих нажатий";
                    public static readonly string Result5 = "Количество запаздывающих нажатий";
                    public static readonly string Result6 = "Количество пропусков";
                    public static readonly string Result7 = "Среднее время реагирования при опережающих нажатиях";
                    public static readonly string Result8 = "Среднее время реагирования при запаздывающих нажатиях";
                }

                public Реакция_на_движущийся_объект_РДО()
                {
                }
            }

            public class Готовность_к_тестированию_ЭПФС : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднее время реагирования";
                    public static readonly string Result2 = "Количество неправильных нажатий";
                    public static readonly string Result3 = "Количество реакций не на удлиненный сигнал";
                    public static readonly string Result4 = "Количество пропусков удлиненного сигнала";
                    public static readonly string Result5 = "Общее количество ошибок";
                }

                public Готовность_к_тестированию_ЭПФС()
                {
                }
            }

            public class Готовность_к_экстренному_действию_ГЭД : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднеарифметическое время реагирования на сигналы без предупреждения";
                    public static readonly string Result2 = "Среднеарифметическое время реагирования на сигналы с предупреждением";
                    public static readonly string Result3 = "Число пропущенных сигналов без предупреждения";
                    public static readonly string Result4 = "Готовность";
                    public static readonly string Result5 = "Число пропущенных сигналов с предупреждением";
                    public static readonly string Result6 = "Таблица_Действие";
                    public static readonly string Result7 = "Таблица_Время";
                    public static readonly string Result8 = "Сдвиг";
                    public static readonly string Result9 = "Число реагирований при отсутствии сигналов";
                    public static readonly string Result10 = "Кол-во реагирований на сигналы с предупреждением большие 1 с.";
                    public static readonly string Result11 = "Таблица_Реакция";
                }

                public Готовность_к_экстренному_действию_ГЭД()
                {
                }
            }

            public class Готовность_к_экстренному_действию_2_ГЭД_2 : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "ГЭД-1 Среднеарифметическое время реагирования на сигналы без предупреждения";
                    public static readonly string Result2 = "ГЭД-1 Среднеарифметическое время реагирования на сигналы с предупреждением";
                    public static readonly string Result3 = "ГЭД-1 Число пропущенных сигналов без предупреждения";
                    public static readonly string Result4 = "ГЭД-1 Готовность";
                    public static readonly string Result5 = "ГЭД-1 Число пропущенных сигналов с предупреждением";
                    public static readonly string Result6 = "ГЭД-1 Число реагирований при отсутствии сигналов";
                    public static readonly string Result7 = "ГЭД-1 Кол-во реагирований на сигналы с предупреждением большие 1 с";
                    public static readonly string Result8 = "ГЭД-1 Таблица_Действие";
                    public static readonly string Result9 = "ГЭД-1 Таблица_Время";
                    public static readonly string Result10 = "ГЭД-1 Таблица_Реакция";
                    public static readonly string Result11 = "ГЭД-2 Ср. арифм. времён реагирования на все перескоки без предупреждения";
                    public static readonly string Result12 = "ГЭД-2 Ср. арифм. времён реагирования на перескоки c предупреждением";
                    public static readonly string Result13 = "ГЭД-2 Число перескоков превышений сопротивления";
                    public static readonly string Result14 = "ГЭД-2 Число пропусков перескоков без предупреждения";
                    public static readonly string Result15 = "ГЭД-2 Отношение пропущенных скачков к поданным";
                    public static readonly string Result16 = "ГЭД-2 Показатель  надёжности  работы в состоянии утомления";
                    public static readonly string Result17 = "ГЭД-2 Таблица_Действие";
                    public static readonly string Result18 = "ГЭД-2 Таблица_Время";
                    public static readonly string Result19 = "ГЭД-2 Таблица_Реакция";
                    public static readonly string Result20 = "ГЭД-2 Таблица_Сопротивление";
                    public static readonly string Result21 = "ГЭД-2 Количество пропусков скачков подряд";
                    public static readonly string Result22 = "ГЭД-2 Число реагирований при отсутствии сигналов";
                    public static readonly string Result23 = "ГЭД-2 Число пропущенных сигналов с предупреждением";
                }

                public Готовность_к_экстренному_действию_2_ГЭД_2()
                {
                }
            }

            public class Готовность_к_экстренным_действиям_ГЭД_1М : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднеарифметическое время реагирования на сигналы без предупреждения";
                    public static readonly string Result2 = "Среднеарифметическое время реагирования на сигналы с предупреждением";
                    public static readonly string Result3 = "Число пропущенных сигналов без предупреждения";
                    public static readonly string Result4 = "Готовность";
                    public static readonly string Result5 = "Число пропущенных сигналов с предупреждением";
                    public static readonly string Result6 = "Таблица_Действие";
                    public static readonly string Result7 = "Таблица_Время";
                    public static readonly string Result8 = "Число реагирований при отсутствии сигналов";
                    public static readonly string Result9 = "Кол-во реагирований на сигналы с предупреждением большие 1 с.";
                    public static readonly string Result10 = "Таблица_Реакция";
                }

                public Готовность_к_экстренным_действиям_ГЭД_1М()
                {
                }
            }

            public class Готовность_к_экстренным_действиям_ГЭД_2М : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "ГЭД-1 Среднеарифметическое время реагирования на сигналы без предупреждения";
                    public static readonly string Result2 = "ГЭД-1 Среднеарифметическое время реагирования на сигналы с предупреждением";
                    public static readonly string Result3 = "ГЭД-1 Число пропущенных сигналов без предупреждения";
                    public static readonly string Result4 = "ГЭД-1 Готовность";
                    public static readonly string Result5 = "ГЭД-1 Число пропущенных сигналов с предупреждением";
                    public static readonly string Result6 = "ГЭД-1 Число реагирований при отсутствии сигналов";
                    public static readonly string Result7 = "ГЭД-1 Кол-во реагирований на сигналы с предупреждением большие 1 с";
                    public static readonly string Result8 = "ГЭД-1 Таблица_Действие";
                    public static readonly string Result9 = "ГЭД-1 Таблица_Время";
                    public static readonly string Result10 = "ГЭД-1 Таблица_Реакция";
                    public static readonly string Result11 = "ГЭД-2 Ср. арифм. времён реагирования на все перескоки без предупреждения";
                    public static readonly string Result12 = "ГЭД-2 Ср. арифм. времён реагирования на перескоки c предупреждением";
                    public static readonly string Result13 = "ГЭД-2 Число перескоков превышений сопротивления";
                    public static readonly string Result14 = "ГЭД-2 Число пропусков перескоков без предупреждения";
                    public static readonly string Result15 = "ГЭД-2 Отношение пропущенных скачков к поданным";
                    public static readonly string Result16 = "ГЭД-2 Показатель  надёжности  работы в состоянии утомления";
                    public static readonly string Result17 = "ГЭД-2 Таблица_Действие";
                    public static readonly string Result18 = "ГЭД-2 Таблица_Время";
                    public static readonly string Result19 = "ГЭД-2 Таблица_Реакция";
                    public static readonly string Result20 = "ГЭД-2 Таблица_Сопротивление";
                    public static readonly string Result21 = "ГЭД-2 Количество пропусков скачков подряд";
                    public static readonly string Result22 = "ГЭД-2 Число реагирований при отсутствии сигналов";
                    public static readonly string Result23 = "ГЭД-2 Число пропущенных сигналов с предупреждением";
                }

                public Готовность_к_экстренным_действиям_ГЭД_2М()
                {
                }
            }

            public class Простая_двигательная_реакция_ПДР : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Cреднеарифметическое время реагирования";
                    public static readonly string Result2 = "Cреднеквадратическое отклонение времени реагирования";
                    public static readonly string Result3 = "Количество пропусков";
                    public static readonly string Result4 = "Количество нажатий на жёлтый сигнал";
                    public static readonly string Result5 = "Все значения времён реагирования на сигналы";
                }

                public Простая_двигательная_реакция_ПДР()
                {
                }
            }

            public class Оценка_скорости_переделки_навыков_СПН : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднее время реагирования на зелёные сигналы";
                    public static readonly string Result2 = "Среднее квадратичное отклонение реагирования на зелёные сигналы";
                    public static readonly string Result3 = "Количество неправильных нажатий (нажатие не на ту кнопку)";
                    public static readonly string Result4 = "Количество ошибок (нажатие на красно – зелёный сигнал)";
                    public static readonly string Result5 = "Средние времена реагирования";
                }

                public Оценка_скорости_переделки_навыков_СПН()
                {
                }
            }

            public class Статический_тремор_ТРЕМ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "График амплитуды отклонения";
                    public static readonly string Result2 = "Тремор";
                    public static readonly string Result3 = "Количество выходов щупа за уровень допустимого отклонения";
                    public static readonly string Result4 = "Суммарное время пребывания щупа за уровнем допустимого отклонения";
                }

                public Статический_тремор_ТРЕМ()
                {
                }
            }

            public class Оценка_стрессоустойчивости_М_СТР_М : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Задание 1. Cреднеарифметическое T реагирования";
                    public static readonly string Result2 = "Задание 2. Cреднеарифметическое Т реагирования";
                    public static readonly string Result3 = "Задание 3. Среднее время реагирования (Т3)";
                    public static readonly string Result4 = "Задание 3. Количество ошибок (нажатие на красный сигнал)";
                    public static readonly string Result5 = "Задание 3. Количество " + '"' + "пустых" + '"' + " нажатий на зелёную кнопку между загораниями красного и зелёного сигналов";
                    public static readonly string Result6 = "Задание 4. Среднее время реагирования (Т4)";
                    public static readonly string Result7 = "Задание 4. Количество ошибок (нажатие на красный сигнал)";
                    public static readonly string Result8 = "Задание 4. Количество " + '"' + "пустых" + '"' + " нажатий на зелёную кнопку между загораниями красного и зелёного сигналов";
                    public static readonly string Result9 = "Задание 4. Количество дополнительных пар сигналов";
                    public static readonly string Result10 = "Задание 4. Количество реакций на дополнительные красные сигналы";
                    public static readonly string Result11 = "Таблица Задание 1: тип сигнала";
                    public static readonly string Result12 = "Таблица Задание 1: реакция";
                    public static readonly string Result13 = "Таблица Задание 2: тип сигнала";
                    public static readonly string Result14 = "Таблица Задание 2: реакция";
                    public static readonly string Result15 = "Таблица Задание 3: тип сигнала";
                    public static readonly string Result16 = "Таблица Задание 3: реакция";
                    public static readonly string Result17 = "Таблица Задание 4: тип сигнала";
                    public static readonly string Result18 = "Таблица Задание 4: реакция";
                    public static readonly string Result19 = "Разница времён реагирования (Т3 - Т4)";
                    public static readonly string Result20 = "Разница количества ошибок между 4-ым и 3-им заданиями";
                }

                public Оценка_стрессоустойчивости_М_СТР_М()
                {
                }
            }

            public class Оценка_стрессоустойчивости_СТР_M2 : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Задание 1. Cреднеарифметическое T реагирования";
                    public static readonly string Result2 = "Задание 2. Cреднеарифметическое Т реагирования";
                    public static readonly string Result3 = "Задание 3. Среднее время реагирования (Т3)";
                    public static readonly string Result4 = "Задание 3. Количество ошибок (нажатие на красный сигнал)";
                    public static readonly string Result5 = "Задание 3. Количество " + '"' + "пустых" + '"' + " нажатий на зелёную кнопку между загораниями красного и зелёного сигналов";
                    public static readonly string Result6 = "Задание 4. Среднее время реагирования (Т4)";
                    public static readonly string Result7 = "Задание 4. Количество ошибок (нажатие на красный сигнал)";
                    public static readonly string Result8 = "Задание 4. Количество " + '"' + "пустых" + '"' + " нажатий на зелёную кнопку между загораниями красного и зелёного сигналов";
                    public static readonly string Result9 = "Задание 4. Количество дополнительных пар сигналов";
                    public static readonly string Result10 = "Задание 4. Количество реакций на дополнительные красные сигналы";
                    public static readonly string Result11 = "Таблица Задание 1: тип сигнала";
                    public static readonly string Result12 = "Таблица Задание 1: реакция";
                    public static readonly string Result13 = "Таблица Задание 2: тип сигнала";
                    public static readonly string Result14 = "Таблица Задание 2: реакция";
                    public static readonly string Result15 = "Таблица Задание 3: тип сигнала";
                    public static readonly string Result16 = "Таблица Задание 3: реакция";
                    public static readonly string Result17 = "Таблица Задание 4: тип сигнала";
                    public static readonly string Result18 = "Таблица Задание 4: реакция";
                    public static readonly string Result19 = "Разница времён реагирования (Т3 - Т4)";
                    public static readonly string Result20 = "Разница количества ошибок между 4-ым и 3-им заданиями";
                }

                public Оценка_стрессоустойчивости_СТР_M2()
                {
                }
            }

            public class Оценка_стрессоустойчивости_СТР : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Задание 1. Cреднеарифметическое T реагирования";
                    public static readonly string Result2 = "Задание 2. Cреднеарифметическое Т реагирования";
                    public static readonly string Result3 = "Задание 3. Среднее время реагирования (Т3)";
                    public static readonly string Result4 = "Задание 3. Количество ошибок (нажатие на красный сигнал)";
                    public static readonly string Result5 = "Задание 3. Количество " + '"' + "пустых" + '"' + " нажатий на зелёную кнопку между загораниями красного и зелёного сигналов";
                    public static readonly string Result6 = "Задание 4. Среднее время реагирования (Т4)";
                    public static readonly string Result7 = "Задание 4. Количество ошибок (нажатие на красный сигнал)";
                    public static readonly string Result8 = "Задание 4. Количество " + '"' + "пустых" + '"' + " нажатий на зелёную кнопку между загораниями красного и зелёного сигналов";
                    public static readonly string Result9 = "Разница времён реагирования (Т3 - Т4)";
                    public static readonly string Result10 = "Разница количества ошибок между 4-ым и 3-им заданиями";
                }

                public Оценка_стрессоустойчивости_СТР()
                {
                }
            }

            public class Переключение_внимания_и_помехоустойчивость_ПВПУ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Время выполнения 1-го задания";
                    public static readonly string Result2 = "Время выполнения 2-го задания";
                    public static readonly string Result3 = "Время выполнения 3-го задания";
                    public static readonly string Result4 = "Время выполнения 4-го задания";
                    public static readonly string Result5 = "Количество ошибок в 1 задании";
                    public static readonly string Result6 = "Количество ошибок вo 2 задании";
                    public static readonly string Result7 = "Количество ошибок в 3 задании";
                    public static readonly string Result8 = "Количество ошибок в 4 задании";
                    public static readonly string Result9 = "Время переключения внимания";
                    public static readonly string Result10 = "Помехоустойчивость";
                    public static readonly string Result11 = "Разница числа ошибок в 4-ом и 3-ем заданиях";
                    public static readonly string Result12 = "Нажатые числа в 1-ом задании";
                    public static readonly string Result13 = "Нажатые числа во 2-ом задании";
                    public static readonly string Result14 = "Нажатые числа в 3-ом задании";
                    public static readonly string Result15 = "Нажатые числа в 4-ом задании";
                    public static readonly string Result16 = "Нажатые числа в 1-ом повторном задании";
                    public static readonly string Result17 = "Нажатые числа в 2-ом повторном задании";
                    public static readonly string Result18 = "Нажатые числа в 3-ом повторном задании";
                }

                public Переключение_внимания_и_помехоустойчивость_ПВПУ()
                {
                }
            }

            public class Переключение_внимания_2_ПВ_2 : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Время выполнения 1-го задания";
                    public static readonly string Result2 = "Время выполнения 2-го задания";
                    public static readonly string Result3 = "Время выполнения 3-го задания";
                    public static readonly string Result4 = "Время выполнения 4-го задания";
                    public static readonly string Result5 = "Количество ошибок в 1 задании";
                    public static readonly string Result6 = "Количество ошибок вo 2 задании";
                    public static readonly string Result7 = "Количество ошибок в 3 задании";
                    public static readonly string Result8 = "Количество ошибок в 4 задании";
                    public static readonly string Result9 = "Время переключения внимания";
                    public static readonly string Result10 = "Помехоустойчивость";
                    public static readonly string Result11 = "Разница числа ошибок в 4-ом и 3-ем заданиях";
                    public static readonly string Result12 = "Нажатые числа в 1-ом задании";
                    public static readonly string Result13 = "Нажатые числа во 2-ом задании";
                    public static readonly string Result14 = "Нажатые числа в 3-ом задании";
                    public static readonly string Result15 = "Нажатые числа в 4-ом задании";
                    public static readonly string Result16 = "Нажатые числа в 1-ом повторном задании";
                    public static readonly string Result17 = "Нажатые числа в 2-ом повторном задании";
                    public static readonly string Result18 = "Нажатые числа в 3-ом повторном задании";
                }

                public Переключение_внимания_2_ПВ_2()
                {
                }
            }

            public class Теппинг_тест_310_ТЕП_310 : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Количество касаний на 1-ом временном интервале";
                    public static readonly string Result2 = "Количество касаний на 2-ом временном интервале";
                    public static readonly string Result3 = "Количество касаний на 3-ом временном интервале";
                    public static readonly string Result4 = "Количество касаний на 4-ом временном интервале";
                    public static readonly string Result5 = "Количество касаний на 5-ом временном интервале";
                    public static readonly string Result6 = "Количество касаний на 6-ом временном интервале";
                }

                public Теппинг_тест_310_ТЕП_310()
                {
                }
            }

            public class Теппинг_тест : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Касания каждые 10 секунд";
                    public static readonly string Result2 = "Касания каждые 5 секунд";
                }

                public Теппинг_тест()
                {
                }
            }

            public class Проба_на_моторную_согласованность_МС : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Общее время выполнения";
                    public static readonly string Result2 = "Количество превышений интервала неодновременности";
                    public static readonly string Result3 = "Начало движения левой рукой";
                    public static readonly string Result4 = "Начало движения правой рукой";
                }

                public Проба_на_моторную_согласованность_МС()
                {
                }
            }

            public class Тремор_3 : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Отвестие номер 1 Количество касаний";
                    public static readonly string Result2 = "Отвестие номер 1 Сумма";
                    public static readonly string Result3 = "Отвестие номер 2 Количество касаний";
                    public static readonly string Result4 = "Отвестие номер 2 Сумма";
                    public static readonly string Result5 = "Отвестие номер 3 Количество касаний";
                    public static readonly string Result6 = "Отвестие номер 3 Сумма";
                }

                public Тремор_3()
                {
                }
            }

            public class Оценка_бдительности_ОБ : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднеарифметическое время реагирования на экстренные сигналы";
                    public static readonly string Result2 = "Среднеарифметическое время реагирования на сигналы с предупреждением";
                    public static readonly string Result3 = "Среднеарифметическое время реагирования на красные сигналы";
                    public static readonly string Result4 = "Количество пропусков экстренных сигналов";
                    public static readonly string Result5 = "Количество пропусков сигналов с предупреждением";
                    public static readonly string Result6 = "Количество пропусков красных сигналов";
                    public static readonly string Result7 = "Количество неправильных нажатий на экстренные сигналы";
                    public static readonly string Result8 = "Количество неправильных нажатий на сигналы с предупреждением";
                    public static readonly string Result9 = "Количество неправильных нажатий на красные сигналы";
                    public static readonly string Result10 = "Показатель бдительности";
                    public static readonly string Result11 = "Общее количество пропусков";
                    public static readonly string Result12 = "Общее количество неправильных нажатий";
                }

                public Оценка_бдительности_ОБ()
                {
                }
            }

            public class Оценка_бдительности_ОБ_М : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Среднеарифметическое время реагирования на экстренные сигналы";
                    public static readonly string Result2 = "Среднеарифметическое время реагирования на сигналы с предупреждением";
                    public static readonly string Result3 = "Среднеарифметическое время реагирования на красные сигналы";
                    public static readonly string Result4 = "Количество пропусков экстренных сигналов";
                    public static readonly string Result5 = "Количество пропусков сигналов с предупреждением";
                    public static readonly string Result6 = "Количество пропусков красных сигналов";
                    public static readonly string Result7 = "Количество неправильных нажатий на экстренные сигналы";
                    public static readonly string Result8 = "Количество неправильных нажатий на сигналы с предупреждением";
                    public static readonly string Result9 = "Количество неправильных нажатий на красные сигналы";
                    public static readonly string Result10 = "Показатель бдительности";
                    public static readonly string Result11 = "Общее количество пропусков";
                    public static readonly string Result12 = "Общее количество неправильных нажатий";
                }

                public Оценка_бдительности_ОБ_М()
                {
                }
            }

            public class Оценка_глазомера_М : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Средний процент отклонения линий";
                    public static readonly string Result2 = "Длины нарисованных отрезков";
                    public static readonly string Result3 = "Отклонения от заданного отрезка";
                    public static readonly string Result4 = "Длины эталонов с пропорциональным изменением длины";
                }

                public Оценка_глазомера_М()
                {
                }
            }

            public class Оценка_моторной_согласованности_М_МС_м : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Result1 = "Общее время выполнения";
                    public static readonly string Result2 = "Количество превышений интервала неодновременности";
                    public static readonly string Result3 = "Начало движения левой рукой";
                    public static readonly string Result4 = "Начало движения правой рукой";
                    public static readonly string Result5 = "Среднее время реакции на появление сигналов в центре экрана";
                    public static readonly string Result6 = "Количество ошибок реагирования на сигналы в центре экрана";
                    public static readonly string Result7 = "Количество пропусков сигналов в центре экрана";
                }

                public Оценка_моторной_согласованности_М_МС_м()
                {
                }
            }

            public class Опрос_совместимости : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Compatibility = "Compatibility";
                }

                public Опрос_совместимости()
                {
                    UseExternalDevices = false;
                }
            }

            public class Аддикция_и_аддиктивное_поведение : TestMetadata
            {
                public class Keys
                {
                    public static readonly string Уровень_проявления_аддикции = "Уровень проявления аддикции (УП)";
                    public static readonly string Изменение_толерантности = "Изменение толерантности";
                    public static readonly string Синдром_отмены = "Синдром отмены";
                    public static readonly string Потеря_контроля = "Потеря контроля";
                    public static readonly string Неудачные_попытки_воздержаться = "Неудачные попытки воздержаться";
                    public static readonly string Употребление_несмотря_на = "Употребление «несмотря на…»";
                    public static readonly string Отрицание_своей_зависимости = "Отрицание своей зависимости";
                    public static readonly string Резкие_изменения_в_образе_жизни = "Резкие изменения в образе жизни";
                    public static readonly string Степень_выраженности_аддикции = "Степень выраженности аддикции (СВ)";
                }

                public Аддикция_и_аддиктивное_поведение()
                {
                    UseExternalDevices = false;
                }
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests\Source\TestParameter.cs


using System.Runtime.Serialization;

namespace Updk7.Tests
{
    [DataContract]
    public class TestParameter
    {
        public TestParameter()
        {
        }

        public TestParameter(string key, string value)
        {
            Key = key;
            StringValue = value;
        }

        [DataMember]
        public string Key { get; set; }

        [DataMember]
        public string StringValue { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests\Source\TestResults.cs


using System;
using System.Collections.Generic;
using System.Linq;

namespace Updk7.Tests
{
    public class TestResults
    {
        public static readonly string ErrorKey = "ERROR";

        public TestResults()
        {
        }

        public TestResults(TestType test)
        {
            Test = test;
        }

        public TestType Test { get; set; }

        public DateTime BeginTime { get; set; }

        public DateTime? EndTime { get; set; }

        public List<TestResultValue> Values { get; set; } = new List<TestResultValue>();

        public TestResultValue this[string key] => Values.FirstOrDefault(s => s.Key == key);

        public bool ContainsValues => Values != null && Values.Count != 0;

        public bool ContainsException => Values != null && Values.Any(rv => rv.Key == ErrorKey);

        public TestResults AddValue(TestResultValue value)
        {
            Values.Add(value);
            return this;
        }

        public TestResults AddException(Exception ex)
        {
            Values.Add(new TestResultValue(ErrorKey, ex.Message));
            return this;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests\Source\TestResultsSerializer.cs


using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace Updk7.Tests
{
    public static class TestResultsSerializer
    {
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct ResultsHeader
        {
            public int TestType;
            public DateTime BeginTime;
            public DateTime EndTime;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct ValueHeader
        {
            public byte Id;
            public byte DataType;
            public ushort DataSize;
        }

        private const int SizeInt = 4;
        private const int SizeFloat = 4;

        public static byte[] Serialize(TestResults results)
        {
            var buffer = new byte[SizeOf(results)];
            var testInfo = Metadata.GetTestInfo(results.Test);

            unsafe
            {
                fixed (byte* pBuffer = buffer)
                {
                    byte* ptr = pBuffer;

                    var pResultsHeader = (ResultsHeader*)ptr;
                    pResultsHeader->TestType = (int)results.Test;
                    pResultsHeader->BeginTime = results.BeginTime;
                    pResultsHeader->EndTime = results.EndTime.HasValue ? results.EndTime.Value : DateTime.MinValue;
                    ptr += sizeof(ResultsHeader);

                    foreach (var value in results.Values)
                    {
                        var pValueHeader = (ValueHeader*)ptr;
                        pValueHeader->Id = (byte)testInfo.GetResultKeyId(value.Key);
                        pValueHeader->DataType = (byte)value.ValueType;
                        pValueHeader->DataSize = (ushort)SizeOf(value);
                        ptr += sizeof(ValueHeader);

                        switch (value.ValueType)
                        {
                            case TestResultValueType.Int:
                                *((int*)ptr) = value.Int;
                                break;
                            case TestResultValueType.Float:
                                *((float*)ptr) = value.Float;
                                break;
                            case TestResultValueType.IntArray:
                                Marshal.Copy(value.IntArray, 0, new IntPtr(ptr), value.IntArray.Length);
                                break;
                            case TestResultValueType.FloatArray:
                                Marshal.Copy(value.FloatArray, 0, new IntPtr(ptr), value.FloatArray.Length);
                                break;
                            case TestResultValueType.String:
                                var bytesData = Encoding.UTF8.GetBytes(value.String);
                                Marshal.Copy(bytesData, 0, new IntPtr(ptr), bytesData.Length);
                                break;
                            default:
                                throw new ArgumentOutOfRangeException();
                        }

                        ptr += pValueHeader->DataSize;
                    }
                }
            }

            return buffer;
        }

        public static TestResults Deserialize(byte[] buffer)
        {
            var results = new TestResults();

            unsafe
            {
                fixed (byte* pBuffer = buffer)
                {
                    byte* pEnd = pBuffer + buffer.Length;
                    byte* ptr = pBuffer;

                    var pResultsHeader = (ResultsHeader*)ptr;
                    ptr += sizeof(ResultsHeader);

                    results.Test = (TestType)pResultsHeader->TestType;
                    results.BeginTime = pResultsHeader->BeginTime;
                    results.EndTime = pResultsHeader->EndTime;
                    if (results.EndTime == DateTime.MinValue)
                        results.EndTime = null;

                    var testInfo = Metadata.GetTestInfo(results.Test);

                    while (ptr < pEnd)
                    {
                        var pValueHeader = (ValueHeader*)ptr;
                        ptr += sizeof(ValueHeader);

                        var valueKey = testInfo.GetResultKeyById(pValueHeader->Id);
                        switch ((TestResultValueType)pValueHeader->DataType)
                        {
                            case TestResultValueType.Int:
                                var intValue = *((int*)ptr);
                                results.AddValue(new TestResultValue(valueKey, intValue));
                                break;
                            case TestResultValueType.Float:
                                var floatValue = *((float*)ptr);
                                results.AddValue(new TestResultValue(valueKey, floatValue));
                                break;
                            case TestResultValueType.IntArray:
                                var intArray = new int[pValueHeader->DataSize / SizeInt];
                                Marshal.Copy(new IntPtr(ptr), intArray, 0, intArray.Length);
                                results.AddValue(new TestResultValue(valueKey, intArray));
                                break;
                            case TestResultValueType.FloatArray:
                                var floatArray = new float[pValueHeader->DataSize / SizeFloat];
                                Marshal.Copy(new IntPtr(ptr), floatArray, 0, floatArray.Length);
                                results.AddValue(new TestResultValue(valueKey, floatArray));
                                break;
                            case TestResultValueType.String:
                                var dataBytes = new byte[pValueHeader->DataSize];
                                Marshal.Copy(new IntPtr(ptr), dataBytes, 0, dataBytes.Length);
                                var stringValue = Encoding.UTF8.GetString(dataBytes);
                                results.AddValue(new TestResultValue(valueKey, stringValue));
                                break;
                            default:
                                throw new ArgumentOutOfRangeException();
                        }

                        ptr += pValueHeader->DataSize;
                    }
                }
            }

            return results;
        }

        public static int SizeOf(TestResults results)
        {
            unsafe
            {
                var valueHeaderSize = sizeof(ValueHeader);
                return sizeof(ResultsHeader) + results.Values.Sum(v => SizeOf(v) + valueHeaderSize);
            }
        }

        public static int SizeOf(TestResultValue value)
        {
            switch (value.ValueType)
            {
                case TestResultValueType.Int: return SizeInt;
                case TestResultValueType.Float: return SizeFloat;
                case TestResultValueType.IntArray: return SizeInt * value.IntArray.Length;
                case TestResultValueType.FloatArray: return SizeFloat * value.FloatArray.Length;
                case TestResultValueType.String: return Encoding.UTF8.GetByteCount(value.String);
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests\Source\TestResultValue.cs


using System;

namespace Updk7.Tests
{
    public enum TestResultValueType
    {
        Int = 1,
        Float,
        IntArray,
        FloatArray,
        String
    }

    public class TestResultValue
    {
        public TestResultValue(string key, object value)
        {
            Key = key;
            Raw = value;
        }

        public string Key { get; set; }

        public object Raw { get; private set; }

        public bool IsArray => IsTypeOf<int[]>() || IsTypeOf<float[]>();

        public int Int
        {
            get { return Get<int>(); }
            set { Set(value); }
        }

        public float Float
        {
            get { return Get<float>(); }
            set { Set(value); }
        }

        public string String
        {
            get { return Get<string>(); }
            set { Set(value); }
        }

        public int[] IntArray
        {
            get { return Get<int[]>(); }
            set { Set(value); }
        }

        public float[] FloatArray
        {
            get { return Get<float[]>(); }
            set { Set(value); }
        }

        public TestResultValueType ValueType
        {
            get
            {
                if (IsTypeOf<int>())
                    return TestResultValueType.Int;
                else if (IsTypeOf<float>())
                    return TestResultValueType.Float;
                else if (IsTypeOf<int[]>())
                    return TestResultValueType.IntArray;
                else if (IsTypeOf<float[]>())
                    return TestResultValueType.FloatArray;
                else if (IsTypeOf<string>())
                    return TestResultValueType.String;
                else
                    throw new ArgumentOutOfRangeException();
            }
        }

        public T Get<T>()
        {
            if (Raw is T)
                return (T)Raw;
            else
                throw new InvalidCastException();
        }

        public void Set<T>(T value)
        {
            if (Raw is T)
                Raw = value;
            else
                throw new InvalidCastException();
        }

        public bool IsTypeOf<T>()
        {
            return Raw is T;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests\Source\TestType.cs


namespace Updk7.Tests
{
    public enum TestType : int
    {
        Готовность_к_тестированию_ЭПФС = 1,
        Оценка_склонности_к_риску_ОСР = 2,
        Распределение_внимания_РВ = 3,
        Оценка_монотоноустойчивости_ОМУ = 4,
        Оценка_бдительности_ОБ = 5,
        Оценка_устойчивости_внимания_УВ = 6,
        Концентрация_внимания_КВ = 7,
        Оценка_скорости_переделки_навыков_СПН = 8,
        Эмоциональная_устойчивость_ЭУ = 9,
        Оценка_объема_внимания_ОВ = 10,
        Оценка_глазомера_ОГ = 11,
        Корректурная_проба_КП = 12,
        Оценка_стрессоустойчивости_СТР = 13,
        Оценка_стрессоустойчивости_М_СТР_М = 14,
        Сложная_двигательная_реакция_30_СДР_30 = 15,
        Сложная_двигательная_реакция_СДР_100 = 16,
        Простая_двигательная_реакция_ПДР = 17,
        Критическая_частота_слияния_миганий_КЧСМ = 18,
        Чувство_времени_ЧВ = 19,
        Теппинг_тест_310_ТЕП_310 = 20,
        Статический_тремор_ТРЕМ = 21,
        Готовность_к_экстренному_действию_ГЭД = 22,
        Готовность_к_экстренному_действию_2_ГЭД_2 = 23,
        Экспресс_проба_бдительности_УРБ = 24,
        Реакция_на_движущийся_объект_РДО = 25,
        Переключение_внимания_и_помехоустойчивость_ПВПУ = 26,
        Переключение_внимания_2_ПВ_2 = 27,
        Тест_Игра_5 = 28,
        Тест_Айзенка_EPQ = 29,
        Опросник_САН = 30,
        Тест_уровня_тревожности_Тейлор = 31,
        Тест_Спилберга = 32,
        Тест_Лири = 33,
        Тест_MMPI_Березина = 34,
        Q_Сортировка = 35,
        Опроснок_профессиональных_предпочтений_Голланд = 36,
        Тип_акцентуации_личности_Шмишека_Леонгарда = 37,
        Ценностные_ориентации_Рокич = 38,
        Тест_Потребность_в_достижении_ПД = 39,
        Тест_Стиль_руководства_СР = 40,
        Тест_Кетелла = 41,
        Тест_Уровень_субъективного_контроля_УСК = 42,
        Тест_Люшера = 43,
        Тест_словесных_ассоциаций = 44,
        Диагностики_мотивации_к_избеганию_неудач_Элерса_МИН = 45,
        Тест_антиципационной_состоятельности_АС = 46,
        Опросник_Личностный_профиль_ЛП = 47,
        Методика_Аналогии = 48,
        Диагностики_мотивации_к_достижению_успеха_Элерса_МДУ = 49,
        Тест_опросник_на_исследование_волевой_саморегуляции_ИВС = 50,
        Методика_диагностики_уровня_агрессии_Басс_Дарки = 51,
        Тест_опросник_Индекс_жизненного_стиля_ИЖС = 52,
        Сокращенный_многопрофильный_опросник_личности_СМОЛ = 53,
        Тест_опросник_Склонность_к_зависимому_поведению_ЗП = 54,
        Методика_ДОРС = 55,
        Комплексная_оценка_психологического_состояния_КОПС = 56,
        Диагностика_степени_готовности_склонности_к_риску_Шуберт = 57,
        Уровень_восприятия_скорости_и_расстояния_УВСР = 58,
        Сложная_двигательная_реакция_СДР_М = 59,
        Проба_на_моторную_согласованность_МС = 60,
        Теппинг_тест = 61,
        Социометрический_опрос = 62,
        Оценка_нервно_психической_устойчивости_НПУ = 63,
        Тест_Томаса_Конфликтность = 64,
        Тест_Зунга = 65,
        Опрос_совместимости = 66,
        Тремор_3 = 67,
        Методика_оценки_оперативной_памяти_ОВ_М = 68,
        Оценка_стрессоустойчивости_СТР_M2 = 69,
        Оценка_бдительности_ОБ_М = 70,
        Оценка_монотоноустойчивости_ОМУ_М = 71,
        Готовность_к_экстренным_действиям_ГЭД_1М = 72,
        Готовность_к_экстренным_действиям_ГЭД_2М = 73,
        Структура_темперамента_Смирнов = 74,
        Адаптивность = 75,
        Оценка_глазомера_М = 76,
        Оценка_моторной_согласованности_М_МС_м = 77,
        Аддикция_и_аддиктивное_поведение = 78
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests\Source\ResultParsers\Compatibility\Parser.cs


using System.Linq;
using System.Collections.Generic;

namespace Updk7.Tests.ResultParsers.Compatibility
{
    public enum PollResult
    {
        Agree,
        Disagree,
        NoMatter
    }

    public class AnswerInfo
    {
        public AnswerInfo(int id, PollResult result)
        {
            TesteeId = id;
            AnswerValue = result;
        }

        public int TesteeId { get; set; }

        public PollResult AnswerValue { get; set; }
    }

    public static class Parser
    {
        public static IEnumerable<AnswerInfo> ParseResults(TestResults results)
        {
            var assistants = new List<AnswerInfo>();
            var value = results.Values.FirstOrDefault(v => v.Key == "Compatibility");

            if (value != null && !string.IsNullOrEmpty(value.String))
            {
                var resultsStr = value.String.Split(';');
                foreach (var str in resultsStr)
                    assistants.AddRange(parseResultString(str));
            }

            return assistants;
        }

        private static IEnumerable<AnswerInfo> parseResultString(string str)
        {
            var splitStr = str.Split(':');
            var resultType = splitStr[0];
            var idStr = splitStr[1].Split(',');

            var poll = PollResult.Agree;
            if (resultType.Contains("Disagree"))
                poll = PollResult.Disagree;
            else if (resultType.Contains("NoMatter"))
                poll = PollResult.NoMatter;

            foreach (var id in idStr)
            {
                var testeeId = 0;
                if (!int.TryParse(id, out testeeId))
                    continue;

                yield return new AnswerInfo(testeeId, poll);
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\AppFolders.cs


using System;
using System.Windows;
using System.IO;

namespace Updk7.Wpf
{
    public class AppFolders
    {
        public AppFolders(string appTitle = null)
        {
            if (string.IsNullOrEmpty(appTitle))
                appTitle = Application.Current.GetType().FullName;

            var commonFolder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
            Root = $@"{commonFolder}\Neurocom\{appTitle}";
            Settings = Root;
            Logs = $@"{Root}\Log";
            Backups = $@"{Root}\dBackup";
        }

        public string Root { get; set; }

        public string Logs { get; set; }

        public string Backups { get; set; }

        public string Settings { get; set; }

        public void CreateIfNotExists()
        {
            Action<string> createDirectory = d =>
            {
                if (!Directory.Exists(d))
                    Directory.CreateDirectory(d);
            };

            createDirectory(Root);
            createDirectory(Logs);
            createDirectory(Backups);
            createDirectory(Settings);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\AppLog.cs


using Serilog;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace Updk7.Wpf
{
    public class AppLog
    {
        private static string _logFilePrefix = "log";
        private string _logFolder;
        private ILogger _logger;

        public AppLog(string logFolderPath)
        {
            Debug.Assert(!string.IsNullOrEmpty(logFolderPath));
            _logger = createLogger(logFolderPath);
            _logFolder = logFolderPath;
        }

        public static AppLog Current { get; set; }

        public AppLog Info(string message)
        {
            if (_logger != null)
                _logger.Information(message);

            return this;
        }

        public AppLog Warning(string message)
        {
            if (_logger != null)
                _logger.Warning(message);

            return this;
        }

        public AppLog Error(string message)
        {
            if (_logger != null)
                _logger.Error(message);

            return this;
        }

        public void CleanupLogFolder(int maxFilesCount)
        {
            CleanupLogFolder(_logFolder, maxFilesCount);
        }

        public static void CleanupLogFolder(string logFolderPath, int maxFilesCount)
        {
            var logFiles = Directory.GetFiles(logFolderPath)
                .Where(f => f.Contains(_logFilePrefix))
                .ToList();

            logFiles.Sort();
            if (logFiles.Count < maxFilesCount)
                return;

            while (logFiles.Count > maxFilesCount)
            {
                var file = logFiles[0];
                logFiles.RemoveAt(0);

                try
                {
                    File.Delete(file);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"Ошибка при удалении файла лога: {file}");
                    Debug.WriteLine(ex.Message);
                }
            }
        }

        private static ILogger createLogger(string logFolderPath)
        {
            ILogger logger = null;

            try
            {
                if (!Directory.Exists(logFolderPath))
                    Directory.CreateDirectory(logFolderPath);

                var date = DateTime.Now.ToString("yyyy-MM-dd");
                var logFile = $@"{logFolderPath}\{_logFilePrefix}-{date}.txt";

                logger = new LoggerConfiguration()
                    .WriteTo.File(logFile)
                    .CreateLogger();
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Не удалось создать файл лога приложения");
                Debug.WriteLine(ex.Message);
            }

            return logger;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\AppManager.cs


using System;
using System.ComponentModel;
using System.Windows;

namespace Updk7.Wpf
{
    public class AppManager
    {
        public enum WindowClosingActionType
        {
            ShutdownApp,
            HideWindow
        }

        private AppMutex _appMutex;
        private Application _application;
        private Window _shell;

        public AppManager(Application application, string appName)
        {
            _application = application;
            _appMutex = new AppMutex(application.GetType().FullName);

            if (!_appMutex.IsOriginInstance)
                _appMutex.WriteToOriginInstance("ShowWindow");
            else
            {
                AppName = appName;
                RutokenMonitor = new Rutoken.Common.RutokenMonitor(AppName);
                RutokenMonitor.Run();

                if (!IsRutokenConnected)
                    Interactions.InteractionService.Current.ShowErrorMessage("Rutoken для активации ПО не найден!", null);
                else
                {
                    Folders = new AppFolders(AppName);
                    Folders.CreateIfNotExists();
                    AppSettingsFile = $@"{Folders.Settings}\{AppName}.config";
                    CanBoot = true;

                    _appMutex.InstanceMessageReceived += (o, e) => ShowShell();
                    setupTray();
                    setupApplication(application);
                }
            }
        }

        public Window Shell
        {
            get { return _shell; }
            set { setupShell(value); }
        }

        public WindowClosingActionType WindowClosingAction { get; set; } = WindowClosingActionType.HideWindow;

        public AppFolders Folders { get; private set; }

        public AppTray Tray { get; private set; }

        public string AppName { get; set; }

        public string AppSettingsFile { get; set; }

        public bool IsRutokenConnected => RutokenMonitor.ContainsFeature(AppName);

        public bool CanBoot { get; private set; } = false;

        public Rutoken.Common.RutokenMonitor RutokenMonitor { get; }

        public void ShowShell(WindowState windowState = WindowState.Normal)
        {
            if (_shell != null)
            {
                _shell.Show();
                _shell.Activate();
                _shell.WindowState = windowState;
            }
        }

        public void HideShell()
        {
            if (_shell != null)
            {
                _shell.WindowState = WindowState.Minimized;
                _shell.ShowInTaskbar = false;
            }
        }

        public void Shutdown()
        {
            if (_application != null)
                _application.Shutdown();
        }

        private void setupTray()
        {
            var iconUri = new Uri(@"pack://application:,,,/updk7.Wpf;component/Icons/LogoNeuron.ico");
            
            Tray = new AppTray();
            Tray.SetIcon(iconUri);
            Tray.SetDoubleClickCommand(new Prism.Commands.DelegateCommand(() => ShowShell()));
        }

        private void setupApplication(Application application)
        {
            _application.ShutdownMode = ShutdownMode.OnExplicitShutdown;
            _application.Exit += onApplicationExit;
            _application.SessionEnding += onApplicationSessionEnding;
        }

        private void setupShell(Window value)
        {
            if (_shell != null)
            {
                _shell.StateChanged -= onMainWindowStateChanged;
                _shell.Closing -= onMainWindowClosing;
            }

            _shell = value;

            if (_shell != null)
            {
                _shell.StateChanged += onMainWindowStateChanged;
                _shell.Closing += onMainWindowClosing;
            }
        }

        private void onApplicationExit(object sender, ExitEventArgs e)
        {
            Tray.Dispose();
        }

        private void onApplicationSessionEnding(object sender, SessionEndingCancelEventArgs e)
        {
            _application.Shutdown();
        }

        private void onMainWindowClosing(object sender, CancelEventArgs e)
        {
#if !DEBUG
           if (WindowClosingAction == WindowClosingActionType.ShutdownApp)
                Shutdown();
            else
            {
                HideShell();
                e.Cancel = true;
            }
#else
            Shutdown();
#endif
        }

        private void onMainWindowStateChanged(object sender, EventArgs e)
        {
            _shell.ShowInTaskbar = true;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\AppMutex.cs


using System;
using System.Diagnostics;
using System.Threading;

namespace Updk7.Wpf
{
    internal class AppMutex : IDisposable
    {
        private string _instanceName;
        private readonly Mutex _instanceMutex;
        private AppPipeReader _messageReader;
        private bool _disposedValue = false;
        private readonly string _pipeName;

        public AppMutex(string instanceName)
        {
            Debug.Assert(!string.IsNullOrEmpty(instanceName));
            _instanceName = instanceName;
            _instanceMutex = new Mutex(true, _instanceName);
            _pipeName = $"{_instanceName}Pipe";

            IsOriginInstance = acquireMutex();
            if (IsOriginInstance)
            {
                _messageReader = new AppPipeReader(_pipeName);
                _messageReader.MessageReceived += (o, e) => InstanceMessageReceived?.Invoke(this, e);
                _messageReader.Start();
            }
        }

        ~AppMutex()
        {
            Dispose(false);
        }

        public event EventHandler<string> InstanceMessageReceived;

        public bool IsOriginInstance { get; }

        public void Dispose()
        {
            Dispose(true);
        }

        public void WriteToOriginInstance(string message)
        {
            if (!IsOriginInstance)
                AppPipeWriter.Write(_pipeName, message);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposedValue)
            {
                if (disposing)
                {
                    if (_messageReader != null)
                        _messageReader.Dispose();
                }

                if (IsOriginInstance)
                    _instanceMutex.Close();

                _disposedValue = true;
            }
        }

        private bool acquireMutex()
        {
            try
            {
                return _instanceMutex.WaitOne(0, true);
            }
            catch (AbandonedMutexException)
            {
                return true;
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\AppPipeReader.cs


using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;

namespace Updk7.Wpf
{
    internal class AppPipeReader : IDisposable
    {
        private readonly string _pipeName;
        private CancellationTokenSource _cancellationSource;
        private Task _serverLoopTask;
        private bool _disposedValue = false;
        private object _syncRoot = new object();

        public AppPipeReader(string pipeName)
        {
            Debug.Assert(!string.IsNullOrEmpty(pipeName));
            _pipeName = pipeName;
        }

        public event EventHandler<string> MessageReceived;

        public void Dispose()
        {
            Dispose(true);
        }

        public void Start()
        {
            lock (_syncRoot)
            {
                if (!_disposedValue && _cancellationSource == null)
                {
                    _cancellationSource = new CancellationTokenSource();
                    _serverLoopTask = serverLoop(_cancellationSource.Token);
                }
            }
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposedValue)
            {
                if (disposing)
                    _cancellationSource.Cancel();

                _disposedValue = true;
            }
        }

        private async Task serverLoop(CancellationToken ct)
        {
            using (var pipeServerStream = new NamedPipeServerStream(_pipeName, PipeDirection.In, 1,
                PipeTransmissionMode.Byte))
            {
                while (!ct.IsCancellationRequested)
                {
                    try
                    {
                        // Простенькая реализация для передачи сообщений между запущенными
                        // процессами. Никаких проверок и тайм-аутов нет, после получения сообщения 
                        // разрываем связь, клиент действует  аналогичным образом - отправляет по 
                        // одному сообщению за сессию. Формат сообщения - двай байта длины, байты сообщения.
                        // Сами сообщения в формате ASCII
                        await pipeServerStream.WaitForConnectionAsync(ct);
                        var message = readMessage(pipeServerStream);
                        pipeServerStream.Disconnect();

                        MessageReceived?.Invoke(this, message);
                    }
                    catch (Exception)
                    {
                    }
                }
            }
        }

        private string readMessage(Stream stream)
        {
            var length = stream.ReadByte() | stream.ReadByte() << 8;
            var buffer = new byte[length];
            stream.Read(buffer, 0, length);
            return Encoding.ASCII.GetString(buffer);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\AppPipeWriter.cs


using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Diagnostics;

namespace Updk7.Wpf
{
    internal static class AppPipeWriter
    {
        public static void Write(string pipeName, string message)
        {
            const int Timeout = 50;

            Debug.Assert(!string.IsNullOrEmpty(pipeName));

            using (var pipeStream = new NamedPipeClientStream(".", pipeName, PipeDirection.Out,
                PipeOptions.WriteThrough))
            {
                try
                {
                    pipeStream.Connect(Timeout);
                    if (!pipeStream.IsConnected)
                        return;

                    writeMessage(pipeStream, message);
                    pipeStream.Flush();
                    pipeStream.WaitForPipeDrain();
                }
                catch (Exception)
                {
                }
            }
        }

        private static void writeMessage(Stream stream, string message)
        {
            stream.WriteByte((byte)(message.Length & 0xff));
            stream.WriteByte((byte)((message.Length >> 8) & 0xff));
            var buffer = Encoding.ASCII.GetBytes(message);
            stream.Write(buffer, 0, buffer.Length);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\AppStrings.cs


using System.Windows;

namespace Updk7.Wpf
{
    public static class AppStrings
    {
        private static ResourceDictionary _localizationDictionary = new ResourceDictionary();

        public static void AddMergedDictionary(ResourceDictionary dictionary)
        {
            _localizationDictionary.MergedDictionaries.Add(dictionary);
        }

        public static bool Contains(object key)
        {
            return _localizationDictionary.Contains(key);
        }

        public static string GetString(object key)
        {
            return _localizationDictionary.Contains(key)
                ? _localizationDictionary[key] as string
                : key.ToString();
        }

        public static void SetString(object key, string value)
        {
            _localizationDictionary[key] = value;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\AppTray.cs


using System;
using System.Windows.Input;
using System.Windows.Controls;
using System.Windows.Media.Imaging;

namespace Updk7.Wpf
{
    public class AppTray : IDisposable
    {
        private readonly Hardcodet.Wpf.TaskbarNotification.TaskbarIcon _taskBarIcon =
            new Hardcodet.Wpf.TaskbarNotification.TaskbarIcon();

        private ContextMenu _contextMenu = new ContextMenu();

        public AppTray()
        {
            _taskBarIcon.MenuActivation = Hardcodet.Wpf.TaskbarNotification.PopupActivationMode.RightClick;
            _taskBarIcon.ContextMenu = _contextMenu;
        }

        public void SetDoubleClickCommand(ICommand command)
        {
            _taskBarIcon.DoubleClickCommand = command;
        }
        
        public void SetIcon(Uri iconUri)
        {
            var iconSource = BitmapFrame.Create(iconUri);
            _taskBarIcon.IconSource = iconSource;
        }

        public void SetToolTipText(string text)
        {
            _taskBarIcon.ToolTipText = text;
        }

        public void AddContextMenuItem(string header, ICommand command)
        {
            var menuItem = new MenuItem();
            menuItem.Header = header;
            menuItem.Command = command;

            _contextMenu.Items.Add(menuItem);
        }

        public void Dispose()
        {
            _taskBarIcon.Dispose();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\BitmapImageHelper.cs


using System.IO;
using System.Windows.Media.Imaging;

namespace Updk7.Wpf
{
    public static class BitmapImageHelper
    {
        public static byte[] GetBytes(BitmapImage image)
        {
            if (image == null)
                return null;

            byte[] data = null;
            var encoder = new JpegBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(image));

            using (var stream = new MemoryStream())
            {
                encoder.Save(stream);
                data = stream.ToArray();
            }

            return data;
        }

        public static BitmapImage CreateImage(byte[] data)
        {
            if (data == null)
                return null;

            var image = new BitmapImage();
            using (var stream = new MemoryStream(data))
            {
                image.BeginInit();
                image.CacheOption = BitmapCacheOption.OnLoad;
                image.StreamSource = stream;
                image.EndInit();
            }

            return image;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\ConfigurationBase.cs


using System;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations.Schema;
using System.Collections.Generic;

namespace Updk7.Wpf
{
    public abstract class ConfigurationBase
    {
        protected interface IConfigurationPropertyConverter
        {
            string Convert(object propertyValue);
            object ConvertBack(string configurationValue);
        }

        private class IntToStringConverter : IConfigurationPropertyConverter
        {
            public string Convert(object propertyValue) => propertyValue.ToString();
            public object ConvertBack(string configurationValue) => int.Parse(configurationValue);
        }

        private class StringToStringConverter : IConfigurationPropertyConverter
        {
            public string Convert(object propertyValue) => propertyValue != null ? propertyValue.ToString() : string.Empty;
            public object ConvertBack(string configurationValue) => configurationValue;
        }

        private class DefaultConverter : StringToStringConverter
        {
        }

        private static IConfigurationPropertyConverter _defaultConverter = new DefaultConverter();

        protected Dictionary<Type, IConfigurationPropertyConverter> Converters;

        public ConfigurationBase(string fileName)
        {
            Converters = new Dictionary<Type, IConfigurationPropertyConverter>()
            {
                [typeof(int)] = new IntToStringConverter(),
                [typeof(string)] = new StringToStringConverter()
            };

            ConfigurationFile = fileName;
            (File.Exists(ConfigurationFile) ? (Action)Load : Save)();
        }

        [NotMapped]
        public string ConfigurationFile { get; private set; }

        public void Save()
        {
            var file = new ExeConfigurationFileMap() { ExeConfigFilename = ConfigurationFile };
            var configuration = ConfigurationManager.OpenMappedExeConfiguration(file, ConfigurationUserLevel.None);
            var propertiesInfo = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (var info in propertiesInfo)
            {
                var notMapped = info.GetCustomAttribute<NotMappedAttribute>();
                if (notMapped != null)
                    continue;

                var propertyConverter = GetPropertyConverter(info.PropertyType);
                var propertyValue = propertyConverter.Convert(info.GetValue(this));

                if (configuration.AppSettings.Settings.AllKeys.Contains(info.Name))
                    configuration.AppSettings.Settings[info.Name].Value = propertyValue;
                else
                    configuration.AppSettings.Settings.Add(info.Name, propertyValue);
            }

            configuration.Save();
        }

        public void Load()
        {
            var file = new ExeConfigurationFileMap() { ExeConfigFilename = ConfigurationFile };
            var configuration = ConfigurationManager.OpenMappedExeConfiguration(file, ConfigurationUserLevel.None);
            var propertiesInfo = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (var key in configuration.AppSettings.Settings.AllKeys)
            {
                var property = propertiesInfo.FirstOrDefault(p => p.Name == key);
                if (property == null)
                    continue;

                var propertyConverter = GetPropertyConverter(property.PropertyType);
                var propertyValue = propertyConverter.ConvertBack(configuration.AppSettings.Settings[key].Value);

                property.SetValue(this, propertyValue);
            }
        }

        public virtual bool IsValid()
        {
            return true;
        }

        protected IConfigurationPropertyConverter GetPropertyConverter(Type propertyType)
        {
            return Converters.ContainsKey(propertyType)
                ? Converters[propertyType]
                : _defaultConverter;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\DocumentHelper.cs


using System;
using System.IO;
using System.IO.Packaging;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Xps.Packaging;
using System.Windows.Xps.Serialization;

namespace Updk7.Wpf
{
    public static class DocumentHelper
    {
        private static class Strings
        {
            public static readonly string PrintCaption = "Печать отчета";
        }

        public static void Print(FlowDocument report)
        {
            var dialog = new System.Windows.Controls.PrintDialog();
            if (dialog.ShowDialog() == true)
            {
                var paginator = (report as IDocumentPaginatorSource).DocumentPaginator;
                var reportPaginator = new PdfPaginator(paginator);
                dialog.PrintDocument(reportPaginator, Strings.PrintCaption);
            }
        }

        public static FlowDocument CreateFlowDocumentCopy(FlowDocument origin)
        {
            var copyString = XamlWriter.Save(origin);
            var copy = XamlReader.Parse(copyString) as FlowDocument;
            copy.DataContext = origin.DataContext;
            return copy;
        }

        public static void SaveAsPdf(FlowDocument report, string pdfFileName)
        {
            var xpsFilename = Path.GetTempPath() + $"Updk7-Doc-{DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss")}.xps";

            try
            {
                // Создаем XPS-документ
                using (var stream = new FileStream(xpsFilename, FileMode.Create))
                {
                    using (var package = Package.Open(stream, FileMode.Create, FileAccess.ReadWrite))
                    {
                        using (var xpsDoc = new XpsDocument(package, CompressionOption.NotCompressed))
                        {
                            var rsm = new XpsSerializationManager(new XpsPackagingPolicy(xpsDoc), false);
                            var paginator = ((IDocumentPaginatorSource)report).DocumentPaginator;
                            var reportPaginator = new PdfPaginator(paginator);
                            rsm.SaveAsXaml(reportPaginator);
                            rsm.Commit();
                        }
                    }
                }

                // Конвертируем XPS-документ в pdf
                using (var pdfDoc = new Spire.Pdf.PdfDocument())
                {
                    pdfDoc.LoadFromFile(xpsFilename, Spire.Pdf.FileFormat.XPS);
                    pdfDoc.SaveToFile(pdfFileName, Spire.Pdf.FileFormat.PDF);
                }
            }
            finally
            {
                cleanUp(xpsFilename);
            }
        }

        private static void cleanUp(string tempFileName)
        {
            try
            {
                if (File.Exists(tempFileName))
                    File.Delete(tempFileName);
            }
            catch (Exception)
            {
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\EditorMode.cs


namespace Updk7.Wpf
{
    public enum EditorMode
    {
        Create,
        Edit
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\EventListenersLocator.cs


using Prism.Ioc;

namespace Updk7.Wpf
{
    public class EventListenersLocator
    {
        private Prism.Events.IEventAggregator _eventAggregator;
        private IContainerProvider _containerProvider;
        private IContainerRegistry _containerRegistry;

        public EventListenersLocator(IContainerProvider containerProvider, IContainerRegistry containerRegistry)
        {
            _containerProvider = containerProvider;
            _containerRegistry = containerRegistry;
            _eventAggregator = containerProvider.Resolve<Prism.Events.IEventAggregator>();
        }

        public static EventListenersLocator Current { get; set; }

        public EventListenersLocator Register<T>() where T : IEventListener
        {
            _containerRegistry.RegisterSingleton<T>();
            var listener = _containerProvider.Resolve<T>();
            listener.Subscribe(_eventAggregator);
            return this;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\IEventListener.cs


namespace Updk7.Wpf
{
    public interface IEventListener
    {
        void Subscribe(Prism.Events.IEventAggregator eventAggregator);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\PdfPaginator.cs


using System;
using System.Globalization;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Updk7.Wpf
{
    internal class PdfPaginator : DocumentPaginator
    {
        private DocumentPaginator _sourcePaginator;
        private static BitmapImage Logo;

        static PdfPaginator()
        {
            Logo = loadLogoImage();
        }

        public PdfPaginator(DocumentPaginator sourcePaginator)
        {
            var scale = 96 / 25.4;
            _sourcePaginator = sourcePaginator;
            _sourcePaginator.PageSize = new Size(210 * scale, 297 * scale);
        }

        public override bool IsPageCountValid => _sourcePaginator.IsPageCountValid;

        public override int PageCount => _sourcePaginator.PageCount;

        public override Size PageSize
        {
            get { return _sourcePaginator.PageSize; }
            set { _sourcePaginator.PageSize = value; }
        }

        public override IDocumentPaginatorSource Source => _sourcePaginator.Source;

        public override DocumentPage GetPage(int pageNumber)
        {
            _sourcePaginator.ComputePageCount();
            DocumentPage page = _sourcePaginator.GetPage(pageNumber);

            var newPage = new ContainerVisual();
            var pageMask = new DrawingVisual();

            var typeface = new Typeface(new FontFamily("Segoe UI"), FontStyles.Normal,
                FontWeights.Light, FontStretches.Normal);

            using (DrawingContext ctx = pageMask.RenderOpen())
            {
                var text = $"Страница {pageNumber + 1} из {PageCount}";
                FormattedText formattedText = new FormattedText(text,
                    CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
                    typeface, 10, Brushes.Black, 1.0);

                var origin = new Point(PageSize.Width - formattedText.Width - 30,
                    PageSize.Height - formattedText.Height - 20);

                ctx.DrawText(formattedText, origin);
                ctx.DrawImage(Logo, new Rect(30, origin.Y, 80, 12));
            }

            newPage.Children.Add(page.Visual);
            newPage.Children.Add(pageMask);

            return new DocumentPage(newPage, PageSize, page.BleedBox, page.ContentBox);
        }

        private static BitmapImage loadLogoImage()
        {
            var uri = new Uri($"pack://application:,,,/Updk7.Wpf;component/Resources/logo.png");
            var image = new BitmapImage();
            image.BeginInit();
            image.UriSource = uri;
            image.EndInit();

            return image;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\ViewModelLocator.cs


using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Windows;

namespace Updk7.Wpf
{
    public class ViewModelLocator
    {
        private class NavigationKeyInfo
        {
            public string Region { get; set; }

            public Func<object> ViewModel { get; set; }
        }

        private Dictionary<string, NavigationKeyInfo> _navigationKeys = new Dictionary<string, NavigationKeyInfo>();
        private Dictionary<Type, Type> _viewModelToViewDictionary = new Dictionary<Type, Type>();
        private Prism.Regions.IRegionManager _regionManager;
        private Func<Type, object> _viewModelFactory;

        public ViewModelLocator(Prism.Regions.IRegionManager regionManager, Prism.Ioc.IContainerProvider container)
        {
            Debug.Assert(regionManager != null);
            Debug.Assert(container != null);
            _regionManager = regionManager;
            _viewModelFactory = t => container.Resolve(t);
            Prism.Mvvm.ViewModelLocationProvider.SetDefaultViewModelFactory(_viewModelFactory);
        }

        public static ViewModelLocator Current { get; set; }

        public ViewModelLocator Register<TViewModel, TView>(string regionName = null)
            where TView : FrameworkElement
        {
            Prism.Mvvm.ViewModelLocationProvider.Register<TView>(() => _viewModelFactory(typeof(TViewModel)));
            _viewModelToViewDictionary[typeof(TViewModel)] = typeof(TView);
            if (!string.IsNullOrEmpty(regionName))
                RegisterViewInRegion(regionName, typeof(TView));

            return this;
        }

        public FrameworkElement ResolveView<TView>()
        {
            return ResolveView(typeof(TView));
        }

        public FrameworkElement ResolveView(Type viewModelType)
        {
            if (_viewModelToViewDictionary.ContainsKey(viewModelType))
            {
                var viewType = _viewModelToViewDictionary[viewModelType];
                return Activator.CreateInstance(viewType) as FrameworkElement;
            }

            return null;
        }

        public void RegisterViewInRegion(string regionName, Type viewType)
        {
            Func<bool> registerView = () =>
            {
                if (_regionManager.Regions.ContainsRegionWithName(regionName))
                {
                    _regionManager.RegisterViewWithRegion(regionName, viewType);
                    return true;
                }

                return false;
            };
            
            NotifyCollectionChangedEventHandler lazyRegistration = null;
            lazyRegistration = (o, e) =>
            {
                if (registerView())
                    _regionManager.Regions.CollectionChanged -= lazyRegistration;
            };

            if (!registerView())
                _regionManager.Regions.CollectionChanged += lazyRegistration;
        }

        public void RegisterNavigationKey(string key, string region, Func<object> viewModel)
        {
            _navigationKeys[key] = new NavigationKeyInfo()
            { 
                Region = region,
                ViewModel = viewModel
            };
        }

        public void NavigateTo(string navigationKey)
        {
            var info = _navigationKeys[navigationKey];
            NavigateTo(info.Region, info.ViewModel());
        }

        public void NavigateTo(string regionName, object viewModel)
        {
            var viewType = getViewType(regionName, viewModel);
            if (viewType != null)
                _regionManager.RequestNavigate(regionName, viewType.Name);
        }

        private Type getViewType(string regionName, object viewModel)
        {
            var viewModeltype = viewModel.GetType();
            if (_viewModelToViewDictionary.ContainsKey(viewModeltype))
                return _viewModelToViewDictionary[viewModeltype];

            foreach (var region in _regionManager.Regions)
            {
                if (region.Name != regionName)
                    continue;

                foreach (FrameworkElement view in region.Views)
                {
                    if (view.DataContext == viewModel)
                    {
                        var viewType = view.GetType();
                        _viewModelToViewDictionary[viewModeltype] = viewType;
                        return viewType;
                    }
                }
            }

            return null;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Behaviors\PasswordBoxBehavior.cs


using System.Windows;
using System.Windows.Controls;

namespace Updk7.Wpf.Behaviors
{
    public class PasswordBoxBehavior : Microsoft.Xaml.Behaviors.Behavior<PasswordBox>
    {
        private bool _skipUpdate;

        public static readonly DependencyProperty PasswordProperty =
            DependencyProperty.Register(nameof(Password),
                typeof(string),
                typeof(PasswordBoxBehavior),
                new PropertyMetadata(default(string)));

        public string Password
        {
            get { return (string)GetValue(PasswordProperty); }
            set { SetValue(PasswordProperty, value); }
        }

        protected override void OnAttached()
        {
            AssociatedObject.PasswordChanged += onPasswordBoxPasswordChanged;
        }

        protected override void OnDetaching()
        {
            AssociatedObject.PasswordChanged -= onPasswordBoxPasswordChanged;
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);

            if (AssociatedObject == null)
                return;

            if (e.Property == PasswordProperty)
            {
                if (!_skipUpdate)
                {
                    _skipUpdate = true;
                    AssociatedObject.Password = e.NewValue as string;
                    _skipUpdate = false;
                }
            }
        }

        private void onPasswordBoxPasswordChanged(object sender, RoutedEventArgs e)
        {
            _skipUpdate = true;
            Password = AssociatedObject.Password;
            _skipUpdate = false;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Commands\CommandBase.cs


using System;
using System.Windows.Input;

namespace Updk7.Wpf.Commands
{
    public abstract class CommandBase : Prism.Mvvm.BindableBase, ICommand
    {
        public event EventHandler CanExecuteChanged;

        public virtual bool CanExecute(object parameter)
        {
            return false;
        }

        public abstract void Execute(object parameter);

        protected void RaiseCanExecuteChanged()
        {
            CanExecuteChanged?.Invoke(this, EventArgs.Empty);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Commands\CommandManager.cs


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Input;
using Prism.Ioc;

namespace Updk7.Wpf.Commands
{
    public class CommandManager
    {
        private Dictionary<object, Type> _resolveDictionary = new Dictionary<object, Type>();
        private IContainerProvider _provider;
        private IContainerRegistry _registry;

        public CommandManager(IContainerRegistry registry, IContainerProvider provider)
        {
            Debug.Assert(registry != null);
            Debug.Assert(provider != null);
            _provider = provider;
            _registry = registry;
        }

        public static CommandManager Current { get; set; }

        public ICommand Resolve(object key)
        {
            Debug.Assert(key != null);

            if (key == null || !_resolveDictionary.ContainsKey(key))
                return null;

            return _provider.Resolve(_resolveDictionary[key]) as ICommand;
        }

        public CommandManager Register<TCommand>(object key)
            where TCommand : ICommand
        {
            Debug.Assert(key != null);
            _registry.RegisterSingleton<TCommand>();
            _resolveDictionary[key] = typeof(TCommand);
            return this;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Commands\CommandProvider.cs


using System;
using System.Windows.Markup;

namespace Updk7.Wpf.Commands
{
    public class CommandProvider : MarkupExtension
    {
        public CommandProvider()
        {
        }

        public CommandProvider(object commandKey)
        {
            CommandKey = commandKey;
        }

        public object CommandKey { get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return CommandManager.Current?.Resolve(CommandKey);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Controls\Slider.cs


using System.Windows;
using System.Windows.Media;

namespace Updk7.Wpf.Controls
{
    public class Slider : FrameworkElement
    {
        private Pen _borderPen = new Pen(Brushes.LightGray, 1.0);

        public Slider()
        {
        }

        public double Minimum
        {
            get { return (double)GetValue(MinimumProperty); }
            set { SetValue(MinimumProperty, value); }
        }

        public static readonly DependencyProperty MinimumProperty =
            DependencyProperty.Register(
                nameof(Minimum),
                typeof(double),
                typeof(Slider),
                new FrameworkPropertyMetadata(-1.0, FrameworkPropertyMetadataOptions.AffectsRender));

        public double Maximum
        {
            get { return (double)GetValue(MaximumProperty); }
            set { SetValue(MaximumProperty, value); }
        }

        public static readonly DependencyProperty MaximumProperty =
            DependencyProperty.Register(
                nameof(Maximum),
                typeof(double),
                typeof(Slider),
                new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender));

        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register(
                nameof(Value),
                typeof(double),
                typeof(Slider),
                new FrameworkPropertyMetadata(0.5, FrameworkPropertyMetadataOptions.AffectsRender));

        public Brush PositiveForeground
        {
            get { return (Brush)GetValue(PositiveForegroundProperty); }
            set { SetValue(PositiveForegroundProperty, value); }
        }

        public static readonly DependencyProperty PositiveForegroundProperty =
            DependencyProperty.Register(
                nameof(PositiveForeground),
                typeof(Brush),
                typeof(Slider),
                new FrameworkPropertyMetadata(Brushes.LimeGreen, FrameworkPropertyMetadataOptions.AffectsRender));

        public Brush NegativeForeground
        {
            get { return (Brush)GetValue(NegativeForegroundProperty); }
            set { SetValue(NegativeForegroundProperty, value); }
        }

        public static readonly DependencyProperty NegativeForegroundProperty =
            DependencyProperty.Register(
                nameof(NegativeForeground),
                typeof(Brush),
                typeof(Slider),
                new FrameworkPropertyMetadata(Brushes.Red, FrameworkPropertyMetadataOptions.AffectsRender));

        public double EdgeRadius
        {
            get { return (double)GetValue(EdgeRadiusProperty); }
            set { SetValue(EdgeRadiusProperty, value); }
        }

        public static readonly DependencyProperty EdgeRadiusProperty =
            DependencyProperty.Register(
                nameof(EdgeRadius),
                typeof(double),
                typeof(Slider),
                new FrameworkPropertyMetadata(5.0, FrameworkPropertyMetadataOptions.AffectsRender));

        protected override void OnRender(DrawingContext drawingContext)
        {
            if (Maximum <= Minimum)
                return;

            var foreground = Value < 0.0 ? NegativeForeground : PositiveForeground;
            var zeroCoord = transform(0.0);
            var rectWidth = transform(Value) - zeroCoord;
            var rect = rectWidth >= 0.0
                ? new Rect(zeroCoord, 0.0, rectWidth, ActualHeight)
                : new Rect(zeroCoord + rectWidth, 0.0, -rectWidth, ActualHeight);

            drawingContext.DrawRoundedRectangle(foreground, _borderPen, rect, EdgeRadius, EdgeRadius);
        }

        private double transform(double x)
        {
            return (saturateValue(x) - Minimum) / (Maximum - Minimum) * ActualWidth;
        }

        private double saturateValue(double value)
        {
            if (value > Maximum)
                return Maximum;
            if (value < Minimum)
                return Minimum;

            return value;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Controls\PopupTable\PopupTableControl.xaml

<UserControl x:Class="Updk7.Wpf.Controls.PopupTable.PopupTableControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Controls.PopupTable"
             mc:Ignorable="d">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <TextBox 
            Grid.Column="0"
            Margin="0,1,2,1"
            Text="{Binding SelectedItems, Mode=OneWay}"
            IsReadOnly="True"
            Validation.ErrorTemplate="{x:Null}"
            TextWrapping="Wrap"
            Visibility="{Binding Visibility, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"
            />
        <ToggleButton
            Grid.Column="1"
            x:Name="DriveToggleButton"
            Margin="3,1,0,1"
            Height="25"
            Width="80"
            Style="{StaticResource FlatToggleButtonStyle}">
            <Grid>
                <TextBlock Text="Изменить" />
                <Popup
                    HorizontalOffset="-20"
                    VerticalOffset="-10"
                    Placement="Top"
                    AllowsTransparency="True"
                    StaysOpen="False"
                    PlacementTarget="{Binding ElementName=DriveToggleButton}"
                    IsOpen="{Binding IsChecked, ElementName=DriveToggleButton}">
                    <Border
                        BorderBrush="Black"
                        BorderThickness="0.75"
                        Background="{StaticResource WindowBackground}">
                        <ItemsControl
                            ItemsSource="{Binding TableItems}"
                            Background="Transparent">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <CheckBox 
                                        Margin="3"
                                        Style="{StaticResource FlatCheckBoxStyle}"
                                        IsChecked="{Binding IsChecked}"
                                        Content="{Binding Title}"
                                        />
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </Border>
                </Popup>
            </Grid>
        </ToggleButton>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Controls\PopupTable\PopupTableControl.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Controls.PopupTable
{
    /// <summary>
    /// Interaction logic for PopupTableControl.xaml
    /// </summary>
    public partial class PopupTableControl : UserControl
    {
        public PopupTableControl()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Controls\PopupTable\PopupTableDataContext.cs


using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;

namespace Updk7.Wpf.Controls.PopupTable
{
    public class PopupTableDataContext : Prism.Mvvm.BindableBase
    {
        private bool _skipNotification = false;

        public PopupTableDataContext()
        {
        }

        private IEnumerable<PopupTableItem> _tableItems;

        public IEnumerable<PopupTableItem> TableItems
        {
            get { return _tableItems; }
            private set { SetProperty(ref _tableItems, value); }
        }

        private string _selectedItems;

        /// <summary>
        /// Список ключей выбранных значений через запятую
        /// </summary>
        public string SelectedItems
        {
            get { return _selectedItems; }
            private set { SetProperty(ref _selectedItems, value); }
        }

        public void SetTableItems(IEnumerable<KeyValuePair<string, string>> items)
        {
            if (TableItems != null)
            {
                foreach (var item in TableItems)
                    item.PropertyChanged -= onTableItemPropertyChanged;
            }

            if (items != null)
            {
                TableItems = items.Select(i =>
                {
                    var viewModel = new PopupTableItem(i.Key, i.Value);
                    viewModel.PropertyChanged += onTableItemPropertyChanged;
                    return viewModel;
                }).ToArray();
            }
            else
                TableItems = null;
        }

        public void SetTableItems(IEnumerable<string> items)
        {
            SetTableItems(items.Select(i => new KeyValuePair<string, string>(i, i)).ToArray());
        }

        public void SetTableItems(params string[] items)
        {
            SetTableItems(items.AsEnumerable());
        }

        public void SetSelectedItems(string keysString)
        {
            _skipNotification = true;

            var splitData = keysString.Split(',').Select(c => c.Trim()).ToArray();
            foreach (var categoryViewModel in TableItems)
                categoryViewModel.IsChecked = splitData.Any(d => categoryViewModel.Key == d);

            _skipNotification = false;
            SelectedItems = getSelectedCategories();
        }

        private void onTableItemPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (!_skipNotification && e.PropertyName == nameof(PopupTableItem.IsChecked))
                SelectedItems = getSelectedCategories();
        }

        private string getSelectedCategories()
        {
            return TableItems
                .Where(c => c.IsChecked)
                .Select(c => c.Key)
                .Aggregate(string.Empty, (acc, key) => !string.IsNullOrEmpty(acc) ? $"{acc}, {key}" : key);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Controls\PopupTable\PopupTableItem.cs


namespace Updk7.Wpf.Controls.PopupTable
{
    public class PopupTableItem : Prism.Mvvm.BindableBase
    {
        public PopupTableItem(string key, string description)
        {
            Key = key;
            Title = key != description
                ? $"{key} ({description})"
                : key;
        }

        public string Key { get; }

        public string Title { get; }

        private bool _isChecked;

        public bool IsChecked
        {
            get { return _isChecked; }
            set { SetProperty(ref _isChecked, value); }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Converters\BoolToBrushConverter.cs


using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;

namespace Updk7.Wpf.Converters
{
    public class BoolToBrushConverter : MarkupExtension, IValueConverter
    {
        public BoolToBrushConverter()
        {
        }

        public Brush TrueBrush { get; set; } = Brushes.Black;

        public Brush FalseBrush { get; set; } = Brushes.DarkGray;

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is bool)
                return (bool)value ? TrueBrush : FalseBrush;

            return TrueBrush;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Converters\BoolToStringConverter.cs


using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;

namespace Updk7.Wpf.Converters
{
    public class BoolToStringConverter : MarkupExtension, IValueConverter
    {
        public BoolToStringConverter()
        {
        }

        public string TrueString { get; set; } = "true";

        public string FalseString { get; set; } = "false";

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is bool)
                return (bool)value ? TrueString : FalseString;

            return FalseString;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Converters\EnumToBoolConverter.cs


using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;

namespace Updk7.Wpf.Converters
{
    public class EnumToBoolConverter : MarkupExtension, IValueConverter
    {
        public EnumToBoolConverter()
        {
        }

        public string EnumValue { get; set; }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
                return DependencyProperty.UnsetValue;

            if (string.IsNullOrEmpty(EnumValue))
                return DependencyProperty.UnsetValue;

            if (!Enum.IsDefined(value.GetType(), value))
                return DependencyProperty.UnsetValue;

            var enumValue = Enum.Parse(value.GetType(), EnumValue);

            return enumValue.Equals(value);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if ((bool)value)
            {
                Type enumType = null;
                if (targetType.IsEnum)
                    enumType = targetType;
                else if (!targetType.IsEnum && targetType.GetGenericTypeDefinition() == typeof(Nullable<>))
                    enumType = Nullable.GetUnderlyingType(targetType);

                if (enumType != null && Enum.IsDefined(enumType, EnumValue))
                    return Enum.Parse(enumType, EnumValue);
            }

            return Binding.DoNothing;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Converters\KeyToStringConverter.cs


using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows.Data;
using System.Windows.Markup;
using System.Diagnostics;

namespace Updk7.Wpf.Converters
{
    public class KeyToStringConverter : MarkupExtension, IValueConverter
    {
        private Dictionary<object, string> _conversion = new Dictionary<object, string>();

        public KeyToStringConverter()
        {
        }

        public IEnumerable KeysSource
        {
            set
            {
                foreach (var key in value)
                    _conversion[key] = getString(key);
            }
        }

        public Type EnumKeySource
        {
            set
            {
                Debug.Assert(value.IsEnum);
                KeysSource = Enum.GetValues(value);
            }
        }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (!_conversion.ContainsKey(value))
                _conversion[value] = getString(value);

            return _conversion[value];
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var str = value as string;
            var keyValue = _conversion.FirstOrDefault(kvp => kvp.Value == str);
            return keyValue.Key != null ? keyValue.Key : str;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }

        private string getString(object key)
        {
            return AppStrings.Contains(key) ? AppStrings.GetString(key) : key.ToString();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Converters\KeyToStringItemsSource.cs


using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Markup;

namespace Updk7.Wpf.Converters
{
    public class KeyToStringItemsSource : MarkupExtension
    {
        private IEnumerable _keys;

        public KeyToStringItemsSource()
        {
        }

        public IEnumerable KeysSource
        {
            set { _keys = value; }
        }

        public Type EnumKeysSource
        {
            set 
            {
                Debug.Assert(value.IsEnum);
                _keys = Enum.GetValues(value); 
            }
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            var results = new List<string>();

            foreach (var key in _keys)
            {
                var str = AppStrings.Contains(key)
                    ? AppStrings.GetString(key)
                    : key.ToString();
                results.Add(str);
            }

            return results;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Converters\NullableIntToStringConverter.cs


using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;

namespace Updk7.Wpf.Converters
{
    public class NullableIntToStringConverter : MarkupExtension, IValueConverter
    {
        public NullableIntToStringConverter()
        {
        }

        public string NullString { get; set; } = "N/A";

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var v = (int?)value;
            return v.HasValue ? v.ToString() : NullString;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Converters\ObjectToVisibilityConverter.cs


using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;

namespace Updk7.Wpf.Converters
{
    public class ObjectToVisibilityConverter : MarkupExtension, IValueConverter
    {
        public ObjectToVisibilityConverter()
        {
        }

        public bool InverseFlag { get; set; }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var boolValue = value != null;
            if (value is string)
                boolValue = !string.IsNullOrEmpty(value as string);
            if (value is bool)
                boolValue = (bool)value;

            if (!InverseFlag)
            {
                return boolValue
                    ? Visibility.Visible
                    : Visibility.Collapsed;
            }
            else
            {
                return boolValue
                    ? Visibility.Collapsed
                    : Visibility.Visible;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Converters\RangeToItemsSource.cs


using System;
using System.Linq;
using System.Windows.Markup;

namespace Updk7.Wpf.Converters
{
    public class RangeToItemsSource : MarkupExtension
    {
        public RangeToItemsSource()
        {
        }

        public int Start { get; set; } = 0;

        public int Count { get; set; } = 1;

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return Enumerable.Range(Start, Count).ToArray();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Converters\StringToBoolMatchConverter.cs


using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;

namespace Updk7.Wpf.Converters
{
    public class StringToBoolMatchConverter : MarkupExtension, IValueConverter
    {
        public StringToBoolMatchConverter()
        {
        }

        public string MatchString { get; set; }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value.ToString() == MatchString;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return MatchString;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Interactions\ConfirmationInteraction.cs


using System;

namespace Updk7.Wpf.Interactions
{
    public class ConfirmationInteraction : Prism.Mvvm.BindableBase, IInteractionViewModel
    {
        public ConfirmationInteraction()
        {
            setupCommands();
        }

        public event EventHandler CompleteInteractionRequest;

        public Prism.Commands.DelegateCommand AcceptCommand { get; protected set; }

        public Prism.Commands.DelegateCommand CancelCommand { get; protected set; }

        private string _message;

        public string Message
        {
            get { return _message; }
            set { SetProperty(ref _message, value); }
        }

        private bool _confirmed;

        public bool Confirmed
        {
            get { return _confirmed; }
            set { SetProperty(ref _confirmed, value); }
        }

        protected virtual bool CanConfirm()
        {
            return true;
        }

        private void setupCommands()
        {
            CancelCommand = new Prism.Commands.DelegateCommand(onCompleteInteraction);
            AcceptCommand = new Prism.Commands.DelegateCommand(
                () => { Confirmed = true; onCompleteInteraction(); },
                CanConfirm);
        }

        private void onCompleteInteraction()
        {
            CompleteInteractionRequest?.Invoke(this, EventArgs.Empty);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Interactions\ErrorInteraction.cs


using System.Windows.Media.Imaging;

namespace Updk7.Wpf.Interactions
{
    public class ErrorInteraction : NotificationInteraction
    {
        public ErrorInteraction()
        {
            NotificationImage = Styles.DefaultImages.ErrorImage;
        }

        private BitmapImage _notificationImage;

        public BitmapImage NotificationImage
        {
            get { return _notificationImage; }
            protected set { SetProperty(ref _notificationImage, value); }
        }

        private string _message;

        public string Message
        {
            get { return _message; }
            set { SetProperty(ref _message, value); }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Interactions\IInteractionService.cs


using System;
using System.Windows;

namespace Updk7.Wpf.Interactions
{
    public interface IInteractionService
    {
        void ShowErrorMessage(string error, Action completeAction = null, bool asDialog = true);

        void ShowInformationMessage(string message, Action completeAction = null, bool asDialog = true);

        void ShowInteractionModel(IInteractionViewModel viewModel, Action completeAction = null, bool asDialog = true);

        IInteractionService Register<TViewModel, TView>()
           where TViewModel : IInteractionViewModel
           where TView : FrameworkElement, new();
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Interactions\IInteractionViewModel.cs


using System;

namespace Updk7.Wpf.Interactions
{
    public interface IInteractionViewModel
    {
        event EventHandler CompleteInteractionRequest;
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Interactions\InformationInteraction.cs


namespace Updk7.Wpf.Interactions
{
    public class InformationInteraction : ErrorInteraction
    {
        public InformationInteraction()
        {
            NotificationImage = Styles.DefaultImages.InformationImage;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Interactions\InteractionService.cs


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;

namespace Updk7.Wpf.Interactions
{
    public class InteractionService : IInteractionService
    {
        private Dictionary<Type, Func<object>> _resolvesDictionary = new Dictionary<Type, Func<object>>();

        static InteractionService()
        {
            Current = new InteractionService();
        }

        public InteractionService()
        {
            Register<ConfirmationInteraction, Views.ConfirmationView>();
            Register<ErrorInteraction, Views.NotificationView>();
            Register<InformationInteraction, Views.NotificationView>();
        }

        public static IInteractionService Current { get; private set; }

        public void ShowErrorMessage(string error, Action completeAction, bool asDialog = true)
        {
            if (AppLog.Current != null)
                AppLog.Current.Error($"InteractionService.ShowErrorMessage() -> {error}");

            var viewModel = new ErrorInteraction();
            viewModel.Message = error;
            ShowInteractionModel(viewModel, completeAction, asDialog);
        }

        public void ShowInformationMessage(string message, Action completeAction = null, bool asDialog = true)
        {
            if (AppLog.Current != null)
                AppLog.Current.Info($"InteractionService.ShowInformationMessage() -> {message}");

            var viewModel = new InformationInteraction();
            viewModel.Message = message;
            ShowInteractionModel(viewModel, completeAction);
        }

        public void ShowInteractionModel(IInteractionViewModel viewModel, Action completeAction, bool asDialog = true)
        {
            Debug.Assert(_resolvesDictionary.ContainsKey(viewModel.GetType()));
            var view = _resolvesDictionary[viewModel.GetType()]();
            var interactionWindow = createInteractionWindow();
            Debug.Assert(view != null);

            EventHandler completeRequestHandler = (o, e) => interactionWindow.Close();
            EventHandler windowClosedHandler = null;

            windowClosedHandler = (o, e) =>
            {
                viewModel.CompleteInteractionRequest -= completeRequestHandler;
                interactionWindow.Closed -= windowClosedHandler;
                interactionWindow.Content = null;
                interactionWindow.DataContext = null;
                completeAction?.Invoke();
            };

            viewModel.CompleteInteractionRequest += completeRequestHandler;
            interactionWindow.DataContext = viewModel;
            interactionWindow.SetInteractionContent(view);
            interactionWindow.Closed += windowClosedHandler;

            if (asDialog)
                interactionWindow.ShowDialog();
            else
                interactionWindow.Show();
        }

        public IInteractionService Register<TViewModel, TView>()
           where TViewModel : IInteractionViewModel
           where TView : FrameworkElement, new()
        {
            _resolvesDictionary[typeof(TViewModel)] = () => new TView();
            return this;
        }

        private Views.InteractionWindow createInteractionWindow()
        {
            var window = new Views.InteractionWindow();
            var mainWindow = Application.Current.MainWindow;

            window.ShowInTaskbar = false;
            window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
            window.SizeToContent = SizeToContent.WidthAndHeight;
            window.ResizeMode = ResizeMode.NoResize;

            if (mainWindow != null && mainWindow.Visibility != Visibility.Collapsed)
            {
                window.Title = mainWindow.Title;
                window.Owner = mainWindow;
            }

            return window;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Interactions\NotificationInteraction.cs


using System;

namespace Updk7.Wpf.Interactions
{
    public class NotificationInteraction : Prism.Mvvm.BindableBase, IInteractionViewModel
    {
        public NotificationInteraction()
        {
            CompleteInteractionCommand = new Prism.Commands.DelegateCommand(OnCompleteInteraction);
        }

        public event EventHandler CompleteInteractionRequest;

        public Prism.Commands.DelegateCommand CompleteInteractionCommand { get; }

        protected void OnCompleteInteraction()
        {
            CompleteInteractionRequest?.Invoke(this, EventArgs.Empty);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Interactions\Views\ConfirmationView.xaml

<UserControl x:Class="Updk7.Wpf.Interactions.Views.ConfirmationView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Interactions.Views"
             mc:Ignorable="d" 
             MinWidth="500"
             MaxWidth="1000">
    <Grid
        Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid
            Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Image 
                Grid.Column="0" 
                Width="96"
                Margin="5,5,15,5"
                Source="{StaticResource QuestionImage}" 
                />
            <TextBlock 
                Grid.Column="1"
                Text="{Binding Message}"
                FontSize="14"
                FontWeight="Normal"
                TextWrapping="Wrap"
                VerticalAlignment="Center"
                HorizontalAlignment="Center"
                />
        </Grid>
        <Grid
            Grid.Row="1"
            Margin="0,5,0,0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Button 
                Grid.Column="1"
                Width="80"
                Height="30"
                HorizontalAlignment="Right"
                FontSize="14"
                FontWeight="Bold"
                Content="Принять"
                Command="{Binding AcceptCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
            <Button 
                Grid.Column="2"
                Width="80"
                Height="30"
                FontSize="14"
                FontWeight="Bold"
                HorizontalAlignment="Right"
                Content="Отменить"
                Command="{Binding CancelCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Interactions\Views\ConfirmationView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Interactions.Views
{
    /// <summary>
    /// Interaction logic for ConfirmationView.xaml
    /// </summary>
    public partial class ConfirmationView : UserControl
    {
        public ConfirmationView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Interactions\Views\InteractionWindow.xaml

<Window x:Class="Updk7.Wpf.Interactions.Views.InteractionWindow"
        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:Updk7.Wpf.Interactions.Views"
        xmlns:styles="clr-namespace:Updk7.Wpf.Styles"
        mc:Ignorable="d"
        Title="УПДК"
        Style="{StaticResource WindowStyle}"
        Background="{StaticResource WindowBackground}"
        BorderBrush="Black"
        BorderThickness="1.5"
        styles:WindowHeader.CanBeMaximized="False"
        styles:WindowHeader.CanBeMinimized="False"
        WindowStartupLocation="CenterScreen">
    <Grid
        Margin="3,0,3,3">
        <ContentPresenter 
            x:Name="ContentView"
            />
        
    </Grid>
</Window>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Interactions\Views\InteractionWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace Updk7.Wpf.Interactions.Views
{
    /// <summary>
    /// Interaction logic for InteractionWindow.xaml
    /// </summary>
    public partial class InteractionWindow : Window
    {
        public InteractionWindow()
        {
            InitializeComponent();
        }

        public void SetInteractionContent(object content)
        {
            ContentView.Content = content;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Interactions\Views\NotificationView.xaml

<UserControl x:Class="Updk7.Wpf.Interactions.Views.NotificationView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Interactions.Views"
             mc:Ignorable="d" 
             MaxHeight="700"
             MaxWidth="600"
             MinWidth="300">
    <Grid
        Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid
            Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Image 
                Grid.Column="0" 
                Width="60"
                Margin="5,5,15,5"
                Source="{Binding NotificationImage}" 
                />
            <TextBlock 
                Grid.Column="1"
                Text="{Binding Message}"
                FontSize="14"
                FontWeight="Normal"
                TextWrapping="Wrap"
                VerticalAlignment="Center"
                HorizontalAlignment="Center"
                />
        </Grid>
        <Grid
            Grid.Row="1"
            Margin="0,5,0,0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Button
                Grid.Column="1"
                Width="120"
                Height="30"
                HorizontalAlignment="Right"
                FontSize="14"
                FontWeight="Bold"
                Content="Все понятно"
                Command="{Binding CompleteInteractionCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Interactions\Views\NotificationView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Interactions.Views
{
    /// <summary>
    /// Interaction logic for NotificationView.xaml
    /// </summary>
    public partial class NotificationView : UserControl
    {
        public NotificationView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Shell\IShellController.cs


using System;
using System.ComponentModel;
using System.Windows;

namespace Updk7.Wpf.Shell
{
    public interface IShellController : INotifyPropertyChanged
    {
        string Title { get; set; }

        FrameworkElement CurrentView { get; }

        string CurrentNavigationKey { get; }

        IShellController Register<TView>(string navigationKey) where TView : FrameworkElement, new();

        IShellController Register<TView>(string navigationKey, Func<TView> viewFactory) where TView : FrameworkElement;

        void NavigateTo(string navigationKey);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Shell\ShellController.cs


using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Updk7.Wpf.Shell
{
    public class ShellController : Prism.Mvvm.BindableBase, IShellController
    {
        private Dictionary<string, Func<FrameworkElement>> _viewFactories =
            new Dictionary<string, Func<FrameworkElement>>();

        public ShellController()
        {
            var textBox = new TextBlock();
            textBox.Text = "Загрузка...";
            textBox.VerticalAlignment = VerticalAlignment.Center;
            textBox.HorizontalAlignment = HorizontalAlignment.Center;
            textBox.FontSize = 16;
            textBox.FontWeight = FontWeights.Bold;
            textBox.Background = Brushes.Transparent;
            CurrentView = textBox;
        }

        private string _title = "Нейроком. УПДК";

        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }

        private FrameworkElement _currentView;

        public FrameworkElement CurrentView
        {
            get { return _currentView; }
            private set { SetProperty(ref _currentView, value); }
        }

        private string _currentNavigationKey;

        public string CurrentNavigationKey
        {
            get { return _currentNavigationKey; }
            private set { SetProperty(ref _currentNavigationKey, value); }
        }

        public IShellController Register<TView>(string navigationKey)
            where TView : FrameworkElement, new()
        {
            var view = new Lazy<FrameworkElement>(() => new TView());
            Register(navigationKey, () => view.Value);
            return this;
        }

        public IShellController Register<TView>(string navigationKey, Func<TView> viewFactory)
            where TView : FrameworkElement
        {
            _viewFactories[navigationKey] = viewFactory;
            return this;
        }

        public void NavigateTo(string navigationKey)
        {
            if (_currentNavigationKey != navigationKey)
            {
                CurrentNavigationKey = navigationKey;
                CurrentView = _viewFactories[navigationKey]();
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Shell\ShellWindow.xaml

<Window x:Class="Updk7.Wpf.Shell.ShellWindow"
        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:prism="http://prismlibrary.com/"
        Title="{Binding Title}"
        WindowStartupLocation="CenterScreen"
        Background="{StaticResource WindowBackground}"
        Style="{StaticResource WindowStyle}"
        BorderBrush="Black"
        BorderThickness="1"
        prism:ViewModelLocator.AutoWireViewModel="True"
        mc:Ignorable="d"
        Height="768" 
        Width="1024">
    <Grid>
        <ContentControl 
            Content="{Binding CurrentView}"
            />
    </Grid>
</Window>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Shell\ShellWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace Updk7.Wpf.Shell
{
    /// <summary>
    /// Interaction logic for ShellWindow.xaml
    /// </summary>
    public partial class ShellWindow : Window
    {
        public ShellWindow()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Shell\WindowHelper.cs


using System;
using System.Windows;

namespace Updk7.Wpf.Shell
{
    public static class WindowHelper
    {
        public static void MoveToSecondScreen(Window window)
        {
            if (System.Windows.Forms.Screen.AllScreens.Length < 2)
                return;

            try
            {
                var screen = System.Windows.Forms.Screen.AllScreens[1];
                var workingArea = screen.WorkingArea;
                window.WindowStartupLocation = WindowStartupLocation.Manual;
                window.Top = workingArea.Top;
                window.Left = workingArea.Left;
            }
            catch (Exception)
            {
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Styles\DefaultImages.cs


using System.Windows;
using System.Windows.Media.Imaging;

namespace Updk7.Wpf.Styles
{
    public static class DefaultImages
    {
        private static BitmapImage _employeePhoto;
        private static BitmapImage _errorImage;
        private static BitmapImage _informationImage;

        public static BitmapImage EmployeePhoto
        {
            get
            {
                if (_employeePhoto == null)
                    _employeePhoto = Application.Current.FindResource("EmployeeImage") as BitmapImage;

                return _employeePhoto;
            }
        }

        public static BitmapImage ErrorImage
        {
            get
            {
                if (_errorImage == null)
                    _errorImage = Application.Current.FindResource("ErrorImage") as BitmapImage;

                return _errorImage;
            }
        }

        public static BitmapImage InformationImage
        {
            get
            {
                if (_informationImage == null)
                    _informationImage = Application.Current.FindResource("InformationImage") as BitmapImage;

                return _informationImage;
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Styles\TabItemHeader.cs


using System.Windows;

namespace Updk7.Wpf.Styles
{
    public class TabItemHeader
    {
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.RegisterAttached(
                "Text",
                typeof(string),
                typeof(TabItemHeader),
                new PropertyMetadata(null));

        public static string GetText(DependencyObject obj)
        {
            return (string)obj.GetValue(TextProperty);
        }

        public static void SetText(DependencyObject obj, string value)
        {
            obj.SetValue(TextProperty, value);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Styles\WindowHeader.cs


using System.Windows;

namespace Updk7.Wpf.Styles
{
    public sealed class WindowHeader : DependencyObject
    {
        public WindowHeader()
        {
        }

        public static readonly DependencyProperty ContentProperty =
            DependencyProperty.RegisterAttached(
                "Content",
                typeof(object),
                typeof(WindowHeader),
                new PropertyMetadata(null));

        public static object GetContent(DependencyObject obj)
        {
            return obj.GetValue(ContentProperty);
        }

        public static void SetContent(DependencyObject obj, object value)
        {
            obj.SetValue(ContentProperty, value);
        }

        public static readonly DependencyProperty CanBeMaximizedProperty =
            DependencyProperty.RegisterAttached(
                "CanBeMaximized",
                typeof(bool),
                typeof(WindowHeader),
                new PropertyMetadata(true));

        public static bool GetCanBeMaximized(DependencyObject obj)
        {
            return (bool)obj.GetValue(CanBeMaximizedProperty);
        }

        public static void SetCanBeMaximized(DependencyObject obj, bool value)
        {
            obj.SetValue(CanBeMaximizedProperty, value);
        }

        public static bool GetCanBeMinimized(DependencyObject obj)
        {
            return (bool)obj.GetValue(CanBeMinimizedProperty);
        }

        public static void SetCanBeMinimized(DependencyObject obj, bool value)
        {
            obj.SetValue(CanBeMinimizedProperty, value);
        }

        public static readonly DependencyProperty CanBeMinimizedProperty =
            DependencyProperty.RegisterAttached(
                "CanBeMinimized",
                typeof(bool),
                typeof(WindowHeader),
                new PropertyMetadata(true));
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Styles\WindowStyle.cs


using System.Diagnostics;
using System.Windows;
using System.Windows.Input;

namespace Updk7.Wpf.Styles
{
    public partial class WindowStyle
    {
        public WindowStyle()
        {
        }

        private void OnChromeMouseClick(object sender, MouseButtonEventArgs e)
        {
            var window = sender.AsWindow();

            if (e.ClickCount == 2)
                OnMaximaizeWindow(sender, e);
            else if (e.LeftButton == MouseButtonState.Pressed)
                window.DragMove();
        }

        private void OnWindowClose(object sender, RoutedEventArgs e)
        {
            var window = sender.AsWindow();
            window.Close();
        }

        private void OnMinimizeVindow(object sender, RoutedEventArgs e)
        {
            sender.AsWindow().WindowState = WindowState.Minimized;
        }

        private void OnMaximaizeWindow(object sender, RoutedEventArgs e)
        {
            var window = sender.AsWindow();
            var canBeMaximized = WindowHeader.GetCanBeMaximized(window);
            if (!canBeMaximized)
                return;

            window.WindowState = window.WindowState == WindowState.Maximized
                ? WindowState.Normal
                : WindowState.Maximized;
        }
    }

    internal static class WindowExtensions
    {
        public static Window AsWindow(this object sender)
        {
            var window = (sender as FrameworkElement).TemplatedParent as Window;
            Debug.Assert(window != null);

            return window;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Validation\DataErrorInfoContainer.cs


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections;
using System.Linq;
using System.Diagnostics;

namespace Updk7.Wpf.Validation
{
    /// <summary>
    /// Контейнер сообщений об ошибках с явной реализацией <see cref="INotifyDataErrorInfo"/> 
    /// </summary>
    /// <remarks>
    /// Контейнер поддерживает как явное задание ошибок для хранимых имен свойств
    /// <see cref="SetCustomError(string, string)"/>, так и функциональную проверку с заданием 
    /// предиката типа <see cref="Func{Boolean}<>"/>. Дополнительно возможно задавать правила
    /// валидации через непосредственную реализацию интерфейса <see cref="IValidationRule"/>
    /// </remarks>
    public class DataErrorInfoContainer : INotifyDataErrorInfo
    {
        private readonly Dictionary<string, PropertyValidationObject> _propertyValidationObjects =
            new Dictionary<string, PropertyValidationObject>();

        /// <summary>
        /// Создание экземпляра объекта
        /// </summary>
        public DataErrorInfoContainer()
        {
        }

        /// <summary>
        /// Флаг наличия ошибок
        /// </summary>
        /// <remarks>
        /// Реализация интерфейса <see cref="INotifyDataErrorInfo"/>
        /// </remarks>
        public bool HasErrors { get { return _propertyValidationObjects.Values.Any(v => v.HasErrors); } }

        /// <summary>
        /// Событие изменения хранимых ошибок в контейнере
        /// </summary>
        /// <remarks>
        /// Реализация интерфейса <see cref="INotifyDataErrorInfo"/>
        /// </remarks>
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        /// <summary>
        /// Возвращает перечисление ошибок для заданного хранимого имени свойства
        /// </summary>
        /// <remarks>
        /// Реализация интерфейса <see cref="INotifyDataErrorInfo"/>
        /// </remarks>
        /// <param name="propertyName">Хранимое имя свойства, для которого возвраащаются ошибки</param>
        /// <returns>Перечисление ошибок, пустое в случае их отсутствия</returns>
        public IEnumerable GetErrors(string propertyName)
        {
            return !string.IsNullOrEmpty(propertyName) && _propertyValidationObjects.ContainsKey(propertyName)
                ? _propertyValidationObjects[propertyName].GetErrors()
                : Enumerable.Empty<string>();
        }

        public IEnumerable<string> GetErrors()
        {
            return _propertyValidationObjects.Values
                .Where(o => o.HasErrors)
                .SelectMany(o => o.GetErrors());
        }

        /// <summary>
        /// Вызов методов валидации для всех имен, хранимых в контейнере
        /// </summary>
        public void UpdateValidation()
        {
            foreach (var propertyName in _propertyValidationObjects.Keys)
                UpdatePropertyValidation(propertyName);
        }

        /// <summary>
        /// Вызов метода валидации для заданного имени свойства
        /// </summary>
        /// <param name="propertyName">Имя валидируемого свойства</param>
        public void UpdatePropertyValidation(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName) || !_propertyValidationObjects.ContainsKey(propertyName))
                return;

            var validator = getPropertyValidator(propertyName);
            validator.UpdateValidation();
            notifyErrorsChange(propertyName);
        }

        /// <summary>
        /// Проверка валидности свойства с заданным именем
        /// </summary>
        /// <remarks>
        /// При вызове данного метода событие изменения ошибок свойства не генерируется
        /// </remarks>
        /// <param name="propertyName">Имя проверяемого свойства</param>
        /// <returns>Флаг валидности свойства</returns>
        public bool IsPropertyValid(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName) || !_propertyValidationObjects.ContainsKey(propertyName))
                return true;

            var validator = getPropertyValidator(propertyName);
            validator.UpdateValidation();

            return !validator.HasErrors;
        }

        /// <summary>
        /// Добавление правила валидации
        /// </summary>
        /// <param name="propertyName">Имя хранимого свойства для которого добавляется правило</param>
        /// <param name="predicate">Предикат правила валидации</param>
        /// <param name="errorMessage">Сообщение об ошибке при неуспешной валидации</param>
        public void AddValidationRule(string propertyName, Func<bool> predicate, string errorMessage)
        {
            Debug.Assert(!string.IsNullOrEmpty(propertyName));

            var validator = getPropertyValidator(propertyName);
            validator.AddRule(predicate, errorMessage);
        }

        /// <summary>
        /// Добавление правила валидации
        /// </summary>
        /// <param name="propertyName">Имя хранимого свойства для которого добавляется правило</param>
        /// <param name="rule">Реализация правила валидации</param>
        public void AddValidationRule(string propertyName, IValidationRule rule)
        {
            Debug.Assert(!string.IsNullOrEmpty(propertyName));
            Debug.Assert(rule != null);

            var validator = getPropertyValidator(propertyName);
            validator.AddRule(rule);
        }

        /// <summary>
        /// Задание ошибки для хранимого свойства
        /// </summary>
        /// <remarks>
        /// После задания ошибки свойство считается не прошедшим валидацию, метод
        /// <see cref="GetErrors(string)"/> для свойства возвращает заданную строку ошибки.
        /// Сброс состояния происходит после вызова метода <see cref="ClearCustomErrors(string)"/>
        /// Допустим многократный вызов, при этом каждое новое сообщение добавляется во внутренний 
        /// список ошибок
        /// </remarks>
        /// <param name="propertyName">Имя хранимого свойства для которого задается ошибка</param>
        /// <param name="errorMessage">Сообщение об ошибке</param>
        public void SetCustomError(string propertyName, string errorMessage)
        {
            Debug.Assert(!string.IsNullOrEmpty(propertyName));
            Debug.Assert(!string.IsNullOrEmpty(errorMessage));

            var validator = getPropertyValidator(propertyName);
            validator.SetCustomError(errorMessage);
            notifyErrorsChange(propertyName);
        }

        /// <summary>
        /// Сброс состояния ошибки для заданного свойства
        /// </summary>
        /// <param name="propertyName">Имя свойства</param>
        public void ClearCustomErrors(string propertyName)
        {
            Debug.Assert(!string.IsNullOrEmpty(propertyName));

            var validator = getPropertyValidator(propertyName);
            validator.ClearCustomErrors();
            notifyErrorsChange(propertyName);
        }

        private PropertyValidationObject getPropertyValidator(string propertyName)
        {
            if (!_propertyValidationObjects.ContainsKey(propertyName))
                _propertyValidationObjects.Add(propertyName, new PropertyValidationObject(propertyName));

            return _propertyValidationObjects[propertyName];
        }

        private void notifyErrorsChange(string propertyName)
        {
            ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Validation\IPropertyValidator.cs


using System.Collections.Generic;

namespace Updk7.Wpf.Validation
{
    /// <summary>
    /// Вспомогательный интерфейс валидации данных
    /// </summary>
    /// <remarks>
    /// Используется в классе <see cref="DataErrorInfoContainer"/>
    /// </remarks>
    internal interface IPropertyValidator
    {
        /// <summary>
        /// Флаг наличия ошибки
        /// </summary>
        bool HasErrors { get; }

        /// <summary>
        /// Возвращает перечисление ошибок свойства
        /// </summary>
        /// <returns>Ошибки</returns>
        IEnumerable<string> GetErrors();

        /// <summary>
        /// Обновление валидации свойства
        /// </summary>
        void UpdateValidation();
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Validation\IValidationRule.cs


namespace Updk7.Wpf.Validation
{
    /// <summary>
    /// Интерфейс правила валидации
    /// </summary>
    /// <remarks>
    /// Используется в классе <see cref="DataErrorInfoContainer"/>
    /// </remarks>
    public interface IValidationRule
    {
        /// <summary>
        /// Флаг валидности данных
        /// </summary>
        bool IsValid { get; }

        /// <summary>
        /// Сообщение об ошибке
        /// </summary>
        string Error { get; }

        /// <summary>
        /// Обновление валидации данных
        /// </summary>
        void UpdateValidation();
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Validation\PropertyCustomErrorsSet.cs


using System.Collections.Generic;
using System.Diagnostics;

namespace Updk7.Wpf.Validation
{
    /// <summary>
    /// Внутренний класс хранения пользовательских сообщений об ошибках
    /// </summary>
    /// <remarks>
    /// Используется в классе <see cref="DataErrorInfoContainer"/>. Хранит пользовательские сообщения
    /// об ошибках, заданные явно
    /// </remarks>
    internal class PropertyCustomErrorsSet : IPropertyValidator
    {
        private readonly List<string> _errorsList = new List<string>();

        /// <summary>
        /// Создание экземпляра объекта
        /// </summary>
        public PropertyCustomErrorsSet()
        {
        }

        /// <summary>
        /// Флаг наличия ошибки
        /// </summary>
        /// <remarks>
        /// Реализация интерфейса <see cref="IPropertyValidator"/>
        /// </remarks>
        public bool HasErrors { get { return _errorsList.Count != 0; } }

        /// <summary>
        /// Возвращает перечисление ошибок
        /// </summary>
        /// <remarks>
        /// Реализация интерфейса <see cref="IPropertyValidator"/>
        /// </remarks>
        /// <returns>Ошибки</returns>
        public IEnumerable<string> GetErrors()
        {
            return _errorsList;
        }

        /// <summary>
        /// Обновление валидации
        /// </summary>
        ///<remarks>
        /// Реализация интерфейса <see cref="IPropertyValidator"/>
        /// </remarks>
        public void UpdateValidation()
        {
        }

        /// <summary>
        /// Задание ошибки
        /// </summary>
        /// <remarks>
        /// После задания ошибки свойстов <see cref="HasErrors"/> выставляется в истину.
        /// Допустим многократный вызов, при этом каждое новое сообщение добавляется 
        /// во внутренний список ошибок
        /// </remarks>
        /// <param name="errorMessage">Сообщение об ошибке</param>
        public void SetError(string errorMessage)
        {
            Debug.Assert(!string.IsNullOrEmpty(errorMessage));
            _errorsList.Add(errorMessage);
        }

        /// <summary>
        /// Очистка списка ошибок
        /// </summary>
        /// <remarks>
        /// После вызова метода свойство <see cref="HasErrors"/> сбрасывается
        /// </remarks>
        public void ClearErrors()
        {
            _errorsList.Clear();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Validation\PropertyValidationObject.cs


using System;
using System.Collections.Generic;
using System.Linq;

namespace Updk7.Wpf.Validation
{
    /// <summary>
    /// Внутренний класс валидации свойств
    /// </summary>
    /// <remarks>
    /// Является аггрегацией классов валидации <see cref="PropertyCustomErrorsSet"/> и
    /// <see cref="PropertyValidationRules"/>. Используется в классе <see cref="DataErrorInfoContainer"/>
    /// </remarks>
    internal class PropertyValidationObject : IPropertyValidator
    {
        private readonly PropertyValidationRules _rulesErrorsSet = new PropertyValidationRules();
        private readonly PropertyCustomErrorsSet _customErrorsSet = new PropertyCustomErrorsSet();
        private readonly List<IPropertyValidator> _validators = new List<IPropertyValidator>();

        /// <summary>
        /// Создание экземпляра объекта
        /// </summary>
        public PropertyValidationObject(string propertyName)
        {
            PropertyName = propertyName;
            _validators.Add(_rulesErrorsSet);
            _validators.Add(_customErrorsSet);
        }

        /// <summary>
        /// Имя проверяемого свойства
        /// </summary>
        public string PropertyName { get; }

        /// <summary>
        /// Флаг наличия ошибки
        /// </summary>
        /// <remarks>
        /// Реализация интерфейса <see cref="IPropertyValidator"/>
        /// </remarks>
        public bool HasErrors { get { return _validators.Any(v => v.HasErrors); } }

        /// <summary>
        /// Возвращает перечисление ошибок
        /// </summary>
        /// <remarks>
        /// Реализация интерфейса <see cref="IPropertyValidator"/>
        /// </remarks>
        /// <returns>Ошибки</returns>
        public IEnumerable<string> GetErrors()
        {
            return from validator in _validators
                   where validator.HasErrors
                   from error in validator.GetErrors()
                   select error;
        }

        /// <summary>
        /// Обновление валидации
        /// </summary>
        ///<remarks>
        /// Реализация интерфейса <see cref="IPropertyValidator"/>
        /// </remarks>
        public void UpdateValidation()
        {
            foreach (var validator in _validators)
                validator.UpdateValidation();
        }

        /// <summary>
        /// Добавление сособщения об ошибке
        /// </summary>
        /// <remarks>
        /// Пробрасывается напрямую в <see cref="PropertyCustomErrorsSet.SetError(string)"/>
        /// </remarks>
        /// <param name="errorMessage">Сообщение об ошибке</param>
        public void SetCustomError(string errorMessage)
        {
            _customErrorsSet.SetError(errorMessage);
        }

        /// <summary>
        /// Очистка сообщений об ошибках
        /// </summary>
        /// <remarks>
        /// Пробрасывается напрямую в <see cref="PropertyCustomErrorsSet.ClearErrors"/>
        /// </remarks>
        public void ClearCustomErrors()
        {
            _customErrorsSet.ClearErrors();
        }

        /// <summary>
        /// Добавление правила валидации
        /// </summary>
        /// <remarks>
        /// Пробрасывается напрямую в <see cref="PropertyValidationRules.AddRule(Func{bool}, string)"/>
        /// </remarks>
        /// <param name="predicate">Предикат валидации</param>
        /// <param name="errorMessage">Сообщение об ошибке</param>
        public void AddRule(Func<bool> predicate, string errorMessage)
        {
            _rulesErrorsSet.AddRule(predicate, errorMessage);
        }

        /// <summary>
        /// Добавление правила валидации
        /// </summary>
        /// <remarks>
        /// Пробрасывается напрямую в <see cref="PropertyValidationRules.AddRule(IValidationRule)"/>
        /// </remarks>
        /// <param name="rule">Правило валидации</param>
        public void AddRule(IValidationRule rule)
        {
            _rulesErrorsSet.AddRule(rule);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Validation\PropertyValidationRules.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace Updk7.Wpf.Validation
{
    /// <summary>
    /// Внутренний класс набора правил валидации
    /// </summary>
    /// <remarks>
    /// Используется в классе <see cref="DataErrorInfoContainer"/>
    /// </remarks>
    internal class PropertyValidationRules : IPropertyValidator
    {
        /// <summary>
        /// Внутренний контейнер для правила валидации
        /// </summary>
        private class ValidationRule : IValidationRule
        {
            private readonly Func<bool> _predicate;

            public ValidationRule(Func<bool> predicate, string errorMessage)
            {
                Debug.Assert(predicate != null);
                Debug.Assert(!string.IsNullOrEmpty(errorMessage));

                Error = errorMessage;
                _predicate = predicate;
            }

            /// <summary>
            /// Флаг валидности свойства
            /// </summary>
            public bool IsValid { get; private set; }

            /// <summary>
            /// Сообщение об ошибке
            /// </summary>
            public string Error { get; private set; }

            /// <summary>
            /// Обновление флага валидности <see cref="IsValid"/> свйоства
            /// </summary>
            public void UpdateValidation()
            {
                IsValid = _predicate();
            }
        }

        private readonly List<IValidationRule> _rulesList = new List<IValidationRule>();
        private bool _hasErrors = false;

        /// <summary>
        /// Создание экземпляра объекта
        /// </summary>
        public PropertyValidationRules()
        {
        }

        /// <summary>
        /// Флаг наличия ошибки
        /// </summary>
        /// <remarks>
        /// Реализация интерфейса <see cref="IPropertyValidator"/>
        /// </remarks>
        public bool HasErrors { get { return _hasErrors; } }

        /// <summary>
        /// Возвращает перечисление ошибок
        /// </summary>
        /// <remarks>
        /// Реализация интерфейса <see cref="IPropertyValidator"/>
        /// </remarks>
        /// <returns>Ошибки</returns>
        public IEnumerable<string> GetErrors()
        {
            return from rule in _rulesList
                   where !rule.IsValid
                   select rule.Error;
        }

        /// <summary>
        /// Обновление валидации
        /// </summary>
        ///<remarks>
        /// Реализация интерфейса <see cref="IPropertyValidator"/>
        /// </remarks>
        public void UpdateValidation()
        {
            _hasErrors = false;
            foreach (var rule in _rulesList)
            {
                rule.UpdateValidation();
                _hasErrors |= !rule.IsValid;
            }
        }

        /// <summary>
        /// Добавление правила валидации
        /// </summary>
        /// <remarks>
        /// Правило валидации считается выполненным, если <paramref name="predicate"/> возвращает истину
        /// </remarks>
        /// <param name="predicate">Предикат валидации</param>
        /// <param name="errorMessage">Сообщение об ошибке</param>
        public void AddRule(Func<bool> predicate, string errorMessage)
        {
            _rulesList.Add(new ValidationRule(predicate, errorMessage));
        }

        /// <summary>
        /// Добавление правила валидации
        /// </summary>
        /// <param name="rule">Реализация правила валидации</param>
        public void AddRule(IValidationRule rule)
        {
            Debug.Assert(rule != null);
            _rulesList.Add(rule);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Source\Validation\ValidatedBindableBase.cs


using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace Updk7.Wpf.Validation
{
    /// <summary>
    /// Базовая модель представления c валидацией
    /// </summary>
    /// <remarks>
    /// Реализует интерфейсы <see cref="INotifyDataErrorInfo"/> и <see cref="INotifyPropertyChanged"/>
    /// </remarks>
    public class ValidatedBindableBase : Prism.Mvvm.BindableBase, INotifyDataErrorInfo
    {
        protected DataErrorInfoContainer ErrorsContainer = new DataErrorInfoContainer();

        public ValidatedBindableBase()
        {
            ErrorsContainer.ErrorsChanged += OnErrorsChanged;
        }

        private bool _hasErrors;

        public bool HasErrors
        {
            get { return _hasErrors; }
            private set { SetProperty(ref _hasErrors, value); }
        }

        private string _lastError;

        public string LastError
        {
            get { return _lastError; }
            private set { SetProperty(ref _lastError, value); }
        }

        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged
        {
            add { ErrorsContainer.ErrorsChanged += value; }
            remove { ErrorsContainer.ErrorsChanged -= value; }
        }

        /// <summary>
        /// Возвращает перечисление ошибок для заданного хранимого имени свойства
        /// </summary>
        /// <remarks>
        /// Реализация интерфейса <see cref="INotifyDataErrorInfo"/>
        /// </remarks>
        /// <param name="propertyName">Хранимое имя свойства, для которого возвраащаются ошибки</param>
        /// <returns>Перечисление ошибок, пустое в случае их отсутствия</returns>
        public IEnumerable GetErrors(string propertyName)
        {
            return ErrorsContainer.GetErrors(propertyName);
        }

        public IEnumerable<string> GetErrors()
        {
            return ErrorsContainer.GetErrors();
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);

            if (e.PropertyName != nameof(HasErrors) && e.PropertyName != nameof(LastError))
                ErrorsContainer.UpdatePropertyValidation(e.PropertyName);
        }

        protected virtual void OnErrorsChanged(object sender, DataErrorsChangedEventArgs e)
        {
            LastError = ErrorsContainer.GetErrors().FirstOrDefault();
            HasErrors = ErrorsContainer.HasErrors;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Themes\Generic.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Light/Generic.xaml" />
    </ResourceDictionary.MergedDictionaries>
    
</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Themes\Light\Brushes.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <SolidColorBrush x:Key="NotmalTextForeground" Color="Black" />
    <SolidColorBrush x:Key="DisabledTextForeground" Color="#A0A0A0" />
    <SolidColorBrush x:Key="MouseHoverTextForeground" Color="Gray" />
    <SolidColorBrush x:Key="WindowBackground" Color="#D0D0D0" />
    <SolidColorBrush x:Key="WindowTitleForegroundBrush" Color="DeepSkyBlue" />
    <SolidColorBrush x:Key="SelectionGrayBrush" Color="#C1C1C1" />

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Themes\Light\FlatButtonStyle.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style x:Key="FlatButtonStyle" TargetType="{x:Type Button}">
        <Setter Property="BorderBrush" Value="DeepSkyBlue" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="Foreground" Value="DeepSkyBlue" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Border
                        x:Name="PART_Border"
                        Padding="{TemplateBinding Padding}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        Background="Transparent">
                        <ContentPresenter
                            Content="{TemplateBinding Content}" 
                            VerticalAlignment="Center"
                            HorizontalAlignment="Center"
                            />
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="PART_Border" Property="BorderBrush" Value="Yellow" />
                            <Setter TargetName="PART_Border" Property="BorderThickness" Value="2" />
                        </Trigger>
                        <Trigger Property ="IsPressed" Value ="True">
                            <Setter TargetName="PART_Border" Property="Background" Value="Gray" />
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="PART_Border" Property="BorderBrush" Value="Black" />
                            <Setter Property="Opacity" Value="0.3" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Themes\Light\FlatCheckBoxStyle.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style x:Key="FlatCheckBoxStyle" TargetType="{x:Type CheckBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type CheckBox}">
                    <Grid
                        Margin="{TemplateBinding Margin}">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="26" />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <Viewbox>
                            <Path
                                x:Name="PART_Path"
                                Grid.Column="0"
                                Margin="3,0,5,0"
                                VerticalAlignment="Center"
                                HorizontalAlignment="Center"
                                Stroke="Black"
                                StrokeThickness="2"
                                Fill="Orange"
                                Stretch="UniformToFill">
                                <Path.Data>
                                    <EllipseGeometry 
                                        Center="14,14" 
                                        RadiusX="6" 
                                        RadiusY="6" 
                                        />
                                </Path.Data>
                            </Path>
                        </Viewbox>
                        <TextBlock 
                            x:Name="PART_Text"
                            Grid.Column="1"
                            FontSize="{TemplateBinding FontSize}"
                            FontWeight="{TemplateBinding FontWeight}"
                            Text="{TemplateBinding Content}"
                            Foreground="{TemplateBinding Foreground}"
                            TextWrapping="Wrap"
                            VerticalAlignment="Center"
                            HorizontalAlignment="Left"
                            />
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsChecked" Value="False">
                            <Setter TargetName="PART_Path" Property="Fill" Value="Transparent" />
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="PART_Path" Property="Data">
                                <Setter.Value>
                                    <RectangleGeometry Rect="0,0,10,3" />
                                </Setter.Value>
                            </Setter>
                            <Setter TargetName="PART_Path" Property="Stroke" Value="Gray" />
                            <Setter TargetName="PART_Path" Property="Fill" Value="Gray" />
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="PART_Path" Property="Stroke" Value="Black" />
                            <Setter TargetName="PART_Path" Property="Effect">
                                <Setter.Value>
                                    <DropShadowEffect 
                                        BlurRadius="4"  
                                        Opacity="0.4"
                                        />
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Themes\Light\FlatDataGridStyle.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Brushes.xaml" />
    </ResourceDictionary.MergedDictionaries>
    
    <!--Стиль текстового блока для DataGridTextColumn-->
    <Style x:Key="DataGridTextColumnElementStyle" TargetType="{x:Type TextBlock}">
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="Margin" Value="5,2,0,2" />
        <Setter Property="TextTrimming" Value="CharacterEllipsis" />
    </Style>

    <!--Стиль ячейки таблицы - убираем рамку выделения ячейки-->
    <Style x:Key="DataGridCellStyle" TargetType="{x:Type DataGridCell}">
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="Focusable" Value="False" />
        <Style.Triggers>
            <Trigger Property="IsSelected" Value="True">
                <Setter Property="Background" Value="LimeGreen" />
                <Setter Property="Foreground" Value="Black" />
            </Trigger>
        </Style.Triggers>
    </Style>

    <!--Стиль заголовка колонок таблицы-->
    <Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
        <Setter Property="Background" Value="DeepSkyBlue" />
        <Setter Property="BorderBrush" Value="LightGray" />
        <Setter Property="BorderThickness" Value="0,0,1,0" />
        <Setter Property="FontSize" Value="14" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="Padding" Value="10,3,5,3" />
    </Style>

    <!--Стиль DataGrid-->
    <Style x:Key="FlatDataGridStyle" TargetType="{x:Type DataGrid}">
        <Setter Property="AutoGenerateColumns" Value="False" />
        <Setter Property="FontSize" Value="14" />
        <Setter Property="FontWeight" Value="Normal" />
        <Setter Property="CanUserAddRows" Value="False" />
        <Setter Property="CanUserDeleteRows" Value="False" />
        <Setter Property="CanUserReorderColumns" Value="False" />
        <Setter Property="CanUserResizeRows" Value="False" />
        <Setter Property="BorderBrush" Value="Transparent" />
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="RowBackground" Value="Transparent" />
        <Setter Property="SnapsToDevicePixels" Value="True" />
        <Setter Property="SelectionMode" Value="Single" />
        <Setter Property="SelectionUnit" Value="FullRow" />
        <Setter Property="HorizontalGridLinesBrush" Value="White" />
        <Setter Property="VerticalGridLinesBrush" Value="Transparent" />
        <Setter Property="HeadersVisibility" Value="Column" />
        <Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
        <Setter Property="AlternatingRowBackground" Value="DarkGray" />
        <Setter Property="AlternationCount" Value="2" />
        <Setter Property="CellStyle" Value="{StaticResource DataGridCellStyle}" />
        <Setter Property="ColumnHeaderStyle" Value="{StaticResource DataGridColumnHeaderStyle}" />
    </Style>
    
</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Themes\Light\FlatRadioButtonStyle.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <Style x:Key="FlatRadioButtonStyle" TargetType="{x:Type RadioButton}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type RadioButton}">
                    <Grid
                        Margin="{TemplateBinding Margin}">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="26" />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <Viewbox>
                            <Path
                                x:Name="PART_Path"
                                Grid.Column="0"
                                Margin="3,0,5,0"
                                VerticalAlignment="Center"
                                HorizontalAlignment="Center"
                                Stroke="Black"
                                StrokeThickness="2"
                                Fill="Orange"
                                Stretch="UniformToFill">
                                <Path.Data>
                                    <EllipseGeometry 
                                        Center="14,14" 
                                        RadiusX="6" 
                                        RadiusY="6" 
                                        />
                                </Path.Data>
                            </Path>
                        </Viewbox>
                        <TextBlock 
                            x:Name="PART_Text"
                            Grid.Column="1"
                            FontSize="{TemplateBinding FontSize}"
                            FontWeight="{TemplateBinding FontWeight}"
                            Text="{TemplateBinding Content}"
                            Foreground="{TemplateBinding Foreground}"
                            TextWrapping="Wrap"
                            VerticalAlignment="Center"
                            HorizontalAlignment="Left"
                            />
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsChecked" Value="False">
                            <Setter TargetName="PART_Path" Property="Fill" Value="Transparent" />
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="PART_Path" Property="Data">
                                <Setter.Value>
                                    <RectangleGeometry Rect="0,0,10,3" />
                                </Setter.Value>
                            </Setter>
                            <Setter TargetName="PART_Path" Property="Stroke" Value="Gray" />
                            <Setter TargetName="PART_Path" Property="Fill" Value="Gray" />
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="PART_Path" Property="Stroke" Value="Black" />
                            <Setter TargetName="PART_Path" Property="Effect">
                                <Setter.Value>
                                    <DropShadowEffect 
                                        BlurRadius="4"  
                                        Opacity="0.4"
                                        />
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        
    </Style>
    
</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Themes\Light\FlatRectRadioButtonStyle.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style x:Key="FlatRectRadioButtonStyle" TargetType="{x:Type RadioButton}">
        <Setter Property="Margin" Value="3" />
        <Setter Property="FontSize" Value="14" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="Padding" Value="10" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type RadioButton}">
                    <Border
                        x:Name="PART_Border"
                        BorderBrush="DeepSkyBlue"
                        BorderThickness="2"
                        Background="Transparent">
                        <ContentControl 
                            x:Name="PART_Content"
                            Content="{TemplateBinding Content}"
                            VerticalAlignment="Center"
                            HorizontalAlignment="Center"
                            />
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="PART_Border" Property="BorderBrush" Value="Gray" />
                        </Trigger>
                        <Trigger Property="IsChecked" Value="True">
                            <Setter TargetName="PART_Border" Property="Background" Value="Orange" />
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="PART_Border" Property="BorderBrush" Value="Black" />
                            <Setter TargetName="PART_Border" Property="Background" Value="Transparent" />
                            <Setter Property="Opacity" Value="0.3" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Themes\Light\FlatTabItemStyle.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Brushes.xaml" />
    </ResourceDictionary.MergedDictionaries>
    
    <Style x:Key="FlatTabItemStyle" TargetType="{x:Type TabItem}">
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="BorderBrush" Value="Transparent" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TabItem}">
                    <Grid>
                        <TextBlock 
                            x:Name="PART_Header"
                            VerticalAlignment="Center"
                            HorizontalAlignment="Center"
                            FontSize="{TemplateBinding FontSize}"
                            FontWeight="{TemplateBinding FontWeight}"
                            FontFamily="{TemplateBinding FontFamily}"
                            Margin="{TemplateBinding Padding}"
                            Text="{TemplateBinding Header}"
                            Foreground="{StaticResource DisabledTextForeground}"
                            />
                    </Grid>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding IsMouseOver, ElementName=PART_Header}" Value="True">
                            <Setter 
                                TargetName="PART_Header"
                                Property="Foreground" 
                                Value="{StaticResource MouseHoverTextForeground}" 
                                />
                        </DataTrigger>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter 
                                TargetName="PART_Header" 
                                Property="Foreground" 
                                Value="{StaticResource NotmalTextForeground}" 
                                />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Themes\Light\FlatToggleButtonStyle.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style x:Key="FlatToggleButtonStyle" TargetType="{x:Type ToggleButton}">
        <Setter Property="BorderBrush" Value="DeepSkyBlue" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="Foreground" Value="DeepSkyBlue" />
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ToggleButton}">
                    <Border
                        x:Name="PART_Border"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        Background="{TemplateBinding Background}">
                        <ContentPresenter
                            Content="{TemplateBinding Content}" 
                            VerticalAlignment="Center"
                            HorizontalAlignment="Center"
                            />
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="PART_Border" Property="BorderBrush" Value="Yellow" />
                            <Setter TargetName="PART_Border" Property="BorderThickness" Value="2" />
                        </Trigger>
                        <Trigger Property ="IsPressed" Value ="True">
                            <Setter TargetName="PART_Border" Property="Background" Value="Gray" />
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="PART_Border" Property="BorderBrush" Value="Black" />
                            <Setter Property="Opacity" Value="0.3" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Themes\Light\Generic.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Brushes.xaml" />
        <ResourceDictionary Source="FlatTabItemStyle.xaml" />
        <ResourceDictionary Source="FlatDataGridStyle.xaml" />
        <ResourceDictionary Source="FlatButtonStyle.xaml" />
        <ResourceDictionary Source="WindowStyle.xaml" />
        <ResourceDictionary Source="FlatCheckBoxStyle.xaml" />
        <ResourceDictionary Source="FlatToggleButtonStyle.xaml" />
        <ResourceDictionary Source="HeaderLabelStyle.xaml" />
        <ResourceDictionary Source="FlatRectRadioButtonStyle.xaml" />
        <ResourceDictionary Source="FlatRadioButtonStyle.xaml" />
    </ResourceDictionary.MergedDictionaries>

    <BitmapImage x:Key="BlackPlusImage" UriSource="Pictures/BlackPlus.png" />
    <BitmapImage x:Key="BlackMinusImage" UriSource="Pictures/BlackMinus.png" />
    <BitmapImage x:Key="EmployeesArchImage" UriSource="Pictures/EmployeesArch.png" />
    <BitmapImage x:Key="ReportsImage" UriSource="Pictures/Reports.png" />
    <BitmapImage x:Key="StartTestImage" UriSource="Pictures/StartTest.png" />
    <BitmapImage x:Key="BlackPencilImage" UriSource="Pictures/BlackPencil.png" />
    <BitmapImage x:Key="EmployeeImage" UriSource="Pictures/Employee.png" />
    <BitmapImage x:Key="PauseTestImage" UriSource="Pictures/Pause.png" />
    <BitmapImage x:Key="BreakTestImage" UriSource="Pictures/Break.png" />
    <BitmapImage x:Key="ComputerImage" UriSource="Pictures/Computer.png" />
    <BitmapImage x:Key="FindImage" UriSource="Pictures/Find.png" />
    <BitmapImage x:Key="PrinterImage" UriSource="Pictures/Printer.png" />
    <BitmapImage x:Key="PdfImage" UriSource="Pictures/Pdf.png" />
    <BitmapImage x:Key="QuestionImage" UriSource="Pictures/Question.png" />
    <BitmapImage x:Key="ZoomInImage" UriSource="Pictures/ZoomIn.png" />
    <BitmapImage x:Key="ZoomOutImage" UriSource="Pictures/ZoomOut.png" />
    <BitmapImage x:Key="ErrorImage" UriSource="Pictures/Error.png" />
    <BitmapImage x:Key="ShutdownImage" UriSource="Pictures/Shutdown.png" />
    <BitmapImage x:Key="AdministratorImage" UriSource="Pictures/Administrator.png" />
    <BitmapImage x:Key="UserImage" UriSource="Pictures/User.png" />
    <BitmapImage x:Key="KeyImage" UriSource="Pictures/Key.png" />
    <BitmapImage x:Key="InformationImage" UriSource="Pictures/Information.png" />
    <BitmapImage x:Key="SaveImage" UriSource="Pictures/Save.png" />
    <BitmapImage x:Key="CsvFileImage" UriSource="Pictures/CsvFile.png" />
    <BitmapImage x:Key="CompatibilityImage" UriSource="Pictures/Compatibility.png" />
    <BitmapImage x:Key="LinkImage" UriSource="Pictures/Link.png" />
    <BitmapImage x:Key="UnlinkImage" UriSource="Pictures/Unlink.png" />
    <BitmapImage x:Key="SettingsImage" UriSource="Pictures/Settings.png" />
    <BitmapImage x:Key="LoadImage" UriSource="Pictures/Load.png" />
    <BitmapImage x:Key="LoadAddImage" UriSource="Pictures/LoadAdd.png" />
    <BitmapImage x:Key="ConclusionImage" UriSource="Pictures/Conclusion.png" />

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Themes\Light\HeaderLabelStyle.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style x:Key="HeaderLabelStyle" TargetType="{x:Type Label}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Label}">
                    <Border
                        Grid.Row="0"
                        Grid.Column="1"
                        Background="DeepSkyBlue"
                        Padding="10,3,2,3"
                        Margin="1,0,0,0">
                        <TextBlock
                            FontWeight="Bold"
                            FontSize="14"
                            Text="{TemplateBinding Content}"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf\Themes\Light\WindowStyle.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:s="clr-namespace:Updk7.Wpf.Styles"
                    x:Class="Updk7.Wpf.Styles.WindowStyle">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Brushes.xaml" />
        <ResourceDictionary Source="FlatButtonStyle.xaml" />
    </ResourceDictionary.MergedDictionaries>

    <Style x:Key="WindowStyle" TargetType="Window">
        <Style.Resources>
            <!--Стиль кнопок управления окном-->
            <Style x:Key="FlatChromeButtonStyle" TargetType="Button" BasedOn="{StaticResource FlatButtonStyle}">
                <Setter Property="Focusable" Value="False" />
                <Setter Property="FontFamily" Value="Webdings" />
                <Setter Property="FontSize" Value="16" />
                <Setter Property="FontWeight" Value="Light" />
                <Setter Property="BorderBrush" Value="Transparent" />
                <Setter Property="BorderThickness" Value="0" />
                <Setter Property="Foreground" Value="Black" />
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="true">
                        <Setter Property="Foreground" Value="{StaticResource MouseHoverTextForeground}" />
                    </Trigger>
                </Style.Triggers>
            </Style>
            <BooleanToVisibilityConverter x:Key="boolToVisibilityConverter" />
        </Style.Resources>
        <Setter Property="OverridesDefaultStyle" Value="True" />
        <Setter Property="WindowStyle" Value="None" />
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="Foreground" Value="{StaticResource NotmalTextForeground}"/>
        <!--Если не задавать в явном виде максимальные размеры окна, оно скрывает taskbar при максимизации-->
        <Setter Property="MaxWidth" Value="{DynamicResource {x:Static SystemParameters.MaximizedPrimaryScreenWidthKey}}" />
        <Setter Property="MaxHeight" Value="{DynamicResource {x:Static SystemParameters.MaximizedPrimaryScreenHeightKey}}" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Window">
                    <Border
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                        <DockPanel
                            Background="{StaticResource WindowBackground}">
                            <Border
                                DockPanel.Dock="Top"
                                Height="35"
                                Background="Transparent"
                                MouseLeftButtonDown="OnChromeMouseClick">
                                <Border.Resources>
                                    <Style TargetType="Button" BasedOn="{StaticResource FlatChromeButtonStyle}" />
                                </Border.Resources>
                                <!--Сетка элементов заголовка окна-->
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition />
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>
                                    <!--Заголовок окна-->
                                    <StackPanel Orientation="Horizontal">
                                        <Border 
                                            x:Name="PART_Border"
                                            Background="DeepSkyBlue"
                                            HorizontalAlignment="Left"
                                            Margin="10,0,0,0">
                                            <StackPanel Orientation="Horizontal">
                                                <TextBlock 
                                                    FontSize="12"
                                                    VerticalAlignment="Center"
                                                    HorizontalAlignment="Center"
                                                    TextTrimming="CharacterEllipsis"
                                                    FontWeight="Medium"
                                                    Margin="5"
                                                    Foreground="AliceBlue"
                                                    Text="{TemplateBinding Title}"/>
                                            </StackPanel>
                                        </Border>
                                        <ContentControl 
                                            Margin="25,1,1,1"
                                            VerticalAlignment="Center"
                                            Content="{Binding RelativeSource={RelativeSource TemplatedParent}, 
                                                Path=(s:WindowHeader.Content)}"
                                            />
                                    </StackPanel>
                                    <!--Кнопка минимизации окна-->
                                    <Button
                                        Grid.Column="1"
                                        Content="0"
                                        Width="25"
                                        Click="OnMinimizeVindow"
                                        Visibility="{Binding RelativeSource={RelativeSource TemplatedParent},
                                            Path=(s:WindowHeader.CanBeMinimized), 
                                            Converter={StaticResource boolToVisibilityConverter}}"
                                        />
                                    <!--Кнопка максимизации окна-->
                                    <Button
                                        Grid.Column="2"
                                        Content="2"
                                        Click="OnMaximaizeWindow"
                                        Width="25"
                                        Visibility="{Binding RelativeSource={RelativeSource TemplatedParent},
                                            Path=(s:WindowHeader.CanBeMaximized), 
                                            Converter={StaticResource boolToVisibilityConverter}}"
                                        />
                                    <!--Кнопка закрытия окна-->
                                    <Button
                                        Grid.Column="3"
                                        Content="r"
                                        Width="25"
                                        Click="OnWindowClose"
                                        />
                                </Grid>
                            </Border>
                            <ContentPresenter />
                        </DockPanel>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Resources\SharedStringsRu.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:tests="clr-namespace:Updk7.Tests;assembly=Updk7.Tests"
                    xmlns:entities="clr-namespace:Updk7.Data.Entities;assembly=Updk7.Data">

    <!--UserRole-->
    <sys:String x:Key="admin">Администратор</sys:String>
    <sys:String x:Key="psycho">Психолог</sys:String>
    
    <!--GenderType-->
    <sys:String x:Key="{x:Static entities:GenderType.Male}">мужской</sys:String>
    <sys:String x:Key="{x:Static entities:GenderType.Female}">женский</sys:String>

    <!-- TestType -->
    <sys:String x:Key="{x:Static tests:TestType.Готовность_к_тестированию_ЭПФС}">Готовность к тестированию</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_склонности_к_риску_ОСР}">Склонность к риску</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Распределение_внимания_РВ}">Переключаемость и распределение внимания</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_монотоноустойчивости_ОМУ}">Оценка монотоноустойчивости</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_бдительности_ОБ}">Оценка бдительности</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_устойчивости_внимания_УВ}">Устойчивость внимания и динамика работоспособности</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Концентрация_внимания_КВ}">Концентрация внимания</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_скорости_переделки_навыков_СПН}">Скорость формирования психомоторных навыков</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Эмоциональная_устойчивость_ЭУ}">Эмоциональная устойчивость</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_объема_внимания_ОВ}">Оперативная память</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_глазомера_ОГ}">Глазомер - 1</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Корректурная_проба_КП}">Корректурная проба</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_стрессоустойчивости_СТР}">Стрессоустойчивость</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_стрессоустойчивости_М_СТР_М}">Стрессоустойчивость-М</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Сложная_двигательная_реакция_30_СДР_30}">Сложная двигательная реакция (СДР-30)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Сложная_двигательная_реакция_СДР_100}">Сложная двигательная реакция (СДР-100)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Простая_двигательная_реакция_ПДР}">Простая двигательная реакция (ПДР-30)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Критическая_частота_слияния_миганий_КЧСМ}">Критическая частота слияния световых мельканий</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Чувство_времени_ЧВ}">Чувство времени</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Теппинг_тест_310_ТЕП_310}">Теппинг-тест (310)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Статический_тремор_ТРЕМ}">Статический тремор</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Готовность_к_экстренному_действию_ГЭД}">Готовность к экстренным действиям - 1</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Готовность_к_экстренному_действию_2_ГЭД_2}">Готовность к экстренным действиям - 2</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Экспресс_проба_бдительности_УРБ}">Учебный режим оценки бдительности</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Реакция_на_движущийся_объект_РДО}">Реакция на движущийся объект</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ}">Переключение внимания и помехоустойчивость</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Переключение_внимания_2_ПВ_2}">Переключение внимания 25/48</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_Игра_5}">Игра 5</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_Айзенка_EPQ}">Тест Айзенка EPQ</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Опросник_САН}">Опросник САН</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_уровня_тревожности_Тейлор}">Тест уровня тревожности (Тейлор)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_Спилберга}">Тест Спилбергера</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_Лири}">Тест Лири</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_MMPI_Березина}">Тест MMPI (Березин)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Q_Сортировка}">Тест Q-сортировка</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Опроснок_профессиональных_предпочтений_Голланд}">Тест-опросник "Профессиональные предпочтения" (Голланд)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тип_акцентуации_личности_Шмишека_Леонгарда}">Методика изучения акцентуации личности (Леонгард-Шмишек)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Ценностные_ориентации_Рокич}">Методика "Ценностные ориентации" (по Рокичу)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_Потребность_в_достижении_ПД}">Тест-опросник "Потребность в достижении"</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_Стиль_руководства_СР}">Тест-опросник "Стиль руководства"</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_Кетелла}">16-факторный личностный опросник Кетелла</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_Уровень_субъективного_контроля_УСК}">Методика диагностики уровня субъективного контроля</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_Люшера}">Тест Люшера</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_словесных_ассоциаций}">Тест словесных ассоциаций</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Диагностики_мотивации_к_избеганию_неудач_Элерса_МИН}">Методика диагностики к избеганию неудач Т. Элерса</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_антиципационной_состоятельности_АС}">Тест антиципационной состоятельности (прогностической компетенции)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Опросник_Личностный_профиль_ЛП}">Опросник "Личностный профиль"</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Методика_Аналогии}">Методика аналогии</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Диагностики_мотивации_к_достижению_успеха_Элерса_МДУ}">Методика диагностики мотивации к успеху Т. Элерса</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_опросник_на_исследование_волевой_саморегуляции_ИВС}">Методика оценки волевой саморугуляции</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Методика_диагностики_уровня_агрессии_Басс_Дарки}">Методик диагностики уровня агрессии (Басс-Дарки)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_опросник_Индекс_жизненного_стиля_ИЖС}">Тест-опросник "Индекс жизненного стиля"</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Сокращенный_многопрофильный_опросник_личности_СМОЛ}">Сокращенный многопрофильный опросник личности</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_опросник_Склонность_к_зависимому_поведению_ЗП}">Тест-опросник "Склонность к зависимому поведению"</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Методика_ДОРС}">Методика ДОРС</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Комплексная_оценка_психологического_состояния_КОПС}">Комплексная оценка психологического состояния</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Диагностика_степени_готовности_склонности_к_риску_Шуберт}">Методика диагностики степени склонности к риску (по Шуберту)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Проба_на_моторную_согласованность_МС}">Моторная согласованность действий рук - 1</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Опрос_совместимости}">Социометрия</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_бдительности_ОБ_М}">Оценка бдительности (М)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_нервно_психической_устойчивости_НПУ}">Оценка нервно психической устойчивости</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_Томаса_Конфликтность}">Конфликтность (Томас)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тест_Зунга}">Тест Зунга</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Структура_темперамента_Смирнов}">Структура темперамента (Смирнов)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Уровень_восприятия_скорости_и_расстояния_УВСР}">Восприятие пространственных отношений и времени</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Сложная_двигательная_реакция_СДР_М}">Психомоторика</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Теппинг_тест}">Теппинг-тест (30 с)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Тремор_3}">Тремор (3)</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Методика_оценки_оперативной_памяти_ОВ_М}">Оперативная память - М</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_стрессоустойчивости_СТР_M2}">Стрессоустойчивость - 2</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_монотоноустойчивости_ОМУ_М}">Монотоноустойчивость - М</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Готовность_к_экстренным_действиям_ГЭД_1М}">Готовность к экстренным действиям - 1М</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Готовность_к_экстренным_действиям_ГЭД_2М}">Готовность к экстренным действиям - 2М</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_глазомера_М}">Глазомер - 2</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Оценка_моторной_согласованности_М_МС_м}">Моторная согласованность действий рук – 2</sys:String>
    <sys:String x:Key="{x:Static tests:TestType.Аддикция_и_аддиктивное_поведение}">Аддикция и аддиктивное поведение</sys:String>
</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\IAutorizationManager.cs


using System;

namespace Updk7.Wpf.Master.Assets
{
    public interface IAutorizationManager
    {
        event EventHandler UserAutorizationComplete;

        Data.Entities.User AutorizedUser { get; }

        void Reset();
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\ILockScreenController.cs


using System;

namespace Updk7.Wpf.Master.Assets
{
    public interface ILockScreenController
    {
        event EventHandler UnlockApplication;

        Data.Entities.User UserData { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\INavigationController.cs


namespace Updk7.Wpf.Master.Assets
{
    public enum NavigationTargetPage
    {
        Login,
        Work,
        Lock
    }

    public interface INavigationController
    {
        void NavigateToPage(NavigationTargetPage page);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\MasterBootstrapper.cs


using System;
using System.Windows;
using Prism.Ioc;

namespace Updk7.Wpf.Master.Assets
{
    public abstract class MasterBootstrapper
    {
        protected AppManager AppManager;
        protected IContainerRegistry ContainerRegistry;
        protected IContainerProvider ContainerProvider;

        public MasterBootstrapper(AppManager appManager, IContainerRegistry containerRegistry,
            IContainerProvider containerProvider)
        {
            AppManager = appManager;
            ContainerRegistry = containerRegistry;
            ContainerProvider = containerProvider;
        }
        
        public Window ShellWindow { get; private set; }

        public MasterConfiguration Configuration { get; private set; }

        public virtual void Run()
        {
            ContainerRegistry.RegisterInstance(AppManager);
            ContainerRegistry.RegisterInstance(AppManager.RutokenMonitor);
            CreateAppLog();

            AppLog.Current.Info("*** Bootstrapper.Run()");

            try
            {
                RegisterInteractions();
                Configuration = LoadConfiguration();
                LoadStrings();
                CreateDataLayer();
                CreateDatabaseBackup();
                CreateTestingTreeFactory();
                CreateTestParametersFactory();
                CreateNetworkLayer();
                CreateViewModelLocator();
                CreateEventListenersLocator();
                CreateShellController();
                CreateShell();
                CreateCommandManager();
                CreateAutorization();
                CreateLockScreen();
                CreateNavigationController();
                RegisterTrayCommands();

                AppLog.Current.Info("*** Bootstrapper.Run() -> Complete");
            }
            catch (Exception ex)
            {
                AppLog.Current.Error("*** Bootstrapper.Run() -> Failed");
                AppLog.Current.Error(ex.Message);
                AppManager.Shutdown();
            }
        }

        protected virtual void CreateNetworkLayer()
        {
            AppLog.Current.Info(nameof(CreateNetworkLayer));

            var service = new Network.Local.Master.UpdkService(Configuration.ServerPort);
            service.Run();

            ContainerRegistry.RegisterInstance(service);
        }

        protected abstract void CreateDataLayer();

        protected abstract void CreateTestingTreeFactory();

        protected virtual void CreateDatabaseBackup()
        {
            AppLog.Current.Info("Create backup");

            try
            {
                Data.Repositories.Backup.Create(AppManager.Folders.Root, AppManager.Folders.Backups);
            }
            catch (Exception ex)
            {
                AppLog.Current.Error("Backup failed");
                AppLog.Current.Error(ex.Message);
            }
        }

        protected virtual void CreateTestParametersFactory()
        {
            ContainerRegistry.RegisterSingleton<Data.ITestsParametersFactory, Data.TestsParametersFactory>();
        }

        protected virtual MasterConfiguration LoadConfiguration()
        {
            AppLog.Current.Info(nameof(LoadConfiguration));

            var configuration = new MasterConfiguration(AppManager.AppSettingsFile);
            if (!configuration.IsValid())
            {
                if (string.IsNullOrEmpty(configuration.DataConnection))
                    configuration.DataConnection = GetDefaultDataConnection();

                ApproveConfiguration(configuration);
                configuration.Save();
            }

            ContainerRegistry.RegisterInstance<MasterConfiguration>(configuration);

            return configuration;
        }

        protected virtual void ApproveConfiguration(MasterConfiguration configuration)
        {
            var interaction = new Interactions.ChangeSettingsInteraction();
            interaction.WelcomeMessage = "Проверьте корректность конфигурации перед запуском приложения";
            interaction.InteractionData.SetupData(configuration);
            Wpf.Interactions.InteractionService.Current.ShowInteractionModel(interaction, () => 
            {
                if (interaction.Confirmed && !interaction.InteractionData.HasErrors)
                {
                    interaction.InteractionData.UpdateConfiguration(configuration);
                    try
                    {
                        configuration.Save();
                    }
                    catch (Exception ex)
                    {
                        var message = "Не удалось сохранить файл конфигурации\n" + ex.Message;
                        Wpf.Interactions.InteractionService.Current.ShowErrorMessage(message);
                        AppLog.Current.Error(message);
                    }
                }
                else
                    AppManager.Shutdown();
            });
        }

        protected virtual void LoadStrings()
        {
            AppLog.Current.Info(nameof(LoadStrings));

            var uri = new Uri("Updk7.Wpf.Master.Assets;component/Resources/SharedStringsRu.xaml", UriKind.Relative);
            var dictionary = new ResourceDictionary() { Source = uri };
            AppStrings.AddMergedDictionary(dictionary);

            Tests.Metadata.SetTestNameFactory(t => AppStrings.GetString(t));

            AppStrings.SetString("Organization", Configuration.Organization);
        }

        protected virtual string GetDefaultDataConnection()
        {
            return $"{AppManager.AppName}.db";
        }

        protected virtual void CreateAppLog()
        {
            AppLog.Current = new AppLog(AppManager.Folders.Logs);
            AppLog.Current.CleanupLogFolder(20);
        }

        protected virtual void CreateViewModelLocator()
        {
            var regionManager = ContainerProvider.Resolve<Prism.Regions.IRegionManager>();
            ViewModelLocator.Current = new ViewModelLocator(regionManager, ContainerProvider);
        }

        protected virtual void CreateEventListenersLocator()
        {
            EventListenersLocator.Current = new EventListenersLocator(ContainerProvider, ContainerRegistry);
        }

        protected virtual void CreateCommandManager()
        {
            AppLog.Current.Info(nameof(CreateCommandManager));

            Wpf.Commands.CommandManager.Current = new Wpf.Commands.CommandManager(ContainerRegistry, ContainerProvider)
                .Register<Commands.ShutdownCommand>("Shutdown")
                .Register<Commands.ChangeUserCommand>("ChangeUser")
                .Register<Commands.ShowLockScreenCommand>("ShowLockScreen")
                .Register<Commands.ChangeUserPasswordCommand>("ChangeUserPassword");
        }

        protected virtual void CreateShellController()
        {
            var shellController = new Wpf.Shell.ShellController()
                .Register<Shell.AutorizationRegionView>(Shell.NavigationKeys.Autorization)
                .Register<Shell.AdministratorRegionView>(Shell.NavigationKeys.Administrator)
                .Register<Shell.PsychologistRegionView>(Shell.NavigationKeys.Psychologist)
                .Register<Shell.LockScreenRegionView>(Shell.NavigationKeys.LockScreen);

            ContainerRegistry
                .RegisterInstance(shellController);

            ViewModelLocator.Current
                .Register<Wpf.Shell.IShellController, Wpf.Shell.ShellWindow>();
        }

        protected virtual void CreateShell()
        {
            AppLog.Current.Info(nameof(CreateShell));

            ShellWindow = new Wpf.Shell.ShellWindow();
            ShellWindow.Title = "УПДК 7.0";

            AppManager.Shell = ShellWindow;
            Application.Current.MainWindow = ShellWindow;

            AppManager.ShowShell();
        }

        protected virtual void CreateAutorization()
        {
            ContainerRegistry.RegisterSingleton<IAutorizationManager, ViewModels.AutorizationViewModel>();
            ViewModelLocator.Current.Register<IAutorizationManager, Views.AutorizationView>(Shell.RegionKeys.AutorizationRegion);
        }

        protected virtual void CreateLockScreen()
        {
            ContainerRegistry.RegisterSingleton<ILockScreenController, ViewModels.LockScreenViewModel>();
            ViewModelLocator.Current.Register<ILockScreenController, Views.LockScreenView>(Shell.RegionKeys.LockScreenRegion);
        }

        protected virtual void CreateNavigationController()
        {
            ContainerRegistry.RegisterSingleton<INavigationController, NavigationController>();
        }

        protected virtual void RegisterInteractions()
        {
            AppLog.Current.Info(nameof(RegisterInteractions));

            ContainerRegistry.RegisterInstance(Wpf.Interactions.InteractionService.Current);

            Wpf.Interactions.InteractionService.Current
                .Register<Interactions.ChangeUserPasswordInteraction, Interactions.Views.ChangeUserPasswordView>()
                .Register<Interactions.ChangeSettingsInteraction, Interactions.Views.ChangeSettingsView>();
        }

        protected virtual void RegisterTrayCommands()
        {
            var shutdownCommand = Wpf.Commands.CommandManager.Current.Resolve("Shutdown");
            AppManager.Tray.AddContextMenuItem("Выход из приложения", shutdownCommand);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\MasterConfiguration.cs


namespace Updk7.Wpf.Master.Assets
{
    public class MasterConfiguration : ConfigurationBase
    {
        public static readonly string DefaultOrganization = "???";

        public MasterConfiguration(string fileName) : base(fileName)
        {
        }

        public int ServerPort { get; set; } = 27040;

        public string DataConnection { get; set; }

        public string Organization { get; set; } = DefaultOrganization;

        public override bool IsValid()
        {
            if (ServerPort <= 0 || ServerPort >= ushort.MaxValue)
                return false;
            if (string.IsNullOrEmpty(DataConnection))
                return false;
            if (string.IsNullOrEmpty(Organization) || Organization == DefaultOrganization)
                return false;

            return true;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\NavigationController.cs


using System;

namespace Updk7.Wpf.Master.Assets
{
    public class NavigationController : INavigationController
    {
        private Wpf.Shell.IShellController _shellController;
        private IAutorizationManager _autorizationManager;

        public NavigationController(Wpf.Shell.IShellController shellController, IAutorizationManager autorizationManager,
            ILockScreenController lockScreen)
        {
            _shellController = shellController;
            _autorizationManager = autorizationManager;
            _autorizationManager.UserAutorizationComplete += (o, e) => NavigateToPage(NavigationTargetPage.Work);
            lockScreen.UnlockApplication += (o, e) => NavigateToPage(NavigationTargetPage.Work);
        }

        public void NavigateToPage(NavigationTargetPage page)
        {
            var navigationKey = Shell.NavigationKeys.LockScreen;

            switch (page)
            {
                case NavigationTargetPage.Lock:
                    break;
                case NavigationTargetPage.Login:
                    navigationKey = Shell.NavigationKeys.Autorization;
                    break;
                case NavigationTargetPage.Work:
                    navigationKey = _autorizationManager.AutorizedUser.Role == Data.Entities.UserRoles.Admin
                        ? Shell.NavigationKeys.Administrator
                        : Shell.NavigationKeys.Psychologist;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            AppLog.Current.Info($"Navigate -> {navigationKey}");
            _shellController.NavigateTo(navigationKey);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\TesteeShortInfo.cs


using System.Windows.Media.Imaging;

namespace Updk7.Wpf.Master.Assets
{
    public class TesteeShortInfo
    {
        public TesteeShortInfo()
        {
        }

        public Data.Entities.Testee Entitie { get; set; }

        public string FullName { get; set; }

        public BitmapImage Photo { get; set; }

        public string Comment { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\TestGroupInfo.cs


using System;
using System.Collections.Generic;

namespace Updk7.Wpf.Master.Assets
{
    public class TestGroupInfo
    {
        public TestGroupInfo()
        {
        }

        public TestGroupInfo(Data.Entities.Testee testee, Data.TestGroupRun testGroup,
            IEnumerable<Tests.TestResults> results, DateTime testingDate)
        {
            Testee = testee;
            TestGroup = testGroup;
            Results = results;
            TestingDate = testingDate;
        }

        public Data.Entities.Testee Testee { get; }

        public Data.TestGroupRun TestGroup { get; }

        public IEnumerable<Tests.TestResults> Results { get; }

        public DateTime TestingDate { get; }

        public Data.TestGroupResults ToTestGroupResults()
        {
            var group = new Data.TestGroupResults();
            group.GroupKey = TestGroup.GroupKey;
            group.GroupTitle = TestGroup.GroupKey;
            group.TestResults = Results;
            group.TestingDate = TestingDate;

            return group;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\TestingInfo.cs


namespace Updk7.Wpf.Master.Assets
{
    public class TestingInfo
    {
        public TestingInfo()
        {
        }

        public TestingInfo(WorkplaceInfo workplace, TesteeShortInfo testee, Data.TestingPlan testingPlan)
        {
            Workplace = workplace;
            Testee = testee;
            TestingPlan = testingPlan;
        }

        public WorkplaceInfo Workplace { get; set; }

        public TesteeShortInfo Testee { get; set; }

        public Data.TestingPlan TestingPlan { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\TestStartedInfo.cs


namespace Updk7.Wpf.Master.Assets
{
    public class TestStartedInfo
    {
        public TestStartedInfo()
        {
        }

        public TestStartedInfo(TesteeShortInfo testee, Data.TestRun testRun, WorkplaceInfo workplace)
        {
            Testee = testee;
            TestRun = testRun;
            Workplace = workplace;
        }

        public TesteeShortInfo Testee { get; set; }

        public WorkplaceInfo Workplace { get; set; }

        public Data.TestRun TestRun { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\WorkplaceInfo.cs


namespace Updk7.Wpf.Master.Assets
{
    public class WorkplaceInfo
    {
        public WorkplaceInfo()
        {
        }

        public WorkplaceInfo(string title, string address, bool isBusy, Network.Local.ErrorFlags error)
        {
            Title = title;
            Address = address;
            IsBusy = isBusy;
            Error = error;
        }

        public string Title { get; set; }

        public string Address { get; set; }

        public bool IsBusy { get; set; }

        public Network.Local.ErrorFlags Error { get; set; }

        public bool HasErrors => Error != Network.Local.ErrorFlags.NoErrors;
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Commands\ChangeUserCommand.cs


namespace Updk7.Wpf.Master.Assets.Commands
{
    public class ChangeUserCommand : Wpf.Commands.CommandBase
    {
        private Events.ShutdownRequestEvent _shutdownRequestEvent;
        private INavigationController _navigationController;

        public ChangeUserCommand(Prism.Events.IEventAggregator eventAggregator, INavigationController navigationController)
        {
            _shutdownRequestEvent = eventAggregator.GetEvent<Events.ShutdownRequestEvent>();
            _navigationController = navigationController;
        }

        public override bool CanExecute(object parameter) => true;

        public override void Execute(object parameter)
        {
            AppLog.Current.Info("ChangeUserCommand");

            var shutdownData = new Events.ShutdownRequestEventData();
            _shutdownRequestEvent.Publish(shutdownData);

            if (shutdownData.CanShutdown)
                _navigationController.NavigateToPage(NavigationTargetPage.Login);
            else
            {
                var message = shutdownData.ToString();
                Wpf.Interactions.InteractionService.Current.ShowErrorMessage(message);
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Commands\ChangeUserPasswordCommand.cs


using System;

namespace Updk7.Wpf.Master.Assets.Commands
{
    public class ChangeUserPasswordCommand : Wpf.Commands.CommandBase
    {
        private class ErrorStrings
        {
            public static readonly string UserPassword = "Введен некорректный пароль пользователя";
            public static readonly string PasswordNotConfirmed = "Новый пароль и его подтверждение не совпадают";
            public static readonly string EmptyPassword = "Пустые строки недопустимы в качестве пароля";
            public static readonly string ChangePasswordFailed = "Не удалось обновить пароль пользователя";
        }

        private Wpf.Interactions.IInteractionService _interactionService = Wpf.Interactions.InteractionService.Current;
        private Data.Repositories.IUsersRepository _usersRepository;
        private IAutorizationManager _autorizationManager;

        public ChangeUserPasswordCommand(IAutorizationManager autorizationManager, 
            Data.Repositories.IUsersRepository usersRepository)
        {
            _usersRepository = usersRepository;
            _autorizationManager = autorizationManager;
        }

        public override bool CanExecute(object parameter) => true;

        public override void Execute(object parameter)
        {
            AppLog.Current.Warning($"ChangeUserPasswordCommand({_autorizationManager.AutorizedUser.Id})");

            var interaction = new Interactions.ChangeUserPasswordInteraction();
            _interactionService.ShowInteractionModel(interaction, () => 
            {
                if (canChangePassword(interaction))
                {
                    AppLog.Current.Warning("User password has been changed");
                    updatePassword(_autorizationManager.AutorizedUser.Id, interaction.InteractionData.NewPassword);
                }
            });
        }

        private void updatePassword(int userId, string password)
        {
            try
            {
                var userEntitie = _usersRepository.GetById(userId);
                userEntitie.Password = password;
                _usersRepository.Update(userEntitie);
            }
            catch (Exception ex)
            {
                _interactionService.ShowErrorMessage($"{ErrorStrings.ChangePasswordFailed}\n{ex.Message}");
            }
        }

        private bool canChangePassword(Interactions.ChangeUserPasswordInteraction interaction)
        {
            if (!interaction.Confirmed)
                return false;

            var autorizedUser = _autorizationManager.AutorizedUser;
            if (autorizedUser.Password != interaction.InteractionData.UserPassword)
            {
                _interactionService.ShowErrorMessage(ErrorStrings.UserPassword);
                return false;
            }

            if (string.IsNullOrEmpty(interaction.InteractionData.NewPassword) ||
                string.IsNullOrEmpty(interaction.InteractionData.ConfirmNewPassword))
            {
                _interactionService.ShowErrorMessage(ErrorStrings.EmptyPassword);
                return false;
            }

            if (interaction.InteractionData.NewPassword != interaction.InteractionData.ConfirmNewPassword)
            {
                _interactionService.ShowErrorMessage(ErrorStrings.PasswordNotConfirmed);
                return false;
            }

            return true;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Commands\ShowLockScreenCommand.cs


namespace Updk7.Wpf.Master.Assets.Commands
{
    public class ShowLockScreenCommand : Wpf.Commands.CommandBase
    {
        private NavigationController _navigationCotroller;
        private IAutorizationManager _autorizationManager;
        private ILockScreenController _lockSCreenController;

        public ShowLockScreenCommand(NavigationController navigationController, IAutorizationManager autorizationManager,
            ILockScreenController lockScreenController)
        {
            _navigationCotroller = navigationController;
            _autorizationManager = autorizationManager;
            _lockSCreenController = lockScreenController;
        }

        public override bool CanExecute(object parameter) => true;

        public override void Execute(object parameter)
        {
            _lockSCreenController.UserData = _autorizationManager.AutorizedUser;
            _navigationCotroller.NavigateToPage(NavigationTargetPage.Lock);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Commands\ShutdownCommand.cs


namespace Updk7.Wpf.Master.Assets.Commands
{
    public class ShutdownCommand : Wpf.Commands.CommandBase
    {
        private Events.ShutdownRequestEvent _shutdownRequestEvent;
        private AppManager _appManager;

        public ShutdownCommand(AppManager appManager, Prism.Events.IEventAggregator eventAggregator)
        {
            _shutdownRequestEvent = eventAggregator.GetEvent<Events.ShutdownRequestEvent>();
            _appManager = appManager;
        }

        public override bool CanExecute(object parameter) => true;

        public override void Execute(object parameter)
        {
            var shutdownData = new Events.ShutdownRequestEventData();
            _shutdownRequestEvent.Publish(shutdownData);

            if (shutdownData.CanShutdown)
                shutdown();
            else
            {
                var message = shutdownData.ToString();
                Wpf.Interactions.InteractionService.Current.ShowErrorMessage(message);
            }
        }

        private void shutdown()
        {
            _appManager.Shutdown();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Events\CreateTestingPlanEvent.cs


namespace Updk7.Wpf.Master.Assets.Events
{
    public class CreateTestingPlanEventData
    {
        public CreateTestingPlanEventData(TesteeShortInfo testee)
        {
            Testee = testee;
        }

        public TesteeShortInfo Testee { get; set; }
    }

    public class CreateTestingPlanEvent : Prism.Events.PubSubEvent<CreateTestingPlanEventData>
    {
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Events\ShowTesteeReportsEvent.cs


namespace Updk7.Wpf.Master.Assets.Events
{
    public class ShowTesteeReportsEventData
    {
        public ShowTesteeReportsEventData(TesteeShortInfo testee)
        {
            Testee = testee;
        }

        public TesteeShortInfo Testee { get; set; }
    }

    public class ShowTesteeReportsEvent : Prism.Events.PubSubEvent<ShowTesteeReportsEventData>
    {
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Events\ShutdownRequestEvent.cs


using System.Collections.Generic;
using System.Linq;

namespace Updk7.Wpf.Master.Assets.Events
{
    public class ShutdownRequestEventData
    {
        private List<string> _cancelReasons = new List<string>();

        public IEnumerable<string> CancelReasons => _cancelReasons;

        public bool CanShutdown => _cancelReasons.Count == 0;

        public void AddCancelReason(string reason)
        {
            _cancelReasons.Add(reason);
        }

        public override string ToString()
        {
            return _cancelReasons.Aggregate(string.Empty, (current, next) => current + $"{next}\n");
        }
    }

    public class ShutdownRequestEvent : Prism.Events.PubSubEvent<ShutdownRequestEventData>
    {
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Events\StartTestingEvent.cs


namespace Updk7.Wpf.Master.Assets.Events
{
    public class StartTestingEventData
    {
       public TestingInfo TestingInfo { get; set; }
    }

    public class StartTestingEvent : Prism.Events.PubSubEvent<StartTestingEventData>
    {
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Events\TestCompleteEvent.cs


namespace Updk7.Wpf.Master.Assets.Events
{
    public class TestCompleteEventData
    {
        public TestCompleteEventData()
        {
        }

        public TestCompleteEventData(TesteeShortInfo testee, Tests.TestResults results,
            WorkplaceInfo workplace)
        {
            Testee = testee;
            Results = results;
            Workplace = workplace;
        }

        public TesteeShortInfo Testee { get; set; }

        public Tests.TestResults Results { get; set; }

        public WorkplaceInfo Workplace { get; set; }
    }

    public class TestCompleteEvent : Prism.Events.PubSubEvent<TestCompleteEventData>
    {
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Events\TestingPlanCompleteEvent.cs


namespace Updk7.Wpf.Master.Assets.Events
{
    public class TestingPlanCompleteEventData
    {
        public TestingPlanCompleteEventData()
        {
        }

        public TestingPlanCompleteEventData(TestingInfo testingInfo, Tests.TestType? errorTest = null)
        {
            TestingInfo = testingInfo;
            ErrorTest = errorTest;
        }

        public TestingInfo TestingInfo { get; }

        public Tests.TestType? ErrorTest { get; }
    }

    public class TestingPlanCompleteEvent : Prism.Events.PubSubEvent<TestingPlanCompleteEventData>
    {
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Events\TestStartedEevnt.cs


namespace Updk7.Wpf.Master.Assets.Events
{
    public class TestStartedEventData
    {
        public TestStartedEventData()
        {
        }

        public TestStartedEventData(TestStartedInfo testStartedInfo)
        {
            TestStartedInfo = testStartedInfo;
        }

        public TestStartedInfo TestStartedInfo { get; set; }
    }

    public class TestStartedEevnt : Prism.Events.PubSubEvent<TestStartedEventData>
    {
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Events\WorkplaceDisconnectedEvent.cs


namespace Updk7.Wpf.Master.Assets.Events
{
    public class WorkplaceDisconnectedEventData
    {
        public Data.Entities.Testee Testee { get; set; }

        public WorkplaceInfo Workplace { get; set; }
    }

    public class WorkplaceDisconnectedEvent : Prism.Events.PubSubEvent<WorkplaceDisconnectedEventData>
    {
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Events\WorkplacesDiscoveryEvent.cs


using System.Collections.Generic;

namespace Updk7.Wpf.Master.Assets.Events
{
    public class WorkplacesDiscoveryEventData
    {
        public IEnumerable<WorkplaceInfo> Workplaces { get; set; }
    }

    public class WorkplacesDiscoveryEvent : Prism.Events.PubSubEvent<WorkplacesDiscoveryEventData>
    {
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Interactions\ChangeSettingsInteraction.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Assets.Interactions
{
    public class ChangeSettingsInteraction : Wpf.Interactions.ConfirmationInteraction
    {
        public ChangeSettingsInteraction()
        {
            InteractionData.PropertyChanged += onInteractionDataChanged;
        }

        protected override bool CanConfirm() => !InteractionData.HasErrors;

        public string WelcomeMessage { get; set; } = "Новые настройки вступят в силу после перезапуска приложения";

        public ViewModels.ChangeSettingsViewModel InteractionData { get; } =
           new ViewModels.ChangeSettingsViewModel();

        private void onInteractionDataChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(InteractionData.HasErrors))
                AcceptCommand.RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Interactions\ChangeUserPasswordInteraction.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Assets.Interactions
{
    public class ChangeUserPasswordInteraction : Wpf.Interactions.ConfirmationInteraction
    {
        public ChangeUserPasswordInteraction()
        {
            InteractionData.PropertyChanged += onInteractionDataChanged;
        }

        public ViewModels.UserPasswordEditorViewModel InteractionData { get; } =
            new ViewModels.UserPasswordEditorViewModel();

        protected override bool CanConfirm() => !InteractionData.HasErrors;

        private void onInteractionDataChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(InteractionData.HasErrors))
                AcceptCommand.RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Interactions\Views\ChangeSettingsView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Assets.Interactions.Views.ChangeSettingsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:views="clr-namespace:Updk7.Wpf.Master.Assets.Views"
             mc:Ignorable="d" >
    <Grid
        Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!--Заголовок панели-->
        <Grid
            Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="40" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Image
                Grid.Column="0" 
                Margin="5"
                Source="{StaticResource SettingsImage}" />
            <TextBlock 
                Grid.Column="1"
                FontSize="18"
                FontWeight="Bold"
                Margin="5,1,1,1"
                VerticalAlignment="Center"
                HorizontalAlignment="Left"
                Text="Редактирование настроек приложения"
                />
        </Grid>
        <!--Панель редактора-->
        <Grid
            Grid.Row="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <TextBlock 
                Grid.Row="0"
                Text="{Binding WelcomeMessage}"
                VerticalAlignment="Center"
                HorizontalAlignment="Center"
                FontSize="14"
                FontWeight="Medium"
                Margin="0,3,0,5"
                />
            <views:ChangeSettingsView 
                Grid.Row="1"
                DataContext="{Binding InteractionData}"
                />
        </Grid>
        <!--Кнопки управления-->
        <Grid
            Grid.Row="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <!--Кнопка Применить-->
            <Button 
                Grid.Column="1"
                Width="80"
                Height="30"
                HorizontalAlignment="Right"
                FontSize="14"
                FontWeight="Bold"
                Content="Принять"
                Command="{Binding AcceptCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
            <!--Кнопка Отменить-->
            <Button 
                Grid.Column="2"
                Width="80"
                Height="30"
                FontSize="14"
                FontWeight="Bold"
                HorizontalAlignment="Right"
                Content="Отменить"
                Command="{Binding CancelCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Interactions\Views\ChangeSettingsView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Assets.Interactions.Views
{
    /// <summary>
    /// Interaction logic for ChangeSettingsView.xaml
    /// </summary>
    public partial class ChangeSettingsView : UserControl
    {
        public ChangeSettingsView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Interactions\Views\ChangeUserPasswordView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Assets.Interactions.Views.ChangeUserPasswordView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:views="clr-namespace:Updk7.Wpf.Master.Assets.Views"
             mc:Ignorable="d" 
             Width="500">
    <Grid
        Margin="3,0,3,3">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!--Заголовок панели-->
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="40" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Image
                Grid.Column="0" 
                Margin="5"
                Source="{StaticResource KeyImage}"/>
            <TextBlock 
                Grid.Column="1"
                FontSize="18"
                FontWeight="Bold"
                Margin="5,1,1,1"
                VerticalAlignment="Center"
                HorizontalAlignment="Left"
                Text="Изменение пароля текущего пользователя"
                />
        </Grid>
        <!--Редактор пароля пользователя-->
        <views:UserPasswordEditorView 
            Grid.Row="1"
            DataContext="{Binding InteractionData}"
            />
        <!--Кнопки управления-->
        <Grid
            Grid.Row="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <!--Кнопка Применить-->
            <Button
                Grid.Column="1"
                Width="80"
                Height="30"
                HorizontalAlignment="Right"
                FontSize="14"
                FontWeight="Bold"
                Content="Принять"
                Command="{Binding AcceptCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
            <!--Кнопка Отменить-->
            <Button
                Grid.Column="2"
                Width="80"
                Height="30"
                FontSize="14"
                FontWeight="Bold"
                HorizontalAlignment="Right"
                Content="Отменить"
                Command="{Binding CancelCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Interactions\Views\ChangeUserPasswordView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Assets.Interactions.Views
{
    /// <summary>
    /// Interaction logic for ChangeUserPasswordView.xaml
    /// </summary>
    public partial class ChangeUserPasswordView : UserControl
    {
        public ChangeUserPasswordView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Shell\AdministratorRegionView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Assets.Shell.AdministratorRegionView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Assets.Shell"
             xmlns:prism="http://prismlibrary.com/"
             xmlns:styles="clr-namespace:Updk7.Wpf.Styles;assembly=Updk7.Wpf"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.Resources>
            <Style TargetType="{x:Type TabItem}" BasedOn="{StaticResource FlatTabItemStyle}">
                <Setter Property="Header" Value="{Binding Path=(styles:TabItemHeader.Text)}" />
                <Setter Property="FontSize" Value="20" />
                <Setter Property="FontWeight" Value="Bold" />
                <Setter Property="Padding" Value="15,2,15,2" />
            </Style>
        </Grid.Resources>
        <TabControl
            prism:RegionManager.RegionName="{x:Static local:RegionKeys.AdministratorTabRegion}"
            Grid.Row="1"
            BorderBrush="Transparent"
            Background="Transparent"
            Margin="5"
            SelectedIndex="0"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Shell\AdministratorRegionView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Assets.Shell
{
    /// <summary>
    /// Interaction logic for AdministratorRegionView.xaml
    /// </summary>
    public partial class AdministratorRegionView : UserControl
    {
        public AdministratorRegionView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Shell\AutorizationRegionView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Assets.Shell.AutorizationRegionView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Assets.Shell"
             xmlns:prism="http://prismlibrary.com/"
             mc:Ignorable="d" 
             d:DesignHeight="450" 
             d:DesignWidth="800">
    <Grid>
        <ContentControl 
            prism:RegionManager.RegionName="{x:Static local:RegionKeys.AutorizationRegion}"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Shell\AutorizationRegionView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Assets.Shell
{
    /// <summary>
    /// Interaction logic for AutorizationRegionView.xaml
    /// </summary>
    public partial class AutorizationRegionView : UserControl
    {
        public AutorizationRegionView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Shell\LockScreenRegionView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Assets.Shell.LockScreenRegionView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Assets.Shell"
             xmlns:prism="http://prismlibrary.com/"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <ContentControl 
            prism:RegionManager.RegionName="{x:Static local:RegionKeys.LockScreenRegion}"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Shell\LockScreenRegionView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Assets.Shell
{
    /// <summary>
    /// Interaction logic for LockScreenRegionView.xaml
    /// </summary>
    public partial class LockScreenRegionView : UserControl
    {
        public LockScreenRegionView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Shell\NavigationKeys.cs


namespace Updk7.Wpf.Master.Assets.Shell
{
    public static class NavigationKeys
    {
        public static readonly string Autorization = "Autorization";
        public static readonly string Psychologist = "Psychologist";
        public static readonly string Administrator = "Administrator";
        public static readonly string LockScreen = "LockScreen";
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Shell\PsychologistRegionView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Assets.Shell.PsychologistRegionView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Assets.Shell"
             xmlns:prism="http://prismlibrary.com/"
             xmlns:styles="clr-namespace:Updk7.Wpf.Styles;assembly=Updk7.Wpf"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.Resources>
            <Style TargetType="{x:Type TabItem}" BasedOn="{StaticResource FlatTabItemStyle}">
                <Setter Property="Header" Value="{Binding Path=(styles:TabItemHeader.Text)}" />
                <Setter Property="FontSize" Value="20" />
                <Setter Property="FontWeight" Value="Bold" />
                <Setter Property="Padding" Value="15,2,15,2" />
            </Style>
        </Grid.Resources>
        <TabControl
            prism:RegionManager.RegionName="{x:Static local:RegionKeys.PsychoTabRegion}"
            Grid.Row="1"
            BorderBrush="Transparent"
            Background="Transparent"
            Margin="5"
            SelectedIndex="0"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Shell\PsychologistRegionView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Assets.Shell
{
    /// <summary>
    /// Interaction logic for PsychologistRegionView.xaml
    /// </summary>
    public partial class PsychologistRegionView : UserControl
    {
        public PsychologistRegionView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Shell\RegionKeys.cs


namespace Updk7.Wpf.Master.Assets.Shell
{
    public static class RegionKeys
    {
        public static readonly string AutorizationRegion = "AutorizationRegion";
        public static readonly string AdministratorTabRegion = "AdministratorTabRegion";
        public static readonly string PsychoTabRegion = "PsychoTabRegion";
        public static readonly string LockScreenRegion = "LockScreenRegion";
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\ViewModels\AutorizationViewModel.cs


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace Updk7.Wpf.Master.Assets.ViewModels
{
    public class AutorizationViewModel : Prism.Mvvm.BindableBase, IAutorizationManager
    {
        public class UserInfoViewModel : Prism.Mvvm.BindableBase
        {
            public UserInfoViewModel(Data.Entities.User user)
            {
                SourceData = user;
                FullName = user.FullName;
                RegistrationDate = user.RegistrationDate;
                Role = user.Role;
            }

            public Data.Entities.User SourceData { get; }

            public string FullName { get; }

            public string Role { get; }

            public DateTime RegistrationDate { get; }

            private string _enteredPassword;

            public string EnteredPassword
            {
                get { return _enteredPassword; }
                set { SetProperty(ref _enteredPassword, value); }
            }
        }

        private class Errors
        {
            public static readonly string IncorrectPassword = "Введен неверный пароль";
        }

        private Data.Repositories.IUsersRepository _usersRepository;

        public AutorizationViewModel(Data.Repositories.IUsersRepository usersRepository)
        {
            AcceptUserCommand = new Prism.Commands.DelegateCommand(acceptUser, () => SelectedUser != null);

            _usersRepository = usersRepository;
            _usersRepository.DataChanged += (o, e) => updateUsers();
            updateUsers();
        }

        public event EventHandler UserAutorizationComplete;

        public Prism.Commands.DelegateCommand AcceptUserCommand { get; }

        private IEnumerable<UserInfoViewModel> _users;

        public IEnumerable<UserInfoViewModel> Users
        {
            get { return _users; }
            private set { SetProperty(ref _users, value); }
        }

        private UserInfoViewModel _selectedUser;

        public UserInfoViewModel SelectedUser
        {
            get { return _selectedUser; }
            set { SetProperty(ref _selectedUser, value); }
        }

        private string _loginError;

        public string LoginError
        {
            get { return _loginError; }
            private set { SetProperty(ref _loginError, value); }
        }

        private Data.Entities.User _autorizedUser;

        public Data.Entities.User AutorizedUser
        {
            get { return _autorizedUser; }
            private set { SetProperty(ref _autorizedUser, value); }
        }

        public void Reset()
        {
            AutorizedUser = null;
            LoginError = string.Empty;
            clearPasswords();
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(SelectedUser))
                LoginError = string.Empty;

            base.OnPropertyChanged(e);
        }

        private void updateUsers()
        {
            Users = _usersRepository.Get(removed: false)
                .Select(u => new UserInfoViewModel(u))
                .ToList();

            var psichologist = Users.FirstOrDefault(u => u.Role == Data.Entities.UserRoles.Psycho);
            SelectedUser = psichologist != null
                ? psichologist
                : Users.FirstOrDefault();

            updateCommands();
        }

        private void acceptUser()
        {
            if (SelectedUser.EnteredPassword != SelectedUser.SourceData.Password)
                LoginError = Errors.IncorrectPassword;
            else
            {
                LoginError = string.Empty;
                AutorizedUser = SelectedUser.SourceData;
                onUserAutorized();
            }
        }

        private void clearPasswords()
        {
            foreach (var user in Users)
                user.EnteredPassword = string.Empty;
        }

        private void updateCommands()
        {
            AcceptUserCommand.RaiseCanExecuteChanged();
        }

        private void onUserAutorized()
        {
            AppLog.Current.Info($"User autorized ({AutorizedUser.Id})");

            clearPasswords();
            UserAutorizationComplete?.Invoke(this, EventArgs.Empty);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\ViewModels\ChangeSettingsViewModel.cs


using System.IO;

namespace Updk7.Wpf.Master.Assets.ViewModels
{
    public class ChangeSettingsViewModel : Validation.ValidatedBindableBase
    {
        private class Errors
        {
            public static readonly string InvalidPort = "Задано некорректное значение IP-порта. Допустимые значения 0-65535";
            public static readonly string InvalidTitle = "Некорректное название организации";
            public static readonly string InvalidDataFile = "Не задано имя файла БД";
        }

        public ChangeSettingsViewModel()
        {
            setupValidationRules();
        }

        private string _portText = "27040";

        public string PortText
        {
            get { return _portText; }
            set { SetProperty(ref _portText, value); }
        }

        private string _organization;

        public string Organization
        {
            get { return _organization; }
            set { SetProperty(ref _organization, value); }
        }

        private string _dataSourceFile;

        public string DataSourceFile
        {
            get { return _dataSourceFile; }
            set { SetProperty(ref _dataSourceFile, value); }
        }

        private string _workFolder;

        public string WorkFolder
        {
            get { return _workFolder; }
            set { SetProperty(ref _workFolder, value); }
        }

        public void SetupData(MasterConfiguration configuration)
        {
            PortText = configuration.ServerPort.ToString();
            Organization = configuration.Organization;
            DataSourceFile = configuration.DataConnection;
            WorkFolder = Path.GetDirectoryName(configuration.ConfigurationFile);
        }

        public void UpdateConfiguration(MasterConfiguration configuration)
        {
            configuration.ServerPort = int.Parse(PortText);
            configuration.Organization = Organization;
            configuration.DataConnection = DataSourceFile;
        }

        private void setupValidationRules()
        {
            ErrorsContainer.AddValidationRule(
                nameof(PortText),
                () =>
                {
                    if (!string.IsNullOrEmpty(PortText))
                    {
                        var portValue = 0;
                        if (int.TryParse(PortText, out portValue))
                        {
                            if (portValue > 0 && portValue <= 65535)
                                return true;
                        }
                    }
                    return false;
                }, Errors.InvalidPort);

            ErrorsContainer.AddValidationRule(
                nameof(Organization),
                () => !string.IsNullOrEmpty(Organization) && Organization != MasterConfiguration.DefaultOrganization,
                Errors.InvalidTitle);

            ErrorsContainer.AddValidationRule(
                nameof(DataSourceFile),
                () => !string.IsNullOrEmpty(DataSourceFile),
                Errors.InvalidDataFile);

            ErrorsContainer.UpdateValidation();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\ViewModels\LockScreenViewModel.cs


using System;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Assets.ViewModels
{
    public class LockScreenViewModel : Prism.Mvvm.BindableBase, ILockScreenController
    {
        private class Errors
        {
            public static readonly string IncorrectPassword = "Введен неверный пароль";
        }

        public LockScreenViewModel()
        {
            AcceptUserCommand = new Prism.Commands.DelegateCommand(acceptUser, () => UserData != null);
        }

        public event EventHandler UnlockApplication;

        public Prism.Commands.DelegateCommand AcceptUserCommand { get; }

        private  Data.Entities.User _userData;

        public Data.Entities.User UserData
        {
            get { return _userData; }
            set { SetProperty(ref _userData, value); }
        }

        private string _userName;

        public string UserName
        {
            get { return _userName; }
            private set { SetProperty(ref _userName, value); }
        }

        private string _userRole;

        public string UserRole
        {
            get { return _userRole; }
            private set { SetProperty(ref _userRole, value); }
        }

        private string _enteredPassword;

        public string EnteredPassword
        {
            get { return _enteredPassword; }
            set { SetProperty(ref _enteredPassword, value); }
        }

        private string _loginError;

        public string LoginError
        {
            get { return _loginError; }
            private set { SetProperty(ref _loginError, value); }
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(UserData))
                updateLockScreenData();

            base.OnPropertyChanged(args);
        }

        private void updateLockScreenData()
        {
            EnteredPassword = string.Empty;

            if (UserData != null)
            {
                UserName = UserData.FullName;
                UserRole = UserData.Role == Data.Entities.UserRoles.Admin
                    ? "Администратор"
                    : "Психолог";
            }
            else
                UserName = string.Empty;

            AcceptUserCommand.RaiseCanExecuteChanged();
        }

        private void acceptUser()
        {
            if (EnteredPassword != UserData.Password)
                LoginError = Errors.IncorrectPassword;
            else
            {
                LoginError = string.Empty;
                EnteredPassword = string.Empty;
                UnlockApplication?.Invoke(this, EventArgs.Empty);
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\ViewModels\TesteeCollectionRowViewModel.cs


using System;
using System.Diagnostics;

namespace Updk7.Wpf.Master.Assets.ViewModels
{
    public class TesteeCollectionRowViewModel : Prism.Mvvm.BindableBase
    {
        public TesteeCollectionRowViewModel()
        {
        }

        public TesteeCollectionRowViewModel(Data.Entities.Testee testee)
        {
            Update(testee);
        }

        private Data.Entities.Testee _sourceData;

        public Data.Entities.Testee SourceData
        {
            get { return _sourceData; }
            private set { SetProperty(ref _sourceData, value); }
        }

        private string _fullName;

        public string FullName
        {
            get { return _fullName; }
            private set { SetProperty(ref _fullName, value); }
        }

        private DateTime _birthDate;

        public DateTime BirthDate
        {
            get { return _birthDate; }
            private set { SetProperty(ref _birthDate, value); }
        }

        private DateTime _registrationDate;

        public DateTime RegistrationDate
        {
            get { return _registrationDate; }
            private set { SetProperty(ref _registrationDate, value); }
        } 

        public virtual void Update(Data.Entities.Testee testee)
        {
            Debug.Assert(testee != null);
            SourceData = testee;
            FullName = testee.FullName;
            BirthDate = testee.BirthDate;
            RegistrationDate = testee.RegistrationDate;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\ViewModels\TesteeCollectionViewModel.cs


using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Data;

namespace Updk7.Wpf.Master.Assets.ViewModels
{
    public abstract class TesteeCollectionViewModel<TViewModel> : Prism.Mvvm.BindableBase
        where TViewModel : TesteeCollectionRowViewModel
    {
        protected List<TViewModel> TesteesCollection = new List<TViewModel>();

        public TesteeCollectionViewModel()
        {
            TesteesCollectionView = CollectionViewSource.GetDefaultView(TesteesCollection);
            TesteesCollectionView.SortDescriptions.Add(new SortDescription(nameof(TesteeCollectionRowViewModel.FullName),
                ListSortDirection.Ascending));
            TesteesCollectionView.CurrentChanged += (o, e) => 
                SelectedRow = TesteesCollectionView.CurrentItem as TViewModel;
        }

        public ICollectionView TesteesCollectionView { get; }

        private TViewModel _selectedRow;

        public TViewModel SelectedRow        
        {
            get { return _selectedRow; }
            set { SetProperty(ref _selectedRow, value); }
        }

        public virtual void SetTestees(IEnumerable<Data.Entities.Testee> testees)
        {
            TesteesCollection.Clear();
            TesteesCollection.AddRange(testees.Select(t => CreateTesteeViewModel(t)));
            TesteesCollectionView.Refresh();
        }

        public virtual void AddTestee(Data.Entities.Testee testee)
        {
            TesteesCollection.Add(CreateTesteeViewModel(testee));
            TesteesCollectionView.Refresh();
        }

        public virtual void UpdateTestee(Data.Entities.Testee testee)
        {
            var viewModel = TesteesCollection.FirstOrDefault(vm => vm.SourceData.Id == testee.Id);
            if (viewModel != null)
                viewModel.Update(testee);
        }

        public virtual void RemoveTestee(Data.Entities.Testee testee)
        {
            var viewModel = TesteesCollection.FirstOrDefault(vm => vm.SourceData.Id == testee.Id);
            if (viewModel != null)
            {
                TesteesCollection.Remove(viewModel);
                TesteesCollectionView.Refresh();
            }
        }

        protected abstract TViewModel CreateTesteeViewModel(Data.Entities.Testee testee);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\ViewModels\TesteeInfoViewModel.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Assets.ViewModels
{
    public class TesteeInfoViewModel : Prism.Mvvm.BindableBase
    {
        private static class Strings
        {
            public static readonly string NoEmployee = "Сотрудник не выбран";
        }

        public TesteeInfoViewModel()
        {
        }

        private TesteeShortInfo _sourceData;

        public TesteeShortInfo SourceData
        {
            get { return _sourceData; }
            set { SetProperty(ref _sourceData, value); }
        }

        private string _fullName = Strings.NoEmployee;

        public string FullName
        {
            get { return _fullName; }
            private set { SetProperty(ref _fullName, value); }
        }

        private string _comment;

        public string Comment
        {
            get { return _comment; }
            private set { SetProperty(ref _comment, value); }
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(SourceData))
                setTesteeInfo(_sourceData);

            base.OnPropertyChanged(args);
        }

        private void setTesteeInfo(TesteeShortInfo testeeInfo)
        {
            if (testeeInfo != null)
            {
                FullName = testeeInfo.FullName;
                Comment = testeeInfo.Comment;
            }
            else
            {
                FullName = Strings.NoEmployee;
                Comment = null;
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\ViewModels\UserPasswordEditorViewModel.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Assets.ViewModels
{
    public class UserPasswordEditorViewModel : Validation.ValidatedBindableBase
    {
        private class ErrorStrings
        {
            public static readonly string UserPassword = "Не введен пароль текущего пользователя";
            public static readonly string NewPassword = "Не введен новый пароль";
            public static readonly string ConfirmNewPassword = "Новый пароль не подтвержден";
            public static readonly string PasswordsNotEqual = "Новый пароль и его подтверждение не совпадают";
        }

        public UserPasswordEditorViewModel()
        {
            setupValidatoinRules();
        }

        private string _userPassword;

        public string UserPassword
        {
            get { return _userPassword; }
            set { SetProperty(ref _userPassword, value); }
        }

        private string _newPassword;

        public string NewPassword
        {
            get { return _newPassword; }
            set { SetProperty(ref _newPassword, value); }
        }

        private string _confirmNewPassword;

        public string ConfirmNewPassword
        {
            get { return _confirmNewPassword; }
            set { SetProperty(ref _confirmNewPassword, value); }
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ConfirmNewPassword))
                OnPropertyChanged(new PropertyChangedEventArgs(nameof(NewPassword)));

            base.OnPropertyChanged(e);
        }

        private void setupValidatoinRules()
        {
            ErrorsContainer.AddValidationRule(
                nameof(UserPassword),
                () => !string.IsNullOrEmpty(UserPassword),
                ErrorStrings.UserPassword);

            ErrorsContainer.AddValidationRule(
                nameof(NewPassword),
                () => !string.IsNullOrEmpty(NewPassword),
                ErrorStrings.NewPassword);

            ErrorsContainer.AddValidationRule(
                nameof(NewPassword),
                () => NewPassword == ConfirmNewPassword,
                ErrorStrings.PasswordsNotEqual);

            ErrorsContainer.AddValidationRule(
                nameof(ConfirmNewPassword),
                () => !string.IsNullOrEmpty(ConfirmNewPassword),
                ErrorStrings.ConfirmNewPassword);

            ErrorsContainer.UpdateValidation();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Views\AutorizationView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Assets.Views.AutorizationView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             xmlns:customBehaviors="clr-namespace:Updk7.Wpf.Behaviors;assembly=Updk7.Wpf"
             xmlns:commands="clr-namespace:Updk7.Wpf.Commands;assembly=Updk7.Wpf"
             xmlns:converters="clr-namespace:Updk7.Wpf.Converters;assembly=Updk7.Wpf"
             mc:Ignorable="d" 
             d:DesignHeight="450" 
             d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <DockPanel
            Grid.Row="0"
            VerticalAlignment="Center"
            HorizontalAlignment="Center">
            <!--Заголовок-->
            <TextBlock 
                DockPanel.Dock="Top"
                HorizontalAlignment="Center"
                Text="Пользователи"
                FontSize="24"
                FontWeight="Medium"
                Margin="5"
                />
            <!--Список пользователей-->
            <ListView
                DockPanel.Dock="Top"
                MaxWidth="600"
                Background="Transparent"
                ItemsSource="{Binding Users}"
                IsSynchronizedWithCurrentItem="True"
                SelectedItem="{Binding SelectedUser}"
                BorderBrush="Transparent"
                ScrollViewer.HorizontalScrollBarVisibility="Disabled">
                <ListView.ItemContainerStyle>
                    <Style TargetType="{x:Type ListViewItem}">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type ListViewItem}">
                                    <Grid
                                        Margin="0,10,0,10">
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="Auto" />
                                            <ColumnDefinition Width="Auto" />
                                            <ColumnDefinition />
                                        </Grid.ColumnDefinitions>
                                        <Ellipse 
                                            x:Name="PART_Ellipse"
                                            Grid.Column="0"
                                            Margin="3"
                                            Width="20"
                                            Height="20"
                                            Stroke="Black"
                                            StrokeThickness="2"
                                            Fill="Transparent"
                                            />
                                        <Image 
                                            x:Name="PART_Image"
                                            Grid.Column="1"
                                            Margin="3"
                                            Width="48"
                                            Height="48"
                                            Source="{StaticResource UserImage}"
                                            />
                                        <Grid
                                            Grid.Column="2">
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="Auto" />
                                                <RowDefinition Height="Auto" />
                                            </Grid.RowDefinitions>
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="Auto" />
                                                <ColumnDefinition />
                                            </Grid.ColumnDefinitions>
                                            <!--Данные профиля-->
                                            <TextBlock 
                                                Grid.Row="0"
                                                Grid.Column="0"
                                                Grid.ColumnSpan="2"
                                                FontSize="16"
                                                FontWeight="Bold"
                                                VerticalAlignment="Center">
                                                <Run 
                                                    Text="{Binding FullName, Mode=OneWay}" 
                                                    />
                                                <LineBreak />
                                                <Run 
                                                    FontSize="14"
                                                    FontWeight="Light"
                                                    Text="{Binding Role, Mode=OneWay, Converter={converters:KeyToStringConverter}}" 
                                                    />
                                            </TextBlock>
                                            <!--Пароль-->
                                            <TextBlock
                                                Grid.Row="1"
                                                Grid.Column="0"
                                                x:Name="PART_PasswordTitle"
                                                Visibility="Hidden"
                                                Text="Пароль:"
                                                FontWeight="Medium"
                                                Margin="0,5,5,0"
                                                VerticalAlignment="Center"
                                                />
                                            <PasswordBox 
                                                Grid.Row="1"
                                                Grid.Column="1"
                                                x:Name="PART_PasswordBox"
                                                Visibility="Hidden"
                                                Margin="0,5,5,0"
                                                PasswordChar="*"
                                                Height="20"
                                                Width="140">
                                                <PasswordBox.InputBindings>
                                                    <KeyBinding 
                                                        Key="Enter"
                                                        Command="{Binding DataContext.AcceptUserCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                                                        />
                                                </PasswordBox.InputBindings>
                                                <i:Interaction.Behaviors>
                                                    <customBehaviors:PasswordBoxBehavior Password="{Binding EnteredPassword, Mode=TwoWay}" />
                                                </i:Interaction.Behaviors>
                                            </PasswordBox>
                                        </Grid>
                                    </Grid>
                                    <ControlTemplate.Triggers>
                                        <DataTrigger Binding="{Binding Role}" Value="Администратор">
                                            <Setter TargetName="PART_Image" Property="Source" Value="{StaticResource AdministratorImage}" />
                                        </DataTrigger>
                                        <Trigger Property="IsMouseOver" Value="True">
                                            <Setter TargetName="PART_Ellipse" Property="Fill" Value="Gray" />
                                        </Trigger>
                                        <Trigger Property="IsSelected" Value="True">
                                            <Setter TargetName="PART_Ellipse" Property="Fill" Value="Orange" />
                                            <Setter TargetName="PART_PasswordTitle" Property="Visibility" Value="Visible" />
                                            <Setter TargetName="PART_PasswordBox" Property="Visibility" Value="Visible" />
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </ListView.ItemContainerStyle>
            </ListView>
            <TextBlock 
                DockPanel.Dock="Bottom"
                Text="{Binding LoginError, Mode=OneWay}"
                Foreground="Red"
                FontSize="16"
                FontWeight="Medium"
                HorizontalAlignment="Center"
                />
        </DockPanel>
        <!--Кнопки управления-->
        <Grid
            Grid.Row="2">
            <Button
                Margin="10,0,0,10"
                Height="45"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Выход из УПДК"
                BorderThickness="2"
                HorizontalAlignment="Left"
                Command="{commands:CommandProvider Shutdown}">
                <Image Margin="3" Source="{StaticResource ShutdownImage}" />
            </Button>
            <Button 
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Style="{StaticResource FlatButtonStyle}"
                Height="32"
                Width="130"
                FontSize="16"
                FontWeight="Bold"
                Content="Вход"
                Command="{Binding AcceptUserCommand}"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Views\AutorizationView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Assets.Views
{
    /// <summary>
    /// Interaction logic for AutorizationView.xaml
    /// </summary>
    public partial class AutorizationView : UserControl
    {
        public AutorizationView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Views\ChangePasswordButton.xaml

<UserControl x:Class="Updk7.Wpf.Master.Assets.Views.ChangePasswordButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:commands="clr-namespace:Updk7.Wpf.Commands;assembly=Updk7.Wpf"
             mc:Ignorable="d" >
    <Grid>
        <Button 
            Style="{StaticResource FlatButtonStyle}"
            ToolTip="Изменить пароль текущего пользователя"
            BorderThickness="2"
            Command="{commands:CommandProvider ChangeUserPassword}">
            <Image Margin="3" Source="{StaticResource KeyImage}" />
        </Button>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Views\ChangePasswordButton.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Assets.Views
{
    /// <summary>
    /// Interaction logic for ChangePasswordButton.xaml
    /// </summary>
    public partial class ChangePasswordButton : UserControl
    {
        public ChangePasswordButton()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Views\ChangeSettingsView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Assets.Views.ChangeSettingsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             MinWidth="490">
    <Grid>
        <Grid.Resources>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="HorizontalAlignment" Value="Left" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <Setter Property="Margin" Value="5,3,5,3" />
            </Style>
            <Style TargetType="{x:Type TextBox}">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="FontWeight" Value="Normal" />
                <Setter Property="HorizontalAlignment" Value="Stretch" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <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>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <!--Наименование оргинизации-->
        <TextBlock Grid.Row="0" Grid.Column="0" Text="Организация" />
        <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Organization, UpdateSourceTrigger=PropertyChanged}" />
        <!--Имя БД-->
        <TextBlock Grid.Row="1" Grid.Column="0" Text="Файл БД" />
        <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding DataSourceFile, UpdateSourceTrigger=PropertyChanged}" />
        <!--Порт-->
        <TextBlock Grid.Row="2" Grid.Column="0" Text="Порт"/>
        <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding PortText, UpdateSourceTrigger=PropertyChanged}" />
        <!--Общая папка приложения-->
        <TextBlock Grid.Row="3" Grid.Column="0" Text="Рабочая папка" />
        <TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding WorkFolder}" />
        <!--Сообщение об ошибке-->
        <TextBlock
            Grid.Row="4"
            Grid.ColumnSpan="2"
            Foreground="Red"
            Margin="3"
            Text="{Binding LastError}"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Views\ChangeSettingsView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Assets.Views
{
    /// <summary>
    /// Interaction logic for ChangeSettingsView.xaml
    /// </summary>
    public partial class ChangeSettingsView : UserControl
    {
        public ChangeSettingsView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Views\LockScreenView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Assets.Views.LockScreenView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
             xmlns:customBehaviors="clr-namespace:Updk7.Wpf.Behaviors;assembly=Updk7.Wpf"
             mc:Ignorable="d" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             xmlns:commands="clr-namespace:Updk7.Wpf.Commands;assembly=Updk7.Wpf"
             d:DesignHeight="450"
             d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!--Текущий пользователь-->
        <DockPanel
            Grid.Row="0"
            VerticalAlignment="Center"
            HorizontalAlignment="Center">
            <TextBlock 
                DockPanel.Dock="Top"
                HorizontalAlignment="Center"
                Text="Текущий пользователь"
                FontSize="24"
                FontWeight="Medium"
                Margin="5"
                />
            <!--Данные пользователя-->
            <Grid
                DockPanel.Dock="Top">
                <Grid.Resources>
                    <Style x:Key="UserImageStyle" TargetType="{x:Type Image}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding UserRole}" Value="Администратор">
                                <Setter Property="Source" Value="{StaticResource AdministratorImage}" />
                            </DataTrigger>
                            <DataTrigger Binding="{Binding UserRole}" Value="Психолог">
                                <Setter Property="Source" Value="{StaticResource UserImage}" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Grid.Resources>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <!--Иконка пользователя-->
                <Image 
                    Grid.Column="0"
                    Style="{StaticResource UserImageStyle}"
                    Margin="3"
                    Width="48"
                    Height="48"
                    />
                <!--Пользователя-->
                <Grid
                    Grid.Column="1">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <!--Данные профиля-->
                    <TextBlock 
                        Grid.Row="0"
                        Grid.Column="0"
                        Grid.ColumnSpan="2"
                        FontSize="16"
                        FontWeight="Bold"
                        VerticalAlignment="Center">
                        <Run Text="{Binding UserName, Mode=OneWay}" />
                        <LineBreak />
                        <Run 
                            FontSize="14"
                            FontWeight="Light"
                            Text="{Binding UserRole, Mode=OneWay}" 
                            />
                    </TextBlock>
                    <!--Пароль-->
                    <TextBlock
                        Grid.Row="1"
                        Grid.Column="0"
                        Text="Пароль:"
                        FontWeight="Medium"
                        Margin="0,5,5,0"
                        VerticalAlignment="Center"
                        />
                    <PasswordBox 
                        Grid.Row="1"
                        Grid.Column="1"
                        Margin="0,5,5,0"
                        PasswordChar="*"
                        Height="20"
                        Width="140">
                        <PasswordBox.InputBindings>
                            <KeyBinding 
                                Key="Enter"
                                Command="{Binding DataContext.AcceptUserCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                                />
                        </PasswordBox.InputBindings>
                        <i:Interaction.Behaviors>
                            <customBehaviors:PasswordBoxBehavior Password="{Binding EnteredPassword, Mode=TwoWay}" />
                        </i:Interaction.Behaviors>
                    </PasswordBox>
                </Grid>
            </Grid>
            <!--Сообщение об ошибке-->
            <TextBlock 
                DockPanel.Dock="Bottom"
                Text="{Binding LoginError, Mode=OneWay}"
                Foreground="Red"
                FontSize="16"
                FontWeight="Medium"
                HorizontalAlignment="Center"
                />
        </DockPanel>
        <!--Кнопки управления-->
        <Grid
            Grid.Row="1">
            <Button
                Margin="10,0,0,10"
                Height="45"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Выход из УПДК"
                BorderThickness="2"
                HorizontalAlignment="Left"
                Command="{commands:CommandProvider Shutdown}">
                <Image Margin="3" Source="{StaticResource ShutdownImage}" />
            </Button>
            <Button 
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Style="{StaticResource FlatButtonStyle}"
                Height="32"
                Width="130"
                FontSize="16"
                FontWeight="Bold"
                Content="Вход"
                Command="{Binding AcceptUserCommand}"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Views\LockScreenView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Assets.Views
{
    /// <summary>
    /// Interaction logic for LockScreenView.xaml
    /// </summary>
    public partial class LockScreenView : UserControl
    {
        public LockScreenView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Views\ShutdownButton.xaml

<UserControl x:Class="Updk7.Wpf.Master.Assets.Views.ShutdownButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Assets.Views"
             xmlns:commands="clr-namespace:Updk7.Wpf.Commands;assembly=Updk7.Wpf"
             mc:Ignorable="d">
    <Grid>
        <ToggleButton
            x:Name="PART_Button"
            BorderThickness="2"
            ToolTip="Сменить пользователя или завершить работу программы"
            Style="{StaticResource FlatToggleButtonStyle}">
            <Image Margin="3" Source="{StaticResource ShutdownImage}" />
        </ToggleButton>
        <Popup
            IsOpen="{Binding IsChecked, ElementName=PART_Button}"
            StaysOpen="False"
            Placement="Right"
            HorizontalOffset="10"
            VerticalOffset="-30"
            AllowsTransparency="True">
            <Border
                Background="{StaticResource WindowBackground}"
                BorderBrush="Black"
                BorderThickness="1">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.Resources>
                        <Style x:Key="ActionButtonStyle" TargetType="{x:Type Button}">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type Button}">
                                        <Border
                                            x:Name="PART_Border"
                                            SnapsToDevicePixels="True"
                                            BorderBrush="Transparent"
                                            BorderThickness="1"
                                            Padding="{TemplateBinding Padding}">
                                            <ContentPresenter 
                                                VerticalAlignment="Center"
                                                HorizontalAlignment="Center"
                                                />
                                        </Border>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsMouseOver" Value="True">
                                                <Setter TargetName="PART_Border" Property="Background" Value="Gray" />
                                                <Setter TargetName="PART_Border" Property="BorderBrush" Value="Black" />
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </Grid.Resources>
                    <Button
                        Grid.Row="0"
                        Style="{StaticResource ActionButtonStyle}"
                        Margin="2"
                        Padding="5"
                        FontSize="14"
                        FontWeight="Medium"
                        Content="Экран блокировки"
                        Command="{commands:CommandProvider ShowLockScreen}"
                        />
                    <Button 
                        Grid.Row="1"
                        Style="{StaticResource ActionButtonStyle}"
                        Margin="2"
                        Padding="5"
                        FontSize="14"
                        FontWeight="Medium"
                        Content="Сменить пользователя"
                        Command="{commands:CommandProvider ChangeUser}"
                        />
                    <Button 
                        Grid.Row="2"
                        Style="{StaticResource ActionButtonStyle}"
                        Margin="2"
                        Padding="5"
                        FontSize="14"
                        FontWeight="Medium"
                        Content="Выход"
                        Command="{commands:CommandProvider Shutdown}"
                        />
                </Grid>
            </Border>
        </Popup>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Views\ShutdownButton.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Assets.Views
{
    /// <summary>
    /// Interaction logic for ShutdownButton.xaml
    /// </summary>
    public partial class ShutdownButton : UserControl
    {
        public ShutdownButton()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Views\UserPasswordEditorView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Assets.Views.UserPasswordEditorView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
             xmlns:behaviors="clr-namespace:Updk7.Wpf.Behaviors;assembly=Updk7.Wpf"
             mc:Ignorable="d">
    <Grid>
        <Grid.Resources>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="HorizontalAlignment" Value="Left" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <Setter Property="Margin" Value="5,3,5,3" />
            </Style>
            <Style TargetType="{x:Type PasswordBox}">
                <Setter Property="HorizontalAlignment" Value="Stretch" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <Setter Property="PasswordChar" Value="*" />
                <Setter Property="Height" Value="20" />
            </Style>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <!--Пароль пользователя-->
        <TextBlock 
            Grid.Row="0" 
            Grid.Column="0" 
            Text="Пароль пользователя" 
            />
        <PasswordBox 
            Grid.Row="0"
            Grid.Column="1">
            <i:Interaction.Behaviors>
                <behaviors:PasswordBoxBehavior Password="{Binding UserPassword, Mode=TwoWay}" />
            </i:Interaction.Behaviors>
        </PasswordBox>
        <!--Новый пароль-->
        <TextBlock
            Grid.Row="1" 
            Grid.Column="0" 
            Text="Новый пароль"
            />
        <PasswordBox 
            Grid.Row="1"
            Grid.Column="1">
            <i:Interaction.Behaviors>
                <behaviors:PasswordBoxBehavior Password="{Binding NewPassword, Mode=TwoWay}" />
            </i:Interaction.Behaviors>
        </PasswordBox>
        <!--Повторный ввод пароля-->
        <TextBlock 
            Grid.Row="2" 
            Grid.Column="0" 
            Text="Повторите новый пароль"
            />
        <PasswordBox 
            Grid.Row="2"
            Grid.Column="1">
            <i:Interaction.Behaviors>
                <behaviors:PasswordBoxBehavior Password="{Binding ConfirmNewPassword, Mode=TwoWay}" />
            </i:Interaction.Behaviors>
        </PasswordBox>
        <!--Строка с сообщением об ошибке-->
        <TextBlock 
            Grid.Row="4"
            Grid.ColumnSpan="2"
            Text="{Binding LastError, Mode=OneWay}"
            Foreground="Red"
            FontSize="14"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Assets\Source\Views\UserPasswordEditorView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Assets.Views
{
    /// <summary>
    /// Interaction logic for UserPasswordEditorView.xaml
    /// </summary>
    public partial class UserPasswordEditorView : UserControl
    {
        public UserPasswordEditorView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\AdministrationModule.cs


using Prism.Ioc;

namespace Updk7.Wpf.Master.Modules
{
    public class AdministrationModule : Prism.Modularity.IModule
    {
        public AdministrationModule()
        {
        }

        public void OnInitialized(IContainerProvider containerProvider)
        {
            AppLog.Current.Info("AdministrationModule.OnInitialized()");
            AppLog.Current.Info("AdministrationModule.OnInitialized() -> Complete");
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            AppLog.Current.Info("AdministrationModule.RegisterTypes()");

            registerInteractions();
            registerCommands();
            registerRegions(containerRegistry);

            AppLog.Current.Info("AdministrationModule.RegisterTypes() -> Complete");
        }

        private void registerInteractions()
        {
            Interactions.InteractionService.Current
                .Register<Administration.Interaction.ChnageUserProfileInteraction, Administration.Interaction.Views.ChangeUserProfileView>();
        }

        private void registerCommands()
        {
            Commands.CommandManager.Current
                .Register<Administration.Commands.AddUserProfileCommand>("AddUserProfile")
                .Register<Administration.Commands.ChangeUserProfileCommand>("ChangeUserProfile")
                .Register<Administration.Commands.ChangeUserStatusCommand>("ChangeUserStatus")
                .Register<Administration.Commands.ChangeSettingsCommand>("ChangeSettings");
        }

        private void registerRegions(IContainerRegistry containerRegistry)
        {
            var Region = Assets.Shell.RegionKeys.AdministratorTabRegion;
            
            containerRegistry.RegisterSingleton<Administration.IUserSelection, Administration.ViewModels.UserSelectionViewModel>();
            ViewModelLocator.Current
                .Register<Administration.IUserSelection, Administration.Views.UserSelectionView>(Region);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\CompatibilityModule.cs


using Prism.Ioc;

namespace Updk7.Wpf.Master.Modules
{
    public class CompatibilityModule : Prism.Modularity.IModule
    {
        private AppManager _appManager;
        private Assets.MasterConfiguration _masterConfiguration;

        public CompatibilityModule(AppManager appManager, Assets.MasterConfiguration masterConfiguration)
        {
            _appManager = appManager;
            _masterConfiguration = masterConfiguration;
        }

        public void OnInitialized(IContainerProvider containerProvider)
        {
            AppLog.Current.Info("CompatibilityModule.OnInitialized()");
            
            registerEventListeners();
            registerNavigationKey(containerProvider);
            registerToolbarButton(containerProvider);
            InitializeParametersFactory(containerProvider);
            InitializeTestingTreeHook(containerProvider);
            InitializeReportGenerator(containerProvider);

            AppLog.Current.Info("CompatibilityModule.OnInitialized() -> Complete");
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            AppLog.Current.Info("CompatibilityModule.RegisterTypes()");

            RegisterDataLayer(containerRegistry, _appManager, _masterConfiguration);
            RegisterReportGenerator(containerRegistry);
            RegisterCommands();
            RegisterRegions(containerRegistry);
            RegisterParametersGenerator(containerRegistry);

            AppLog.Current.Info("CompatibilityModule.RegisterTypes() -> Complete");
        }

        private void registerEventListeners()
        {
        }

        private void registerNavigationKey(IContainerProvider containerProvider)
        {
            ViewModelLocator.Current.RegisterNavigationKey(
                "Compatibility",
                Assets.Shell.RegionKeys.PsychoTabRegion,
                () => containerProvider.Resolve<Compatibility.ICompatibilityViewer>());
        }

        private void registerToolbarButton(IContainerProvider containerProvider)
        {
            containerProvider.Resolve<Testees.ITesteesToolbar>()
                .AddElement(new Compatibility.Views.NavigationToolbarButton());
        }

        protected virtual void RegisterCommands()
        {
            Commands.CommandManager.Current
                .Register<Compatibility.Commands.ShowCompatibilityCommand>("ShowCompatibility")
                .Register<Compatibility.Commands.LinkEmployeesCommand>("LinkEmployees")
                .Register<Compatibility.Commands.UnlinkEmployeesCommand>("UnlinkEmployees")
                .Register<Compatibility.Commands.SaveCompatibilityReportCommand>("SaveCompatibilityReportCommand");
        }

        protected virtual void RegisterParametersGenerator(IContainerRegistry containerRegistry)
        {
        }

        protected virtual void RegisterReportGenerator(IContainerRegistry containerRegistry)
        {
        }

        protected virtual void RegisterDataLayer(IContainerRegistry containerRegistry, AppManager appManager,
            Assets.MasterConfiguration masterConfiguration)
        {
        }

        protected virtual void InitializeParametersFactory(IContainerProvider containerProvider)
        {
        }

        protected virtual void InitializeTestingTreeHook(IContainerProvider containerProvider)
        {
        }

        protected virtual void InitializeReportGenerator(IContainerProvider containerProvider)
        {
        }

        protected virtual void RegisterRegions(IContainerRegistry containerRegistry)
        {
            var region = Assets.Shell.RegionKeys.PsychoTabRegion;

            containerRegistry
                .RegisterSingleton<Compatibility.ICompatibilityViewer, Compatibility.ViewModels.CompatibilityViewModel>();

            ViewModelLocator.Current
                .Register<Compatibility.ICompatibilityViewer, Compatibility.Views.CompatibilityView>(region);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetworkModule.cs


using Prism.Ioc;

namespace Updk7.Wpf.Master.Modules
{
    public class LocalNetworkModule : Prism.Modularity.IModule
    {
        public LocalNetworkModule()
        {
        }

        public void OnInitialized(IContainerProvider containerProvider)
        {
            AppLog.Current.Info("LocalNetworkModule.OnInitialized()");

            registerEventListeners();

            AppLog.Current.Info("LocalNetworkModule.OnInitialized() -> Complete");
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            AppLog.Current.Info("LocalNetworkModule.RegisterTypes()");

            registerInteractions();
            registerCommands();
            registerRegions(containerRegistry);

            AppLog.Current.Info("LocalNetworkModule.RegisterTypes() -> Complete");
        }

        private void registerInteractions()
        {
        }

        private void registerCommands()
        {
        }

        public void registerEventListeners()
        {
            EventListenersLocator.Current
                .Register<LocalNetwork.EventListeners.StartTestingListener>()
                .Register<LocalNetwork.EventListeners.ShutdownRequestListener>()
                .Register<LocalNetwork.EventListeners.WorkplacesDiscoveryListener>();
        }

        private void registerRegions(IContainerRegistry containerRegistry)
        {
            var Region = Assets.Shell.RegionKeys.PsychoTabRegion;

            containerRegistry.RegisterSingleton<LocalNetwork.IWorkplacesController, LocalNetwork.ViewModels.WorkplacesViewModel>();
            ViewModelLocator.Current
                .Register<LocalNetwork.IWorkplacesController, LocalNetwork.Views.WorkplacesView>(Region);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\ReportsModule.cs


using Prism.Ioc;

namespace Updk7.Wpf.Master.Modules
{
    public class ReportsModule : Prism.Modularity.IModule
    {
        public ReportsModule()
        {
        }

        public void OnInitialized(IContainerProvider containerProvider)
        {
            AppLog.Current.Info("ReportsModule.OnInitialized()");

            registerEventListeners();
            registerNavigationKey(containerProvider);

            if (containerProvider.IsRegistered<Tests.Reports.Wpf.ITestsGroupReportGenerator>())
            {
                AppLog.Current.Info("Register ITestsGroupReportGenerator");

                var generator = containerProvider.Resolve<Tests.Reports.Wpf.ITestsGroupReportGenerator>();
                var reportsFactory = containerProvider.Resolve<Tests.Reports.Wpf.ReportsFactory>();
                reportsFactory.TestsGroups.SetReportGenerator(generator);
            }

            AppLog.Current.Info("ReportsModule.OnInitialized() -> Complete");
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            AppLog.Current.Info("ReportsModule.RegisterTypes()");

            RegisterInteractions();
            RegisterCommands();
            registerReportsGenerators(containerRegistry);
            registerRegions(containerRegistry);

            AppLog.Current.Info("ReportsModule.RegisterTypes() -> Complete");
        }

        protected virtual void RegisterInteractions()
        {
        }

        protected virtual void RegisterCommands()
        {
        }

        private void registerReportsGenerators(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<Tests.Reports.Wpf.ReportsFactory>();
            RegisterTestGroupReportsGenerator(containerRegistry);
        }

        private void registerEventListeners()
        {
            EventListenersLocator.Current.Register<Reports.EventListeners.ShowTesteeReportsListener>();
        }

        private void registerNavigationKey(IContainerProvider containerProvider)
        {
            ViewModelLocator.Current.RegisterNavigationKey(
                "Reports",
                Assets.Shell.RegionKeys.PsychoTabRegion,
                () => containerProvider.Resolve<Reports.ViewModels.ReportsViewModel>());
        }

        private void registerRegions(IContainerRegistry containerRegistry)
        {
            var region = Assets.Shell.RegionKeys.PsychoTabRegion;

            containerRegistry
                .RegisterSingleton<Reports.ViewModels.ReportsViewModel>()
                .RegisterSingleton<Reports.IReportsController, Reports.ReportsController>();

            ViewModelLocator.Current
                .Register<Reports.ViewModels.ReportsViewModel, Reports.Views.ReportsView>(region);

            RegisterTesteeInfo(containerRegistry);
            RegisterReportsTree(containerRegistry);
            RegisterReportsBrowser(containerRegistry);
        }

        protected virtual void RegisterTestGroupReportsGenerator(IContainerRegistry containerRegistry)
        {
            // Здесь регистрируется реализация Updk7.Tests.Reports.Wpf.ITestsGroupReportGenerator
            // для генерации отчетов по группам тестов
        }

        protected virtual void RegisterTesteeInfo(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<Reports.IReportsTesteeInfo, Reports.ViewModels.ReportsTesteeInfoViewModel>();
            ViewModelLocator.Current
                .Register<Reports.IReportsTesteeInfo, Reports.Views.ReportsTesteeInfoView>(Reports.RegionKeys.ReportsTesteeInfoRegion);
        }

        protected virtual void RegisterReportsTree(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<Reports.IReportsTree, Reports.ViewModels.ReportsTreeViewModel>();
            ViewModelLocator.Current
                .Register<Reports.IReportsTree, Reports.Views.ReportsTreeView>(Reports.RegionKeys.ReportsTreeRegion);
        }

        protected virtual void RegisterReportsBrowser(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<Reports.IReportsBrowser, Reports.ViewModels.ReportsBrowserViewModel>();
            ViewModelLocator.Current
                .Register<Reports.IReportsBrowser, Reports.Views.ReportsBrowserView>(Reports.RegionKeys.ReportsBrowserRegion);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TesteesModule.cs


using Prism.Ioc;
using System.Globalization;

namespace Updk7.Wpf.Master.Modules
{
    public abstract class TesteesModule : Prism.Modularity.IModule
    {
        public TesteesModule()
        {
        }

        public void OnInitialized(IContainerProvider containerProvider)
        {
            AppLog.Current.Info("TesteesModule.OnInitialized()");

            var editorController = containerProvider.Resolve<Testees.ITesteeEditorController>();
            RegisterTesteeCommonEditor(editorController, containerProvider);
            RegisterTesteeCustomEditor(editorController, containerProvider);

            registerEventListeners();

            AppLog.Current.Info("TesteesModule.OnInitialized() -> Complete");
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            AppLog.Current.Info("TesteesModule.RegisterTypes()");

            RegisterResultsSaveStrategy(containerRegistry);
            RegisterTesteeDataUpdater(containerRegistry);

            registerInteractions();
            registerCommands();
            registerRegions(containerRegistry);

            AppLog.Current.Info("TesteesModule.RegisterTypes() -> Complete");
        }

        protected abstract void RegisterTesteeSlelectionView(IContainerRegistry containerRegistry, string regionKey);

        protected abstract void RegisterTesteeCustomEditor(Testees.ITesteeEditorController controller, IContainerProvider containerProvider);

        protected virtual void RegisterTesteeCommonEditor(Testees.ITesteeEditorController controller, IContainerProvider containerProvider)
        {
            controller.RegisterEditor(new Testees.ViewModels.TesteeCommonEditorViewModel(), new Testees.Views.TesteeCommonEditorView());
        }

        protected virtual void RegisterTesteesToolbar(IContainerRegistry containerRegistry, string regionKey)
        {
            containerRegistry.RegisterSingleton<Testees.ITesteesToolbar, Testees.ViewModels.TesteesToolbarViewModel>();
            ViewModelLocator.Current
                .Register<Testees.ITesteesToolbar, Testees.Views.TesteesToolbarView>(regionKey);
        }

        protected virtual void RegisterArchiveTesteeSelectionView(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<Testees.ITesteeArchiveSelection, Testees.ViewModels.TesteeArchiveSelectionViewModel>();
            ViewModelLocator.Current
                .Register<Testees.ITesteeArchiveSelection, Testees.Views.TesteeArchiveSelectionView>();
        }

        protected virtual void RegisterResultsSaveStrategy(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<Testees.ITestResultsSaveStrategy, Testees.TestResultsSaveStrategy>();
        }

        protected virtual void RegisterTesteeDataUpdater(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<Testees.ITesteeDataUpdater, Testees.TesteeDataUpdaterStub>();
        }

        private void registerInteractions()
        {
            Interactions.InteractionService.Current
                .Register<Testees.Interactions.ShowTesteesArchiveInteraction, Testees.Interactions.Views.ShowTesteesArchiveView>()
                .Register<Testees.Interactions.ShowTesteeEditorInteraction, Testees.Interactions.Views.ShowTesteeEditorView>()
                .Register<Testees.Interactions.FindTesteeInteraction, Testees.Interactions.Views.FindTesteeView>();
        }

        private void registerCommands()
        {
            Commands.CommandManager.Current
                .Register<Testees.Commands.AddTesteeCommand>("AddTestee")
                .Register<Testees.Commands.CreateTestingPlanCommand>("CreateTestingPlan")
                .Register<Testees.Commands.EditTesteeCommand>("EditTestee")
                .Register<Testees.Commands.RemoveTesteeToArchiveCommand>("RemoveTesteeToArchive")
                .Register<Testees.Commands.ShowArchiveCommand>("ShowArchive")
                .Register<Testees.Commands.ShowReportsCommand>("ShowReports")
                .Register<Testees.Commands.RestoreTesteeFromArchiveCommand>("RestoreTesteeFromArchive")
                .Register<Testees.Commands.LoadPhotoCommand>("LoadPhoto")
                .Register<Testees.Commands.FindTesteeCommand>("FindTestee");
        }

        private void registerEventListeners()
        {
            EventListenersLocator.Current.Register<Testees.EventListeners.TestStartStopListener>();
        }

        private void registerRegions(IContainerRegistry containerRegistry)
        {
            var region = Assets.Shell.RegionKeys.PsychoTabRegion;

            containerRegistry
                .RegisterSingleton<Testees.ViewModels.TesteesViewModel>()
                .RegisterSingleton<Testees.ITesteeEditorController, Testees.ViewModels.TesteeEditorControllerViewModel>();

            ViewModelLocator.Current
                .Register<Testees.ViewModels.TesteesViewModel, Testees.Views.TesteesView>(region)
                .Register<Testees.ITesteeEditorController, Testees.Views.TesteeEditorControllerView>();

            RegisterTesteeSlelectionView(containerRegistry, Testees.RegionKeys.TesteeSelectionRegion);
            RegisterTesteesToolbar(containerRegistry, Testees.RegionKeys.TesteesToolbarRegion);
            RegisterArchiveTesteeSelectionView(containerRegistry);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlanModule.cs


using Prism.Ioc;

namespace Updk7.Wpf.Master.Modules
{
    public class TestingPlanModule : Prism.Modularity.IModule
    {
        public TestingPlanModule()
        {
        }

        public void OnInitialized(IContainerProvider containerProvider)
        {
            AppLog.Current.Info("TestingPlanModule.OnInitialized()");

            registerEventListeners();
            registerNavigationKey(containerProvider);

            AppLog.Current.Info("TestingPlanModule.OnInitialized() -> Complete");
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            AppLog.Current.Info("TestingPlanModule.RegisterTypes()");

            registerInteractions();
            registerCommands();
            registerReportsGenerators(containerRegistry);
            registerRegions(containerRegistry);

            AppLog.Current.Info("TestingPlanModule.RegisterTypes() -> Complete");
        }

        private void registerInteractions()
        {
        }

        private void registerCommands()
        {
            Commands.CommandManager.Current
                .Register<TestingPlan.Commands.StartTestingCommand>("StartTesting");
        }

        private void registerReportsGenerators(IContainerRegistry containerRegistry)
        {
        }

        private void registerEventListeners()
        {
            EventListenersLocator.Current.Register<TestingPlan.EventListeners.CreateTestingPlanListener>();
        }

        private void registerNavigationKey(IContainerProvider containerProvider)
        {
            ViewModelLocator.Current.RegisterNavigationKey(
                "TestingPlan",
                Assets.Shell.RegionKeys.PsychoTabRegion,
                () => containerProvider.Resolve<TestingPlan.ViewModels.TestingPlanViewModel>());
        }

        private void registerRegions(IContainerRegistry containerRegistry)
        {
            var region = Assets.Shell.RegionKeys.PsychoTabRegion;

            containerRegistry
                .RegisterSingleton<TestingPlan.ViewModels.TestingPlanViewModel>()
                .RegisterSingleton<TestingPlan.ITestingPlanController, TestingPlan.TestingPlanController>()
                .RegisterSingleton<TestingPlan.ITestingPlanWorkplaces, TestingPlan.ViewModels.TestingPlanWorkplacesViewModel>();

            ViewModelLocator.Current
                .Register<TestingPlan.ITestingPlanWorkplaces, TestingPlan.Views.TestingPlanWorkplacesView>()
                .Register<TestingPlan.ViewModels.TestingPlanViewModel, TestingPlan.Views.TestingPlanView>(region);

            RegisterTesteeInfo(containerRegistry);
            RegisterTestingPlanTree(containerRegistry);
            RegisterTestsSelection(containerRegistry);
        }

        protected virtual void RegisterTesteeInfo(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<TestingPlan.ITestingPlanTesteeInfo, TestingPlan.ViewModels.TestingPlanTesteeInfoViewModel>();
            ViewModelLocator.Current
                .Register<TestingPlan.ITestingPlanTesteeInfo, TestingPlan.Views.TestingPlanTesteeInfoView>(TestingPlan.RegionKeys.TestingPlanTesteeInfoRegion);
        }

        protected virtual void RegisterTestingPlanTree(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<TestingPlan.ITestingPlanTree, TestingPlan.ViewModels.TestingPlanTreeViewModel>();
            ViewModelLocator.Current
                .Register<TestingPlan.ITestingPlanTree, TestingPlan.Views.TestingPlanTreeView>(TestingPlan.RegionKeys.TestingPlanTreeRegion);
        }

        protected virtual void RegisterTestsSelection(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<TestingPlan.ITestingPlanTestsSelection, TestingPlan.ViewModels.TestingPlanTestsSelectionViewModel>();
            ViewModelLocator.Current
                .Register<TestingPlan.ITestingPlanTestsSelection, TestingPlan.Views.TestingPlanTestsSelectionView>(TestingPlan.RegionKeys.TestingPlanTestsRegion);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\IUserSelection.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Administration
{
    public interface IUserSelection : INotifyPropertyChanged
    {
        Data.Entities.User SelectedUser { get; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\Commands\AddUserProfileCommand.cs


namespace Updk7.Wpf.Master.Modules.Administration.Commands
{
    public class AddUserProfileCommand : Wpf.Commands.CommandBase
    {
        private Data.Repositories.IUsersRepository _usersRepository;

        public AddUserProfileCommand(Data.Repositories.IUsersRepository usersRepository)
        {
            _usersRepository = usersRepository;
        }

        public override bool CanExecute(object parameter) => true;

        public override void Execute(object parameter)
        {
            AppLog.Current.Info("AddUserProfileCommand");

            var interaction = new Interaction.ChnageUserProfileInteraction();
            Interactions.InteractionService.Current.ShowInteractionModel(interaction, () =>
            {
                if (interaction.Confirmed && !interaction.InteractionData.HasErrors)
                {
                    var user = interaction.InteractionData.SourceData;
                    _usersRepository.Add(user);

                    AppLog.Current.Info($"New user has been added ({user.Id})");
                }
            });
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\Commands\ChangeSettingsCommand.cs


using System;

namespace Updk7.Wpf.Master.Modules.Administration.Commands
{
    public class ChangeSettingsCommand : Wpf.Commands.CommandBase
    {
        private Assets.MasterConfiguration _configuration;
        private AppManager _appManager;

        public ChangeSettingsCommand(Assets.MasterConfiguration configuration, AppManager appManager)
        {
            _appManager = appManager;
            _configuration = configuration;
        }

        public override bool CanExecute(object parameter) => true;

        public override void Execute(object parameter)
        {
            AppLog.Current.Info("ChangeSettingsCommand");

            var interaction = new Assets.Interactions.ChangeSettingsInteraction();
            interaction.InteractionData.SetupData(_configuration);
            Interactions.InteractionService.Current.ShowInteractionModel(interaction, () =>
            { 
                if (interaction.Confirmed && !interaction.InteractionData.HasErrors)
                {
                    interaction.InteractionData.UpdateConfiguration(_configuration);
                    try
                    {
                        _configuration.Save();
                        _appManager.WindowClosingAction = AppManager.WindowClosingActionType.ShutdownApp;
                    }
                    catch (Exception ex)
                    {
                        var message = "Не удалось сохранить файл конфигурации\n" + ex.Message;
                        Interactions.InteractionService.Current.ShowErrorMessage(message);
                        AppLog.Current.Error(message);
                    }
                }
            });
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\Commands\ChangeUserProfileCommand.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Administration.Commands
{
    public class ChangeUserProfileCommand : Wpf.Commands.CommandBase
    {
        private Data.Repositories.IUsersRepository _usersRepository;
        private IUserSelection _userSelection;

        public ChangeUserProfileCommand(IUserSelection userSelection, Data.Repositories.IUsersRepository usersRepository)
        {
            _usersRepository = usersRepository;
            _userSelection = userSelection;
            _userSelection.PropertyChanged += onUserSelectionPropertyChanged;
        }

        public override bool CanExecute(object parameter)
        {
            return _userSelection.SelectedUser != null;
        }

        public override void Execute(object parameter)
        {
            AppLog.Current.Info("ChangeUserProfileCommand");

            var interaction = new Interaction.ChnageUserProfileInteraction();
            interaction.InteractionData.SourceData = _userSelection.SelectedUser;
            Wpf.Interactions.InteractionService.Current.ShowInteractionModel(interaction, () =>
            {
                if (interaction.Confirmed && !interaction.InteractionData.HasErrors)
                {
                    var user = interaction.InteractionData.SourceData;
                    _usersRepository.Update(user);

                    AppLog.Current.Info($"User profile has been updated ({user.Id})");
                }
            });
        }

        private void onUserSelectionPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(IUserSelection.SelectedUser))
                RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\Commands\ChangeUserStatusCommand.cs


using System;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Administration.Commands
{
    public class ChangeUserStatusCommand : Wpf.Commands.CommandBase
    {
        private Assets.IAutorizationManager _autorizationManager;
        private Data.Repositories.IUsersRepository _usersRepository;
        private IUserSelection _userSelection;

        public ChangeUserStatusCommand(Assets.IAutorizationManager autorizationManager, 
            IUserSelection userSelection, Data.Repositories.IUsersRepository usersRepository)
        {
            _autorizationManager = autorizationManager;
            _usersRepository = usersRepository;
            _userSelection = userSelection;
            _userSelection.PropertyChanged += onUserSelectionPropertyChanged;
        }

        public override bool CanExecute(object parameter)
        {
            return _userSelection.SelectedUser != null
                && _userSelection.SelectedUser.Id != _autorizationManager.AutorizedUser.Id;
        }

        public override void Execute(object parameter)
        {
            var entitie = _userSelection.SelectedUser;
            entitie.Removed ^= true;
            if (entitie.Removed)
                entitie.RemoveDate = DateTime.Now;
            else
                entitie.RemoveDate = null;

            _usersRepository.Update(entitie);

            AppLog.Current.Info($"User status has been changed ({entitie.Id}, removed: {entitie.Removed})");
        }

        private void onUserSelectionPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(IUserSelection.SelectedUser))
                RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\Interaction\ChnageUserProfileInteraction.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Administration.Interaction
{
    public class ChnageUserProfileInteraction : Wpf.Interactions.ConfirmationInteraction
    {
        public ChnageUserProfileInteraction()
        {
            InteractionData.PropertyChanged += onInteractionDataChanged;
        }

        public ViewModels.UserProfileEditorViewModel InteractionData { get; } =
            new ViewModels.UserProfileEditorViewModel();

        protected override bool CanConfirm() => !InteractionData.HasErrors;

        private void onInteractionDataChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(InteractionData.HasErrors))
                AcceptCommand.RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\Interaction\Views\ChangeUserProfileView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Administration.Interaction.Views.ChangeUserProfileView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             xmlns:views="clr-namespace:Updk7.Wpf.Master.Modules.Administration.Views"
             Width="500">
    <Grid
        Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!--Заголовок панели-->
        <Grid>
            <Grid.Resources>
                <Style x:Key="HeaderImageStyle" TargetType="{x:Type Image}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding InteractionData.EditorMode}" Value="Create">
                            <Setter Property="Source" Value="{StaticResource BlackPlusImage}" />
                        </DataTrigger>
                        <DataTrigger Binding="{Binding InteractionData.EditorMode}" Value="Edit">
                            <Setter Property="Source" Value="{StaticResource BlackPencilImage}" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
                <Style x:Key="HeaderTextBlockStyle" TargetType="{x:Type TextBlock}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding InteractionData.EditorMode}" Value="Create">
                            <Setter Property="Text" Value="Добавление пользователя" />
                        </DataTrigger>
                        <DataTrigger Binding="{Binding InteractionData.EditorMode}" Value="Edit">
                            <Setter Property="Text" Value="Редактирование данных пользователя" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Grid.Resources>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="40" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Image
                Grid.Column="0" 
                Margin="5"
                Style="{StaticResource HeaderImageStyle}" />
            <TextBlock 
                Grid.Column="1"
                FontSize="18"
                FontWeight="Bold"
                Margin="5,1,1,1"
                VerticalAlignment="Center"
                HorizontalAlignment="Left"
                Style="{StaticResource HeaderTextBlockStyle}"
                />
        </Grid>
        <!--Редактор данных пользователя-->
        <views:UserProfileEditorView 
            Grid.Row="1"
            DataContext="{Binding InteractionData}"
            />
        <!--Кнопки управления-->
        <Grid
            Grid.Row="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <!--Кнопка Применить-->
            <Button 
                Grid.Column="1"
                Width="80"
                Height="30"
                HorizontalAlignment="Right"
                FontSize="14"
                FontWeight="Bold"
                Content="Принять"
                Command="{Binding AcceptCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
            <!--Кнопка Отменить-->
            <Button 
                Grid.Column="2"
                Width="80"
                Height="30"
                FontSize="14"
                FontWeight="Bold"
                HorizontalAlignment="Right"
                Content="Отменить"
                Command="{Binding CancelCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\Interaction\Views\ChangeUserProfileView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Administration.Interaction.Views
{
    /// <summary>
    /// Interaction logic for ChangeUserProfileView.xaml
    /// </summary>
    public partial class ChangeUserProfileView : UserControl
    {
        public ChangeUserProfileView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\ViewModels\UserProfileEditorViewModel.cs


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Updk7.Wpf.Master.Modules.Administration.ViewModels
{
    public class UserProfileEditorViewModel : Validation.ValidatedBindableBase
    {
        private class Strings
        {
            public static readonly string ErrorLastName = "Не задана фамилия пользователя";
            public static readonly string ErrorFirstName = "Не задано имя пользователя";
            public static readonly string ErrorPassword = "Не задан пароль пользователя";
            public static readonly string ErrorConfirmPassword = "Введенный пароль не подтвержден повторным вводом";
        }

        private bool _hasName = true;

        public UserProfileEditorViewModel()
        {
            setupValidationRules();
        }

        private string _lastName = string.Empty;

        public string LastName
        {
            get { return _lastName; }
            set { SetProperty(ref _lastName, value); }
        }

        private string _firstName = string.Empty;

        public string FirstName
        {
            get { return _firstName; }
            set { SetProperty(ref _firstName, value); }
        }

        private string _patronymic = string.Empty;

        public string Patronymic
        {
            get { return _patronymic; }
            set { SetProperty(ref _patronymic, value); }
        }

        private string _role = Data.Entities.UserRoles.Psycho;

        public string Role
        {
            get { return _role; }
            set { SetProperty(ref _role, value); }
        }

        private string _enteredPassword = string.Empty;

        public string EnteredPassword
        {
            get { return _enteredPassword; }
            set { SetProperty(ref _enteredPassword, value); }
        }

        private string _confirmPasswrod = string.Empty;

        public string ConfirmPassword
        {
            get { return _confirmPasswrod; }
            set { SetProperty(ref _confirmPasswrod, value); }
        }

        private EditorMode _editorMode = EditorMode.Create;

        public EditorMode EditorMode
        {
            get { return _editorMode; }
            private set { SetProperty(ref _editorMode, value); }
        }

        private Data.Entities.User _sourceData;

        public Data.Entities.User SourceData
        {
            get
            {
                if (_sourceData == null)
                    _sourceData = new Data.Entities.User();
                updateEntitie(_sourceData);
                return _sourceData;
            }
            set
            {
                EditorMode = value != null ? EditorMode.Edit : EditorMode.Create;
                _sourceData = value;
                setUserData(_sourceData);
            }
        }

        private void updateEntitie(Data.Entities.User entitie)
        {
            entitie.FullName = $"{LastName} {FirstName} {Patronymic}".Trim();
            if (EditorMode == EditorMode.Create)
            {
                entitie.RegistrationDate = DateTime.Now;
                entitie.Role = _role;
                entitie.Password = _enteredPassword;
            }
        }

        private void setUserData(Data.Entities.User sourceData)
        {
            if (sourceData == null)
            {
                LastName = FirstName = Patronymic = EnteredPassword = ConfirmPassword = string.Empty;
                Role = Data.Entities.UserRoles.Psycho;
            }
            else
            {
                var nameParts = sourceData.FullName.Split(' ');
                LastName = nameParts[0];
                FirstName = nameParts.Length > 1 ? nameParts[1] : string.Empty;
                Patronymic = nameParts.Length == 3 ? nameParts[2] : string.Empty;
                Role = sourceData.Role;
                EnteredPassword = ConfirmPassword = sourceData.Password;

                _hasName = nameParts.Length > 1;
            }

            ErrorsContainer.UpdateValidation();
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ConfirmPassword))
                ErrorsContainer.UpdatePropertyValidation(nameof(EnteredPassword));

            base.OnPropertyChanged(e);
        }

        private void setupValidationRules()
        {
            Func<bool> isPasswordsEqual = () =>
                _enteredPassword == _confirmPasswrod;

            Func<bool> _checkName = () =>
            {
                return EditorMode == EditorMode.Edit && _hasName
                    ? !string.IsNullOrEmpty(FirstName)
                    : true;
            };

            ErrorsContainer.AddValidationRule(
                nameof(LastName),
                () => !string.IsNullOrEmpty(LastName),
                Strings.ErrorLastName);

            ErrorsContainer.AddValidationRule(
                nameof(FirstName),
                _checkName,
                Strings.ErrorFirstName);

            ErrorsContainer.AddValidationRule(
                nameof(EnteredPassword),
                () => !string.IsNullOrEmpty(EnteredPassword),
                Strings.ErrorPassword);

            ErrorsContainer.AddValidationRule(
                nameof(EnteredPassword),
                isPasswordsEqual,
                Strings.ErrorConfirmPassword);

            ErrorsContainer.UpdateValidation();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\ViewModels\UserSelectionInfoViewModel.cs


using System;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Administration.ViewModels
{
    public class UserSelectionInfoViewModel : Prism.Mvvm.BindableBase
    {
        public UserSelectionInfoViewModel()
        {
        }

        public UserSelectionInfoViewModel(Data.Entities.User entitie)
        {
            Update(entitie);
        }

        private Data.Entities.User _sourceData;

        public Data.Entities.User SourceData
        {
            get { return _sourceData; }
            private set { SetProperty(ref _sourceData, value); }
        } 

        private string _fullName;

        public string FullName
        {
            get { return _fullName; }
            private set { SetProperty(ref _fullName, value); }
        }

        private string _role;

        public string Role
        {
            get { return _role; }
            private set { SetProperty(ref _role, value); }
        }

        private bool _removed;

        public bool Removed
        {
            get { return _removed; }
            private set { SetProperty(ref _removed, value); }
        }

        private DateTime _registrationDate;

        public DateTime RegistrationDate
        {
            get { return _registrationDate; }
            private set { SetProperty(ref _registrationDate, value); }
        }

        private DateTime? _removeDate;

        public DateTime? RemoveDate
        {
            get { return _removeDate; }
            set { SetProperty(ref _removeDate, value); }
        } 

        public void Update(Data.Entities.User entitie)
        {
            FullName = entitie.FullName;
            Role = entitie.Role;
            Removed = entitie.Removed;
            RegistrationDate = entitie.RegistrationDate;
            RemoveDate = entitie.RemoveDate;
            SourceData = entitie;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\ViewModels\UserSelectionViewModel.cs


using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;

namespace Updk7.Wpf.Master.Modules.Administration.ViewModels
{
    public class UserSelectionViewModel : Prism.Mvvm.BindableBase, IUserSelection
    {
        private Data.Repositories.IUsersRepository _usersRepository;

        public UserSelectionViewModel(Data.Repositories.IUsersRepository usersRepository)
        {
            _usersRepository = usersRepository;
            _usersRepository.DataChanged += onUsersRepositoryDataChanged;
            setupUsersInfo();
        }

        public ObservableCollection<UserSelectionInfoViewModel> UsersInfo { get; } = 
            new ObservableCollection<UserSelectionInfoViewModel>();

        private UserSelectionInfoViewModel _selectedUserInfo;

        public UserSelectionInfoViewModel SelectedUserInfo
        {
            get { return _selectedUserInfo; }
            set { SetProperty(ref _selectedUserInfo, value); }
        } 

        private Data.Entities.User _selectedUser;

        public Data.Entities.User SelectedUser
        {
            get { return _selectedUser; }
            set { SetProperty(ref _selectedUser, value); }
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(SelectedUserInfo))
                SelectedUser = SelectedUserInfo?.SourceData;

            base.OnPropertyChanged(args);
        }

        private void onUsersRepositoryDataChanged(object sender, Data.Repositories.RepositoryDataChangedEventArgs e)
        {
            var entitie = e.Entity as Data.Entities.User;
            Debug.Assert(entitie != null);

            if (e.Operation == Data.Repositories.RepositoryDataChangedOperation.Create)
                UsersInfo.Add(new UserSelectionInfoViewModel(entitie));
            else if (e.Operation == Data.Repositories.RepositoryDataChangedOperation.Update)
            {
                var info = UsersInfo.First(u => u.SourceData.Id == entitie.Id);
                info.Update(entitie);
            }
            else
                Debug.Assert(false);
        }

        private void setupUsersInfo()
        {
            foreach (var user in _usersRepository.Get())
                UsersInfo.Add(new UserSelectionInfoViewModel(user));
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\Views\UserProfileEditorView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Administration.Views.UserProfileEditorView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:entities="clr-namespace:Updk7.Data.Entities;assembly=Updk7.Data"
             xmlns:behaviors="clr-namespace:Updk7.Wpf.Behaviors;assembly=Updk7.Wpf"
             xmlns:converters="clr-namespace:Updk7.Wpf.Converters;assembly=Updk7.Wpf"
             mc:Ignorable="d" >
    <UserControl.Resources>
        <Style x:Key="GridStyle" TargetType="{x:Type Grid}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding EditorMode}" Value="Edit">
                    <Setter Property="Visibility" Value="Collapsed" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </UserControl.Resources>
    <Grid
        Grid.IsSharedSizeScope="True">
        <Grid.Resources>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="HorizontalAlignment" Value="Left" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <Setter Property="Margin" Value="5,3,5,3" />
            </Style>
            <Style TargetType="{x:Type TextBox}">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="FontWeight" Value="Normal" />
                <Setter Property="HorizontalAlignment" Value="Stretch" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <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>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!--Ф.И.О.-->
        <Grid 
            Grid.Row="0">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" SharedSizeGroup="A" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <!--Фамилия-->
            <TextBlock Grid.Column="0" Grid.Row="0" Text="Фамилия" />
            <TextBox Grid.Column="1" Grid.Row="0" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" />
            <!--Имя-->
            <TextBlock Grid.Column="0" Grid.Row="1" Text="Имя" />
            <TextBox Grid.Column="1" Grid.Row="1" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" />
            <!--Отчество-->
            <TextBlock Grid.Column="0" Grid.Row="2" Text="Отчество" />
            <TextBox Grid.Column="1" Grid.Row="2" Text="{Binding Patronymic, UpdateSourceTrigger=PropertyChanged}" />
        </Grid>
        <!--Должность и пароль-->
        <Grid
            Grid.Row="1"
            Style="{StaticResource GridStyle}">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" SharedSizeGroup="A" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <!--Должность-->
            <TextBlock Grid.Column="0" Grid.Row="0" Text="Должность" />
            <ComboBox
                Grid.Column="1"
                Grid.Row="0"
                ItemsSource="{converters:KeyToStringItemsSource KeysSource={x:Static entities:UserRoles.AllRoles}}"
                SelectedItem="{Binding Role, Converter={converters:KeyToStringConverter KeysSource={x:Static entities:UserRoles.AllRoles}}}"
                IsSynchronizedWithCurrentItem="True"
                VerticalAlignment="Center"
                />
            <!--Пароль-->
            <TextBlock Grid.Column="0" Grid.Row="1" Text="Пароль" />
            <PasswordBox 
            Grid.Row="1"
            Grid.Column="1"
            PasswordChar="*"
            Height="20">
                <i:Interaction.Behaviors>
                    <behaviors:PasswordBoxBehavior Password="{Binding EnteredPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                </i:Interaction.Behaviors>
            </PasswordBox>
            <!--Повторный ввод пароля-->
            <TextBlock Grid.Column="0" Grid.Row="2" Text="Повторите пароль" />
            <PasswordBox 
            Grid.Row="2"
            Grid.Column="1"
            PasswordChar="*"
            Height="20">
                <i:Interaction.Behaviors>
                    <behaviors:PasswordBoxBehavior Password="{Binding ConfirmPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                </i:Interaction.Behaviors>
            </PasswordBox>
        </Grid>
        <!--Сообщение об ошибке-->
        <TextBlock
            Grid.Row="3"
            Foreground="Red"
            Margin="3"
            Text="{Binding LastError}"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\Views\UserProfileEditorView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Administration.Views
{
    /// <summary>
    /// Interaction logic for UserProfileEditorView.xaml
    /// </summary>
    public partial class UserProfileEditorView : UserControl
    {
        public UserProfileEditorView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\Views\UserSelectionToolbarView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Administration.Views.UserSelectionToolbarView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.Administration.Views"
             mc:Ignorable="d" 
             xmlns:commands="clr-namespace:Updk7.Wpf.Commands;assembly=Updk7.Wpf"
             xmlns:assets="clr-namespace:Updk7.Wpf.Master.Assets.Views;assembly=Updk7.Wpf.Master.Assets">
    <DockPanel
        Margin="0,0,8,0">
        <StackPanel
            DockPanel.Dock="Top"
            VerticalAlignment="Top">
            <Button
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Добавление профиля пользователя"
                Margin="0,1,0,5"
                BorderThickness="2"
                Command="{commands:CommandProvider AddUserProfile}">
                <Image Margin="3" Source="{StaticResource BlackPlusImage}" />
            </Button>
            <Button
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Редактирование профиля пользователя"
                Margin="0,0,0,5"
                BorderThickness="2"
                Command="{commands:CommandProvider ChangeUserProfile}">
                <Image Margin="3" Source="{StaticResource BlackPencilImage}" />
            </Button>
            <Button
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Блокировать/разблокировать пользователя"
                Margin="0,0,0,5"
                BorderThickness="2"
                Command="{commands:CommandProvider ChangeUserStatus}">
                <Image Margin="3" Source="{StaticResource LinkImage}" />
            </Button>
        </StackPanel>
        <assets:ShutdownButton 
            VerticalAlignment="Bottom"
            DockPanel.Dock="Bottom"
            Margin="0,5,0,0"
            />
        <assets:ChangePasswordButton 
            VerticalAlignment="Bottom"
            DockPanel.Dock="Bottom"
            />
        <Button
            Style="{StaticResource FlatButtonStyle}"
            ToolTip="Изменить настройки приложения"
            Margin="0,0,0,5"
            BorderThickness="2"
            VerticalAlignment="Bottom"
            Command="{commands:CommandProvider ChangeSettings}">
            <Image Margin="3" Source="{StaticResource SettingsImage}" />
        </Button>
    </DockPanel>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\Views\UserSelectionToolbarView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Administration.Views
{
    /// <summary>
    /// Interaction logic for UserSelectionToolbarView.xaml
    /// </summary>
    public partial class UserSelectionToolbarView : UserControl
    {
        public UserSelectionToolbarView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\Views\UserSelectionView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Administration.Views.UserSelectionView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.Administration.Views"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             xmlns:styles="clr-namespace:Updk7.Wpf.Styles;assembly=Updk7.Wpf"
             xmlns:converters="clr-namespace:Updk7.Wpf.Converters;assembly=Updk7.Wpf"
             styles:TabItemHeader.Text="Пользователи"
             mc:Ignorable="d" >
    <Grid
        Margin="2,5,1,1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <!--Панель инструментов-->
        <local:UserSelectionToolbarView
            Grid.Column="0"
            />
        <!--Таблица пользователей-->
        <DataGrid
            Grid.Column="1"
            Margin="0,0,0,5"
            Style="{StaticResource FlatDataGridStyle}"
            ItemsSource="{Binding UsersInfo}"
            IsSynchronizedWithCurrentItem="True"
            SelectedItem="{Binding SelectedUserInfo}"
            ColumnWidth="*">
            <DataGrid.Resources>
                <Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource DataGridTextColumnElementStyle}" />
                <Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource DataGridCellStyle}" />
            </DataGrid.Resources>
            <DataGrid.Columns>
                <!--Ф.И.О.-->
                <DataGridTextColumn
                    Header="Ф.И.О."
                    IsReadOnly="True"
                    Binding="{Binding FullName, Mode=OneWay}"
                    FontSize="14"
                    FontWeight="Medium"
                    />
                <!--Роль пользователя-->
                <DataGridTextColumn 
                    Header="Роль"
                    IsReadOnly="True"
                    Binding="{Binding Role, Mode=OneWay, Converter={converters:KeyToStringConverter}}"
                    Width="130"
                    />
                <!--Состояние-->
                <DataGridTextColumn 
                    Header="Состояние"
                    IsReadOnly="True"
                    Binding="{Binding Removed, Mode=OneWay, Converter={converters:BoolToStringConverter TrueString=Заблокирован, FalseString=Активен}}"
                    Width="130"
                    />
                <!--Дата регистрации-->
                <DataGridTextColumn 
                    Header="Дата регистрации"
                    IsReadOnly="True"
                    Binding="{Binding RegistrationDate, StringFormat=\{0:yyyy.MM.dd\}, Mode=OneWay}"
                    Width="Auto"
                    />
                <!--Дата блокировки-->
                <DataGridTextColumn 
                    Header="Дата блокировки"
                    IsReadOnly="True"
                    Binding="{Binding RemoveDate, StringFormat=\{0:yyyy.MM.dd\}, Mode=OneWay}"
                    Width="Auto"
                    />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Administration\Views\UserSelectionView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Administration.Views
{
    /// <summary>
    /// Interaction logic for UserSelectionView.xaml
    /// </summary>
    public partial class UserSelectionView : UserControl
    {
        public UserSelectionView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\CompatibilityComparisionData.cs


namespace Updk7.Wpf.Master.Modules.Compatibility
{
    public class CompatibilityComparisionData
    {
        public CompatibilityComparisionData()
        {
        }

        public CompatibilityGroupResults Testee { get; set; }

        public CompatibilityGroupResults[] Candidates { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\CompatibilityFactors.cs


using System;
using System.Linq;

namespace Updk7.Wpf.Master.Modules.Compatibility
{
    internal class CompatibilityFactors
    {
        public class LearyFactor
        {
            private class LearyKeys
            {
                public static readonly string Factor1 = "Фактор \"Авторитарный\"";
                public static readonly string Factor2 = "Фактор \"Независимо - доминирующий\"";
                public static readonly string Factor3 = "Фактор \"Агрессивный\"";
                public static readonly string Factor4 = "Фактор \"Недоверчивый - скептический\"";
                public static readonly string Factor5 = "Фактор \"Покорно - застенчивый\"";
                public static readonly string Factor6 = "Фактор \"Зависимый\"";
                public static readonly string Factor7 = "Фактор \"Сотрудничающий\"";
                public static readonly string Factor8 = "Фактор \"Альтруистический\"";
            }

            public static bool CheckCompatibility(CompatibilityGroupResults results1, CompatibilityGroupResults results2)
            {
                if (results1.Leary == null || results2.Leary == null)
                    return false;

                try
                {
                    var factor1 = getLearyPersonalFactor(results1.Leary);
                    var factor2 = getLearyPersonalFactor(results2.Leary);

                    if (factor1 == 0 || factor2 == 0)
                        return true;

                    if (factor1 == factor2)
                        return false;

                    if ((factor1 == 1 && factor2 == 3) || (factor1 == 3 && factor2 == 1))
                        return false;

                    if ((factor1 == 2 && factor2 == 3) || (factor1 == 3 && factor2 == 2))
                        return false;

                    return true;
                }
                catch (Exception)
                {
                    return false;
                }
            }

            private static int getLearyPersonalFactor(Tests.TestResults learyResults)
            {
                var factor1 = learyResults.Values.FirstOrDefault(r => r.Key == LearyKeys.Factor1).Int;
                var factor2 = learyResults.Values.FirstOrDefault(r => r.Key == LearyKeys.Factor2).Int;
                var factor3 = learyResults.Values.FirstOrDefault(r => r.Key == LearyKeys.Factor3).Int;
                var factor4 = learyResults.Values.FirstOrDefault(r => r.Key == LearyKeys.Factor4).Int;
                var factor5 = learyResults.Values.FirstOrDefault(r => r.Key == LearyKeys.Factor5).Int;
                var factor6 = learyResults.Values.FirstOrDefault(r => r.Key == LearyKeys.Factor6).Int;
                var factor7 = learyResults.Values.FirstOrDefault(r => r.Key == LearyKeys.Factor7).Int;
                var factor8 = learyResults.Values.FirstOrDefault(r => r.Key == LearyKeys.Factor8).Int;

                Func<double, bool> isLead1 = octant =>
                {
                    var d1 = (octant - factor5) >= 4;
                    var d2 = (octant - factor6) >= 4;
                    var d3 = (octant - factor7) >= 4;
                    var d4 = (octant - factor8) >= 4;
                    return (octant >= 9) && d1 && d2 && d3 && d4;
                };

                if (isLead1(factor1) || isLead1(factor2) || isLead1(factor3) || isLead1(factor4))
                    return 1;

                Func<double, bool> isLead2 = octant =>
                {
                    var d1 = (octant - factor1) >= 4;
                    var d2 = (octant - factor2) >= 4;
                    var d3 = (octant - factor3) >= 4;
                    var d4 = (octant - factor4) >= 4;
                    return (octant >= 6) && d1 && d2 && d3 && d4;
                };

                if (isLead2(factor5) || isLead2(factor6) || isLead2(factor7))
                    return 2;

                var sum = factor1 + factor2 + factor3 + factor4 + factor5 + factor6 + factor7 + factor8;
                if (sum >= 84)
                    return 3;

                return 0;
            }
        }

        public class ExpirienceFactor
        {
            public static bool CheckCompatibility(CompatibilityGroupResults results1, CompatibilityGroupResults results2)
            {
                
                if (results1.Testee.EmploymentDate.HasValue && results2.Testee.EmploymentDate.HasValue)
                {
                    return ((DateTime.Now - results1.Testee.EmploymentDate.Value).TotalDays > 365)
                        || ((DateTime.Now - results2.Testee.EmploymentDate.Value).TotalDays > 365);
                }
                
                return false;
            }
        }

        public class ColumnFactor
        {
            public static bool CheckCompatibility(CompatibilityGroupResults results1, CompatibilityGroupResults results2)
            {
                return results1.Testee.Column == results2.Testee.Column;
            }
        }

        public class PollFactor
        {
            public static bool CheckCompatibility(CompatibilityGroupResults results1, CompatibilityGroupResults results2)
            {
                if (results1.Poll == null || results2.Poll == null)
                    return false;

                var testeeId1 = results1.Testee.Id;
                var testeeId2 = results2.Testee.Id;
                var answer1 = results1.Poll.FirstOrDefault(a => a.TesteeId == testeeId2);
                var answer2 = results2.Poll.FirstOrDefault(a => a.TesteeId == testeeId1);

                if (answer1 == null || answer2 == null)
                    return false;

                if (answer1.AnswerValue == Tests.ResultParsers.Compatibility.PollResult.Disagree ||
                    answer2.AnswerValue == Tests.ResultParsers.Compatibility.PollResult.Disagree)
                {
                    return false;
                }

                return true;
            }
        }

        public class PvkFactor
        {
            public static bool CheckCompatibility(CompatibilityGroupResults results1, CompatibilityGroupResults results2)
            {
                var testee1 = results1.Testee;
                var testee2 = results2.Testee;

                if (testee1.PVKGroup.HasValue && testee2.PVKGroup.HasValue)
                {
                    // Группа ПВК получена, уязвимых качеств нет у одного или обоих испытуемых -> совместимы по ПВК
                    if (string.IsNullOrEmpty(testee1.VulnerablePVK) || string.IsNullOrEmpty(testee2.VulnerablePVK))
                        return true;

                    var pvkValues = testee1.VulnerablePVK.Split(',');
                    foreach (var pvk in pvkValues)
                    {
                        // Если есть совпадение по уязвимым качествам -> не совместимы
                        if (testee2.VulnerablePVK.Contains(pvk.Trim()))
                            return false;
                    }
                }

                // У одного или обоих испытуемых профессиональное тестирование не проведено -> не совместимы по ПВК
                return false;
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\CompatibilityGroupResults.cs


namespace Updk7.Wpf.Master.Modules.Compatibility
{
    public class CompatibilityGroupResults
    {
        public Data.Entities.TesteeRailroad Testee { get; set; }

        public Tests.TestResults Leary { get; set; }

        public Tests.ResultParsers.Compatibility.AnswerInfo[] Poll { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\ICompatibilityListReportGenerator.cs


using System.Windows.Documents;

namespace Updk7.Wpf.Master.Modules.Compatibility
{
    public interface ICompatibilityListReportGenerator
    {
        FlowDocument CreateReport();
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\ICompatibilityViewer.cs


using System.Collections.Generic;

namespace Updk7.Wpf.Master.Modules.Compatibility
{
    public interface ICompatibilityViewer : System.ComponentModel.INotifyPropertyChanged
    {
        Data.Entities.Testee Testee { get; }

        Data.Entities.Testee Candidate { get; }

        Data.Entities.Testee AttachedTestee { get; }

        bool CanPin { get; }

        bool IsEmpty { get; }

        void SetTestee(Assets.TesteeShortInfo testeeInfo);

        IEnumerable<ViewModels.CompatibilityInfoViewModel> GetCompatibilityInfo();
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Commands\LinkEmployeesCommand.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Compatibility.Commands
{
    public class LinkEmployeesCommand : Wpf.Commands.CommandBase
    {
        private Data.Repositories.IPinnedTesteesRepository _pinnedTesteesRepository;
        private ICompatibilityViewer _compatibilityViewer;

        public LinkEmployeesCommand(ICompatibilityViewer compatibilityViewer,
            Data.Repositories.IPinnedTesteesRepository pinnedTesteesRepository)
        {
            _pinnedTesteesRepository = pinnedTesteesRepository;
            _compatibilityViewer = compatibilityViewer;
            _compatibilityViewer.PropertyChanged += onCompatibilityViewerPropertyChanged;
        }

        public override bool CanExecute(object parameter)
        {
            return _compatibilityViewer.Testee != null
                && _compatibilityViewer.Candidate != null
                && _compatibilityViewer.CanPin;
        }

        public override void Execute(object parameter)
        {
            _pinnedTesteesRepository.SetPin(_compatibilityViewer.Testee.Id, _compatibilityViewer.Candidate.Id);
        }

        private void onCompatibilityViewerPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ICompatibilityViewer.CanPin) ||
                e.PropertyName == nameof(ICompatibilityViewer.Testee) ||
                e.PropertyName == nameof(ICompatibilityViewer.Candidate))
            {
                RaiseCanExecuteChanged();
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Commands\SaveCompatibilityReportCommand.cs


using System;
using System.ComponentModel;
using Microsoft.Win32;

namespace Updk7.Wpf.Master.Modules.Compatibility.Commands
{
    public class SaveCompatibilityReportCommand : Wpf.Commands.CommandBase
    {
        public static readonly string SaveError = "Не удалось сохранить документ. Проверьте что выбранный файл существует и не открыт в другом приложении";

        private string _lastDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Recent);
        private ICompatibilityListReportGenerator _reportGenerator;
        private ICompatibilityViewer _compatibilityViewer;

        public SaveCompatibilityReportCommand(ICompatibilityViewer compatibilityViewer,
            ICompatibilityListReportGenerator reportGenerator)
        {
            _reportGenerator = reportGenerator;
            _compatibilityViewer = compatibilityViewer;
            _compatibilityViewer.PropertyChanged += onCompatibilityViewerPropertyChanged;
        }

        public override bool CanExecute(object parameter)
        {
            return _compatibilityViewer.Testee != null && !_compatibilityViewer.IsEmpty;
        }

        public override void Execute(object parameter)
        {
            try
            {
                var dialog = new SaveFileDialog();
                dialog.InitialDirectory = _lastDirectory;
                dialog.Filter = "Документы Adobe Acrobat|*.pdf";

                if (dialog.ShowDialog() == true && !string.IsNullOrEmpty(dialog.FileName))
                {
                    DocumentHelper.SaveAsPdf(_reportGenerator.CreateReport(), dialog.FileName);
                    _lastDirectory = System.IO.Path.GetDirectoryName(dialog.FileName);
                }
            }
            catch (Exception)
            {
                Interactions.InteractionService.Current.ShowErrorMessage(SaveError);
            }
        }

        private void onCompatibilityViewerPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ICompatibilityViewer.Testee) ||
                e.PropertyName == nameof(ICompatibilityViewer.IsEmpty))
            {
                RaiseCanExecuteChanged();
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Commands\ShowCompatibilityCommand.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Compatibility.Commands
{
    public class ShowCompatibilityCommand : Wpf.Commands.CommandBase
    {
        protected Testees.ITesteeSelection TesteesSelection;
        protected ICompatibilityViewer CompatibilityViewer;

        public ShowCompatibilityCommand(Testees.ITesteeSelection testeesSelection, 
            ICompatibilityViewer compatibilityViewer)
        {
            CompatibilityViewer = compatibilityViewer;
            TesteesSelection = testeesSelection;
            TesteesSelection.PropertyChanged += onTesteesSelectionPropertyChanged;
        }
       
        public override bool CanExecute(object parameter)
        {
            return TesteesSelection.SelectedTestee != null;
        }

        public override void Execute(object parameter)
        {
            var testeeShortInfo = TesteesSelection.GetSelectedTesteeShortInfo();
            CompatibilityViewer.SetTestee(testeeShortInfo);
            ViewModelLocator.Current.NavigateTo("Compatibility");
        }

        private void onTesteesSelectionPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(Testees.ITesteeSelection.SelectedTestee))
                RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Commands\UnlinkEmployeesCommand.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Compatibility.Commands
{
    public class UnlinkEmployeesCommand : Wpf.Commands.CommandBase
    {
        private Data.Repositories.IPinnedTesteesRepository _pinnedTesteesRepository;
        private ICompatibilityViewer _compatibilityViewer;

        public UnlinkEmployeesCommand(ICompatibilityViewer compatibilityViewer,
            Data.Repositories.IPinnedTesteesRepository pinnedTesteesRepository)
        {
            _pinnedTesteesRepository = pinnedTesteesRepository;
            _pinnedTesteesRepository.DataChanged += onPinnedTesteesRepositoryDataChanged;
            _compatibilityViewer = compatibilityViewer;
            _compatibilityViewer.PropertyChanged += onCompatibilityViewerPropertyChanged;
        }

        public override bool CanExecute(object parameter)
        {
            if (_compatibilityViewer.Testee != null)
            {
                var pin = _pinnedTesteesRepository.GetTesteePin(_compatibilityViewer.Testee.Id);
                return pin != null;
            }

            return false;
        }

        public override void Execute(object parameter)
        {
            _pinnedTesteesRepository.RemovePin(_compatibilityViewer.Testee.Id);
            RaiseCanExecuteChanged();
        }

        private void onCompatibilityViewerPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ICompatibilityViewer.Testee))
                RaiseCanExecuteChanged();
        }

        private void onPinnedTesteesRepositoryDataChanged(object sender, Data.Repositories.RepositoryDataChangedEventArgs e)
        {
            RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\ViewModels\CompatibilityInfoViewModel.cs


namespace Updk7.Wpf.Master.Modules.Compatibility.ViewModels
{
    public class CompatibilityInfoViewModel : Prism.Mvvm.BindableBase
    {
        public CompatibilityInfoViewModel()
        {
        }

        private Data.Entities.Testee _testeeEntitie;

        public Data.Entities.Testee TesteeEntitie
        {
            get { return _testeeEntitie; }
            set { SetProperty(ref _testeeEntitie, value); }
        }

        private string _fullName;

        public string FullName
        {
            get { return _fullName; }
            set { SetProperty(ref _fullName, value); }
        }

        private string _columnNumber;

        public string ColumnNumber
        {
            get { return _columnNumber; }
            set { SetProperty(ref _columnNumber, value); }
        }

        private bool _compatibleByColumn;

        public bool CompatibleByColumn
        {
            get { return _compatibleByColumn; }
            set { SetProperty(ref _compatibleByColumn, value); }
        }

        private bool _compatibleByPvk;

        public bool CompatibleByPvk
        {
            get { return _compatibleByPvk; }
            set { SetProperty(ref _compatibleByPvk, value); }
        }

        private bool _compatibleByLeary;

        public bool CompatibleByLeary
        {
            get { return _compatibleByLeary; }
            set { SetProperty(ref _compatibleByLeary, value); }
        }

        private bool _compatibleBySociometric;

        public bool CompatibleBySociometric
        {
            get { return _compatibleBySociometric; }
            set { SetProperty(ref _compatibleBySociometric, value); }
        }

        private bool _compatibleByExpirience;

        public bool CompatibleByExpirience
        {
            get { return _compatibleByExpirience; }
            set { SetProperty(ref _compatibleByExpirience, value); }
        }

        private int _score;

        public int Score
        {
            get { return _score; }
            private set { SetProperty(ref _score, value); }
        }

        public void UpdateScore()
        {
            var score = 0;
            if (CompatibleByColumn)
                score += 1;
            if (CompatibleByPvk)
                score += 1;
            if (CompatibleByLeary)
                score += 1;
            if (CompatibleBySociometric)
                score += 1;
            if (CompatibleByExpirience)
                score += 1;

            Score = score;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\ViewModels\CompatibilityTableViewModel.cs


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Data;

namespace Updk7.Wpf.Master.Modules.Compatibility.ViewModels
{
    public class CompatibilityTableViewModel : Prism.Mvvm.BindableBase
    {
        private List<CompatibilityInfoViewModel> _rows = new List<CompatibilityInfoViewModel>();

        public CompatibilityTableViewModel()
        {
        }

        private ICollectionView _tableRowsView;

        public ICollectionView TableRowsView
        {
            get { return _tableRowsView; }
            private set { SetProperty(ref _tableRowsView, value); }
        }

        private CompatibilityInfoViewModel _selectedRow;

        public CompatibilityInfoViewModel SelectedRow
        {
            get { return _selectedRow; }
            private set { SetProperty(ref _selectedRow, value); }
        }

        private bool _canPinSelectedTestee;

        public bool CanPinSelectedTestee
        {
            get { return _canPinSelectedTestee; }
            private set { SetProperty(ref _canPinSelectedTestee, value); }
        }

        private int _itemsCount;

        public int ItemsCount
        {
            get { return _itemsCount; }
            private set { SetProperty(ref _itemsCount, value); }
        }

        public void SetData(CompatibilityComparisionData data)
        {
            createTableRows(data);
            createTableView();
            updateCanPinFlag();
            ItemsCount = _rows.Count;
        }

        public void Clear()
        {
            _rows.Clear();
            createTableView();
            ItemsCount = 0;
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(SelectedRow))
                updateCanPinFlag();

            base.OnPropertyChanged(args);
        }

        private void updateCanPinFlag()
        {
            if (SelectedRow == null)
                CanPinSelectedTestee = false;
            else
                CanPinSelectedTestee = SelectedRow.CompatibleByPvk;
        }

        private void createTableRows(CompatibilityComparisionData data)
        {
            _rows.Clear();

            var employee = data.Testee.Testee;
            foreach (var candidateResults in data.Candidates)
            {
                var candidate = candidateResults.Testee;
                var row = new CompatibilityInfoViewModel();
                row.TesteeEntitie = candidate;
                row.FullName = candidate.FullName;
                row.ColumnNumber = candidate.Column;

                row.CompatibleByColumn = CompatibilityFactors.ColumnFactor.CheckCompatibility(data.Testee, candidateResults);
                row.CompatibleBySociometric = CompatibilityFactors.PollFactor.CheckCompatibility(data.Testee, candidateResults);
                row.CompatibleByPvk = CompatibilityFactors.PvkFactor.CheckCompatibility(data.Testee, candidateResults);
                row.CompatibleByLeary = CompatibilityFactors.LearyFactor.CheckCompatibility(data.Testee, candidateResults);
                row.CompatibleByExpirience = CompatibilityFactors.ExpirienceFactor.CheckCompatibility(data.Testee, candidateResults);

                row.UpdateScore();
                _rows.Add(row);
            }

            _rows.Sort((x, y) => y.Score.CompareTo(x.Score));
        }

        private void createTableView()
        {
            if (TableRowsView != null)
                TableRowsView.CurrentChanged -= onTableRowsViewCurrentChanged;

            TableRowsView = CollectionViewSource.GetDefaultView(_rows);
            TableRowsView.CurrentChanged += onTableRowsViewCurrentChanged;
            TableRowsView.Refresh();
        }

        private void onTableRowsViewCurrentChanged(object sender, EventArgs e)
        {
            SelectedRow = _tableRowsView.CurrentItem as CompatibilityInfoViewModel;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\ViewModels\CompatibilityViewModel.cs


using System.Linq;
using System.Collections.Generic;

namespace Updk7.Wpf.Master.Modules.Compatibility.ViewModels
{
    public class CompatibilityViewModel : Prism.Mvvm.BindableBase, ICompatibilityViewer
    {
        protected static readonly string CpGroupKey = "CP";

        private Dictionary<int, CompatibilityGroupResults> _dataMemo = new Dictionary<int, CompatibilityGroupResults>();
        private Data.Repositories.IPinnedTesteesRepository _pinnedTesteesRepository;
        protected Data.Repositories.UpdkRepositories Repositories;

        public CompatibilityViewModel(Data.Repositories.UpdkRepositories updkRepositories,
            Data.Repositories.IPinnedTesteesRepository pinnedTesteesRepository)
        {
            _pinnedTesteesRepository = pinnedTesteesRepository;
            _pinnedTesteesRepository.DataChanged += onPinnedTesteesRepositoryDataChanged;
            Repositories = updkRepositories;
            Repositories.Results.DataChanged += onResultsDataChanged;
            CompatibilityTable.PropertyChanged += onCompatibilityTablePropertyChanged;
        }

        private Data.Entities.Testee _testee;

        public Data.Entities.Testee Testee
        {
            get { return _testee; }
            private set { SetProperty(ref _testee, value); }
        }

        private Data.Entities.Testee _candidate;

        public Data.Entities.Testee Candidate
        {
            get { return _candidate; }
            private set { SetProperty(ref _candidate, value); }
        }

        private Data.Entities.Testee _attachedTestee;

        public Data.Entities.Testee AttachedTestee
        {
            get { return _attachedTestee; }
            private set { SetProperty(ref _attachedTestee, value); }
        }

        private bool _canPin;

        public bool CanPin
        {
            get { return _canPin; }
            private set { SetProperty(ref _canPin, value); }
        }

        private bool _isEmpty;

        public bool IsEmpty
        {
            get { return _isEmpty; }
            protected set { SetProperty(ref _isEmpty, value); }
        }

        public TesteeInfoViewModel TesteeInfo { get; } = new TesteeInfoViewModel();

        public CompatibilityTableViewModel CompatibilityTable { get; } = new CompatibilityTableViewModel();

        public virtual void SetTestee(Assets.TesteeShortInfo testeeInfo)
        {
            TesteeInfo.FullName = testeeInfo.FullName;
            TesteeInfo.BriefInfo = testeeInfo.Comment;
            TesteeInfo.Photo = testeeInfo.Photo;
            TesteeInfo.ColumnNumber = (testeeInfo.Entitie as Data.Entities.TesteeRailroad)?.Column;
            TesteeInfo.AttachedTestee = null;
            TesteeInfo.Message = string.Empty;
            Testee = testeeInfo.Entitie;
            updatePinnedTestee();
        }

        public IEnumerable<CompatibilityInfoViewModel> GetCompatibilityInfo()
        {
            return CompatibilityTable.TableRowsView.Cast<CompatibilityInfoViewModel>().ToArray();
        }

        protected CompatibilityComparisionData LoadCompatibilityComparisionData(Data.Entities.TesteeRailroad testee,
            int professionId)
        {
            var traffic = testee.TrafficId;
            var candidates = Repositories.Testees.Get()
                .Cast<Data.Entities.TesteeRailroad>()
                .Where(t => t.PVKGroup.HasValue && t.ProfessionId == professionId && t.TrafficId == traffic)
                .ToArray();

            var results = new CompatibilityComparisionData()
            {
                Testee = LoadCompatibilityGroupResults(testee),
                Candidates = candidates
                    .Select(c => LoadCompatibilityGroupResults(c))
                    .ToArray()
            };

            return results;
        }

        protected CompatibilityGroupResults LoadCompatibilityGroupResults(Data.Entities.TesteeRailroad testee)
        {
            if (_dataMemo.ContainsKey(testee.Id))
                return _dataMemo[testee.Id];

            var groups = Repositories.Results.GetGroups(testee.Id)
                .Where(g => g.GroupKey == CpGroupKey)
                .OrderByDescending(g => g.TestingDate)
                .ToArray();

            var results = new CompatibilityGroupResults() { Testee = testee };
            foreach (var group in groups)
            {
                var groupTests = Repositories.Results.GetGroupResults(group.Id);
                if (results.Leary == null)
                {
                    var learyEntitie = groupTests.FirstOrDefault(t => t.TestType == Tests.TestType.Тест_Лири);
                    results.Leary = learyEntitie?.Data;
                }

                if (results.Poll == null)
                {
                    var pollEntitie = groupTests.FirstOrDefault(t => t.TestType == Tests.TestType.Опрос_совместимости);
                    if (pollEntitie != null)
                    {
                        results.Poll = Tests.ResultParsers.Compatibility
                            .Parser.ParseResults(pollEntitie.Data)
                            .ToArray();
                    }
                }

                if (results.Leary != null && results.Poll != null)
                    break;
            }

            _dataMemo[testee.Id] = results;

            return results;
        }

        private void onResultsDataChanged(object sender, Data.Repositories.RepositoryDataChangedEventArgs e)
        {
            if (e.Operation == Data.Repositories.RepositoryDataChangedOperation.Create)
            {
                var group = e.Entity as Data.Entities.TestGroup;
                if (group != null && group.GroupKey == CpGroupKey)
                    _dataMemo.Remove(group.TesteeId);
            }
        }

        private void onCompatibilityTablePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(CompatibilityTable.SelectedRow))
                Candidate = CompatibilityTable.SelectedRow?.TesteeEntitie;
            else if (e.PropertyName == nameof(CompatibilityTable.CanPinSelectedTestee))
                CanPin = CompatibilityTable.CanPinSelectedTestee;
            else if (e.PropertyName == nameof(CompatibilityTable.ItemsCount))
                IsEmpty = CompatibilityTable.ItemsCount == 0;
        }

        private void onPinnedTesteesRepositoryDataChanged(object sender, Data.Repositories.RepositoryDataChangedEventArgs e)
        {
            updatePinnedTestee();
        }

        private void updatePinnedTestee()
        {
            if (Testee == null)
                return;

            var pin = _pinnedTesteesRepository.GetTesteePin(Testee.Id);
            if (!pin.HasValue)
            {
                TesteeInfo.AttachedTestee = string.Empty;
                AttachedTestee = null;
            }
            else
            {
                var pinnedTestee = Repositories.Testees.GetById(pin.Value);
                TesteeInfo.AttachedTestee = pinnedTestee.FullName;
                AttachedTestee = pinnedTestee;
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\ViewModels\TesteeInfoViewModel.cs


using System.ComponentModel;
using System.Windows.Media.Imaging;

namespace Updk7.Wpf.Master.Modules.Compatibility.ViewModels
{
    public class TesteeInfoViewModel : Prism.Mvvm.BindableBase
    {
        public TesteeInfoViewModel()
        {
        }

        private string _fullName = "Испытуемый не выбран";

        public string FullName
        {
            get { return _fullName; }
            set { SetProperty(ref _fullName, value); }
        }

        private BitmapImage _photo = Styles.DefaultImages.EmployeePhoto;

        public BitmapImage Photo
        {
            get { return _photo; }
            set { SetProperty(ref _photo, value); }
        }

        private string _briefInfo;

        public string  BriefInfo
        {
            get { return _briefInfo; }
            set { SetProperty(ref _briefInfo, value); }
        }

        private string _columnNumber;

        public string ColumnNumber
        {
            get { return _columnNumber; }
            set { SetProperty(ref _columnNumber, value); }
        }

        private string _attachedTestee;

        public string AttachedTestee
        {
            get { return _attachedTestee; }
            set { SetProperty(ref _attachedTestee, value); }
        }

        private string _message;

        public string Message
        {
            get { return _message; }
            set { SetProperty(ref _message, value); }
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(Photo))
            {
                if (Photo == null)
                    Photo = Styles.DefaultImages.EmployeePhoto;
            }

            base.OnPropertyChanged(args);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Views\CompatibilityTableView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Compatibility.Views.CompatibilityTableView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.Compatibility.Views"
             xmlns:converters="clr-namespace:Updk7.Wpf.Converters;assembly=Updk7.Wpf"
             mc:Ignorable="d" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
        <DataGrid
            Grid.Row="0"
            Style="{StaticResource FlatDataGridStyle}"
            ItemsSource="{Binding TableRowsView}"
            IsSynchronizedWithCurrentItem="True"
            FontSize="12">
            <DataGrid.Resources>
                <converters:BoolToStringConverter x:Key="boolToStringConverter" TrueString="Да" FalseString="" />
            </DataGrid.Resources>
            <DataGrid.Columns>
                <!--Ф.И.О.-->
                <DataGridTextColumn
                    Header="Ф.И.О."
                    IsReadOnly="True"
                    Binding="{Binding FullName, Mode=OneWay}"
                    FontSize="14"
                    Width="*"
                    />
                <!--Колонна-->
                <DataGridTextColumn
                    Header="Колонна"
                    IsReadOnly="True"
                    Binding="{Binding ColumnNumber}"
                    />
                <!--Совместимость по ПВК-->
                <DataGridTextColumn
                    Header="ПВК"
                    IsReadOnly="True"
                    Width="120"
                    Binding="{Binding CompatibleByPvk, Mode=OneWay, Converter={StaticResource boolToStringConverter}}"
                    />
                <!--Совместимость по стажу-->
                <DataGridTextColumn
                    Header="Стаж"
                    IsReadOnly="True"
                    Width="120"
                    Binding="{Binding CompatibleByExpirience, Mode=OneWay, Converter={StaticResource boolToStringConverter}}"
                    />
                <!--Совместимость по Лири-->
                <DataGridTextColumn
                    Header="Тест Лири"
                    IsReadOnly="True"
                    Width="120"
                    Binding="{Binding CompatibleByLeary, Mode=OneWay, Converter={StaticResource boolToStringConverter}}"
                    />
                <!--Социометрия-->
                <DataGridTextColumn
                    Header="Социометрия"
                    IsReadOnly="True"
                    Width="120"
                    Binding="{Binding CompatibleBySociometric, Mode=OneWay, Converter={StaticResource boolToStringConverter}}"
                    />
                <!--Общие быллы-->
                <DataGridTextColumn
                    Header="Уровень"
                    IsReadOnly="True"
                    FontSize="14"
                    FontWeight="Medium"
                    Binding="{Binding Score, Mode=OneWay}"
                    />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Views\CompatibilityTableView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Compatibility.Views
{
    /// <summary>
    /// Interaction logic for CompatibilityTableView.xaml
    /// </summary>
    public partial class CompatibilityTableView : UserControl
    {
        public CompatibilityTableView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Views\CompatibilityView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Compatibility.Views.CompatibilityView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.Compatibility.Views"
             xmlns:styles="clr-namespace:Updk7.Wpf.Styles;assembly=Updk7.Wpf"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             styles:TabItemHeader.Text="Совместимость"
             mc:Ignorable="d" >
    <Grid
        Margin="5,6,2,1">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <local:ToolbarView
            Grid.Row="0"
            Grid.Column="0"
            Margin="0,0,3,0"
            />
        <local:TesteeInfoView
            Grid.Row="0"
            Grid.Column="1"
            Margin="0,0,3,0"
            DataContext="{Binding TesteeInfo}"
            />
        <local:CompatibilityTableView
            Grid.Row="1"
            Grid.Column="1"
            DataContext="{Binding CompatibilityTable}"
            />
        <local:ReportToolbarView 
            Grid.Row="1"
            Grid.Column="0"
            Margin="0,0,3,0"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Views\CompatibilityView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Compatibility.Views
{
    /// <summary>
    /// Interaction logic for CompatibilityView.xaml
    /// </summary>
    public partial class CompatibilityView : UserControl
    {
        public CompatibilityView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Views\NavigationToolbarButton.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Compatibility.Views.NavigationToolbarButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:commands="clr-namespace:Updk7.Wpf.Commands;assembly=Updk7.Wpf"
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.Compatibility.Views"
             mc:Ignorable="d">
    <Grid>
        <Button
            Margin="0,0,0,5"
            Style="{StaticResource FlatButtonStyle}"
            ToolTip="Совместимость выборанного сотрудника"
            BorderThickness="2"
            Command="{commands:CommandProvider ShowCompatibility}">
            <Image Margin="3" Source="{StaticResource CompatibilityImage}" />
        </Button>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Views\NavigationToolbarButton.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Compatibility.Views
{
    /// <summary>
    /// Interaction logic for NavigationToolbarButton.xaml
    /// </summary>
    public partial class NavigationToolbarButton : UserControl
    {
        public NavigationToolbarButton()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Views\ReportToolbarView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Compatibility.Views.ReportToolbarView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.Compatibility.Views"
             xmlns:commands="clr-namespace:Updk7.Wpf.Commands;assembly=Updk7.Wpf"
             mc:Ignorable="d" >
    <Grid>
        <StackPanel
            VerticalAlignment="Top">
            <Button 
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Сохранить отчет в .pdf формате"
                BorderThickness="2"
                Command="{commands:CommandProvider SaveCompatibilityReportCommand}">
                <Image Margin="3" Source="{StaticResource PdfImage}" />
            </Button>
        </StackPanel>
            
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Views\ReportToolbarView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Compatibility.Views
{
    /// <summary>
    /// Interaction logic for ReportToolbarView.xaml
    /// </summary>
    public partial class ReportToolbarView : UserControl
    {
        public ReportToolbarView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Views\TesteeInfoView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Compatibility.Views.TesteeInfoView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.Compatibility.Views"
             xmlns:converters="clr-namespace:Updk7.Wpf.Converters;assembly=Updk7.Wpf"
             mc:Ignorable="d" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Border
            Grid.Row="0"
            Background="DeepSkyBlue"
            Padding="10,3,2,3"
            Margin="1,0,0,0">
            <TextBlock 
                Text="Cотрудник"
                FontSize="14"
                FontWeight="Bold"
                VerticalAlignment="Center"
                />
        </Border>
        <Grid
            Grid.Row="1"
            Margin="5,3,0,3">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <!--Фото-->
            <Border
                Grid.Column="0"
                BorderBrush="Black"
                BorderThickness="1"
                Margin="0,4,4,4"
                Width="100"
                Height="120"
                VerticalAlignment="Top">
                <Grid>
                    <Image 
                        Margin="2"
                        Width="100"
                        Height="120"
                        Stretch="Uniform"
                        Source="{Binding Photo, Mode=OneWay}"
                        />
                </Grid>
            </Border>
            <Grid
                Grid.Column="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <!--Ф.И.О.-->
                <TextBlock 
                    Grid.Row="0"
                    Text="{Binding FullName, Mode=OneWay}"
                    Margin="5"
                    FontSize="18"
                    FontWeight="Medium"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Left"
                    />
                <!--Краткая информация-->
                <TextBlock 
                    Grid.Row="1"
                    Margin="5,0,0,3"
                    FontSize="14"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Left"
                    Visibility="{Binding BriefInfo, Converter={converters:ObjectToVisibilityConverter}, Mode=OneWay}"
                    Text="{Binding BriefInfo, Mode=OneWay}"
                    />
                <!--Колонна-->
                <TextBlock 
                    Grid.Row="2"
                    Margin="5,0,0,3"
                    FontSize="14"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Left"
                    Visibility="{Binding ColumnNumber, Converter={converters:ObjectToVisibilityConverter}, Mode=OneWay}">
                    <Run Text="Колонна: " FontWeight="Medium" />
                    <Run Text="{Binding ColumnNumber, Mode=OneWay}" />
                </TextBlock>
                <!--Кто закреплен-->
                <TextBlock 
                    Grid.Row="3"
                    Margin="5,0,0,0"
                    FontSize="14"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Left"
                    Visibility="{Binding AttachedTestee, Converter={converters:ObjectToVisibilityConverter}, Mode=OneWay}">
                    <Run Text="Прикреплен: " FontWeight="Medium" />
                    <Run Text="{Binding AttachedTestee, Mode=OneWay}" />
                </TextBlock>
                <!--Дополнительное сообщение-->
                <TextBlock 
                    Grid.Row="4"
                    Margin="5,0,0,0"
                    FontSize="14"
                    FontWeight="Medium"
                    Foreground="Red"
                    Text="{Binding Message}"
                    />
            </Grid>
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Views\TesteeInfoView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Compatibility.Views
{
    /// <summary>
    /// Interaction logic for TesteeInfoView.xaml
    /// </summary>
    public partial class TesteeInfoView : UserControl
    {
        public TesteeInfoView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Views\ToolbarView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Compatibility.Views.ToolbarView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.Compatibility.Views"
             xmlns:commands="clr-namespace:Updk7.Wpf.Commands;assembly=Updk7.Wpf"
             mc:Ignorable="d" >
    <Grid>
        <StackPanel
            VerticalAlignment="Top">
            <Button
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Прикрепить сотрудника"
                BorderThickness="2"
                Command="{commands:CommandProvider LinkEmployees}">
                <Image Margin="3" Source="{StaticResource LinkImage}" />
            </Button>
            <Button
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Открепить сотрудника"
                BorderThickness="2"
                Command="{commands:CommandProvider UnlinkEmployees}">
                <Image Margin="2" Source="{StaticResource UnlinkImage}" />
            </Button>
        </StackPanel>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Compatibility\Views\ToolbarView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Compatibility.Views
{
    /// <summary>
    /// Interaction logic for ToolbarView.xaml
    /// </summary>
    public partial class ToolbarView : UserControl
    {
        public ToolbarView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\IWorkplacesController.cs


using System.Collections.Generic;

namespace Updk7.Wpf.Master.Modules.LocalNetwork
{
    public interface IWorkplacesController
    {
        IEnumerable<Assets.WorkplaceInfo> GetWorkplacesInfo();

        void RunTesting(Assets.TestingInfo testingInfo);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\EventListeners\ShutdownRequestListener.cs


using System.Linq;

namespace Updk7.Wpf.Master.Modules.LocalNetwork.EventListeners
{
    public class ShutdownRequestListener : IEventListener
    {
        private class Strings
        {
            public static readonly string TestRunning = "Завершение программы невозможно при запущенном тестировании";
        }

        private IWorkplacesController _workplacesController;

        public ShutdownRequestListener(IWorkplacesController workpkacesController)
        {
            _workplacesController = workpkacesController;
        }

        public void Subscribe(Prism.Events.IEventAggregator eventAggregator)
        {
            eventAggregator
                .GetEvent<Assets.Events.ShutdownRequestEvent>()
                .Subscribe(onShutdownRequest);
        }

        private void onShutdownRequest(Assets.Events.ShutdownRequestEventData data)
        {
            if (_workplacesController.GetWorkplacesInfo().Any(w => w.IsBusy))
                data.AddCancelReason(Strings.TestRunning);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\EventListeners\StartTestingListener.cs


namespace Updk7.Wpf.Master.Modules.LocalNetwork.EventListeners
{
    public class StartTestingListener : IEventListener
    {
        private IWorkplacesController _workplacesController;

        public StartTestingListener(IWorkplacesController workplacesController)
        {
            _workplacesController = workplacesController;
        }

        public void Subscribe(Prism.Events.IEventAggregator eventAggregator)
        {
            eventAggregator
                .GetEvent<Assets.Events.StartTestingEvent>()
                .Subscribe(onRunTesting);
        }

        private void onRunTesting(Assets.Events.StartTestingEventData eventData)
        {
            _workplacesController.RunTesting(eventData.TestingInfo);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\EventListeners\WorkplacesDiscoveryListener.cs


namespace Updk7.Wpf.Master.Modules.LocalNetwork.EventListeners
{
    public class WorkplacesDiscoveryListener : IEventListener
    {
        private IWorkplacesController _workplacesController;

        public WorkplacesDiscoveryListener(IWorkplacesController workplacesController)
        {
            _workplacesController = workplacesController;
        }

        public void Subscribe(Prism.Events.IEventAggregator eventAggregator)
        {
            eventAggregator
                .GetEvent<Assets.Events.WorkplacesDiscoveryEvent>()
                .Subscribe(onWorkplacesDiscovery);
        }

        private void onWorkplacesDiscovery(Assets.Events.WorkplacesDiscoveryEventData eventData)
        {
            eventData.Workplaces = _workplacesController.GetWorkplacesInfo();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\ViewModels\ProgressViewModel.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.LocalNetwork.ViewModels
{
    public class ProgressViewModel : Prism.Mvvm.BindableBase
    {
        private int _completeTestsCount = 0;

        public ProgressViewModel()
        {
            Reset();
        }

        private int _totalTestsCount;

        public int TotalTestsCount
        {
            get { return _totalTestsCount; }
            set { SetProperty(ref _totalTestsCount, value); }
        }

        private double _totalProgress;

        public double TotalProgress
        {
            get { return _totalProgress; }
            private set { SetProperty(ref _totalProgress, value); }
        }

        private string _currentTestTitle;

        public string CurrentTestTitle
        {
            get { return _currentTestTitle; }
            set { SetProperty(ref _currentTestTitle, value); }
        }

        private string _nextTestTitle;

        public string NextTestTitle
        {
            get { return _nextTestTitle; }
            set { SetProperty(ref _nextTestTitle, value); }
        }

        public void IncrementCompleteTestsCount()
        {
            TotalProgress = _totalTestsCount > 0
                ? 100.0 * (++_completeTestsCount) / _totalTestsCount
                : 0;
        }

        public void Reset()
        {
            _completeTestsCount = 0;
            TotalProgress = 0;
            CurrentTestTitle = "Тестирование не проводится";
            NextTestTitle = string.Empty;
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(TotalTestsCount))
                Reset();

            base.OnPropertyChanged(args);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\ViewModels\TesteeInfoViewModel.cs


using System.ComponentModel;
using System.Windows.Media.Imaging;

namespace Updk7.Wpf.Master.Modules.LocalNetwork.ViewModels
{
    public class TesteeInfoViewModel : Prism.Mvvm.BindableBase
    {
        public TesteeInfoViewModel()
        {
        }

        private Assets.TesteeShortInfo _sourceData;

        public Assets.TesteeShortInfo SourceData
        {
            get { return _sourceData; }
            set { SetProperty(ref _sourceData, value); }
        } 

        private string _fullName;

        public string FullName
        {
            get { return _fullName; }
            set { SetProperty(ref _fullName, value); }
        }

        private string _comment;

        public string Comment
        {
            get { return _comment; }
            set { SetProperty(ref _comment, value); }
        }

        private BitmapImage _photo = Styles.DefaultImages.EmployeePhoto;

        public BitmapImage Photo
        {
            get { return _photo; }
            set { SetProperty(ref _photo, value); }
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(SourceData))
            {
                FullName = _sourceData.FullName;
                Comment = _sourceData.Comment;
                Photo = _sourceData.Photo != null ? _sourceData.Photo : Styles.DefaultImages.EmployeePhoto;
            }

            base.OnPropertyChanged(args);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\ViewModels\TestingPlanViewModel.cs


using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.LocalNetwork.ViewModels
{
    public class TestingPlanViewModel : Prism.Mvvm.BindableBase
    {
        public class TestViewModel : Prism.Mvvm.BindableBase
        {
            public TestViewModel(Data.TestRun testRun)
            {
                SourceData = testRun;
            }

            public Data.TestRun SourceData { get; }

            private bool _isComplete;

            public bool IsComplete
            {
                get { return _isComplete; }
                set { SetProperty(ref _isComplete, value); }
            } 
        }

        public class TestGroupViewModel : Prism.Mvvm.BindableBase
        {
            public TestGroupViewModel(Data.TestGroupRun testGroup)
            {
                SourceData = testGroup;
                Tests = testGroup.Tests.Select(t => new TestViewModel(t)).ToArray();
            }

            public Data.TestGroupRun SourceData { get; }

            public IEnumerable<TestViewModel> Tests { get; }
        }

        public TestingPlanViewModel()
        {
        }

        private Data.TestingPlan _sourceData;

        public Data.TestingPlan  SourceData
        {
            get { return _sourceData; }
            set { SetProperty(ref _sourceData, value); }
        }

        private IEnumerable<TestGroupViewModel> _testsGroups;

        public IEnumerable<TestGroupViewModel> TestsGroups
        {
            get { return _testsGroups; }
            private set { SetProperty(ref _testsGroups, value); }
        }

        private int _uniqueTestsCount;

        public int UniqueTestsCount
        {
            get { return _uniqueTestsCount; }
            private set { SetProperty(ref _uniqueTestsCount, value); }
        } 

        public void MarkCompleteTest(Tests.TestType test)
        {
            if (TestsGroups != null)
            {
                var viewModels = TestsGroups.SelectMany(g => g.Tests)
                    .Where(t => t.SourceData.TestType == test);

                foreach (var viewModel in viewModels)
                    viewModel.IsComplete = true;
            }
        }

        public Data.TestRun GetNextTest(Data.TestRun offset = null)
        {
            if (TestsGroups != null)
            {
                var allTests = TestsGroups.SelectMany(g => g.Tests);
                if (offset != null)
                    allTests = allTests.SkipWhile(r => r.SourceData != offset).Skip(1);

                return allTests.FirstOrDefault(t => !t.IsComplete)?.SourceData;
            }

            return null;
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(SourceData))
                setupTestPlan();

            base.OnPropertyChanged(args);
        }

        protected void setupTestPlan()
        {
            if (_sourceData == null)
            {
                TestsGroups = null;
                UniqueTestsCount = 0;
            }
            else
            {
                TestsGroups = _sourceData.TestsGroups.Select(g => new TestGroupViewModel(g)).ToArray();
                UniqueTestsCount = _sourceData.TestsGroups.SelectMany(g => g.Tests).Select(t => t.TestType).ToHashSet().Count;
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\ViewModels\WorkplacesViewModel.cs


using System.Linq;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Data;
using System.Diagnostics;

namespace Updk7.Wpf.Master.Modules.LocalNetwork.ViewModels
{
    public class WorkplacesViewModel : Prism.Mvvm.BindableBase, IWorkplacesController
    {
        private List<WorkplaceViewModel> _workplaces = new List<WorkplaceViewModel>();
        private Network.Local.Master.UpdkService _updkService;
        private Prism.Events.IEventAggregator _eventAggregator;

        public WorkplacesViewModel(Network.Local.Master.UpdkService updkService, Prism.Events.IEventAggregator eventAggregator)
        {
            _eventAggregator = eventAggregator;
            _updkService = updkService;
            _updkService.Clients.CollectionChanged += onClientsCollectionChanged;
            setupClientsCollectionView();
        }

        public ICollectionView WorkplacesCollectionView { get; private set; }

        public void RunTesting(Assets.TestingInfo testingInfo)
        {
            var workplace = _workplaces.FirstOrDefault(w => w.Address == testingInfo.Workplace.Address);
            Debug.Assert(!workplace.IsBusy);

            workplace.TesteeInfo.FullName = "Run testing";
            workplace.RunTesting(testingInfo.Testee, testingInfo.TestingPlan);

            AppLog.Current.Info($"Testing started: {workplace.Title} ({workplace.Address})");
        }

        public IEnumerable<Assets.WorkplaceInfo> GetWorkplacesInfo()
        {
            return _workplaces.Select(w => w.GetWorkplaceInfo()).ToArray();
        }

        private void onClientsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (Network.Local.Master.UpdkClientProxy client in e.OldItems)
                    removeClient(client);
            }

            if (e.NewItems != null)
            {
                foreach (Network.Local.Master.UpdkClientProxy client in e.NewItems)
                    addClient(client);
            }
        }

        private void addClient(Network.Local.Master.UpdkClientProxy client)
        {
            var viewModel = new WorkplaceViewModel(client);
            subscribeToWorkplaceEvents(viewModel);

            _workplaces.Add(viewModel);
            WorkplacesCollectionView.Refresh();

            AppLog.Current.Info($"Workplace added: {viewModel.Title} ({viewModel.Address})");
        }

        private void removeClient(Network.Local.Master.UpdkClientProxy client)
        {
            var viewModel = _workplaces.FirstOrDefault(vm => vm.ClientProxy == client);
            if (viewModel != null)
            {
                viewModel.ClientProxy = null;
                viewModel.BreakTesting();
                unscribeFromWorkplaceEvents(viewModel);

                _workplaces.Remove(viewModel);
                raiseWorplaceDisconnectEvent(viewModel);

                WorkplacesCollectionView.Refresh();

                AppLog.Current.Info($"Workplace removed: {viewModel.Title} ({viewModel.Address})");
            }
        }

        private void raiseWorplaceDisconnectEvent(WorkplaceViewModel workplace)
        {
            var data = new Assets.Events.WorkplaceDisconnectedEventData();
            data.Workplace = workplace.GetWorkplaceInfo();
            if (workplace.TesteeInfo.SourceData != null)
                data.Testee = workplace.TesteeInfo.SourceData.Entitie;

            _eventAggregator.GetEvent<Assets.Events.WorkplaceDisconnectedEvent>()
                .Publish(data);
        }

        private void onWorkplacePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
        }

        private void onWorkplaceTestStarted(object sender, WorkplaceViewModel.TestStartedEventArgs e)
        {
            var workplace = sender as WorkplaceViewModel;
            var info = new Assets.TestStartedInfo(e.Testee, e.TestRun, workplace.GetWorkplaceInfo());

            _eventAggregator.GetEvent<Assets.Events.TestStartedEevnt>()
                .Publish(new Assets.Events.TestStartedEventData(info));
        }

        private void onWorkplaceTestComplete(object sender, WorkplaceViewModel.TestCompleteEventArgs e)
        {
            var workplace = sender as WorkplaceViewModel;

            _eventAggregator.GetEvent<Assets.Events.TestCompleteEvent>()
                .Publish(new Assets.Events.TestCompleteEventData(e.Testee, e.Results, workplace.GetWorkplaceInfo()));
        }

        private void onWorkplaceTestingPlanComplete(object sender, WorkplaceViewModel.TestingPlanCompleteEventArgs e)
        {
            var workplace = sender as WorkplaceViewModel;
            var testingInfo = new Assets.TestingInfo(workplace.GetWorkplaceInfo(), e.Testee, e.TestingPlan);
            var eventData = new Assets.Events.TestingPlanCompleteEventData(testingInfo, e.ErrorTest);

            _eventAggregator.GetEvent<Assets.Events.TestingPlanCompleteEvent>()
                .Publish(eventData);
        }

        private void onWorkplaceShowTesteeReports(object sender, WorkplaceViewModel.ShowTesteeReportsEventArgs e)
        {
            _eventAggregator.GetEvent<Assets.Events.ShowTesteeReportsEvent>()
                .Publish(new Assets.Events.ShowTesteeReportsEventData(e.Testee));
        }

        private void subscribeToWorkplaceEvents(WorkplaceViewModel workplace)
        {
            workplace.PropertyChanged += onWorkplacePropertyChanged;
            workplace.ShowTesteeReports += onWorkplaceShowTesteeReports;
            workplace.TestStarted += onWorkplaceTestStarted;
            workplace.TestComplete += onWorkplaceTestComplete;
            workplace.TestingPlanComplete += onWorkplaceTestingPlanComplete;
        }

        private void unscribeFromWorkplaceEvents(WorkplaceViewModel workplace)
        {
            workplace.PropertyChanged -= onWorkplacePropertyChanged;
            workplace.ShowTesteeReports -= onWorkplaceShowTesteeReports;
            workplace.TestStarted -= onWorkplaceTestStarted;
            workplace.TestComplete -= onWorkplaceTestComplete;
            workplace.TestingPlanComplete -= onWorkplaceTestingPlanComplete;
        }

        private void setupClientsCollectionView()
        {
            WorkplacesCollectionView = CollectionViewSource.GetDefaultView(_workplaces);
            WorkplacesCollectionView.SortDescriptions.Add(new SortDescription(nameof(WorkplaceViewModel.Title),
                ListSortDirection.Ascending));

            foreach (var client in _updkService.Clients)
                addClient(client);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\ViewModels\WorkplaceViewModel.cs


using System;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.LocalNetwork.ViewModels
{
    public class WorkplaceViewModel : Prism.Mvvm.BindableBase
    {
        public class EventArgsBase : EventArgs
        {
            public EventArgsBase(Assets.TesteeShortInfo testee)
            {
                Testee = testee;
            }

            public Assets.TesteeShortInfo Testee { get; }
        }

        public class TestCompleteEventArgs : EventArgsBase
        {
            public TestCompleteEventArgs(Assets.TesteeShortInfo testee, Tests.TestResults results)
                : base(testee)
            {
                Results = results;
            }

            public Tests.TestResults Results { get; }
        }

        public class TestingPlanCompleteEventArgs : EventArgsBase
        {
            public TestingPlanCompleteEventArgs(Assets.TesteeShortInfo testee, Data.TestingPlan testingPlan,
                Tests.TestType? errorTest = null)
                : base(testee)
            {
                TestingPlan = testingPlan;
                ErrorTest = errorTest;
            }

            public Tests.TestType? ErrorTest { get; }

            public Data.TestingPlan TestingPlan { get; }
        }

        public class TestStartedEventArgs : EventArgsBase
        {
            public TestStartedEventArgs(Assets.TesteeShortInfo testee, Data.TestRun testRun)
                : base(testee)
            {
                TestRun = testRun;
            }

            public Data.TestRun TestRun { get; }
        }

        public class ShowTesteeReportsEventArgs : EventArgsBase
        {
            public ShowTesteeReportsEventArgs(Assets.TesteeShortInfo testee)
                : base(testee)
            {
            }
        }

        private class ErrorStrings
        {
            public static readonly string PultDisconnected = "Пульт не подключен";
            public static readonly string NoActivationKey = "Rutoken не подключен";
            public static readonly string TestError = "Внутренняя ошибка теста";
        }

        public WorkplaceViewModel(Network.Local.Master.UpdkClientProxy clientProxy)
        {
            ClientProxy = clientProxy;
            
            BreakTestCommand = new Prism.Commands.DelegateCommand(
                BreakTesting, 
                () => IsBusy);

            ShowTesteeReportsCommand = new Prism.Commands.DelegateCommand(
                () => ShowTesteeReports?.Invoke(this, new ShowTesteeReportsEventArgs(TesteeInfo.SourceData)), 
                () => TesteeInfo.SourceData != null);

            TesteeInfo.PropertyChanged += onTesteeInfoPropertyChanged;
        }

        public Prism.Commands.DelegateCommand BreakTestCommand { get; }

        public Prism.Commands.DelegateCommand ShowTesteeReportsCommand { get; }

        public event EventHandler<TestStartedEventArgs> TestStarted;

        public event EventHandler<TestCompleteEventArgs> TestComplete;

        public event EventHandler<TestingPlanCompleteEventArgs> TestingPlanComplete;

        public event EventHandler<ShowTesteeReportsEventArgs> ShowTesteeReports;

        public TesteeInfoViewModel TesteeInfo { get; } = new TesteeInfoViewModel();

        public TestingPlanViewModel TestingPlan { get; } = new TestingPlanViewModel();

        public ProgressViewModel Progress { get; } = new ProgressViewModel();

        private Network.Local.Master.UpdkClientProxy _clientProxy;

        public Network.Local.Master.UpdkClientProxy ClientProxy
        {
            get { return _clientProxy; }
            set
            {
                unscribeFromProxyEvents(_clientProxy);
                subscribeToProxyEvents(value);
                SetProperty(ref _clientProxy, value); 
            }
        }

        private string  _title;

        public string  Title
        {
            get { return _title; }
            private set { SetProperty(ref _title, value); }
        }

        private string _address;

        public string Address
        {
            get { return _address; }
            private set { SetProperty(ref _address, value); }
        }

        private bool _isBusy;

        public bool IsBusy
        {
            get { return _isBusy; }
            private set { SetProperty(ref _isBusy, value); }
        }

        private string _errorMessage;

        public string  ErrorMessage
        {
            get { return _errorMessage; }
            private set { SetProperty(ref _errorMessage, value); }
        } 

        public void RunTesting(Assets.TesteeShortInfo testee, Data.TestingPlan testingPlan)
        {
            TesteeInfo.SourceData = testee;
            TestingPlan.SourceData = testingPlan;
            Progress.TotalTestsCount = TestingPlan.UniqueTestsCount;
            IsBusy = true;

            runNextTest();
        }

        public void BreakTesting()
        {
            if (IsBusy)
            {
                try
                {
                    if (_clientProxy != null)
                        _clientProxy.BreakTest();
                }
                catch (Exception)
                {
                }

                onTestingPlanComplete();
            }
        }

        public Assets.WorkplaceInfo GetWorkplaceInfo()
        {
            var error = _clientProxy != null ? _clientProxy.Errors : Network.Local.ErrorFlags.NoErrors;
            return new Assets.WorkplaceInfo(Title, Address, IsBusy, error);
        }
        
        private void runNextTest()
        {
            var currentTest = TestingPlan.GetNextTest();
            Tests.TestType? errorTest = null;

            try
            {
                if (currentTest != null && _clientProxy != null)
                {
                    var nextTest = TestingPlan.GetNextTest(currentTest);
                    Progress.CurrentTestTitle = Tests.Metadata.GetTestName(currentTest.TestType);
                    Progress.NextTestTitle = nextTest != null
                        ? Tests.Metadata.GetTestName(nextTest.TestType)
                        : null;

                    AppLog.Current.Info($"TEST STARTING: {Title} ({Address}), {currentTest.TestType}");

                    _clientProxy.StartTest(currentTest.TestType, currentTest.Parameters);
                    onTestStarted(currentTest);
                    return;
                }

                if (currentTest == null && _clientProxy != null)
                    _clientProxy.ShowWelcome();
            }
            catch (Exception ex)
            {
                AppLog.Current.Error($"TEST STARTING EXCEPTION: {ex.Message}");
                errorTest = currentTest.TestType;
            }

            onTestingPlanComplete(errorTest);
        }

        private void onClientProxyTestComplete(object sender, Tests.TestResults e)
        {
            TestComplete?.Invoke(this, new TestCompleteEventArgs(TesteeInfo.SourceData, e));

            AppLog.Current.Info($"TEST COMPLETE: {e.Test}");

            if (e.ContainsException)
            {
                AppLog.Current.Error("*** TEST EXCEPTION!");
                AppLog.Current.Error(e[Tests.TestResults.ErrorKey].String);
                onTestingPlanComplete(e.Test);
            }
            else
            {
                Progress.IncrementCompleteTestsCount();
                TestingPlan.MarkCompleteTest(e.Test);
                runNextTest();
            }
        }

        private void onTestingPlanComplete(Tests.TestType? errorTest = null)
        {
            AppLog.Current.Info($"TESTING COMPLETED: {Title} ({Address})");

            Progress.TotalTestsCount = 0;
            IsBusy = false;

            TestingPlanComplete?.Invoke(this, new TestingPlanCompleteEventArgs(TesteeInfo.SourceData, 
                TestingPlan.SourceData, errorTest));
        }

        private void onTestStarted(Data.TestRun testRun)
        {
            TestStarted?.Invoke(this, new TestStartedEventArgs(TesteeInfo.SourceData, testRun));
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(ClientProxy))
            {
                if (ClientProxy != null)
                {
                    Title = ClientProxy.Title;
                    Address = ClientProxy.Address;
                    updateErrorMessage();
                }
            }
            else if (args.PropertyName == nameof(IsBusy))
                BreakTestCommand.RaiseCanExecuteChanged();

            base.OnPropertyChanged(args);
        }

        private void onTesteeInfoPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(TesteeInfo.SourceData))
                ShowTesteeReportsCommand.RaiseCanExecuteChanged();
        }

        private void onClientProxyPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(_clientProxy.Errors))
                updateErrorMessage();
        }

        private void updateErrorMessage()
        {
            if (_clientProxy.Errors == Network.Local.ErrorFlags.NoErrors)
                ErrorMessage = string.Empty;
            else if (_clientProxy.Errors.HasFlag(Network.Local.ErrorFlags.ActivationKey))
                ErrorMessage = ErrorStrings.NoActivationKey;
            else if (_clientProxy.Errors.HasFlag(Network.Local.ErrorFlags.HardwareError))
                ErrorMessage = ErrorStrings.PultDisconnected;
            else if (_clientProxy.Errors.HasFlag(Network.Local.ErrorFlags.TestInternalError))
                ErrorMessage = ErrorStrings.TestError;
        }

        private void subscribeToProxyEvents(Network.Local.Master.UpdkClientProxy clientProxy)
        {
            if (clientProxy != null)
            {
                clientProxy.TestComplete += onClientProxyTestComplete;
                clientProxy.PropertyChanged += onClientProxyPropertyChanged;
            }
        }

        private void unscribeFromProxyEvents(Network.Local.Master.UpdkClientProxy clientProxy)
        {
            if (clientProxy != null)
            {
                clientProxy.TestComplete -= onClientProxyTestComplete;
                clientProxy.PropertyChanged -= onClientProxyPropertyChanged;
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\Views\ProgressView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.LocalNetwork.Views.ProgressView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.LocalNetwork.Views"
             mc:Ignorable="d" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.Resources>
            <Style TargetType="{x:Type ProgressBar}">
                <Setter Property="Foreground" Value="LimeGreen" />
                <Setter Property="Background" Value="Transparent" />
                <Setter Property="BorderBrush" Value="Gray" />
                <Setter Property="BorderThickness" Value="1" />
                <Setter Property="Margin" Value="7,2,2,2" />
                <Setter Property="Height" Value="15" />
                <Setter Property="VerticalAlignment" Value="Center" />
            </Style>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="FontWeight" Value="Normal" />
                <Setter Property="VerticalAlignment" Value="Center" />
            </Style>
        </Grid.Resources>
        <!--Наименование текущего теста-->
        <TextBlock 
            Grid.Row="0"
            Grid.Column="0"
            Grid.ColumnSpan="2"
            Margin="0,3,0,5"
            Text="{Binding CurrentTestTitle, Mode=OneWay}"
            FontWeight="Medium"
            FontSize="18"
            HorizontalAlignment="Center"
            TextWrapping="WrapWithOverflow"
            />
        <!--Общий прогресс-->
        <TextBlock 
            Grid.Row="1"
            Grid.Column="0"
            Text="Группа тестов"
            VerticalAlignment="Center"
            FontSize="14"
            />
        <ProgressBar 
            Grid.Row="1"
            Grid.Column="1"
            Value="{Binding TotalProgress, Mode=OneWay}"
            />
        <!--Следующий тест-->
        <TextBlock 
            Grid.Row="3"
            Grid.Column="0"
            Text="Далее"
            />
        <TextBlock 
            Grid.Row="3"
            Grid.Column="1"
            Margin="7,0,0,0"
            Text="{Binding NextTestTitle, Mode=OneWay}"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\Views\ProgressView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.LocalNetwork.Views
{
    /// <summary>
    /// Interaction logic for ProgressView.xaml
    /// </summary>
    public partial class ProgressView : UserControl
    {
        public ProgressView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\Views\TesteeInfoView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.LocalNetwork.Views.TesteeInfoView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <!--Фото-->
        <Border
            Grid.Column="0"
            BorderBrush="Black"
            BorderThickness="1"
            Margin="0,4,4,4"
            Width="100"
            Height="120"
            VerticalAlignment="Top">
            <Grid>
                <Image 
                    Margin="2"
                    Width="100"
                    Height="120"
                    Stretch="Uniform"
                    Source="{Binding Photo, Mode=OneWay}"
                    />
            </Grid>
        </Border>
        <StackPanel
            Grid.Column="1"
            Margin="5,5,0,0">
            <!--Полное имя сотрудника-->
            <TextBlock 
                FontSize="18"
                FontWeight="Medium"
                Text="{Binding FullName}"
                />
            <!--Комментарий-->
            <TextBlock 
                FontSize="14"
                FontWeight="Normal"
                Text="{Binding Comment}"
                />
        </StackPanel>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\Views\TesteeInfoView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.LocalNetwork.Views
{
    /// <summary>
    /// Interaction logic for TesteeInfoView.xaml
    /// </summary>
    public partial class TesteeInfoView : UserControl
    {
        public TesteeInfoView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\Views\TestingPlanView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.LocalNetwork.Views.TestingPlanView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:converters="clr-namespace:Updk7.Wpf.Converters;assembly=Updk7.Wpf"
             mc:Ignorable="d">
    <Grid>
        <Grid.Resources>
            <!--Вид группы тестов-->
            <ControlTemplate x:Key="TestsGroupView" TargetType="{x:Type ListViewItem}">
                <Grid
                    DataContext="{Binding}">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <TextBlock 
                        Grid.Row="0"
                        Text="{Binding SourceData.GroupTitle}"
                        FontSize="16"
                        FontWeight="Medium"
                        Foreground="Black"
                        TextWrapping="Wrap"
                        />
                    <ItemsControl
                        Grid.Row="1"
                        Background="Transparent"
                        BorderBrush="Transparent"
                        BorderThickness="0"
                        Margin="10,3,3,3"
                        ItemsSource="{Binding Tests}">
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <Border
                                    x:Name="PART_Border"
                                    BorderBrush="Transparent"
                                    BorderThickness="0"
                                    Background="Transparent">
                                    <TextBlock 
                                        x:Name="PART_TextBlock"
                                        Text="{Binding SourceData.TestType, Converter={converters:KeyToStringConverter}}"
                                        FontSize="14"
                                        FontWeight="Normal"
                                        Foreground="Black"
                                        TextWrapping="Wrap"
                                        />
                                </Border>
                                <DataTemplate.Triggers>
                                    <DataTrigger Binding="{Binding IsComplete}" Value="true">
                                        <Setter TargetName="PART_Border" Property="Background" Value="DarkGray" />
                                    </DataTrigger>
                                </DataTemplate.Triggers>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </Grid>
            </ControlTemplate>
        </Grid.Resources>
        <!--План тестирования-->
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <!--Заголовок панели-->
        <TextBlock 
            FontSize="18"
            FontWeight="Medium"
            TextWrapping="Wrap"
            Text="План тестирования испытуемого"
            VerticalAlignment="Center"
            HorizontalAlignment="Center"
            Foreground="Black"
            />
        <!--Список тестов-->
        <ListView
            Grid.Row="1"
            Background="Transparent"
            BorderBrush="Transparent"
            BorderThickness="0"
            ItemsSource="{Binding TestsGroups}">
            <ListView.ItemContainerStyle>
                <Style TargetType="{x:Type ListViewItem}">
                    <Setter Property="Template" Value="{StaticResource TestsGroupView}" />
                </Style>
            </ListView.ItemContainerStyle>
        </ListView>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\Views\TestingPlanView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.LocalNetwork.Views
{
    /// <summary>
    /// Interaction logic for TestingPlanView.xaml
    /// </summary>
    public partial class TestingPlanView : UserControl
    {
        public TestingPlanView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\Views\ToolbarView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.LocalNetwork.Views.ToolbarView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.LocalNetwork.Views"
             mc:Ignorable="d" >
    <StackPanel
        VerticalAlignment="Top"
        Margin="0,5,8,0">
        <StackPanel.Resources>
            <Style x:Key="ToggleButtonStyle" TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource FlatToggleButtonStyle}">
                <Setter Property="IsEnabled" Value="True" />
                <Setter Property="Margin" Value="0,0,0,5" />
                <Setter Property="ToolTip" Value="Список тестов" />
                <Setter Property="BorderThickness" Value="2" />
                <Style.Triggers>
                    <DataTrigger Binding="{Binding UniqueTestsCount}" Value="0">
                        <Setter Property="IsEnabled" Value="False" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </StackPanel.Resources>
        <!--Кнопка прерывания тестирования-->
        <Button
            Margin="0,0,0,5"
            Style="{StaticResource FlatButtonStyle}"
            ToolTip="Прервать тестирование"
            BorderThickness="2"
            Command="{Binding BreakTestCommand}">
            <Image Margin="3" Source="{StaticResource BreakTestImage}" />
        </Button>
        <!--Кнопка показа отчетов испытуемого-->
        <Button
            Margin="0,0,0,5"
            Style="{StaticResource FlatButtonStyle}"
            ToolTip="Перейти к отчетам сотрудника"
            BorderThickness="2"
            Command="{Binding ShowTesteeReportsCommand}">
            <Image Margin="3" Source="{StaticResource ReportsImage}" />
        </Button>
        <!--Кнопка показа списка тестов-->
        <ToggleButton
            x:Name="PART_ShowTestPlanButton"
            Style="{StaticResource ToggleButtonStyle}"
            DataContext="{Binding TestingPlan}">
            <Grid>
                <Image Margin="3" Source="{StaticResource FindImage}" />
                <!--Выпадающий список тестов-->
                <Popup
                    StaysOpen="False"
                    Margin="5"
                    Placement="Right"
                    HorizontalOffset="5"
                    VerticalOffset="-3"
                    AllowsTransparency="True"
                    IsOpen="{Binding ElementName=PART_ShowTestPlanButton, Path=IsChecked}">
                    <Border
                        BorderBrush="Black"
                        BorderThickness="1"
                        Padding="3"
                        Background="{StaticResource WindowBackground}">
                        <local:TestingPlanView 
                            DataContext="{Binding}"
                            />
                    </Border>
                </Popup>
            </Grid>
        </ToggleButton>
    </StackPanel>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\Views\ToolbarView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.LocalNetwork.Views
{
    /// <summary>
    /// Interaction logic for ToolbarView.xaml
    /// </summary>
    public partial class ToolbarView : UserControl
    {
        public ToolbarView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\Views\WorkplacesView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.LocalNetwork.Views.WorkplacesView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.LocalNetwork.Views"
             mc:Ignorable="d" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             xmlns:styles="clr-namespace:Updk7.Wpf.Styles;assembly=Updk7.Wpf"
             styles:TabItemHeader.Text="Рабочие места">
    <Grid
        Margin="2,5,1,1">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.Resources>
            <BooleanToVisibilityConverter x:Key="boolToVisibilityConverter" />
        </Grid.Resources>
        <TextBlock
            Grid.Row="1"
            VerticalAlignment="Center"
            HorizontalAlignment="Center"
            FontSize="18"
            FontWeight="Medium"
            Text="Нет подключенных рабочих мест"
            Visibility="{Binding WorkplacesCollectionView.IsEmpty, Converter={StaticResource boolToVisibilityConverter}}"
            />
        <ListView
            Grid.Row="1"
            Background="Transparent"
            BorderBrush="Transparent"
            BorderThickness="0"
            ItemsSource="{Binding WorkplacesCollectionView}"
            ScrollViewer.HorizontalScrollBarVisibility="Disabled"
            ScrollViewer.VerticalScrollBarVisibility="Auto">
            <ListView.ItemContainerStyle>
                <Style TargetType="{x:Type ListViewItem}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ListViewItem}">
                                <local:WorkplaceView 
                                    DataContext="{Binding}" 
                                    Margin="0,0,0,10"
                                    />
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListView.ItemContainerStyle>
        </ListView>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\Views\WorkplacesView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.LocalNetwork.Views
{
    /// <summary>
    /// Interaction logic for WorkplacesView.xaml
    /// </summary>
    public partial class WorkplacesView : UserControl
    {
        public WorkplacesView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\Views\WorkplaceView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.LocalNetwork.Views.WorkplaceView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.LocalNetwork.Views"
             mc:Ignorable="d" 
             d:DesignWidth="500">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <!--Заголовок панели-->
        <Border
            Grid.Row="0"
            Grid.Column="1"
            Background="DeepSkyBlue"
            Padding="10,3,0,3"
            Margin="1,0,0,0">
            <Grid>
                <TextBlock 
                    FontSize="14"
                    FontWeight="Bold"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Left">
                    <Run Text="> " FontWeight="ExtraBlack" />
                    <Run Text="{Binding Title, Mode=OneWay}" />
                    <Run Text=" (" />
                    <Run Text="{Binding Address, Mode=OneWay}" />
                    <Run Text=")" />
                </TextBlock>
            </Grid>
        </Border>
        <!--Элементы панели-->
        <Grid
            Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="40" />
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <local:ToolbarView 
                Grid.Column="0"
                DataContext="{Binding}"
                />
            <local:TesteeInfoView 
                DataContext="{Binding TesteeInfo}"
                Grid.Column="1"
                />
            <Grid
                Grid.Column="2">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <local:ProgressView 
                    Grid.Row="0"
                    DataContext="{Binding Progress}"
                    VerticalAlignment="Top"
                    />
                <TextBlock 
                    Grid.Row="1"
                    VerticalAlignment="Top"
                    HorizontalAlignment="Center"
                    Foreground="Red"
                    FontSize="12"
                    FontWeight="Medium"
                    Text="{Binding ErrorMessage, Mode=OneWay}"
                    />
            </Grid>
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\LocalNetwork\Views\WorkplaceView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.LocalNetwork.Views
{
    /// <summary>
    /// Interaction logic for WorkplaceView.xaml
    /// </summary>
    public partial class WorkplaceView : UserControl
    {
        public WorkplaceView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\IReportsBrowser.cs


using System;
using System.Windows.Documents;

namespace Updk7.Wpf.Master.Modules.Reports
{
    public interface IReportsBrowser
    {
        void SetReportFactory(Func<FlowDocument> reportFactory);

        void Clear();
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\IReportsController.cs


namespace Updk7.Wpf.Master.Modules.Reports
{
    public interface IReportsController
    {
        void ShowReports(Assets.TesteeShortInfo testee);

        /// <summary>
        /// Обновление текущего отчета
        /// </summary>
        /// <remarks>
        /// Явное использование метода предполагается при наличии панелей настроек отображаемых отчетов
        /// </remarks>
        void UpdateReport();

        Data.Repositories.UpdkRepositories DataRepositories { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\IReportsTesteeInfo.cs


namespace Updk7.Wpf.Master.Modules.Reports
{
    public interface IReportsTesteeInfo
    {
        Assets.TesteeShortInfo SourceData { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\IReportsTree.cs


using System.Collections.Generic;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Reports
{
    public class ReportsTreeSelectedData
    {
        public ReportsTreeSelectedData(TestGroupReportsInfo group, TestReportsInfo test)
        {
            Group = group;
            Test = test;
        }

        public TestGroupReportsInfo Group { get; }

        public TestReportsInfo Test { get; }
    }

    public interface IReportsTree : INotifyPropertyChanged
    {
        ReportsTreeSelectedData SelectedResults { get; }

        void SetTestingResults(IEnumerable<TestGroupReportsInfo> testsGroups);

        void AppendTestGroup(TestGroupReportsInfo testGroupInfo);

        void AppendTest(TestReportsInfo testInfo);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\RegionKeys.cs


namespace Updk7.Wpf.Master.Modules.Reports
{
    public static class RegionKeys
    {
        public static readonly string ReportsTesteeInfoRegion = "ReportsTesteeInfoRegion";
        public static readonly string ReportsTreeRegion = "ReportsTreeRegion";
        public static readonly string ReportsBrowserRegion = "ReportsBrowserRegion";
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\ReportsController.cs


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;

namespace Updk7.Wpf.Master.Modules.Reports
{
    public class ReportsController : IReportsController
    {
        private ViewModels.ReportsViewModel _reportsViewModel;
        private Tests.Reports.Wpf.ReportsFactory _reportsFactory;
        private Tests.Reports.Wpf.ReportInfo _reportInfo = new Tests.Reports.Wpf.ReportInfo();
        private Data.ITestingTreeFactory _testingTreeFactory;
        private Dictionary<string, string> _groupNames = new Dictionary<string, string>();

        public ReportsController(ViewModels.ReportsViewModel reportsViewModel,
            Tests.Reports.Wpf.ReportsFactory reportsFactory,
            Data.ITestingTreeFactory testingTreeFactory) 
            : this(reportsViewModel, reportsFactory, null, testingTreeFactory)
        {
        }

        public ReportsController(ViewModels.ReportsViewModel reportsViewModel,
            Tests.Reports.Wpf.ReportsFactory reportsFactory,
            Data.Repositories.UpdkRepositories dataRepositories,
            Data.ITestingTreeFactory testingTreeFactory)
        {
            _testingTreeFactory = testingTreeFactory;
            _reportsFactory = reportsFactory;
            _reportsViewModel = reportsViewModel;
            _reportsViewModel.ReportsTree.PropertyChanged += onReportsTreePropertyChanged;
            DataRepositories = dataRepositories;
        }

        private Data.Repositories.UpdkRepositories _dataRepositories;

        public Data.Repositories.UpdkRepositories DataRepositories
        {
            get { return _dataRepositories; }
            set
            {
                unscribeFromRepositories(_dataRepositories);
                _dataRepositories = value;
                subscribeToRepositories(_dataRepositories);
                Clear();
            }
        }

        public void ShowReports(Assets.TesteeShortInfo testee)
        {
            Clear();

            _reportsViewModel.TesteeInfo.SourceData = testee;
            _reportsViewModel.ReportsBrowser.Clear();
            createReportsTree();

            updateReportsInfo(testee);
        }

        public void Clear()
        {
            _reportsViewModel.TesteeInfo.SourceData = null;
            _reportsViewModel.ReportsBrowser.Clear();
            _reportsViewModel.ReportsTree.SetTestingResults(null);
        }

        public void UpdateReport()
        {
            AppLog.Current.Info("ReportsController.UpdateReport()");

            var selectedResults = _reportsViewModel.ReportsTree.SelectedResults;

            if (selectedResults == null || selectedResults.Group == null)
                _reportsViewModel.ReportsBrowser.Clear();
            else
            {
                updateReportUser(selectedResults.Group);
                var testee = _reportInfo.Testee.Entitie as Data.Entities.Testee;

                if (selectedResults.Test != null)
                {
                    AppLog.Current.Info($"ReportsController.UpdateReport() -> Create Test report ({testee.Id}, {selectedResults.Test.Title})");

                    _reportsViewModel.ReportsBrowser.SetReportFactory(() => _reportsFactory.Tests.CreateReport(_reportInfo,
                        selectedResults.Test.SourceEntitie.Data));
                }
                else
                {
                    if (selectedResults.Group.Tests == null)
                    {
                        AppLog.Current.Info("ReportsController.UpdateReport() -> No tests in group");
                        return;
                    }

                    var results = createGroupResults(selectedResults.Group);
                    AppLog.Current.Info($"ReportsController.UpdateReport() -> Create Tests Group report ({testee.Id}, {results.GroupKey})");

                    _reportsViewModel.ReportsBrowser.SetReportFactory(() => _reportsFactory.TestsGroups.CreateReport(_reportInfo, results));
                }
            }
        }

        private void createReportsTree()
        {
            if (_reportsViewModel.TesteeInfo.SourceData == null)
                return;

            var results = loadTesteeData(_reportsViewModel.TesteeInfo.SourceData.Entitie.Id);
            _reportsViewModel.ReportsTree.SetTestingResults(results);
        }

        private void onReportsTreePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (_reportsViewModel.TesteeInfo.SourceData == null)
                return;

            if (e.PropertyName == nameof(IReportsTree.SelectedResults))
                UpdateReport();
        }

        private void onResultsDataChanged(object sender, Data.Repositories.RepositoryDataChangedEventArgs e)
        {
            if (_reportsViewModel.TesteeInfo.SourceData == null)
                return;

            var group = e.Entity as Data.Entities.TestGroup;
            if (group != null && group.TesteeId == _reportsViewModel.TesteeInfo.SourceData.Entitie.Id)
            {
                Debug.WriteLine($"Add test group {group.GroupKey}");

                var groupInfo = new TestGroupReportsInfo()
                { 
                    Title = getGroupTitle(group.GroupKey),
                    TestingDate = group.TestingDate,
                    SourceEntitie = group
                };

                _reportsViewModel.ReportsTree.AppendTestGroup(groupInfo);
            }

            var test = e.Entity as Data.Entities.TestResults;
            if (test != null)
            {
                Debug.WriteLine($"Add test {test.TestType.ToString()}");

                var testInfo = new TestReportsInfo()
                {
                    Title = Tests.Metadata.GetTestName(test.TestType),
                    SourceEntitie = test
                };

                _reportsViewModel.ReportsTree.AppendTest(testInfo);
            }
        }

        private void updateReportsInfo(Assets.TesteeShortInfo testee)
        {
            _reportInfo.Testee.Entitie = testee.Entitie;
            _reportInfo.Testee.Name = testee.FullName;
            _reportInfo.Testee.Comment = testee.Comment;
            _reportInfo.Testee.Photo = testee.Photo;
            _reportInfo.Testee.BirthDate = testee.Entitie.BirthDate;
            _reportInfo.UserName = "Психолог не указан";
            _reportInfo.OrganizationTitle = AppStrings.GetString("Organization");
        }

        private void updateReportUser(TestGroupReportsInfo group)
        {
            _reportInfo.UserName = _dataRepositories.Users.GetById(group.SourceEntitie.UserId).FullName;
        }

        private Data.TestGroupResults createGroupResults(TestGroupReportsInfo groupInfo)
        {
            return new Data.TestGroupResults()
            {
                GroupId = groupInfo.SourceEntitie.Id,
                GroupTitle = groupInfo.Title,
                GroupKey = groupInfo.SourceEntitie.GroupKey,
                TestingDate = groupInfo.TestingDate,
                TestResults = groupInfo.Tests.Select(t => t.SourceEntitie.Data).ToArray()
            };
        }

        private IEnumerable<TestGroupReportsInfo> loadTesteeData(int testeeId)
        {
            AppLog.Current.Info($"ReportsController.loadTesteeData({testeeId})");

            try
            {
                return _dataRepositories.Results.GetGroups(testeeId)
                    ?.Select(ge => new TestGroupReportsInfo()
                    {
                        SourceEntitie = ge,
                        TestingDate = ge.TestingDate,
                        Title = getGroupTitle(ge.GroupKey),
                        Tests = _dataRepositories.Results.GetGroupResults(ge.Id)
                            ?.Select(te => new TestReportsInfo()
                            {
                                SourceEntitie = te,
                                Title = Tests.Metadata.GetTestName(te.TestType)
                            }).ToList()
                    }).ToArray();
            }
            catch (Exception ex)
            {
                AppLog.Current.Error(ex.Message);
                return null;
            }
        }

        private string getGroupTitle(string groupKey)
        {
            if (!_groupNames.ContainsKey(groupKey))
            {
                var node = _testingTreeFactory.GetTestingTree().GetNode(groupKey);
                _groupNames[groupKey] = node != null
                    ? node.Title
                    : $"Группа {groupKey}";
            }

            return _groupNames[groupKey];
        }

        private void subscribeToRepositories(Data.Repositories.UpdkRepositories dataRepositories)
        {
            if (dataRepositories != null)
                dataRepositories.Results.DataChanged += onResultsDataChanged;
        }

        private void unscribeFromRepositories(Data.Repositories.UpdkRepositories dataRepositories)
        {
            if (dataRepositories != null)
                dataRepositories.Results.DataChanged -= onResultsDataChanged;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\TestGroupReportsInfo.cs


using System;
using System.Collections.Generic;

namespace Updk7.Wpf.Master.Modules.Reports
{
    public class TestReportsInfo
    {
        public TestReportsInfo()
        {
        }

        public Data.Entities.TestResults SourceEntitie { get; set; }

        public string Title { get; set; }
    }

    public class TestGroupReportsInfo
    {
        public TestGroupReportsInfo()
        {
        }

        public Data.Entities.TestGroup SourceEntitie { get; set; }

        public string Title { get; set; }

        public DateTime TestingDate { get; set; }

        public List<TestReportsInfo> Tests { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\EventListeners\ShowTesteeReportsListener.cs


namespace Updk7.Wpf.Master.Modules.Reports.EventListeners
{
    public class ShowTesteeReportsListener : IEventListener
    {
        private IReportsController _reportsController;

        public ShowTesteeReportsListener(IReportsController reportsController)
        {
            _reportsController = reportsController;
        }

        public void Subscribe(Prism.Events.IEventAggregator eventAggregator)
        {
            eventAggregator
                .GetEvent<Assets.Events.ShowTesteeReportsEvent>()
                .Subscribe(onShowTesteeReports);
        }

        private void onShowTesteeReports(Assets.Events.ShowTesteeReportsEventData data)
        {
            _reportsController.ShowReports(data.Testee);
            ViewModelLocator.Current.NavigateTo("Reports");
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\ViewModels\ReportsBrowserViewModel.cs


using System;
using System.ComponentModel;
using System.Windows.Documents;
using Microsoft.Win32;

namespace Updk7.Wpf.Master.Modules.Reports.ViewModels
{
    public class ReportsBrowserViewModel : Prism.Mvvm.BindableBase, IReportsBrowser
    {
        private static class Strings
        {
            public static readonly string PrintingError = "Не удалось распечатать документ. Проверьте правильность настройки принтера";
            public static readonly string SaveError = "Не удалось сохранить документ. Проверьте что выбранный файл существует и не открыт в другом приложении";
        }

        private string lastDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Recent);
        private Func<FlowDocument> _reportFactory;

        public ReportsBrowserViewModel()
        {
            SaveReportCommand = new Prism.Commands.DelegateCommand(saveReport, () => Report != null);
            PrintReportCommand = new Prism.Commands.DelegateCommand(printReport, () => Report != null);
            ZoomInCommand = new Prism.Commands.DelegateCommand(() => zoomReport(ZoomFactor), () => Report != null);
            ZoomOutCommand = new Prism.Commands.DelegateCommand(() => zoomReport(-ZoomFactor), () => Report != null);
        }

        public Prism.Commands.DelegateCommand SaveReportCommand { get; }

        public Prism.Commands.DelegateCommand PrintReportCommand { get; }

        public Prism.Commands.DelegateCommand ZoomInCommand { get; }

        public Prism.Commands.DelegateCommand ZoomOutCommand { get; }

        public double ZoomFactor { get; } = 10;
        public double ZoomMinimum { get; } = 60.0;
        public double ZoomMaximum { get; } = 160.0;

        private FlowDocument _report;

        public FlowDocument Report
        {
            get { return _report; }
            private set { SetProperty(ref _report, value); }
        }

        private double _zoom = 70.0;

        public double Zoom
        {
            get { return _zoom; }
            private set { SetProperty(ref _zoom, value); }
        }
        
        public void SetReportFactory(Func<FlowDocument> reportFactory)
        {
            _reportFactory = reportFactory;
            Report = _reportFactory?.Invoke();
        }

        public void Clear()
        {
            Report = null;
            _reportFactory = null;
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(Report))
                updateCommandsCanExecute();

            base.OnPropertyChanged(args);
        }

        private void saveReport()
        {
            try
            {
                var dialog = new SaveFileDialog();
                dialog.InitialDirectory = lastDirectory;
                dialog.Filter = "Документы Adobe Acrobat|*.pdf";

                if (dialog.ShowDialog() == true && !string.IsNullOrEmpty(dialog.FileName))
                {
                    DocumentHelper.SaveAsPdf(_reportFactory(), dialog.FileName);
                    lastDirectory = System.IO.Path.GetDirectoryName(dialog.FileName);
                }
            }
            catch (Exception)
            {
                Interactions.InteractionService.Current.ShowErrorMessage(Strings.SaveError);
            }
        }

        private void printReport()
        {
            try
            {
                if (Report != null)
                    DocumentHelper.Print(_reportFactory());
            }
            catch (Exception)
            {
                Interactions.InteractionService.Current.ShowErrorMessage(Strings.PrintingError);
            }
        }

        private void zoomReport(double zoomFactor)
        {
            var value = _zoom + zoomFactor;
            if (value > ZoomMaximum)
                value = ZoomMaximum;
            if (value < ZoomMinimum)
                value = ZoomMinimum;

            Zoom = value;
        }

        private void updateCommandsCanExecute()
        {
            SaveReportCommand.RaiseCanExecuteChanged();
            PrintReportCommand.RaiseCanExecuteChanged();
            ZoomInCommand.RaiseCanExecuteChanged();
            ZoomOutCommand.RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\ViewModels\ReportsTesteeInfoViewModel.cs


namespace Updk7.Wpf.Master.Modules.Reports.ViewModels
{
    public class ReportsTesteeInfoViewModel : Assets.ViewModels.TesteeInfoViewModel, IReportsTesteeInfo
    {
        public ReportsTesteeInfoViewModel()
        {
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\ViewModels\ReportsTreeNodes.cs


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Updk7.Wpf.Master.Modules.Reports.ViewModels
{
    /// <summary>
    /// Базовый класс узла дерева тестов
    /// </summary>
    public abstract class ReportsTreeNode : Prism.Mvvm.BindableBase
    {
        public ReportsTreeNode()
        {
        }

        public TestGroupReportsInfo SourceGroup { get; set; }

        public TestReportsInfo SourceTest { get; set; }

        private string _title;

        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }

        private bool _isSelected;

        public bool IsSelected
        {
            get { return _isSelected; }
            set { SetProperty(ref _isSelected, value); }
        }
    }

    /// <summary>
    /// Узел дерева, содержащий тест
    /// </summary>
    public class ReportsTreeTestNode : ReportsTreeNode
    {
        public ReportsTreeTestNode()
        {
        }
    }

    /// <summary>
    /// Узел дерева. сожержащий набор групп тестов
    /// </summary>
    public class ReportsTreeTestsGroupSectionNode : ReportsTreeNode
    {
        public ReportsTreeTestsGroupSectionNode()
        {
        }

        public ObservableCollection<ReportsTreeNode> Nodes { get; } =
            new ObservableCollection<ReportsTreeNode>();
    }

    /// <summary>
    /// Узел дерева, содержащий группу тестов
    /// </summary>
    public class ReportsTreeTestsGroupNode : ReportsTreeTestsGroupSectionNode
    {
        public ReportsTreeTestsGroupNode()
        {
        }

        private DateTime? _date;

        public DateTime? Date
        {
            get { return _date; }
            set { SetProperty(ref _date, value); }
        }

        public void AppendTestNode(ReportsTreeTestNode testNode)
        {
            if (SourceGroup.Tests == null)
                SourceGroup.Tests = new List<TestReportsInfo>();

            Nodes.Add(testNode);
            if (SourceGroup.Tests.Find(t => t.SourceEntitie.Id == testNode.SourceTest.SourceEntitie.Id) == null)
                SourceGroup.Tests.Add(testNode.SourceTest);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\ViewModels\ReportsTreeViewModel.cs


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;

namespace Updk7.Wpf.Master.Modules.Reports.ViewModels
{
    public class ReportsTreeViewModel : Prism.Mvvm.BindableBase, IReportsTree
    {
        private static class Strings
        {
            // Строка используется в триггере в ReportsTreeView
            public static readonly string RecentGroups = "Недавние обследования";
        }

        private const int RecentResultsCount = 10;

        public ReportsTreeViewModel()
        {
        }

        public ObservableCollection<ReportsTreeNode> Nodes { get; } =
            new ObservableCollection<ReportsTreeNode>();

        private ReportsTreeSelectedData _selectedResults;

        public ReportsTreeSelectedData SelectedResults
        {
            get { return _selectedResults; }
            private set { SetProperty(ref _selectedResults, value); }
        }
        
        public void SetTestingResults(IEnumerable<TestGroupReportsInfo> testsGroups)
        {
            Clear();

            if (testsGroups != null && testsGroups.Count() != 0)
            {
                foreach (var node in createReportsTree(testsGroups))
                    Nodes.Add(node);

                traverseTree(Nodes, n => n.PropertyChanged += onNodePropertyChanged);
            }
        }

        public void Clear()
        {
            traverseTree(Nodes, n => n.PropertyChanged -= onNodePropertyChanged);
            Nodes.Clear();
            SelectedResults = null;
        }

        public void AppendTestGroup(TestGroupReportsInfo testGroupInfo)
        {
            Func<bool, ReportsTreeTestsGroupNode> createGroupWithAttachedEvents = setTitleAsDate =>
            {
                var groupNode = createTestsGroupNode(testGroupInfo, setTitleAsDate);
                groupNode.PropertyChanged += onNodePropertyChanged;
                foreach (var testNode in groupNode.Nodes)
                    testNode.PropertyChanged += onNodePropertyChanged;
                return groupNode;
            };

            var recentNode = Nodes.FirstOrDefault(n => n.Title == Strings.RecentGroups);
            if (recentNode != null)
            {
                var sectionNode = recentNode as ReportsTreeTestsGroupSectionNode;
                sectionNode.Nodes.Insert(0, createGroupWithAttachedEvents(false));
            }

            var sortedGroup = Nodes.FirstOrDefault(n => n.Title == testGroupInfo.Title) as ReportsTreeTestsGroupSectionNode;
            if (sortedGroup == null)
            {
                var sectionNode = new ReportsTreeTestsGroupSectionNode();
                sectionNode.Title = testGroupInfo.Title;
                sectionNode.PropertyChanged += onNodePropertyChanged;
                sortedGroup = sectionNode;
                Nodes.Add(sortedGroup);
            }

            sortedGroup.Nodes.Insert(0, createGroupWithAttachedEvents(true));
        }

        public void AppendTest(TestReportsInfo testInfo)
        {
            traverseTree(Nodes, n => 
            {
                var group = n as ReportsTreeTestsGroupNode;
                if (group != null && group.SourceGroup.SourceEntitie.Id == testInfo.SourceEntitie.TestGroupId)
                {
                    var testNode = createTestNode(group.SourceGroup, testInfo);
                    testNode.PropertyChanged += onNodePropertyChanged;
                    group.AppendTestNode(testNode);
                }
            });
        }

        private IEnumerable<ReportsTreeNode> createReportsTree(IEnumerable<TestGroupReportsInfo> testsGroups)
        {
            var sortedGroups = testsGroups.OrderByDescending(g => g.TestingDate).ToArray();

            var recentNode = new ReportsTreeTestsGroupSectionNode();
            recentNode.Title = Strings.RecentGroups;
            foreach (var group in sortedGroups.Take(RecentResultsCount))
                recentNode.Nodes.Add(createTestsGroupNode(group));
            yield return recentNode;

            var groupSections = sortedGroups.GroupBy(g => g.Title);
            foreach (var section in groupSections)
            {
                var sectionNode = new ReportsTreeTestsGroupSectionNode();
                sectionNode.Title = section.Key;
                foreach (var group in section)
                    sectionNode.Nodes.Add(createTestsGroupNode(group, setTitleAsDate: true));
                yield return sectionNode;
            }
        }

        private ReportsTreeTestsGroupNode createTestsGroupNode(TestGroupReportsInfo testsGroup, 
            bool setTitleAsDate = false)
        {
            var groupNode = new ReportsTreeTestsGroupNode();
            groupNode.SourceGroup = testsGroup;
            if (setTitleAsDate)
                groupNode.Title = testsGroup.TestingDate.ToString("yyyy.MM.dd HH.mm");
            else
            {
                groupNode.Title = testsGroup.Title;
                groupNode.Date = testsGroup.TestingDate;
            }

            if (testsGroup.Tests != null)
            {
                foreach (var test in testsGroup.Tests)
                    groupNode.Nodes.Add(createTestNode(testsGroup, test));
            }

            return groupNode;
        }

        private ReportsTreeTestNode createTestNode(TestGroupReportsInfo groupInfo, TestReportsInfo testInfo)
        {
            var testNode = new ReportsTreeTestNode();
            testNode.Title = testInfo.Title;
            testNode.SourceTest = testInfo;
            testNode.SourceGroup = groupInfo;

            return testNode;
        }

        private void onNodePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ReportsTreeNode.IsSelected))
                updateSelectionNode(sender as ReportsTreeNode);
        }

        private void updateSelectionNode(ReportsTreeNode node)
        {
            SelectedResults = node.IsSelected && node.SourceGroup != null
                ? new ReportsTreeSelectedData(node.SourceGroup, node.SourceTest)
                : null;
        }

        private void traverseTree(IEnumerable<ReportsTreeNode> nodes, Action<ReportsTreeNode> action)
        {
            if (nodes == null)
                return;

            foreach (var node in nodes)
            {
                action(node);
                traverseTree((node as ReportsTreeTestsGroupSectionNode)?.Nodes, action);
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\ViewModels\ReportsViewModel.cs


namespace Updk7.Wpf.Master.Modules.Reports.ViewModels
{
    public class ReportsViewModel : Prism.Mvvm.BindableBase
    {
        public ReportsViewModel(IReportsTesteeInfo testeeInfo, IReportsTree reportsTree,
            IReportsBrowser reportsBrowser)
        {
            TesteeInfo = testeeInfo;
            ReportsTree = reportsTree;
            ReportsBrowser = reportsBrowser;
        }

        public IReportsTesteeInfo TesteeInfo { get; }

        public IReportsTree ReportsTree { get; }

        public IReportsBrowser ReportsBrowser { get; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\Views\ReportsBrowserToolbarView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Reports.Views.ReportsBrowserToolbarView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" >
    <Grid
        Width="50">
        <StackPanel
            Grid.Column="0"
            Margin="3">
            <Button 
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Сохранить отчет в .pdf формате"
                BorderThickness="2"
                Command="{Binding SaveReportCommand}">
                <Image Margin="3" Source="{StaticResource PdfImage}" />
            </Button>
            <Button 
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Распечатать отчет"
                BorderThickness="2"
                Command="{Binding PrintReportCommand}">
                <Image Margin="3" Source="{StaticResource PrinterImage}" />
            </Button>
            <Button 
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Увеличить масштаб отчета"
                BorderThickness="2"
                Command="{Binding ZoomInCommand}">
                <Image Margin="3" Source="{StaticResource ZoomInImage}" />
            </Button>
            <Button 
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Уменьшить масштаб отчета"
                BorderThickness="2"
                Command="{Binding ZoomOutCommand}">
                <Image Margin="3" Source="{StaticResource ZoomOutImage}" />
            </Button>
        </StackPanel>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\Views\ReportsBrowserToolbarView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Reports.Views
{
    /// <summary>
    /// Interaction logic for ReportsBrowserToolbarView.xaml
    /// </summary>
    public partial class ReportsBrowserToolbarView : UserControl
    {
        public ReportsBrowserToolbarView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\Views\ReportsBrowserView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Reports.Views.ReportsBrowserView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.Reports.Views"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             xmlns:converters="clr-namespace:Updk7.Wpf.Converters;assembly=Updk7.Wpf"
             mc:Ignorable="d">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <!--Панель инструментов-->
        <local:ReportsBrowserToolbarView 
            Grid.Column="0"
            DataContext="{Binding}"
            />
        <!--Отчет-->
        <Grid
            Grid.Column="1">
            <TextBlock
                Text="Для выбранного обследования отсутствует форма отчета"
                VerticalAlignment="Top"
                HorizontalAlignment="Center"
                FontSize="16"
                Margin="10"
                FontWeight="Medium"
                TextWrapping="Wrap"
                TextAlignment="Center"
                Visibility="{Binding Report, Converter={converters:ObjectToVisibilityConverter InverseFlag=True}}"
                />
            <FlowDocumentScrollViewer
                Margin="5"
                IsToolBarVisible="False"
                ZoomIncrement="10"
                MinZoom="{Binding ZoomMinimum, Mode=OneTime}"
                MaxZoom="{Binding ZoomMaximum, Mode=OneTime}"
                Zoom="{Binding Zoom, Mode=OneWay}"
                Visibility="{Binding Report, Converter={converters:ObjectToVisibilityConverter}}"
                Document="{Binding Report}"
                BorderBrush="Transparent"
                VerticalAlignment="Stretch"
                HorizontalAlignment="Stretch"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\Views\ReportsBrowserView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Reports.Views
{
    /// <summary>
    /// Interaction logic for ReportsBrowserView.xaml
    /// </summary>
    public partial class ReportsBrowserView : UserControl
    {
        public ReportsBrowserView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\Views\ReportsTesteeInfoView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Reports.Views.ReportsTesteeInfoView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.Reports.Views"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             xmlns:converters="clr-namespace:Updk7.Wpf.Converters;assembly=Updk7.Wpf"
             mc:Ignorable="d" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock 
            Grid.Row="0"
            FontSize="18"
            FontWeight="Medium"
            Margin="5,5,1,1"
            TextWrapping="Wrap"
            Text="{Binding FullName, Mode=OneWay}"
            />
        <TextBlock 
            Grid.Row="1"
            FontSize="14"
            FontWeight="Normal"
            Margin="5,1,1,5"
            TextWrapping="Wrap"
            Visibility="{Binding Comment, Converter={converters:ObjectToVisibilityConverter}, Mode=OneWay}" 
            Text="{Binding Comment, Mode=OneWay}" />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\Views\ReportsTesteeInfoView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Reports.Views
{
    /// <summary>
    /// Interaction logic for ReportsTesteeInfoView.xaml
    /// </summary>
    public partial class ReportsTesteeInfoView : UserControl
    {
        public ReportsTesteeInfoView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\Views\ReportsTreeView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Reports.Views.ReportsTreeView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             xmlns:viewModels="clr-namespace:Updk7.Wpf.Master.Modules.Reports.ViewModels"
             xmlns:converters="clr-namespace:Updk7.Wpf.Converters;assembly=Updk7.Wpf"
             mc:Ignorable="d">
    <Grid>
        <TreeView
            Grid.Column="1"
            Margin="0,0,3,0"
            Background="Transparent"
            BorderBrush="Transparent"
            BorderThickness="0"
            ItemsSource="{Binding Nodes}"
            ScrollViewer.HorizontalScrollBarVisibility="Disabled">
            <TreeView.Resources>
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
                <!--Стиль текстового блока-->
                <Style x:Key="TextBlockStyle" TargetType="{x:Type TextBlock}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding IsSelected}" Value="True">
                            <Setter Property="FontSize" Value="18" />
                            <Setter Property="FontWeight" Value="Bold" />
                            <Setter Property="Focusable" Value="False" />
                        </DataTrigger>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True" />
                                <Condition Binding="{Binding IsSelected}" Value="False" />
                            </MultiDataTrigger.Conditions>
                            <Setter Property="Foreground" Value="Gray" />
                        </MultiDataTrigger>
                    </Style.Triggers>
                </Style>
                <!--Шаблон для набора групп тестов-->
                <HierarchicalDataTemplate
                    DataType="{x:Type viewModels:ReportsTreeTestsGroupSectionNode}"
                    ItemsSource="{Binding Nodes}">
                    <TextBlock
                        FontSize="14"
                        TextWrapping="Wrap"
                        Padding="0,3,5,3"
                        Style="{StaticResource TextBlockStyle}" 
                        Text="{Binding Title}"
                        />
                </HierarchicalDataTemplate>
                <!--Шаблон для групп тестов-->
                <HierarchicalDataTemplate 
                    DataType="{x:Type viewModels:ReportsTreeTestsGroupNode}" 
                    ItemsSource="{Binding Nodes}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <TextBlock
                            x:Name="PART_TitleTextBlock"
                            Grid.Row="0"
                            TextWrapping="Wrap"
                            FontSize="14"
                            Padding="0,3,5,3"
                            Style="{StaticResource TextBlockStyle}"
                            Text="{Binding Title}"
                            />
                        <TextBlock
                            x:Name="PART_DateTextBlock"
                            Grid.Row="1"
                            FontSize="10"
                            FontWeight="Bold"
                            Margin="3,0,0,0"
                            Visibility="{Binding Date, Converter={converters:ObjectToVisibilityConverter}}" 
                            Text="{Binding Date, StringFormat=\{0:yyyy.MM.dd HH.mm\}}" 
                            />
                    </Grid>
                </HierarchicalDataTemplate>
                <!--Шаблон для тестов-->
                <HierarchicalDataTemplate
                    DataType="{x:Type viewModels:ReportsTreeTestNode}">
                    <TextBlock
                        FontSize="14"
                        TextWrapping="Wrap"
                        Padding="0,3,5,3"
                        Style="{StaticResource TextBlockStyle}" 
                        Text="{Binding Title}"
                        />
                </HierarchicalDataTemplate>
            </TreeView.Resources>
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsExpanded" Value="False" />
                    <Setter Property="IsSelected" Value="{Binding IsSelected}" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Title}" Value="Недавние обследования">
                            <Setter Property="IsExpanded" Value="True" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </TreeView.ItemContainerStyle>
        </TreeView>
    </Grid>
</UserControl>
*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\Views\ReportsTreeView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Reports.Views
{
    /// <summary>
    /// Interaction logic for ReportsTreeView.xaml
    /// </summary>
    public partial class ReportsTreeView : UserControl
    {
        public ReportsTreeView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\Views\ReportsView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Reports.Views.ReportsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:keys="clr-namespace:Updk7.Wpf.Master.Modules.Reports"
             xmlns:styles="clr-namespace:Updk7.Wpf.Styles;assembly=Updk7.Wpf"
             mc:Ignorable="d"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             styles:TabItemHeader.Text="Отчеты">
    <Grid
        Margin="2,5,1,1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*" MinWidth="450" />
            <ColumnDefinition Width="3*" />
        </Grid.ColumnDefinitions>
        <Grid
            Grid.Column="0">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <!--Информация о сотруднике-->
            <Grid
                Grid.Row="0">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Border
                    Grid.Row="0"
                    Background="DeepSkyBlue"
                    Padding="10,3,2,3">
                    <TextBlock 
                        FontSize="14"
                        FontWeight="Bold"
                        Text="Сотрудник"
                        />
                </Border>
                <ContentControl 
                    Grid.Row="1"
                    prism:RegionManager.RegionName="{x:Static keys:RegionKeys.ReportsTesteeInfoRegion}"
                    />
            </Grid>
            <!--Дерево пройденных обследований-->
            <Grid
                Grid.Row="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Border
                    Grid.Row="0"
                    Background="DeepSkyBlue"
                    Padding="10,3,2,3">
                    <TextBlock 
                        FontSize="14"
                        FontWeight="Bold"
                        Text="Обследования и отчеты"
                        />
                </Border>
                <ContentControl
                    Grid.Row="1"
                    prism:RegionManager.RegionName="{x:Static keys:RegionKeys.ReportsTreeRegion}"
                    />
            </Grid>
        </Grid>
        <!--Отчет о тестировании-->
        <Grid
            Grid.Column="1"
            Margin="3,0,0,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Border
                Grid.Row="0"
                Background="DeepSkyBlue"
                Padding="10,3,2,3">
                <TextBlock 
                    FontSize="14"
                    FontWeight="Bold"
                    Text="Обследования и отчеты"
                    />
            </Border>
            <ContentControl 
                Grid.Row="1"
                prism:RegionManager.RegionName="{x:Static keys:RegionKeys.ReportsBrowserRegion}"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Reports\Views\ReportsView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Reports.Views
{
    /// <summary>
    /// Interaction logic for ReportsView.xaml
    /// </summary>
    public partial class ReportsView : UserControl
    {
        public ReportsView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ITesteeArchiveSelection.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Testees
{
    public interface ITesteeArchiveSelection : INotifyPropertyChanged
    {
        Data.Entities.Testee SelectedTestee { get; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ITesteeDataUpdater.cs


namespace Updk7.Wpf.Master.Modules.Testees
{
    public interface ITesteeDataUpdater
    {
        void UpdateTesteeData(Assets.TestGroupInfo group);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ITesteeEditor.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Testees
{
    public interface ITesteeEditor : INotifyPropertyChanged
    {
        string Title { get; }

        bool HasErrors { get; }

        string LastError { get; }

        void SetData(Data.Entities.Testee testee);

        void UpdateEntitie(Data.Entities.Testee testee);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ITesteeEditorController.cs


using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Media.Imaging;

namespace Updk7.Wpf.Master.Modules.Testees
{
    public interface ITesteeEditorController : INotifyPropertyChanged
    {
        Type EntitieType { get; set; }

        bool HasErrors { get; }

        EditorMode EditorMode { get; }

        Data.Entities.Testee SourceData { get; set; }

        BitmapImage SourcePhoto { get; set; }

        void RegisterEditor(ITesteeEditor editor, FrameworkElement editorView);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ITesteeSelection.cs


using System.Collections.Generic;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Testees
{
    public interface ITesteeSelection : INotifyPropertyChanged
    {
        Data.Entities.Testee SelectedTestee { get; set; }

        bool IsSelectedTesteeBusy { get; }

        Assets.TesteeShortInfo GetSelectedTesteeShortInfo();

        IEnumerable<Assets.TesteeShortInfo> GetAllTesteesShortInfo();

        void SetTesteeUnderTest(int testeeId, bool isUnderTest);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ITesteesToolbar.cs


using System.Windows;

namespace Updk7.Wpf.Master.Modules.Testees
{
    public interface ITesteesToolbar
    {
        void AddElement(FrameworkElement toolbarElement);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ITestResultsSaveStrategy.cs


using System;

namespace Updk7.Wpf.Master.Modules.Testees
{
    public interface ITestResultsSaveStrategy
    {
        event EventHandler<Assets.TestGroupInfo> TestGroupSaved;

        void InitializeData(Data.Entities.Testee testee, Data.TestingPlan testingPlan);

        void SaveResults(Data.Entities.Testee testee, Tests.TestResults testResults);

        void TestingComplete(Data.Entities.Testee testee);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\RegionKeys.cs


namespace Updk7.Wpf.Master.Modules.Testees
{
    public static class RegionKeys
    {
        public static readonly string TesteeSelectionRegion = "TesteeSelectionRegion";
        public static readonly string TesteesToolbarRegion = "TesteesToolbarRegion";
        public static readonly string TesteesArchiveRegion = "TesteesArchiveRegion";
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\TesteeDataUpdaterStub.cs


namespace Updk7.Wpf.Master.Modules.Testees
{
    public class TesteeDataUpdaterStub : ITesteeDataUpdater
    {
        public TesteeDataUpdaterStub()
        {
        }

        public void UpdateTesteeData(Assets.TestGroupInfo group)        
        {
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\TesteeSelectionGrid.cs


using System.Windows;
using System.Windows.Controls;

namespace Updk7.Wpf.Master.Modules.Testees
{
    public class TesteeSelectionGrid
    {
        private static DataGrid _targetGrid;

        public static bool GetIsMasterGrid(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsMasterGridProperty);
        }

        public static void SetIsMasterGrid(DependencyObject obj, bool value)
        {
            obj.SetValue(IsMasterGridProperty, value);
        }

        public static readonly DependencyProperty IsMasterGridProperty =
            DependencyProperty.RegisterAttached(
                "IsMasterGrid", 
                typeof(bool), 
                typeof(TesteeSelectionGrid), 
                new PropertyMetadata(false,
                    new PropertyChangedCallback(onIsMasterGridChanged)));

        public static void ScrollToSelectedItem()
        {
            if (_targetGrid != null)
                _targetGrid.ScrollIntoView(_targetGrid.SelectedItem);
        }

        private static void onIsMasterGridChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            _targetGrid = o as DataGrid;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\TestResultsSaveStrategy.cs


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Updk7.Wpf.Master.Modules.Testees
{
    public class TestResultsSaveStrategy : ITestResultsSaveStrategy
    {
        private class TestInfo
        {
            public TestInfo(Tests.TestType test)
            {
                Test = test;
            }

            public Tests.TestType Test { get; set; }

            public bool Finished => Results != null;

            public Tests.TestResults Results { get; set; }
        }

        private class TestGroupInfo
        {
            public Data.TestGroupRun SourceData { get; set; }

            public Data.Entities.TestGroup GroupEntitie { get; set; }

            public IEnumerable<TestInfo> Tests { get; set; }

            public bool Saved => GroupEntitie != null;

            public bool Complete => Tests.All(t => t.Finished);
        }

        private class TestingProgressInfo
        {
            public IEnumerable<TestGroupInfo> TestGroups { get; set; }
        }

        private Dictionary<int, TestingProgressInfo> _resultsDictionary = new Dictionary<int, TestingProgressInfo>();
        private Data.Repositories.ITestResultsRepository _resultsRepository;
        private Assets.IAutorizationManager _autorizationManager;

        public TestResultsSaveStrategy(Data.Repositories.ITestResultsRepository resultsRepository, 
            Assets.IAutorizationManager autorizationManager)
        {
            _resultsRepository = resultsRepository;
            _autorizationManager = autorizationManager;
        }

        public event EventHandler<Assets.TestGroupInfo> TestGroupSaved;

        public void InitializeData(Data.Entities.Testee testee, Data.TestingPlan testingPlan)
        {
            Debug.Assert(!_resultsDictionary.ContainsKey(testee.Id));

            _resultsDictionary[testee.Id] = new TestingProgressInfo()
            { 
                TestGroups = testingPlan.TestsGroups.Select(groupRun => new TestGroupInfo()
                { 
                    SourceData = groupRun,
                    Tests = groupRun.Tests.Select(t => new TestInfo(t.TestType)).ToArray()
                }).ToArray()
            };
        }

        public void SaveResults(Data.Entities.Testee testee, Tests.TestResults testResults)
        {
            if (testResults == null || testResults.ContainsException)
                return;

            Debug.Assert(_resultsDictionary.ContainsKey(testee.Id));

            var progressInfo = _resultsDictionary[testee.Id];
            foreach (var group in progressInfo.TestGroups)
            {
                var testSelection = group.Tests.Where(t => t.Test == testResults.Test);
                Debug.Assert(testSelection.Count() <= 1);

                var test = testSelection.FirstOrDefault();
                if (test == null)
                    continue;

                test.Results = testResults;
                if (!group.Saved)
                    group.GroupEntitie = saveTestGroup(testee, group, testResults.BeginTime);

                saveTestResults(group.GroupEntitie, testResults);
                
                if (group.Complete && TestGroupSaved != null)
                {
                    var results = group.Tests.Select(r => r.Results).ToArray();
                    var info = new Assets.TestGroupInfo(testee, group.SourceData, results, group.GroupEntitie.TestingDate);
                    TestGroupSaved(this, info);
                }
            }
        }

        public void TestingComplete(Data.Entities.Testee testee)
        {
            _resultsDictionary.Remove(testee.Id);
        }

        private Data.Entities.TestGroup saveTestGroup(Data.Entities.Testee testee, TestGroupInfo groupInfo,
            DateTime testingDate)
        {
            var entitie = new Data.Entities.TestGroup()
            {
                GroupKey = groupInfo.SourceData.GroupKey,
                TestingDate = testingDate,
                TesteeId = testee.Id,
                UserId = _autorizationManager.AutorizedUser.Id
            };
            _resultsRepository.Add(entitie);

            return entitie;
        }

        private Data.Entities.TestResults saveTestResults(Data.Entities.TestGroup group, Tests.TestResults results)
        {
            var entitie = new Data.Entities.TestResults()
            {
                TestType = results.Test,
                TestGroupId = group.Id,
                Data = results
            };
            _resultsRepository.Add(entitie);

            return entitie;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Commands\AddTesteeCommand.cs


using System;

namespace Updk7.Wpf.Master.Modules.Testees.Commands
{
    public class AddTesteeCommand : Wpf.Commands.CommandBase
    {
        private class ErrorMessages
        {
            public static readonly string SaveDataFailed = "Не удалось добавить испытуемого в БД";
        }

        private Data.Repositories.ITesteesRepository _testeesRepository;
        private ITesteeEditorController _editorController;

        public AddTesteeCommand(Data.Repositories.ITesteesRepository testeesRepository, ITesteeEditorController editorController)
        {
            _testeesRepository = testeesRepository;
            _editorController = editorController;
        }

        public override bool CanExecute(object parameter)
        {
            return true;
        }

        public override void Execute(object parameter)
        {
            _editorController.SourceData = null;

            var interaction = new Interactions.ShowTesteeEditorInteraction();
            interaction.EditorMode = EditorMode.Create;
            interaction.EditorController = _editorController;
            interaction.EditorView = ViewModelLocator.Current.ResolveView<ITesteeEditorController>();

            Wpf.Interactions.InteractionService.Current.ShowInteractionModel(interaction, () => 
            {
                interaction.EditorView.DataContext = null;
                interaction.EditorView = null;
                interaction.EditorController = null;

                if (interaction.Confirmed)
                {
                    var entitie = _editorController.SourceData;
                    entitie.RegistrationDate = DateTime.Now;

                    try
                    {
                        _testeesRepository.Add(entitie);
                        if (_editorController.SourcePhoto != null)
                        {
                            var bytes = BitmapImageHelper.GetBytes(_editorController.SourcePhoto);
                            _testeesRepository.Photos.SetImageData(entitie.Id, bytes);
                        }

                        AppLog.Current.Info($"Testee added ({entitie.Id})");
                    }
                    catch (Exception ex)
                    {
                        Wpf.Interactions.InteractionService.Current.ShowErrorMessage(ErrorMessages.SaveDataFailed);
                        AppLog.Current.Error("*** SAVE DATA EXCEPTION - Testee");
                        AppLog.Current.Error(ex.Message);
                    }
                }
            });
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Commands\CreateTestingPlanCommand.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Testees.Commands
{
    public class CreateTestingPlanCommand : Wpf.Commands.CommandBase
    {
        private static class ErrorStrings
        {
            public static readonly string NoActivationKey = "Не найден ключ активации Rutoken. Тестирование не может быть запущено";
        }

        private Prism.Events.IEventAggregator _eventAggregator;
        private ITesteeSelection _testeeSelection;
        private Rutoken.Common.RutokenMonitor _rutokenMonitor;

        public CreateTestingPlanCommand(ITesteeSelection testeeSelection, 
            Prism.Events.IEventAggregator eventAggregator,
            Rutoken.Common.RutokenMonitor rutokenMonitor)
        {
            _rutokenMonitor = rutokenMonitor;
            _eventAggregator = eventAggregator;
            _testeeSelection = testeeSelection;
            _testeeSelection.PropertyChanged += onTesteeSelectionPropertyChanged;
        }

        public override bool CanExecute(object parameter)
        {
            return _testeeSelection.SelectedTestee != null
                && !_testeeSelection.IsSelectedTesteeBusy;
        }

        public override void Execute(object parameter)
        {
            if (!_rutokenMonitor.ContainsFeature())
            {
                Wpf.Interactions.InteractionService.Current.ShowErrorMessage(ErrorStrings.NoActivationKey);
                return;
            }

            _eventAggregator
                .GetEvent<Assets.Events.CreateTestingPlanEvent>()
                .Publish(new Assets.Events.CreateTestingPlanEventData(_testeeSelection.GetSelectedTesteeShortInfo()));
        }

        private void onTesteeSelectionPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ITesteeSelection.SelectedTestee) ||
                e.PropertyName == nameof(ITesteeSelection.IsSelectedTesteeBusy) )
            {
                RaiseCanExecuteChanged();
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Commands\EditTesteeCommand.cs


using System;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Testees.Commands
{
    public class EditTesteeCommand : Wpf.Commands.CommandBase
    {
        private class ErrorMessages
        {
            public static readonly string SaveDataFailed = "Не удалось изменить данные испытуемого в БД";
        }

        private ITesteeSelection _testeeSelection;
        private ITesteeEditorController _editorController;
        private Data.Repositories.ITesteesRepository _testeesRepository;

        public EditTesteeCommand(ITesteeSelection testeeSelection, ITesteeEditorController editorController,
            Data.Repositories.ITesteesRepository testeesRepository)
        {
            _editorController = editorController;
            _testeesRepository = testeesRepository;
            _testeeSelection = testeeSelection;
            _testeeSelection.PropertyChanged += onTesteeSelectionPropertyChanged;
        }

        public override bool CanExecute(object parameter)
        {
            return _testeeSelection.SelectedTestee != null;
        }

        public override void Execute(object parameter)
        {
            var photoBytes = _testeesRepository.Photos.GetImageData(_testeeSelection.SelectedTestee.Id);
            var photoOrigin = photoBytes != null ? BitmapImageHelper.CreateImage(photoBytes) : null;
            _editorController.SourceData = _testeeSelection.SelectedTestee;
            _editorController.SourcePhoto = photoOrigin;

            var interaction = new Interactions.ShowTesteeEditorInteraction();
            interaction.EditorMode = EditorMode.Edit;
            interaction.EditorController = _editorController;
            interaction.EditorView = ViewModelLocator.Current.ResolveView<ITesteeEditorController>();

            Wpf.Interactions.InteractionService.Current.ShowInteractionModel(interaction, () =>
            {
                interaction.EditorView.DataContext = null;
                interaction.EditorView = null;
                interaction.EditorController = null;

                try
                {
                    if (interaction.Confirmed)
                    {
                        var entitie = _editorController.SourceData;
                        _testeesRepository.Update(entitie);
                        if (photoOrigin != _editorController.SourcePhoto && _editorController.SourcePhoto != null)
                        {
                            var bytes = BitmapImageHelper.GetBytes(_editorController.SourcePhoto);
                            _testeesRepository.Photos.SetImageData(entitie.Id, bytes);
                        }

                        AppLog.Current.Info($"Testee updated ({entitie.Id})");
                    }
                }
                catch (Exception ex)
                {
                    Wpf.Interactions.InteractionService.Current.ShowErrorMessage(ErrorMessages.SaveDataFailed);
                    AppLog.Current.Error("*** SAVE DATA EXCEPTION - Testee");
                    AppLog.Current.Error(ex.Message);
                }
            });
        }

        private void onTesteeSelectionPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ITesteeSelection.SelectedTestee))
                RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Commands\FindTesteeCommand.cs


using System.Diagnostics;

namespace Updk7.Wpf.Master.Modules.Testees.Commands
{
    internal class FindTesteeCommand : Wpf.Commands.CommandBase
    {
        private ITesteeSelection _testeesSelection;

        public FindTesteeCommand(ITesteeSelection testeeSelection)
        {
            _testeesSelection = testeeSelection;
        }

        public override bool CanExecute(object parameter)
        {
            return true;
        }

        public override void Execute(object parameter)
        {
            var interaction = new Interactions.FindTesteeInteraction();
            interaction.SetData(_testeesSelection.GetAllTesteesShortInfo());

            Wpf.Interactions.InteractionService.Current.ShowInteractionModel(interaction, () =>
            {
                if (interaction.SelectedTestee != null)
                {
                    _testeesSelection.SelectedTestee = interaction.SelectedTestee.Entitie;
                    TesteeSelectionGrid.ScrollToSelectedItem();
                }
            });
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Commands\LoadPhotoCommand.cs


using System;
using System.ComponentModel;
using System.Windows.Media.Imaging;
using Microsoft.Win32;

namespace Updk7.Wpf.Master.Modules.Testees.Commands
{
    public class LoadPhotoCommand : Wpf.Commands.CommandBase
    {
        private ITesteeEditorController _editorController;

        public LoadPhotoCommand(ITesteeEditorController editorController)
        {
            _editorController = editorController;
            _editorController.PropertyChanged += onEditorControllerPropertyChanged;
        }

        private BitmapImage _photo;

        public BitmapImage Photo
        {
            get { return _photo; }
            set { SetProperty(ref _photo, value); }
        } 

        public override bool CanExecute(object parameter) => true;

        public override void Execute(object parameter)
        {
            var dialog = new OpenFileDialog();
            dialog.Filter = "Pictures|*.jpg; *.png; *.bmp";
            var file = dialog.ShowDialog();
            if (!file.HasValue || !file.Value)
                return;

            var uri = new Uri(dialog.FileName, UriKind.Absolute);
            try
            {
                _editorController.SourcePhoto = new BitmapImage(uri);
            }
            catch
            {
                Wpf.Interactions.InteractionService.Current
                    .ShowErrorMessage("Не удалось загрузить фотографию испытуемого");
            }
        }

        private void onEditorControllerPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ITesteeEditorController.SourcePhoto))
                Photo = _editorController.SourcePhoto;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Commands\RemoveTesteeToArchiveCommand.cs


using System;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Testees.Commands
{
    public class RemoveTesteeToArchiveCommand : Wpf.Commands.CommandBase
    {
        private class ErrorMessages
        {
            public static readonly string SaveDataFailed = "Не удалось переместить испытуемого в архив";
        }

        private static readonly string RemoveMessage = @"Переместить выбранного сотрудника ({0}) в архив? Впоследствии в окне архива его можно будет восстановить";

        private Data.Repositories.ITesteesRepository _testeesRepository;
        private ITesteeSelection _testeeSelection;

        public RemoveTesteeToArchiveCommand(ITesteeSelection testeeSelection, 
            Data.Repositories.ITesteesRepository testeesRepository)
        {
            _testeesRepository = testeesRepository;
            _testeeSelection = testeeSelection;
            _testeeSelection.PropertyChanged += onTesteeSelectionPropertyChanged;
        }

        public override bool CanExecute(object parameter)
        {
            return _testeeSelection.SelectedTestee != null;
        }

        public override void Execute(object parameter)
        {
            var questionInteraction = new Wpf.Interactions.ConfirmationInteraction();
            questionInteraction.Message = string.Format(RemoveMessage, _testeeSelection.SelectedTestee.FullName);

            Wpf.Interactions.InteractionService.Current.ShowInteractionModel(questionInteraction, () =>
            {
                if (questionInteraction.Confirmed)
                {
                    try
                    {
                        var testee = _testeeSelection.SelectedTestee;
                        testee.Removed = true;
                        testee.RemoveDate = DateTime.Now;
                        _testeesRepository.Update(testee);

                        AppLog.Current.Info($"Testee removed ({testee.Id})");
                    }
                    catch (Exception ex)
                    {
                        Wpf.Interactions.InteractionService.Current.ShowErrorMessage(ErrorMessages.SaveDataFailed);
                        AppLog.Current.Error("*** SAVE DATA EXCEPTION - Testee");
                        AppLog.Current.Error(ex.Message);
                    }
                }
            });
        }

        private void onTesteeSelectionPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ITesteeSelection.SelectedTestee))
                RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Commands\RestoreTesteeFromArchiveCommand.cs


using System;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Testees.Commands
{
    public class RestoreTesteeFromArchiveCommand : Wpf.Commands.CommandBase
    {
        private class ErrorMessages
        {
            public static readonly string SaveDataFailed = "Не удалось восстановить данные испытуемого из архива";
        }

        private Data.Repositories.ITesteesRepository _testeesRepository;
        private ITesteeArchiveSelection _testeeArchive;

        public RestoreTesteeFromArchiveCommand(ITesteeArchiveSelection testeeArchive,
            Data.Repositories.ITesteesRepository testeesRepository)
        {
            _testeesRepository = testeesRepository;
            _testeeArchive = testeeArchive;
            _testeeArchive.PropertyChanged += onTesteeArchivePropertyChanged;
        }

        public override bool CanExecute(object parameter)
        {
            return _testeeArchive.SelectedTestee != null;
        }

        public override void Execute(object parameter)
        {
            try
            {
                var testee = _testeeArchive.SelectedTestee;
                testee.Removed = false;
                testee.RemoveDate = null;
                _testeesRepository.Update(testee);

                AppLog.Current.Info($"Testee restored ({testee.Id})");
            }
            catch (Exception ex)
            {
                Wpf.Interactions.InteractionService.Current.ShowErrorMessage(ErrorMessages.SaveDataFailed);
                AppLog.Current.Error("*** SAVE DATA EXCEPTION - Testee");
                AppLog.Current.Error(ex.Message);
            }
        }

        private void onTesteeArchivePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ITesteeArchiveSelection.SelectedTestee))
                RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Commands\ShowArchiveCommand.cs


namespace Updk7.Wpf.Master.Modules.Testees.Commands
{
    public class ShowArchiveCommand : Wpf.Commands.CommandBase
    {
        private ITesteeArchiveSelection _archiveTesteeSelection;

        public ShowArchiveCommand(ITesteeArchiveSelection archiveTesteeSelection)
        {
            _archiveTesteeSelection = archiveTesteeSelection;
        }

        public override bool CanExecute(object parameter)
        {
            return true;
        }

        public override void Execute(object parameter)
        {
            var interaction = new Interactions.ShowTesteesArchiveInteraction();
            interaction.ArchiveView = ViewModelLocator.Current.ResolveView<ITesteeArchiveSelection>();
            interaction.ArchiveView.DataContext = _archiveTesteeSelection;
            Wpf.Interactions.InteractionService.Current.ShowInteractionModel(interaction, () => 
            {
                interaction.ArchiveView.DataContext = null;
                interaction.ArchiveView = null;
            });
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Commands\ShowReportsCommand.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Testees.Commands
{
    public class ShowReportsCommand : Wpf.Commands.CommandBase
    {
        private Prism.Events.IEventAggregator _eventAggregator;
        private ITesteeSelection _testeeSelection;

        public ShowReportsCommand(ITesteeSelection testeeSelection, 
            Prism.Events.IEventAggregator eventAggregator)
        {
            _eventAggregator = eventAggregator;
            _testeeSelection = testeeSelection;
            _testeeSelection.PropertyChanged += onTesteeSelectionPropertyChanged;
        }

        public override bool CanExecute(object parameter)
        {
            return _testeeSelection.SelectedTestee != null;
        }

        public override void Execute(object parameter)
        {
            _eventAggregator
                .GetEvent<Assets.Events.ShowTesteeReportsEvent>()
                .Publish(new Assets.Events.ShowTesteeReportsEventData(_testeeSelection.GetSelectedTesteeShortInfo()));
        }

        private void onTesteeSelectionPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ITesteeSelection.SelectedTestee))
                RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\EventListeners\TestStartStopListener.cs


using System;

namespace Updk7.Wpf.Master.Modules.Testees.EventListeners
{
    public class TestStartStopListener : IEventListener
    {
        private class ErrorMessages
        {
            public static readonly string SaveDataFailed = "Не удалось сохранить данные в БД";
        }

        private ITestResultsSaveStrategy _resultsSaveStrategy;
        private ITesteeSelection _testeeSelection;
        private ITesteeDataUpdater _testeeDataUpdater;
        private Wpf.Interactions.IInteractionService _interactionService;

        public TestStartStopListener(ITesteeSelection testeeSelection, 
            ITestResultsSaveStrategy resultsSaveStrategy,
            ITesteeDataUpdater testeeDataUpdater,
            Wpf.Interactions.IInteractionService interactionService)
        {
            _interactionService = interactionService;
            _testeeSelection = testeeSelection;
            _testeeDataUpdater = testeeDataUpdater;
            _resultsSaveStrategy = resultsSaveStrategy;
            _resultsSaveStrategy.TestGroupSaved += onTestGroupSaved;
        }

        public void Subscribe(Prism.Events.IEventAggregator eventAggregator)
        {
            eventAggregator
                .GetEvent<Assets.Events.StartTestingEvent>()
                .Subscribe(onStartTesting);

            eventAggregator
                .GetEvent<Assets.Events.TestStartedEevnt>()
                .Subscribe(onTestStarted);

            eventAggregator
                .GetEvent<Assets.Events.TestCompleteEvent>()
                .Subscribe(onTestComplete);

            eventAggregator
                .GetEvent<Assets.Events.TestingPlanCompleteEvent>()
                .Subscribe(onTestingPlanComplete);

            eventAggregator
                .GetEvent<Assets.Events.WorkplaceDisconnectedEvent>()
                .Subscribe(onWorkplaceDisconnected);
        }

        private void onStartTesting(Assets.Events.StartTestingEventData data)
        {
            _resultsSaveStrategy.InitializeData(data.TestingInfo.Testee.Entitie, data.TestingInfo.TestingPlan);
        }

        private void onTestStarted(Assets.Events.TestStartedEventData data)
        {
            _testeeSelection.SetTesteeUnderTest(data.TestStartedInfo.Testee.Entitie.Id, isUnderTest: true);
        }

        private void onTestComplete(Assets.Events.TestCompleteEventData data)
        {
            try
            {
                _resultsSaveStrategy.SaveResults(data.Testee.Entitie, data.Results);
            }
            catch (Exception ex)
            {
                _interactionService.ShowErrorMessage(ErrorMessages.SaveDataFailed, asDialog: false);
                AppLog.Current.Error("*** SAVE DATA EXCEPTION - TestResults");
                AppLog.Current.Error(ex.Message);
            }
        }

        private void onTestingPlanComplete(Assets.Events.TestingPlanCompleteEventData data)
        {
            resetTesting(data.TestingInfo.Testee.Entitie);
        }

        private void onTestGroupSaved(object sender, Assets.TestGroupInfo e)
        {
            _testeeDataUpdater.UpdateTesteeData(e);
        }

        private void onWorkplaceDisconnected(Assets.Events.WorkplaceDisconnectedEventData data)
        {
            resetTesting(data.Testee);
        }

        private void resetTesting(Data.Entities.Testee testee)
        {
            if (testee != null)
            {
                _testeeSelection.SetTesteeUnderTest(testee.Id, isUnderTest: false);
                _resultsSaveStrategy.TestingComplete(testee);
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Interactions\FindTesteeInteraction.cs


using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.Testees.Interactions
{
    internal class FindTesteeInteraction : Prism.Mvvm.BindableBase, Wpf.Interactions.IInteractionViewModel
    {
        public FindTesteeInteraction()
        {
            SearchResults.PropertyChanged += onSearchResultsPropertyChanged;
            setupCommands();
        }

        public Prism.Commands.DelegateCommand CompleteInteractionRequestCommand { get; private set; }

        public Prism.Commands.DelegateCommand SelectTesteeCommand { get; private set; }

        public event EventHandler CompleteInteractionRequest;

        public ViewModels.TesteeFindViewModel SearchResults { get; } = 
            new ViewModels.TesteeFindViewModel();

        private Assets.TesteeShortInfo _selectedTestee;

        public Assets.TesteeShortInfo SelectedTestee
        {
            get { return _selectedTestee; }
            private set { SetProperty(ref _selectedTestee, value); }
        }

        public void SetData(IEnumerable<Assets.TesteeShortInfo> testees)
        {
            SearchResults.SetTestees(testees);
        }

        private void setupCommands()
        {
            CompleteInteractionRequestCommand = new Prism.Commands.DelegateCommand(
                () => CompleteInteractionRequest?.Invoke(this, EventArgs.Empty));

            SelectTesteeCommand = new Prism.Commands.DelegateCommand(onSelectTestee,
                () => SearchResults.SelectedTestee != null);
        }

        private void onSelectTestee()
        {
            SelectedTestee = SearchResults.SelectedTestee;
            CompleteInteractionRequestCommand.Execute();
        }

        private void onSearchResultsPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(SearchResults.SelectedTestee))
                SelectTesteeCommand.RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Interactions\ShowTesteeEditorInteraction.cs


using System.Windows;

namespace Updk7.Wpf.Master.Modules.Testees.Interactions
{
    public class ShowTesteeEditorInteraction :  Wpf.Interactions.ConfirmationInteraction
    {
        public ShowTesteeEditorInteraction()
        {
        }

        private EditorMode _editorMode;

        public EditorMode EditorMode
        {
            get { return _editorMode; }
            set { SetProperty(ref _editorMode, value); }
        }

        private FrameworkElement _editorView;

        public FrameworkElement EditorView
        {
            get { return _editorView; }
            set { SetProperty(ref _editorView, value); }
        }

        private ITesteeEditorController _editorController;

        public ITesteeEditorController EditorController
        {
            get { return _editorController; }
            set
            {
                unscribeFromControllerEvents(_editorController);
                suvscribeToControllerEvents(value);
                SetProperty(ref _editorController, value);
            }
        }

        protected override bool CanConfirm()
        {
            return _editorController != null 
                ? !_editorController.HasErrors 
                : false;
        }

        private void suvscribeToControllerEvents(ITesteeEditorController controller)
        {
            if (controller != null)
                controller.PropertyChanged += onControllerPropertyChanged;
        }

        private void unscribeFromControllerEvents(ITesteeEditorController controller)
        {
            if (controller != null)
                controller.PropertyChanged -= onControllerPropertyChanged;
        }

        private void onControllerPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ITesteeEditorController.HasErrors))
                AcceptCommand.RaiseCanExecuteChanged();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Interactions\ShowTesteesArchiveInteraction.cs


using System;
using System.Windows;

namespace Updk7.Wpf.Master.Modules.Testees.Interactions
{
    public class ShowTesteesArchiveInteraction : Prism.Mvvm.BindableBase, Wpf.Interactions.IInteractionViewModel
    {
        public ShowTesteesArchiveInteraction()
        {
            CompleteInteractionRequestCommand = 
                new Prism.Commands.DelegateCommand(() => CompleteInteractionRequest?.Invoke(this, EventArgs.Empty));
        }

        public Prism.Commands.DelegateCommand CompleteInteractionRequestCommand { get; set; }

        public event EventHandler CompleteInteractionRequest;

        public FrameworkElement ArchiveView { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Interactions\Views\FindTesteeView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Testees.Interactions.Views.FindTesteeView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.Testees.Interactions.Views"
             xmlns:views="clr-namespace:Updk7.Wpf.Master.Modules.Testees.Views"
             mc:Ignorable="d" 
             Height="500" Width="400">
    <Grid
        Margin="3,0,3,3">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <!--Заголовок панели-->
        <Grid
            Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="40" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Image
                Grid.Column="0" 
                Margin="5"
                Source="{StaticResource FindImage}"/>
            <TextBlock 
                Grid.Column="1"
                FontSize="18"
                FontWeight="Bold"
                Margin="5,1,1,1"
                VerticalAlignment="Center"
                HorizontalAlignment="Left"
                Text="Поиск сотрудника"
                />
        </Grid>
        <!--Панели поиска и кнопки-->
        <Grid
            Grid.Row="1">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <!--Панель поиска сотрудников-->
            <views:TesteeFindView 
                Grid.Row="0"
                DataContext="{Binding SearchResults}"
                />
            <StackPanel
                Grid.Row="1"
                Orientation="Horizontal"
                HorizontalAlignment="Right">
                <!--Выбор сотрудника-->
                <Button 
                    Style="{StaticResource FlatButtonStyle}"
                    Content="Выбрать"
                    FontSize="14"
                    FontWeight="Medium"
                    Margin="3"
                    HorizontalAlignment="Right"
                    Padding="15,3,15,3"
                    Command="{Binding SelectTesteeCommand}"
                    Width="100"
                    Height="30"
                />
                <!--Кнопка закрытия окна-->
                <Button 
                    Style="{StaticResource FlatButtonStyle}"
                    Content="Отмена"
                    FontSize="14"
                    FontWeight="Medium"
                    Margin="3"
                    HorizontalAlignment="Right"
                    Padding="15,3,15,3"
                    Command="{Binding CompleteInteractionRequestCommand}"
                    Width="100"
                    Height="30"
                />
            </StackPanel>
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Interactions\Views\FindTesteeView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Testees.Interactions.Views
{
    /// <summary>
    /// Interaction logic for FindTesteeView.xaml
    /// </summary>
    public partial class FindTesteeView : UserControl
    {
        public FindTesteeView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Interactions\Views\ShowTesteeEditorView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Testees.Interactions.Views.ShowTesteeEditorView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d"
             MinWidth="430"
             MaxWidth="1000">
    <Grid
        Margin="2">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!--Заголовок панели-->
        <Grid>
            <Grid.Resources>
                <Style x:Key="HeaderImageStyle" TargetType="{x:Type Image}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding EditorMode}" Value="Create">
                            <Setter Property="Source" Value="{StaticResource BlackPlusImage}" />
                        </DataTrigger>
                        <DataTrigger Binding="{Binding EditorMode}" Value="Edit">
                            <Setter Property="Source" Value="{StaticResource BlackPencilImage}" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
                <Style x:Key="HeaderTextBlockStyle" TargetType="{x:Type TextBlock}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding EditorMode}" Value="Create">
                            <Setter Property="Text" Value="Добавление испытуемого" />
                        </DataTrigger>
                        <DataTrigger Binding="{Binding EditorMode}" Value="Edit">
                            <Setter Property="Text" Value="Редактирование данных испытуемого" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Grid.Resources>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="40" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Image
                Grid.Column="0" 
                Margin="5"
                Style="{StaticResource HeaderImageStyle}" />
            <TextBlock 
                Grid.Column="1"
                FontSize="18"
                FontWeight="Bold"
                Margin="5,1,1,1"
                VerticalAlignment="Center"
                HorizontalAlignment="Left"
                Style="{StaticResource HeaderTextBlockStyle}"
                />
        </Grid>
        <!--Редактор данных испытуемого-->
        <ContentControl 
            Grid.Row="1"
            Content="{Binding EditorView}"
            />
        <!--Кнопки управления-->
        <Grid
            Grid.Row="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <!--Кнопка Применить-->
            <Button 
                Grid.Column="1"
                Width="80"
                Height="30"
                HorizontalAlignment="Right"
                FontSize="14"
                FontWeight="Bold"
                Content="Принять"
                Command="{Binding AcceptCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
            <!--Кнопка Отменить-->
            <Button 
                Grid.Column="2"
                Width="80"
                Height="30"
                FontSize="14"
                FontWeight="Bold"
                HorizontalAlignment="Right"
                Content="Отменить"
                Command="{Binding CancelCommand}"
                Margin="3"
                Style="{StaticResource FlatButtonStyle}"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Interactions\Views\ShowTesteeEditorView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Testees.Interactions.Views
{
    /// <summary>
    /// Interaction logic for ShowTesteeEditorView.xaml
    /// </summary>
    public partial class ShowTesteeEditorView : UserControl
    {
        public ShowTesteeEditorView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Interactions\Views\ShowTesteesArchiveView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Testees.Interactions.Views.ShowTesteesArchiveView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:commands="clr-namespace:Updk7.Wpf.Commands;assembly=Updk7.Wpf"
             mc:Ignorable="d" 
             Height="600">
    <Grid
        Margin="3,0,3,3">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <!--Заголовок панели-->
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="40" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Image
                Grid.Column="0" 
                Margin="5"
                Source="{StaticResource EmployeesArchImage}"/>
            <TextBlock 
                Grid.Column="1"
                FontSize="18"
                FontWeight="Bold"
                Margin="5,1,1,1"
                VerticalAlignment="Center"
                HorizontalAlignment="Left"
                Text="Архив удаленных сотрудников"
                />
        </Grid>
        <!--Данные и панель инструментов-->
        <Grid
            Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <!--Панель инструментов архива-->
            <StackPanel
                Grid.Column="0"
                Grid.Row="0"
                Margin="0,0,8,0">
                <Button 
                    Margin="0,0,0,5"
                    Style="{StaticResource FlatButtonStyle}"
                    ToolTip="Восстановить выбранного сотрудника"
                    BorderThickness="2"
                    Command="{commands:CommandProvider RestoreTesteeFromArchive}">
                    <Image Margin="3" Source="{StaticResource BlackPlusImage}" />
                </Button>
            </StackPanel>
            <!--Таблица -->
            <ContentControl 
                Grid.Column="1"
                Grid.Row="0"
                Content="{Binding ArchiveView}"
                />
            <!--Кнопка закрытия окна-->
            <Button 
                Grid.Row="1"
                Grid.Column="1"
                Style="{StaticResource FlatButtonStyle}"
                Content="Закрыть окно"
                Margin="3"
                HorizontalAlignment="Right"
                Padding="15,3,15,3"
                Command="{Binding CompleteInteractionRequestCommand}"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Interactions\Views\ShowTesteesArchiveView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Testees.Interactions.Views
{
    /// <summary>
    /// Interaction logic for ShowTesteesArchiveView.xaml
    /// </summary>
    public partial class ShowTesteesArchiveView : UserControl
    {
        public ShowTesteesArchiveView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ViewModels\TesteeArchiveRowViewModel.cs


using System;

namespace Updk7.Wpf.Master.Modules.Testees.ViewModels
{
    public class TesteeArchiveRowViewModel : Assets.ViewModels.TesteeCollectionRowViewModel
    {
        public TesteeArchiveRowViewModel()
        {
        }

        public TesteeArchiveRowViewModel(Data.Entities.Testee testee) : base(testee)
        {
        }

        private DateTime? _removeDate;

        public DateTime? RemoveDate
        {
            get { return _removeDate; }
            private set { SetProperty(ref _removeDate, value); }
        }

        public override void Update(Data.Entities.Testee testee)
        {
            base.Update(testee);
            RemoveDate = testee.RemoveDate;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ViewModels\TesteeArchiveSelectionViewModel.cs


using System.ComponentModel;
using System.Diagnostics;
using System.Linq;

namespace Updk7.Wpf.Master.Modules.Testees.ViewModels
{
    public class TesteeArchiveSelectionViewModel : Assets.ViewModels.TesteeCollectionViewModel<TesteeArchiveRowViewModel>,
        ITesteeArchiveSelection
    {
        public class GenericTesteeRow : Assets.ViewModels.TesteeCollectionRowViewModel
        {
        }

        private Data.Repositories.ITesteesRepository _testeesReposiotry;

        public TesteeArchiveSelectionViewModel(Data.Repositories.ITesteesRepository testeesRepository)
        {
            _testeesReposiotry = testeesRepository;
            _testeesReposiotry.DataChanged += onTesteesReposiotryDataChanged;
            SetTestees(_testeesReposiotry.Get(removed: true));
        }

        private Data.Entities.Testee _selectedTestee;

        public Data.Entities.Testee SelectedTestee
        {
            get { return _selectedTestee; }
            set { SetProperty(ref _selectedTestee, value); }
        } 

        protected override TesteeArchiveRowViewModel CreateTesteeViewModel(Data.Entities.Testee testee)
        {
            return new TesteeArchiveRowViewModel(testee);
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(SelectedRow))
                SelectedTestee = SelectedRow?.SourceData;

            base.OnPropertyChanged(args);
        }

        private void onTesteesReposiotryDataChanged(object sender, Data.Repositories.RepositoryDataChangedEventArgs e)
        {
            var testee = e.Entity as Data.Entities.Testee;
            Debug.Assert(testee != null);

            if (e.Operation == Data.Repositories.RepositoryDataChangedOperation.Remove || !testee.Removed)
                RemoveTestee(e.Entity as Data.Entities.Testee);
            else
            {
                // В арзиве только удаленные испытуемые, редактирование их профиля до восстановления из архива
                // недоступно, поэтому обработка событий репозитория упрощена
                Debug.Assert(TesteesCollection.FirstOrDefault(vm => vm.SourceData.Id == testee.Id) == null);
                AddTestee(testee);
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ViewModels\TesteeCommonEditorViewModel.cs


using System;
using System.Linq;

namespace Updk7.Wpf.Master.Modules.Testees.ViewModels
{
    public class TesteeCommonEditorViewModel : Validation.ValidatedBindableBase, ITesteeEditor
    {
        private static class ErrorsMessages
        {
            public static readonly string FirstNameNotSet = "Не задано имя испытуемого";
            public static readonly string LastNameNotSet = "Не задана фамилия испытуемого";
            public static readonly string BirthDate = "Неправильная дата рождения";
            public static readonly string FirstNameTooLong = "Имя слишком длинное";
            public static readonly string LastNameTooLong = "Фамилия слишком длинная";
            public static readonly string PatronimicTooLong = "Отчество слишком длинное";
        }

        private const int MaxStringLength = 100;

        private DateTime _maxBirthDate = DateTime.Now.Subtract(TimeSpan.FromDays(15 * 365));
        private DateTime _minBirthDate = DateTime.Now.Subtract(TimeSpan.FromDays(75 * 365));

        public TesteeCommonEditorViewModel()
        {
            setupValidationRules();
        }

        public string Title => "Личные данные";

        private string _firstName;

        public string FirstName
        {
            get { return _firstName; }
            set { SetProperty(ref _firstName, value); }
        }

        private string _lastName;

        public string LastName
        {
            get { return _lastName; }
            set { SetProperty(ref _lastName, value); }
        }

        private string _patronymic;

        public string Patronymic
        {
            get { return _patronymic; }
            set { SetProperty(ref _patronymic, value); }
        }

        private DateTime _birthDate = DateTime.Now;

        public DateTime BirthDate
        {
            get { return _birthDate; }
            set { SetProperty(ref _birthDate, value); }
        }
        
        private Data.Entities.GenderType _gender = Data.Entities.GenderType.Male;

        public Data.Entities.GenderType Gender
        {
            get { return _gender; }
            set { SetProperty(ref _gender, value); }
        }

        public void SetData(Data.Entities.Testee testee)
        {
            if (testee == null)
            {
                FirstName = LastName = Patronymic = string.Empty;
                BirthDate = _maxBirthDate;
                Gender = Data.Entities.GenderType.Male;
            }
            else
            {
                var nameParts = testee.FullName.Split(' ')
                    .Where(p => !string.IsNullOrWhiteSpace(p))
                    .ToArray();

                LastName = nameParts[0];
                if (nameParts.Length > 1)
                    FirstName = nameParts[1];
                if (nameParts.Length == 3)
                    Patronymic = nameParts[2];

                BirthDate = testee.BirthDate;
                Gender = testee.Gender;
            }
        }

        public void UpdateEntitie(Data.Entities.Testee testee)
        {
            testee.FullName = $"{LastName.Trim()} {FirstName.Trim()} {Patronymic.Trim()}";
            testee.BirthDate = BirthDate;
            testee.Gender = Gender;
        }

        private void setupValidationRules()
        {
            ErrorsContainer.AddValidationRule(
                nameof(LastName),
                () => !string.IsNullOrEmpty(LastName),
                ErrorsMessages.LastNameNotSet);

            ErrorsContainer.AddValidationRule(
                nameof(LastName),
                () => !string.IsNullOrEmpty(LastName) ? LastName.Length < MaxStringLength : true,
                ErrorsMessages.LastNameTooLong);

            ErrorsContainer.AddValidationRule(
                nameof(FirstName),
                () => !string.IsNullOrEmpty(FirstName),
                ErrorsMessages.FirstNameNotSet);

            ErrorsContainer.AddValidationRule(
                nameof(FirstName),
                () => !string.IsNullOrEmpty(FirstName) ? FirstName.Length < MaxStringLength : true,
                ErrorsMessages.FirstNameTooLong);

            ErrorsContainer.AddValidationRule(
                nameof(Patronymic),
                () => !string.IsNullOrEmpty(Patronymic) ? Patronymic.Length < MaxStringLength : true,
                ErrorsMessages.PatronimicTooLong);

            ErrorsContainer.AddValidationRule(
                nameof(BirthDate),
                () => _birthDate > _minBirthDate && _birthDate < _maxBirthDate,
                ErrorsMessages.BirthDate);

            ErrorsContainer.UpdateValidation();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ViewModels\TesteeEditorControllerViewModel.cs


using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Diagnostics;
using System.Windows.Media.Imaging;
using System.Windows.Controls;
using System.Windows.Media;

namespace Updk7.Wpf.Master.Modules.Testees.ViewModels
{
    public class TesteeEditorControllerViewModel : Prism.Mvvm.BindableBase, ITesteeEditorController
    {
        private List<ITesteeEditor> _editors = new List<ITesteeEditor>();

        public TesteeEditorControllerViewModel()
        {
        }

        public ObservableCollection<FrameworkElement> EditorViews { get; } = new ObservableCollection<FrameworkElement>();

        private EditorMode _editorMode = EditorMode.Create;

        public EditorMode EditorMode
        {
            get { return _editorMode; }
            private set { SetProperty(ref _editorMode, value); }
        }

        private Type _entitieType;

        public Type EntitieType
        {
            get { return _entitieType; }
            set { SetProperty(ref _entitieType, value); }
        }

        private bool _hasErrors;

        public bool HasErrors
        {
            get { return _hasErrors; }
            private set { SetProperty(ref _hasErrors, value); }
        }

        private string _lastError;

        public string LastError
        {
            get { return _lastError; }
            private set { SetProperty(ref _lastError, value); }
        }

        private BitmapImage _sourcePhoto;

        public BitmapImage SourcePhoto
        {
            get { return _sourcePhoto; }
            set { SetProperty(ref _sourcePhoto, value); }
        } 

        private Data.Entities.Testee _sourceData;

        public Data.Entities.Testee SourceData
        {
            get 
            {
                createOrUpdateSourceData();
                return _sourceData; 
            }
            set
            {
                setSourceData(value);
                SetProperty(ref _sourceData, value);
            }
        } 

        public void RegisterEditor(ITesteeEditor editor, FrameworkElement editorView)
        {
            editorView.DataContext = editor;
            editor.PropertyChanged += onEditorPropertyChanged;
            _editors.Add(editor);
            EditorViews.Add(createEditorDecorator(editorView, editor.Title));

            updateErrors();
        }

        private void onEditorPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ITesteeEditor.HasErrors) ||
                e.PropertyName == nameof(ITesteeEditor.LastError))
            {
                updateErrors();
            }
        }

        private void updateErrors()
        {
            var errorEditor = _editors.FirstOrDefault(e => e.HasErrors);
            HasErrors = errorEditor != null;
            LastError = errorEditor?.LastError;
        }

        private void setSourceData(Data.Entities.Testee testee)
        {
            foreach (var editor in _editors)
                editor.SetData(testee);

            EditorMode = testee == null ? EditorMode.Create : EditorMode.Edit;
            _sourceData = testee;
        }

        private void createOrUpdateSourceData()
        {
            Debug.Assert(EntitieType != null);
            Debug.Assert(!HasErrors);

            if (_sourceData == null)
            {
                _sourceData = Activator.CreateInstance(EntitieType) as Data.Entities.Testee;
                Debug.Assert(_sourceData != null);
            }

            foreach (var editor in _editors)
                editor.UpdateEntitie(_sourceData);
        }

        private FrameworkElement createEditorDecorator(FrameworkElement editorPanel, string title)
        {
            if (string.IsNullOrEmpty(title))
                return editorPanel;

            var border = new Border()
            {
                Background = Brushes.DeepSkyBlue,
                Child = new TextBlock()
                {
                    Text = title,
                    FontSize = 14.0,
                    FontWeight = FontWeights.Medium,
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Center,
                    Margin = new Thickness(5, 2, 5, 2)
                }
            };
            
            editorPanel.Margin = new Thickness(5, 2, 5, 2);

            var stackPanel = new StackPanel();
            stackPanel.Children.Add(border);
            stackPanel.Children.Add(editorPanel);

            return stackPanel;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ViewModels\TesteeFindViewModel.cs


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Updk7.Wpf.Master.Modules.Testees.ViewModels
{
    internal class TesteeFindViewModel : Prism.Mvvm.BindableBase
    {
        private static readonly IEnumerable<Assets.TesteeShortInfo> NoMatches = Enumerable.Empty<Assets.TesteeShortInfo>(); 
        private IEnumerable<Assets.TesteeShortInfo> _testeesPool;
        private CancellationTokenSource _cancellationSource = null;

        public TesteeFindViewModel()
        {
            PropertyChanged += async (s, e) =>
            {
                if (e.PropertyName == nameof(SearchPattern))
                    await updateSearchResults();
            };
        }

        public ObservableCollection<Assets.TesteeShortInfo> SearchResults { get; } = 
            new ObservableCollection<Assets.TesteeShortInfo>();

        private Assets.TesteeShortInfo _selectedTestee;

        public Assets.TesteeShortInfo SelectedTestee
        {
            get { return _selectedTestee; }
            set { SetProperty(ref _selectedTestee, value); }
        }

        private string _searchPattern;

        public string SearchPattern
        {
            get { return _searchPattern; }
            set { SetProperty(ref _searchPattern, value); }
        }

        public void SetTestees(IEnumerable<Assets.TesteeShortInfo> testeesPool)
        {
            Debug.Assert(_testeesPool == null);
            _testeesPool = testeesPool;
        }

        private void clearSearchResults()
        {
            SelectedTestee = null;
            SearchResults.Clear();
        }

        private void setSearchResults(IEnumerable<Assets.TesteeShortInfo> results)
        {
            clearSearchResults();
            foreach (var info in results)
                SearchResults.Add(info);
        }

        private async Task updateSearchResults()
        {
            if (_cancellationSource != null)
                _cancellationSource.Cancel();

            try
            {
                _cancellationSource = new CancellationTokenSource();
                var results = await getNamesMatch(SearchPattern, _cancellationSource.Token);
                setSearchResults(results);

                _cancellationSource = null;
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception)
            {
            }
        }

        private Task<IEnumerable<Assets.TesteeShortInfo>> getNamesMatch(string searchPattern, 
            CancellationToken ct)
        {
            Debug.Assert(_testeesPool != null);

            searchPattern = searchPattern.Trim().ToLower();
            if (string.IsNullOrEmpty(searchPattern))
                return Task.FromResult(NoMatches);
            
            return Task.Run(() =>
            {
                var results = new List<Assets.TesteeShortInfo>();
                foreach (var testee in _testeesPool)
                {
                    ct.ThrowIfCancellationRequested();

                    var fullName = testee.FullName.ToLower();
                    if (fullName.Contains(searchPattern))
                        results.Add(testee);
                }
                
                return results.AsEnumerable();
            });
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ViewModels\TesteeRailroadEditorViewModel.cs


using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Updk7.Wpf.Master.Modules.Testees.ViewModels
{
    public class TesteeRailroadEditorViewModel : Validation.ValidatedBindableBase, ITesteeEditor
    {
        private static class ErrorStrings
        {
            public static readonly string ProfessionTraffic = "Некорректное сочетания профессии и типа движения";
            public static readonly string Column = "Не задана колонна";
            public static readonly string TC = "Не задана ТЧ";
        }

        public class KeyValuePairViewModel
        {
            public KeyValuePairViewModel(int key, string title)
            {
                Key = key;
                Title = title;
            }

            public int Key { get; }

            public string Title { get; }
        }

        public TesteeRailroadEditorViewModel()
        {
            setupValidationRules();
        }

        public TesteeRailroadEditorViewModel(Dictionary<int, string> professionTypes, Dictionary<int, string> trafficTypes)
        {
            SetConfiguration(professionTypes, trafficTypes);
            setupValidationRules();
        }

        public string Title => "Профессиональные данные";

        private IEnumerable<KeyValuePairViewModel> _professionTypes;

        public IEnumerable<KeyValuePairViewModel> ProfessionTypes
        {
            get { return _professionTypes; }
            private set { SetProperty(ref _professionTypes, value); }
        }

        private KeyValuePairViewModel _selectedProfession;

        public KeyValuePairViewModel SelectedProfession
        {
            get { return _selectedProfession; }
            set { SetProperty(ref _selectedProfession, value); }
        }

        private IEnumerable<KeyValuePairViewModel> _trafficTypes;

        public IEnumerable<KeyValuePairViewModel> TrafficTypes
        {
            get { return _trafficTypes; }
            private set { SetProperty(ref _trafficTypes, value); }
        }

        private KeyValuePairViewModel _selectedTraffic;

        public KeyValuePairViewModel SelectedTraffic
        {
            get { return _selectedTraffic; }
            set { SetProperty(ref _selectedTraffic, value); }
        }

        private string _column;

        public string Column
        {
            get { return _column; }
            set { SetProperty(ref _column, value); }
        }

        private string _tc;

        public string TC
        {
            get { return _tc; }
            set { SetProperty(ref _tc, value); }
        } 

        public void SetConfiguration(Dictionary<int, string> professionTypes, Dictionary<int, string> trafficTypes)
        {
            ProfessionTypes = professionTypes.Select(p => new KeyValuePairViewModel(p.Key, p.Value)).ToArray();
            SelectedProfession = ProfessionTypes.FirstOrDefault();
            TrafficTypes = trafficTypes.Select(t => new KeyValuePairViewModel(t.Key, t.Value)).ToArray();
            SelectedTraffic = TrafficTypes.FirstOrDefault();
        }

        public virtual void SetData(Data.Entities.Testee testee)
        {
            Debug.Assert(ProfessionTypes != null && TrafficTypes != null);

            if (testee == null)
            {
                SelectedProfession = ProfessionTypes.FirstOrDefault();
                SelectedTraffic = TrafficTypes.FirstOrDefault();
                Column = string.Empty;
                TC = string.Empty;
            }
            else
            {
                Debug.Assert(testee is Data.Entities.TesteeRailroad);
                var rlrdTestee = testee as Data.Entities.TesteeRailroad;
                SelectedProfession = ProfessionTypes.FirstOrDefault(p => p.Key == rlrdTestee.ProfessionId);
                SelectedTraffic = TrafficTypes.FirstOrDefault(t => t.Key == rlrdTestee.TrafficId);
                Column = rlrdTestee.Column;
                TC = rlrdTestee.TC;
            }
        }

        public virtual void UpdateEntitie(Data.Entities.Testee testee)
        {
            Debug.Assert(ProfessionTypes != null && TrafficTypes != null);
            Debug.Assert(SelectedProfession != null);
            Debug.Assert(SelectedTraffic != null);
            Debug.Assert(testee is Data.Entities.TesteeRailroad);

            var rlrdTestee = testee as Data.Entities.TesteeRailroad;
            rlrdTestee.ProfessionId = SelectedProfession.Key;
            rlrdTestee.TrafficId = SelectedTraffic.Key;
            rlrdTestee.Column = Column;
            rlrdTestee.TC = TC;
        }

        protected virtual bool IsProfessionTrafficPairValid()
        {
            return true;
        }

        private void setupValidationRules()
        {
            ErrorsContainer.AddValidationRule(
                nameof(SelectedProfession),
                IsProfessionTrafficPairValid,
                ErrorStrings.ProfessionTraffic);

            ErrorsContainer.AddValidationRule(
                nameof(SelectedTraffic),
                IsProfessionTrafficPairValid,
                ErrorStrings.ProfessionTraffic);

            ErrorsContainer.AddValidationRule(
                nameof(Column),
                () => !string.IsNullOrEmpty(Column),
                ErrorStrings.Column);

            ErrorsContainer.AddValidationRule(
                nameof(TC),
                () => !string.IsNullOrEmpty(TC),
                ErrorStrings.TC);

            ErrorsContainer.UpdateValidation();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ViewModels\TesteeSelectionRowViewModel.cs


namespace Updk7.Wpf.Master.Modules.Testees.ViewModels
{
    public class TesteeSelectionRowViewModel : Assets.ViewModels.TesteeCollectionRowViewModel
    {
        public TesteeSelectionRowViewModel()
        {
        }

        public TesteeSelectionRowViewModel(Data.Entities.Testee testee) : base(testee)
        {
        }

        private bool _isUnderTest;

        public bool IsUnderTest
        {
            get { return _isUnderTest; }
            set { SetProperty(ref _isUnderTest, value); }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ViewModels\TesteeSelectionViewModel.cs


using System.Linq;
using System.ComponentModel;
using System.Collections.Generic;
using System.Net.NetworkInformation;

namespace Updk7.Wpf.Master.Modules.Testees.ViewModels
{
    public class TesteeSelectionViewModel 
        : Assets.ViewModels.TesteeCollectionViewModel<TesteeSelectionRowViewModel>,
        ITesteeSelection
    {
        public TesteeSelectionViewModel()
        {
        }

        private Data.Repositories.ITesteesRepository _testeesRepository;

        public Data.Repositories.ITesteesRepository TesteesRepository
        {
            get { return _testeesRepository; }
            set 
            {
                unscribeFromRepositoryEvents(_testeesRepository);
                SetProperty(ref _testeesRepository, value);
                subscribeToRepositoryEvents(_testeesRepository);
                InitializeTesteesCollection();
            }
        } 

        private Data.Entities.Testee _selectedTestee;

        public Data.Entities.Testee SelectedTestee
        {
            get { return _selectedTestee; }
            set { SetProperty(ref _selectedTestee, value); }
        }

        private bool _isSelectedTesteeBusy;

        public bool IsSelectedTesteeBusy
        {
            get { return _isSelectedTesteeBusy; }
            protected set { SetProperty(ref _isSelectedTesteeBusy, value); }
        }

        public void SetTesteeUnderTest(int testeeId, bool isUnderTest)
        {
            var viewModel = TesteesCollection.FirstOrDefault(vm => vm.SourceData.Id == testeeId);
            if (viewModel != null)
            {
                viewModel.IsUnderTest = isUnderTest;
                if (SelectedRow == viewModel)
                    IsSelectedTesteeBusy = isUnderTest;
            }
        }

        public virtual Assets.TesteeShortInfo GetSelectedTesteeShortInfo()
        {
            if (SelectedTestee == null)
                return null;

            return new Assets.TesteeShortInfo()
            {
                Entitie = SelectedTestee,
                FullName = SelectedTestee.FullName,
            };
        }

        public virtual IEnumerable<Assets.TesteeShortInfo> GetAllTesteesShortInfo()
        {
            return TesteesCollection.Select(t => new Assets.TesteeShortInfo()
            {
                Entitie = t.SourceData,
                FullName = t.FullName
            }).ToArray();
        }

        protected virtual void InitializeTesteesCollection()
        {
            if (TesteesRepository != null)
                SetTestees(TesteesRepository.Get(removed: false));
        }

        protected override TesteeSelectionRowViewModel CreateTesteeViewModel(Data.Entities.Testee testee)
        {
            return new TesteeSelectionRowViewModel(testee);
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(SelectedRow))
            {
                if (SelectedRow != null)
                {
                    SelectedTestee = SelectedRow.SourceData;
                    IsSelectedTesteeBusy = SelectedRow.IsUnderTest;
                }
                else
                {
                    SelectedTestee = null;
                    IsSelectedTesteeBusy = false;
                }
            }
            else if (args.PropertyName == nameof(SelectedTestee))
            {
                if (SelectedTestee == null)
                    SelectedRow = null;
                else
                {
                    SelectedRow = TesteesCollection.FirstOrDefault(
                        r => r.SourceData.Id == SelectedTestee.Id);
                }
            }

            base.OnPropertyChanged(args);
        }

        private void onTesteesRepositoryDataChanged(object sender, Data.Repositories.RepositoryDataChangedEventArgs e)
        {
            var testee = e.Entity as Data.Entities.Testee;

            if (e.Operation == Data.Repositories.RepositoryDataChangedOperation.Remove || testee.Removed)
                RemoveTestee(testee);
            else
            {
                // Испытуемого может не быть в списке, т.к. ранее он был удален в архив
                var viewModel = TesteesCollection.FirstOrDefault(vm => vm.SourceData.Id == testee.Id);
                if (viewModel != null)
                    viewModel.Update(testee);
                else
                    AddTestee(testee);
            }
        }

        private void subscribeToRepositoryEvents(Data.Repositories.ITesteesRepository repository)
        {
            if (repository != null)
                repository.DataChanged += onTesteesRepositoryDataChanged;
        }

        private void unscribeFromRepositoryEvents(Data.Repositories.ITesteesRepository repository)
        {
            if (repository != null)
                repository.DataChanged -= onTesteesRepositoryDataChanged;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ViewModels\TesteesToolbarViewModel.cs


using System.Collections.ObjectModel;
using System.Windows;

namespace Updk7.Wpf.Master.Modules.Testees.ViewModels
{
    public class TesteesToolbarViewModel : Prism.Mvvm.BindableBase, ITesteesToolbar
    {
        public TesteesToolbarViewModel()
        {
        }

        public ObservableCollection<FrameworkElement> ToolbarElements { get; } =
            new ObservableCollection<FrameworkElement>();

        public void AddElement(FrameworkElement toolbarElement)
        {
            ToolbarElements.Add(toolbarElement);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\ViewModels\TesteesViewModel.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Updk7.Wpf.Master.Modules.Testees.ViewModels
{
    public class TesteesViewModel : Prism.Mvvm.BindableBase
    {
        public TesteesViewModel()
        {
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteeArchiveSelectionView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Testees.Views.TesteeArchiveSelectionView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             mc:Ignorable="d" 
             Width="800">
    <Grid>
        <DataGrid
            Grid.Row="0"
            Grid.Column="1"
            Margin="0,0,0,5"
            Style="{StaticResource FlatDataGridStyle}"
            ItemsSource="{Binding TesteesCollectionView}"
            IsSynchronizedWithCurrentItem="True"
            ColumnWidth="*"
            FontSize="12">
            <DataGrid.Resources>
                <Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource DataGridTextColumnElementStyle}" />
                <Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource DataGridCellStyle}" />
            </DataGrid.Resources>
            <DataGrid.Columns>
                <!--Ф.И.О.-->
                <DataGridTextColumn
                    Header="Ф.И.О."
                    IsReadOnly="True"
                    Binding="{Binding FullName, Mode=OneWay}"
                    FontSize="14"
                    Width="*"
                    />
                <!--Дата регистрации-->
                <DataGridTextColumn 
                    Header="Дата регистрации"
                    IsReadOnly="True"
                    Binding="{Binding RegistrationDate, StringFormat=\{0:yyyy.MM.dd\}, Mode=OneWay}"
                    Width="Auto"
                    />
                <!--Дата удаления-->
                <DataGridTextColumn 
                    Header="Дата удаления"
                    IsReadOnly="True"
                    Binding="{Binding RemoveDate, StringFormat=\{0:yyyy.MM.dd\}, Mode=OneWay}"
                    Width="Auto"
                    />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteeArchiveSelectionView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Testees.Views
{
    /// <summary>
    /// Interaction logic for TesteeArchiveSelectionView.xaml
    /// </summary>
    public partial class TesteeArchiveSelectionView : UserControl
    {
        public TesteeArchiveSelectionView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteeCommonEditorView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Testees.Views.TesteeCommonEditorView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:converters="clr-namespace:Updk7.Wpf.Converters;assembly=Updk7.Wpf"
             xmlns:commands="clr-namespace:Updk7.Wpf.Commands;assembly=Updk7.Wpf"
             xmlns:entities="clr-namespace:Updk7.Data.Entities;assembly=Updk7.Data"
             mc:Ignorable="d" >
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <!--Фото-->
        <Grid>
            <Border
                Margin="3"
                BorderBrush="Gray"
                BorderThickness="0.5"
                Width="110"
                Height="120"
                VerticalAlignment="Top"
                HorizontalAlignment="Center">
                <Button
                    Background="Transparent"
                    Command="{commands:CommandProvider LoadPhoto}"
                    x:Name="PART_PhotoButton">
                    <Grid>
                        <Image 
                            Source="{Binding Path=Command.Photo, ElementName=PART_PhotoButton}"
                            />
                        <TextBlock 
                            VerticalAlignment="Center"
                            HorizontalAlignment="Center"
                            FontSize="14"
                            FontWeight="Medium"
                            Text="Фото"
                            Visibility="{Binding Command.Photo, ElementName=PART_PhotoButton, 
                                Converter={converters:ObjectToVisibilityConverter InverseFlag=True}}"
                            />
                    </Grid>
                </Button>
            </Border>
        </Grid>
        <!--Данные испытуемого-->
        <Grid
            Grid.Column="1"
            Margin="0,3,0,0">
            <Grid.Resources>
                <Style TargetType="{x:Type TextBlock}">
                    <Setter Property="FontSize" Value="14" />
                    <Setter Property="HorizontalAlignment" Value="Left" />
                    <Setter Property="VerticalAlignment" Value="Center" />
                    <Setter Property="Margin" Value="5,3,5,3" />
                </Style>
                <Style TargetType="{x:Type TextBox}">
                    <Setter Property="FontSize" Value="14" />
                    <Setter Property="FontWeight" Value="Normal" />
                    <Setter Property="HorizontalAlignment" Value="Stretch" />
                    <Setter Property="VerticalAlignment" Value="Center" />
                    <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 TargetType="{x:Type DatePicker}">
                    <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>
            </Grid.Resources>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <!--Фамилия-->
            <TextBlock Grid.Column="0" Grid.Row="0" Text="Фамилия" />
            <TextBox Grid.Column="1" Grid.Row="0" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" />
            <!--Имя-->
            <TextBlock Grid.Column="0" Grid.Row="1" Text="Имя" />
            <TextBox Grid.Column="1" Grid.Row="1" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" />
            <!--Отчество-->
            <TextBlock Grid.Column="0" Grid.Row="2" Text="Отчество" />
            <TextBox Grid.Column="1" Grid.Row="2" Text="{Binding Patronymic, UpdateSourceTrigger=PropertyChanged}" />
            <!--Пол-->
            <TextBlock Grid.Column="0" Grid.Row="3" Text="Пол" />
            <ComboBox 
                Grid.Column="1"
                Grid.Row="3"
                Margin="0,0,0,3"
                FontSize="14"
                VerticalAlignment="Center"
                HorizontalAlignment="Stretch"
                ItemsSource="{converters:KeyToStringItemsSource EnumKeysSource={x:Type entities:GenderType}}"
                SelectedItem="{Binding Gender, Converter={converters:KeyToStringConverter EnumKeySource={x:Type entities:GenderType}}}"
                IsSynchronizedWithCurrentItem="True"
                />
            <!--Дата рождения-->
            <TextBlock Grid.Column="0" Grid.Row="4" Text="Дата рождения" />
            <DatePicker Grid.Column="1" Grid.Row="4" SelectedDate="{Binding BirthDate}" VerticalAlignment="Center" />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteeCommonEditorView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Testees.Views
{
    /// <summary>
    /// Interaction logic for TesteeCommonEditorView.xaml
    /// </summary>
    public partial class TesteeCommonEditorView : UserControl
    {
        public TesteeCommonEditorView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteeEditorControllerView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Testees.Views.TesteeEditorControllerView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             mc:Ignorable="d" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!--Панель редакторов-->
        <ItemsControl
            Grid.Row="0"
            ItemsSource="{Binding EditorViews}"
            Grid.IsSharedSizeScope="True">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Vertical" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ContentControl 
                        Margin="0,0,0,3"
                        Content="{Binding}" 
                        />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
        <!--Сообщение об ошибке-->
        <TextBlock
            Grid.Row="1"
            Foreground="Red"
            Margin="3"
            HorizontalAlignment="Right"
            Text="{Binding LastError}"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteeEditorControllerView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Testees.Views
{
    /// <summary>
    /// Interaction logic for TesteeEditorControllerView.xaml
    /// </summary>
    public partial class TesteeEditorControllerView : UserControl
    {
        public TesteeEditorControllerView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteeFindView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Testees.Views.TesteeFindView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.Testees.Views"
             mc:Ignorable="d" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <!--Строка поиска-->
        <TextBox 
            Grid.Row="0"
            Name="PART_SearchPattern"
            FontSize="14"
            FontWeight="Medium"
            Margin="3,3,3,5"
            Padding="3"
            Text="{Binding SearchPattern, UpdateSourceTrigger=PropertyChanged}"
            Focusable="True"
            />
        <!--Результаты поиска-->
        <ScrollViewer
            Grid.Row="1"
	        VerticalScrollBarVisibility="Auto"
            HorizontalScrollBarVisibility="Disabled">
            <ListView
		        Margin="3"
		        Background="Transparent"
		        BorderBrush="Transparent"
		        BorderThickness="0"
		        ItemsSource="{Binding SearchResults}"
                SelectedItem="{Binding SelectedTestee}">
                <ListView.Template>
                    <ControlTemplate>
                        <ItemsPresenter />
                    </ControlTemplate>
                </ListView.Template>
                <ListView.ItemContainerStyle>
                    <Style TargetType="{x:Type ListViewItem}">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type ListViewItem}">
                                    <Border
                                        x:Name="PART_Border"
                                        BorderBrush="Transparent"
                                        BorderThickness="0"
                                        Background="Transparent">
                                        <Grid
                                            Margin="5,3,3,3">
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="Auto" />
                                                <RowDefinition Height="Auto" />
                                            </Grid.RowDefinitions>
                                            <TextBlock 
                                                Grid.Row="0"
                                                Text="{Binding FullName}"
                                                FontSize="16"
                                                FontWeight="Medium"
                                                />
                                            <TextBlock 
                                                Grid.Row="1"
                                                Text="{Binding Comment}"
                                                FontSize="12"
                                                FontWeight="Normal"
                                                />
                                        </Grid>
                                    </Border>
                                    <ControlTemplate.Triggers>
                                        <Trigger Property="IsMouseOver" Value="True">
                                            <Setter TargetName="PART_Border" Property="Background" Value="DeepSkyBlue" />
                                        </Trigger>
                                        <Trigger Property="IsSelected" Value="True">
                                            <Setter TargetName="PART_Border" Property="Background" Value="LimeGreen" />
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </ListView.ItemContainerStyle>
            </ListView>
        </ScrollViewer>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteeFindView.xaml.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Testees.Views
{
    public partial class TesteeFindView : UserControl
    {
        public TesteeFindView()
        {
            InitializeComponent();
            Loaded += TesteeFindViewLoaded;
        }

        private void TesteeFindViewLoaded(object sender, RoutedEventArgs e)
        {
            PART_SearchPattern.Focus();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteeRailroadEditorView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Testees.Views.TesteeRailroadEditorView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignWidth="500">
    <Grid
        Margin="0,3,0,0">
        <Grid.Resources>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="HorizontalAlignment" Value="Left" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <Setter Property="Margin" Value="5,3,5,3" />
            </Style>
            <Style TargetType="{x:Type TextBox}">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="FontWeight" Value="Normal" />
                <Setter Property="HorizontalAlignment" Value="Stretch" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <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>
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!--Профессия-->
        <TextBlock Grid.Row="0" Grid.Column="0" Text="Профессия"/>
        <ComboBox 
            Grid.Row="0"
            Grid.Column="1"
            Margin="0,0,0,3"
            VerticalAlignment="Center"
            HorizontalAlignment="Stretch"
            IsSynchronizedWithCurrentItem="True"
            ItemsSource="{Binding ProfessionTypes}"
            SelectedItem="{Binding SelectedProfession}"
            DisplayMemberPath="Title"
            />
        <!--Тип движения-->
        <TextBlock Grid.Row="1" Grid.Column="0" Text="Тип движения" />
        <ComboBox 
            Grid.Row="1"
            Grid.Column="1"
            Margin="0,0,0,3"
            VerticalAlignment="Center"
            HorizontalAlignment="Stretch"
            IsSynchronizedWithCurrentItem="True"
            ItemsSource="{Binding TrafficTypes}"
            SelectedItem="{Binding SelectedTraffic}"
            DisplayMemberPath="Title"
            />
        <!--Колонна-->
        <TextBlock Grid.Row="2" Grid.Column="0" Text="Колонна" />
        <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Column, UpdateSourceTrigger=PropertyChanged}" />
        <!--ТЧ-->
        <TextBlock Grid.Row="3" Grid.Column="0" Text="ТЧ" />
        <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding TC, UpdateSourceTrigger=PropertyChanged}" />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteeRailroadEditorView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Testees.Views
{
    /// <summary>
    /// Interaction logic for TesteeRailroadEditorView.xaml
    /// </summary>
    public partial class TesteeRailroadEditorView : UserControl
    {
        public TesteeRailroadEditorView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteesToolbarView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Testees.Views.TesteesToolbarView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:commands="clr-namespace:Updk7.Wpf.Commands;assembly=Updk7.Wpf"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             mc:Ignorable="d">
    <DockPanel>
        <StackPanel
            DockPanel.Dock="Top"
            VerticalAlignment="Top">
            <Button
                Margin="0,1,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Составление плана тестирования выбранного сотрудника"
                BorderThickness="2"
                Command="{commands:CommandProvider CreateTestingPlan}">
                <Image Margin="3" Source="{StaticResource StartTestImage}" />
            </Button>
            <Button
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Отчеты для выбранного сотрудника"
                BorderThickness="2"
                Command="{commands:CommandProvider ShowReports}">
                <Image Margin="2" Source="{StaticResource ReportsImage}" />
            </Button>
            <Button 
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Добавить сотрудника"
                BorderThickness="2"
                Command="{commands:CommandProvider AddTestee}">
                <Image Margin="3" Source="{StaticResource BlackPlusImage}" />
            </Button>
            <Button
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Редактировать данные сотрудника"
                BorderThickness="2"
                Command="{commands:CommandProvider EditTestee}">
                <Image Margin="3" Source="{StaticResource BlackPencilImage}" />
            </Button>
            <Button 
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Удалить сотрудника"
                BorderThickness="2"
                Command="{commands:CommandProvider RemoveTesteeToArchive}">
                <Image Margin="3" Source="{StaticResource BlackMinusImage}" />
            </Button>
            <Button
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Архив сотрудников"
                BorderThickness="2"
                Command="{commands:CommandProvider ShowArchive}">
                <Image Margin="2" Source="{StaticResource EmployeesArchImage}" />
            </Button>
            <Button
                Margin="0,0,0,5"
                Style="{StaticResource FlatButtonStyle}"
                ToolTip="Поиск сотрудника"
                BorderThickness="2"
                Command="{commands:CommandProvider FindTestee}">
                <Image Margin="2" Source="{StaticResource FindImage}" />
            </Button>
        </StackPanel>
        <ItemsControl
            DockPanel.Dock="Top"
            ItemsSource="{Binding ToolbarElements}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Vertical" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ContentControl Content="{Binding}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </DockPanel>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteesToolbarView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Testees.Views
{
    /// <summary>
    /// Interaction logic for TesteesToolbarView.xaml
    /// </summary>
    public partial class TesteesToolbarView : UserControl
    {
        public TesteesToolbarView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteesView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.Testees.Views.TesteesView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             xmlns:styles="clr-namespace:Updk7.Wpf.Styles;assembly=Updk7.Wpf"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             styles:TabItemHeader.Text="Сотрудники"
             xmlns:keys="clr-namespace:Updk7.Wpf.Master.Modules.Testees"
             xmlns:assets="clr-namespace:Updk7.Wpf.Master.Assets.Views;assembly=Updk7.Wpf.Master.Assets">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <DockPanel
            Grid.Column="0">
            <ContentControl 
                DockPanel.Dock="Top"
                VerticalAlignment="Top"
                Margin="0,0,8,0"
                prism:RegionManager.RegionName="{x:Static keys:RegionKeys.TesteesToolbarRegion}"
                />
            <assets:ShutdownButton
                DockPanel.Dock="Bottom"
                Margin="0,5,8,0"
                VerticalAlignment="Bottom"
                />
            <assets:ChangePasswordButton
                DockPanel.Dock="Bottom"
                Margin="0,0,8,0"
                VerticalAlignment="Bottom"
                />
        </DockPanel>
        <ContentControl 
            Grid.Column="1"
            prism:RegionManager.RegionName="{x:Static keys:RegionKeys.TesteeSelectionRegion}"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\Testees\Views\TesteesView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.Testees.Views
{
    /// <summary>
    /// Interaction logic for TesteesView.xaml
    /// </summary>
    public partial class TesteesView : UserControl
    {
        public TesteesView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\ITestingPlanController.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.TestingPlan
{
    public interface ITestingPlanController : INotifyPropertyChanged
    {
        Assets.TesteeShortInfo TesteeInfo { get; set; }

        bool ContainTests { get; }

        Data.TestingPlan GetTestingPlan();

        void Clear();
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\ITestingPlanTesteeInfo.cs


namespace Updk7.Wpf.Master.Modules.TestingPlan
{
    public interface ITestingPlanTesteeInfo
    {
        Assets.TesteeShortInfo SourceData { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\ITestingPlanTestsSelection.cs


using System.Collections.Generic;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.TestingPlan
{
    public interface ITestingPlanTestsSelection : INotifyPropertyChanged
    {
        int SelectedTestsCount { get; }

        void AppendTestsGroup(Data.TestingTreeGroupNode node);

        IEnumerable<Data.TestGroupRun> GetSelectedTests();

        void Clear();
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\ITestingPlanTree.cs


using System;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.TestingPlan
{
    public interface ITestingPlanTree : INotifyPropertyChanged
    {
        event EventHandler AppendSelectedNode;

        Data.TestingTreeGroupNode SelectedNode { get; }

        void SetTestingTree(Data.TestingTreeGroupNode testingTree);

        void Clear();
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\ITestingPlanWorkplaces.cs


using System.Collections.Generic;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.TestingPlan
{
    public interface ITestingPlanWorkplaces : INotifyPropertyChanged
    {
        Assets.WorkplaceInfo SelectedWorkplace { get; }

        void SetWorkplaces(IEnumerable<Assets.WorkplaceInfo> workplaces);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\RegionKeys.cs


namespace Updk7.Wpf.Master.Modules.TestingPlan
{
    public static class RegionKeys
    {
        public static readonly string TestingPlanTesteeInfoRegion = "TestingPlanTesteeInfoRegion";
        public static readonly string TestingPlanTreeRegion = "TestingPlanTreeRegion";
        public static readonly string TestingPlanTestsRegion = "TestingPlanTestsSelectionRegion";
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\TestingPlanController.cs


using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.TestingPlan
{
    public class TestingPlanController : Prism.Mvvm.BindableBase, ITestingPlanController
    {
        private ViewModels.TestingPlanViewModel _viewModels;
        private Data.ITestingTreeFactory _testingTreeFactory;
        private Data.ITestsParametersFactory _testParametersFactory;

        public TestingPlanController(ViewModels.TestingPlanViewModel viewModels,
            Data.ITestingTreeFactory testingTreeFactory,
            Data.ITestsParametersFactory testParametersFactory)
        {
            _testingTreeFactory = testingTreeFactory;
            _testParametersFactory = testParametersFactory;
            _viewModels = viewModels;
            _viewModels.TestsTree.AppendSelectedNode += (o, e) => onAppendSelectedTestsGroup();
            _viewModels.Tests.PropertyChanged += onTestsPropertyChanged;
        }

        private Assets.TesteeShortInfo _testeeInfo;

        public Assets.TesteeShortInfo TesteeInfo
        {
            get { return _testeeInfo; }
            set { SetProperty(ref _testeeInfo, value); }
        }

        private bool _containTests;

        public bool ContainTests
        {
            get { return _containTests; }
            private set { SetProperty(ref _containTests, value); }
        } 

        public Data.TestingPlan GetTestingPlan()
        {
            if (TesteeInfo == null)
                return null;

            var testingPlan = new Data.TestingPlan();
            foreach (var group in _viewModels.Tests.GetSelectedTests())
            {
                foreach (var testRun in group.Tests)
                {
                    testRun.Parameters = _testParametersFactory
                        .CreateTestParameters(TesteeInfo.Entitie, testRun.TestType);
                }

                testingPlan.AddTestsGroup(group);
            }

            return testingPlan;
        }

        public void Clear()
        {
            _viewModels.TesteeInfo.SourceData = null;
            _viewModels.Tests.Clear();
            _viewModels.TestsTree.Clear();
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(TesteeInfo))
                setTesteeInfo();

            base.OnPropertyChanged(args);
        }

        private void onTestsPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ITestingPlanTestsSelection.SelectedTestsCount))
                ContainTests = _viewModels.Tests.SelectedTestsCount != 0;
        }

        public void onAppendSelectedTestsGroup()
        {
            if (_viewModels.TestsTree.SelectedNode != null)
                _viewModels.Tests.AppendTestsGroup(_viewModels.TestsTree.SelectedNode);
        }

        protected void setTesteeInfo()
        {
            _viewModels.TesteeInfo.SourceData = _testeeInfo;
            _viewModels.Tests.Clear();
            if (_testeeInfo != null)
            {
                var testingTree = _testingTreeFactory.GetTestingTreeNode(_testeeInfo.Entitie);
                _viewModels.TestsTree.SetTestingTree(testingTree);
            }
            else
                _viewModels.TestsTree.Clear();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\Commands\StartTestingCommand.cs


using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;

namespace Updk7.Wpf.Master.Modules.TestingPlan.Commands
{
    public class StartTestingCommand : Wpf.Commands.CommandBase
    {
        private class ErrorStrings
        {
            public static readonly string ExternalDevices = "Выбранные тесты используют пульт для проведения тестирования и не могут быть запущены на выбранном рабочем месте";
            public static readonly string NoSlaveActivationKey = "На компьютере испытуемого не подключен ключ активации Rutoken. Тестирование не может быть запущено на выбранном рабочем месте";
            public static readonly string NoMasterActivationKey = "На компьютере психолога не подключен ключ активации Rutoken";
        }

        private ITestingPlanWorkplaces _workplaceSelection;
        private ITestingPlanController _testingPlanController;
        private Prism.Events.IEventAggregator _eventAggregator;
        private Rutoken.Common.RutokenMonitor _rutokenMonitor;

        public StartTestingCommand(ITestingPlanWorkplaces workplaceSelection, 
            ITestingPlanController testingPlanController,
            Prism.Events.IEventAggregator eventAggregator,
            Rutoken.Common.RutokenMonitor rutokenMonitor)
        {
            _rutokenMonitor = rutokenMonitor;
            _testingPlanController = testingPlanController;
            _testingPlanController.PropertyChanged += onTestingPlanControllerPropertyChanged;
            _workplaceSelection = workplaceSelection;
            _workplaceSelection.PropertyChanged += onWorkplaceSelectionPropertyChanged;
            _eventAggregator = eventAggregator;
        }
        
        public override bool CanExecute(object parameter)
        {
            return _testingPlanController.TesteeInfo != null 
                && _testingPlanController.ContainTests;
        }

        public override void Execute(object parameter)
        {
            // Составление плана тестирование после выбора раобчего места (onWorkplaceSelected)
            _workplaceSelection.SetWorkplaces(getFreeWorkplaces());
        }

        private void onWorkplaceSelectionPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ITestingPlanWorkplaces.SelectedWorkplace))
                onWorkplaceSelected();
        }

        private void onTestingPlanControllerPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ITestingPlanController.ContainTests))
                RaiseCanExecuteChanged();
            else if (e.PropertyName == nameof(ITestingPlanController.TesteeInfo))
                RaiseCanExecuteChanged();
        }

        private void onWorkplaceSelected()
        {
            if (_workplaceSelection.SelectedWorkplace == null)
                return;

            var testingInfo = new Assets.TestingInfo();
            testingInfo.Testee = _testingPlanController.TesteeInfo;
            testingInfo.Workplace = _workplaceSelection.SelectedWorkplace;
            testingInfo.TestingPlan = _testingPlanController.GetTestingPlan();

            if (canStartTesting(testingInfo))
            {
                var startTestingData = new Assets.Events.StartTestingEventData();
                startTestingData.TestingInfo = testingInfo;

                _eventAggregator.GetEvent<Assets.Events.StartTestingEvent>()
                    .Publish(startTestingData);

                _testingPlanController.Clear();
            }
        }

        private IEnumerable<Assets.WorkplaceInfo> getFreeWorkplaces()
        {
            var discoveryData = new Assets.Events.WorkplacesDiscoveryEventData();
            _eventAggregator
                .GetEvent<Assets.Events.WorkplacesDiscoveryEvent>()
                .Publish(discoveryData);

            return discoveryData.Workplaces?.Where(w => !w.IsBusy);
        }

        private bool canStartTesting(Assets.TestingInfo info)
        {
            if (!_rutokenMonitor.ContainsFeature())
            {
                Interactions.InteractionService.Current.ShowErrorMessage(ErrorStrings.NoMasterActivationKey);
                return false;
            }
            else if (info.Workplace.Error.HasFlag(Network.Local.ErrorFlags.ActivationKey))
            {
                Interactions.InteractionService.Current.ShowErrorMessage(ErrorStrings.NoSlaveActivationKey, asDialog: false);
                return false;
            }
            else if (info.Workplace.Error.HasFlag(Network.Local.ErrorFlags.HardwareError))
            {
                var useHardwareDevicee = info.TestingPlan.TestsGroups
                    .Any(g => g.Tests
                        .Any(t => Tests.Metadata.GetTestInfo(t.TestType).UseExternalDevices));

                if (useHardwareDevicee)
                {
                    Interactions.InteractionService.Current.ShowErrorMessage(ErrorStrings.ExternalDevices, asDialog: false);
                    return false;
                }
            }

            return true;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\EventListeners\CreateTestingPlanListener.cs


namespace Updk7.Wpf.Master.Modules.TestingPlan.EventListeners
{
    public class CreateTestingPlanListener : IEventListener
    {
        private ITestingPlanController _testingPlanController;

        public CreateTestingPlanListener(ITestingPlanController testingPlanController)
        {
            _testingPlanController = testingPlanController;
        }

        public void Subscribe(Prism.Events.IEventAggregator eventAggregator)
        {
            eventAggregator
                .GetEvent<Assets.Events.CreateTestingPlanEvent>()
                .Subscribe(onCreateTestingPlan);
        }

        private void onCreateTestingPlan(Assets.Events.CreateTestingPlanEventData data)
        {
            _testingPlanController.TesteeInfo = data.Testee;
            ViewModelLocator.Current.NavigateTo("TestingPlan");
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\ViewModels\TestingPlanTesteeInfoViewModel.cs


namespace Updk7.Wpf.Master.Modules.TestingPlan.ViewModels
{
    public class TestingPlanTesteeInfoViewModel : Assets.ViewModels.TesteeInfoViewModel, ITestingPlanTesteeInfo
    {
        public TestingPlanTesteeInfoViewModel()
        {
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\ViewModels\TestingPlanTestsSelectionViewModel.cs


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;

namespace Updk7.Wpf.Master.Modules.TestingPlan.ViewModels
{
    public class TestingPlanTestsSelectionViewModel : Prism.Mvvm.BindableBase, ITestingPlanTestsSelection
    {
        public TestingPlanTestsSelectionViewModel()
        {
            TestsGroups.CollectionChanged += onTestsGroupsCollectionChanged;
        }

        public ObservableCollection<TestsGroupSelectionInfoViewModel> TestsGroups { get; } =
            new ObservableCollection<TestsGroupSelectionInfoViewModel>();

        private int _selectedTestsCount;

        public int SelectedTestsCount
        {
            get { return _selectedTestsCount; }
            private set { SetProperty(ref _selectedTestsCount, value); }
        } 

        public void Clear()
        {
            while (TestsGroups.Count != 0)
                TestsGroups.RemoveAt(TestsGroups.Count - 1);
        }

        public void AppendTestsGroup(Data.TestingTreeGroupNode node)
        {
            if (TestsGroups.Any(g => g.SourceData.Key == node.Key))
                return;

            TestsGroups.Add(new TestsGroupSelectionInfoViewModel(node));
        }

        public IEnumerable<Data.TestGroupRun> GetSelectedTests()
        {
            foreach (var group in TestsGroups)
            {
                var groupRun = new Data.TestGroupRun();
                groupRun.GroupTitle = group.SourceData.Title;
                groupRun.GroupKey = group.SourceData.Key;
                foreach (var test in group.GroupTests.Where(t => t.IsChecked))
                    groupRun.AddTest(new Data.TestRun(test.SourceData.Test));

                if (groupRun.TestsCount != 0)
                    yield return groupRun;
            }
        }

        private void onTestsGroupsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (TestsGroupSelectionInfoViewModel viewModel in e.NewItems)
                {
                    viewModel.PropertyChanged += onTestsGroupPropertyChanged;
                    viewModel.RemoveRequest += onTestsGroupRemoveRequest;
                }
            }

            if (e.OldItems != null)
            {
                foreach (TestsGroupSelectionInfoViewModel viewModel in e.OldItems)
                {
                    viewModel.PropertyChanged -= onTestsGroupPropertyChanged;
                    viewModel.RemoveRequest -= onTestsGroupRemoveRequest;
                }
            }

            updateSelectedTestsCount();
        }

        private void onTestsGroupPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(TestsGroupSelectionInfoViewModel.SelectedTestsCount))
                updateSelectedTestsCount();
        }

        private void onTestsGroupRemoveRequest(object sender, EventArgs e)
        {
            TestsGroups.Remove(sender as TestsGroupSelectionInfoViewModel);
        }

        private void updateSelectedTestsCount()
        {
            SelectedTestsCount = TestsGroups.Sum(g => g.SelectedTestsCount);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\ViewModels\TestingPlanTreeNodeViewModel.cs


using System.Linq;
using System.Collections.Generic;

namespace Updk7.Wpf.Master.Modules.TestingPlan.ViewModels
{
    public class TestingPlanTreeNodeViewModel : Prism.Mvvm.BindableBase
    {
        public TestingPlanTreeNodeViewModel(Data.TestingTreeGroupNode root)
        {
            SourceData = root;
            Title = root.Title;
            Nodes = root.Nodes.Select(n => new TestingPlanTreeNodeViewModel(n))
                .ToArray();
        }

        public Data.TestingTreeGroupNode SourceData { get; }

        public IEnumerable<TestingPlanTreeNodeViewModel> Nodes { get; }

        public string Title { get; }

        private bool _isSelected;

        public bool IsSelected
        {
            get { return _isSelected; }
            set { SetProperty(ref _isSelected, value); }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\ViewModels\TestingPlanTreeViewModel.cs


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace Updk7.Wpf.Master.Modules.TestingPlan.ViewModels
{
    public class TestingPlanTreeViewModel : Prism.Mvvm.BindableBase, ITestingPlanTree
    {
        public TestingPlanTreeViewModel()
        {
            AppendSelectedNodeCommand = new Prism.Commands.DelegateCommand(
                () => AppendSelectedNode?.Invoke(this, EventArgs.Empty),
                () => SelectedNode != null && SelectedNode.TestsCount != 0);
        }

        public Prism.Commands.DelegateCommand AppendSelectedNodeCommand { get; }

        public event EventHandler AppendSelectedNode;

        private IEnumerable<TestingPlanTreeNodeViewModel> _testsGroupNodes;

        public IEnumerable<TestingPlanTreeNodeViewModel> TestsGroupNodes
        {
            get { return _testsGroupNodes; }
            private set { SetProperty(ref _testsGroupNodes, value); }
        }

        private Data.TestingTreeGroupNode _selectedNode;

        public Data.TestingTreeGroupNode SelectedNode
        {
            get { return _selectedNode; }
            private set { SetProperty(ref _selectedNode, value); }
        } 

        public void SetTestingTree(Data.TestingTreeGroupNode testingTree)
        {
            Clear();
            
            TestsGroupNodes = testingTree.Nodes
                .Select(n => new TestingPlanTreeNodeViewModel(n))
                .ToArray();

            traverseTree(_testsGroupNodes, n => n.PropertyChanged += onNodePropertyChanged);
        }

        public void Clear()
        {
            if (_testsGroupNodes != null)
                traverseTree(_testsGroupNodes, n => n.PropertyChanged -= onNodePropertyChanged);

            SelectedNode = null;
            TestsGroupNodes = null;
        }

        private void onNodePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(TestingPlanTreeNodeViewModel.IsSelected))
            {
                var node = sender as TestingPlanTreeNodeViewModel;
                SelectedNode = node.IsSelected ? node.SourceData : null;
            }
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(SelectedNode))
                AppendSelectedNodeCommand.RaiseCanExecuteChanged();

            base.OnPropertyChanged(args);
        }

        private void traverseTree(IEnumerable<TestingPlanTreeNodeViewModel> nodes, 
            Action<TestingPlanTreeNodeViewModel> action)
        {
            foreach (var node in nodes)
            {
                action(node);
                traverseTree(node.Nodes, action);
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\ViewModels\TestingPlanViewModel.cs


namespace Updk7.Wpf.Master.Modules.TestingPlan.ViewModels
{
    public class TestingPlanViewModel : Prism.Mvvm.BindableBase
    {
        public TestingPlanViewModel(ITestingPlanTesteeInfo testeeInfo, ITestingPlanTree testsTree,
            ITestingPlanTestsSelection tests)
        {
            TesteeInfo = testeeInfo;
            TestsTree = testsTree;
            Tests = tests;
        }

        public ITestingPlanTesteeInfo TesteeInfo { get; }

        public ITestingPlanTree TestsTree { get; }

        public ITestingPlanTestsSelection Tests { get; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\ViewModels\TestingPlanWorkplacesViewModel.cs


using System.Linq;
using System.Collections.Generic;

namespace Updk7.Wpf.Master.Modules.TestingPlan.ViewModels
{
    public class TestingPlanWorkplacesViewModel : Prism.Mvvm.BindableBase, ITestingPlanWorkplaces
    {
        public TestingPlanWorkplacesViewModel()
        {
            WorkplaceSelectedCommand = new Prism.Commands.DelegateCommand<Assets.WorkplaceInfo>(workplaceSelected);
        }

        public Prism.Commands.DelegateCommand<Assets.WorkplaceInfo> WorkplaceSelectedCommand { get; }

        private bool _canSelectWorkplace;

        public bool CanSelectWorkplace
        {
            get { return _canSelectWorkplace; }
            set { SetProperty(ref _canSelectWorkplace, value); }
        }

        private bool _hasWorkplaces;

        public bool HasWorkplaces
        {
            get { return _hasWorkplaces; }
            private set { SetProperty(ref _hasWorkplaces, value); }
        } 

        private IEnumerable<Assets.WorkplaceInfo> _workplaces;

        public IEnumerable<Assets.WorkplaceInfo> Workplaces
        {
            get { return _workplaces; }
            private set { SetProperty(ref _workplaces, value); }
        }

        private Assets.WorkplaceInfo _selectedWorkplace;

        public Assets.WorkplaceInfo SelectedWorkplace
        {
            get { return _selectedWorkplace; }
            private set { SetProperty(ref _selectedWorkplace, value); }
        } 
        
        public void SetWorkplaces(IEnumerable<Assets.WorkplaceInfo> workplaces)
        {
            SelectedWorkplace = null;
            Workplaces = workplaces;
            HasWorkplaces = Workplaces != null && Workplaces.Count() != 0;
            CanSelectWorkplace = true;
        }

        private void workplaceSelected(Assets.WorkplaceInfo workplace)
        {
            CanSelectWorkplace = false;
            SelectedWorkplace = workplace;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\ViewModels\TestSelectionInfoViewModel.cs


namespace Updk7.Wpf.Master.Modules.TestingPlan.ViewModels
{
    public class TestSelectionInfoViewModel : Prism.Mvvm.BindableBase
    {
        public TestSelectionInfoViewModel(Data.TestingTreeTestNode testInfo)
        {
            SourceData = testInfo;
            var metadata = Tests.Metadata.GetTestInfo(testInfo.Test);
            Title = metadata.TestName;
            IsOptional = testInfo.IsOptional;
            if (!IsOptional)
                IsChecked = true;
        }

        public Data.TestingTreeTestNode SourceData { get; }

        public string Title { get; }

        public bool IsOptional { get; }

        private bool _isChecked;

        public bool IsChecked
        {
            get { return _isChecked; }
            set { SetProperty(ref _isChecked, value); }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\ViewModels\TestsGroupSelectionInfoViewModel.cs


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace Updk7.Wpf.Master.Modules.TestingPlan.ViewModels
{
    public class TestsGroupSelectionInfoViewModel : Prism.Mvvm.BindableBase
    {
        public TestsGroupSelectionInfoViewModel(Data.TestingTreeGroupNode node)
        {
            SourceData = node;
            Title = node.Title;
            RemoveRequestCommand = new Prism.Commands.DelegateCommand(() => RemoveRequest?.Invoke(this, EventArgs.Empty));
            setupTestsViewModels();
            updateSelectedTestsCount();
        }

        public Data.TestingTreeGroupNode SourceData { get; }

        public string Title { get; }

        public Prism.Commands.DelegateCommand RemoveRequestCommand { get; }

        public event EventHandler RemoveRequest;

        public IEnumerable<TestSelectionInfoViewModel> GroupTests { get; private set; }

        private int _selectedTestsCount;

        public int SelectedTestsCount
        {
            get { return _selectedTestsCount; }
            set { SetProperty(ref _selectedTestsCount, value); }
        } 

        private void onTestPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(TestSelectionInfoViewModel.IsChecked))
                updateSelectedTestsCount();
        }

        private void updateSelectedTestsCount()
        {
            SelectedTestsCount = GroupTests.Where(t => t.IsChecked).Count();
        }

        private void setupTestsViewModels()
        {
            Func<Data.TestingTreeTestNode, TestSelectionInfoViewModel> factory = n =>
            {
                var viewModel = new TestSelectionInfoViewModel(n);
                viewModel.PropertyChanged += onTestPropertyChanged;
                return viewModel;
            };

            GroupTests = SourceData.Tests.Select(factory).ToArray();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\Views\TestingPlanTesteeInfoView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.TestingPlan.Views.TestingPlanTesteeInfoView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.TestingPlan.Views"
             mc:Ignorable="d" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             xmlns:converters="clr-namespace:Updk7.Wpf.Converters;assembly=Updk7.Wpf">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock 
            Grid.Row="0"
            FontSize="18"
            FontWeight="Medium"
            Margin="5,5,1,1"
            TextWrapping="Wrap"
            Text="{Binding FullName, Mode=OneWay}"
            />
        <TextBlock 
            Grid.Row="1"
            FontSize="14"
            FontWeight="Normal"
            Margin="5,1,1,5"
            TextWrapping="Wrap"
            Visibility="{Binding Comment, Converter={converters:ObjectToVisibilityConverter}, Mode=OneWay}" 
            Text="{Binding Comment, Mode=OneWay}" />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\Views\TestingPlanTesteeInfoView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.TestingPlan.Views
{
    /// <summary>
    /// Interaction logic for TestingPlanTesteeInfoView.xaml
    /// </summary>
    public partial class TestingPlanTesteeInfoView : UserControl
    {
        public TestingPlanTesteeInfoView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\Views\TestingPlanTestsSelectionView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.TestingPlan.Views.TestingPlanTestsSelectionView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.TestingPlan.Views"
             mc:Ignorable="d" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True">
    <Grid>
        <ScrollViewer
            Grid.Row="1"
            VerticalScrollBarVisibility="Auto">
            <ListView
                Margin="3"
                Background="Transparent"
                BorderBrush="Transparent"
                BorderThickness="0"
                ItemsSource="{Binding TestsGroups}"
                ScrollViewer.HorizontalScrollBarVisibility="Disabled">
                <ListView.Template>
                    <ControlTemplate>
                        <ItemsPresenter />
                    </ControlTemplate>
                </ListView.Template>
                <ListView.ItemContainerStyle>
                    <Style TargetType="{x:Type ListViewItem}">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type ListViewItem}">
                                    <local:TestsGroupSelectionInfoView 
                                        DataContext="{Binding}"
                                        />
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </ListView.ItemContainerStyle>
            </ListView>
        </ScrollViewer>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\Views\TestingPlanTestsSelectionView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.TestingPlan.Views
{
    /// <summary>
    /// Interaction logic for TestingPlanTestsSelectionView.xaml
    /// </summary>
    public partial class TestingPlanTestsSelectionView : UserControl
    {
        public TestingPlanTestsSelectionView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\Views\TestingPlanTreeView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.TestingPlan.Views.TestingPlanTreeView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:viewModels="clr-namespace:Updk7.Wpf.Master.Modules.TestingPlan.ViewModels"
             mc:Ignorable="d" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <DockPanel
            Grid.Column="0"
            Margin="0,3,8,0">
            <StackPanel
                DockPanel.Dock="Top"
                VerticalAlignment="Top">
                <Button
                    Style="{StaticResource FlatButtonStyle}"
                    ToolTip="Добавить обследование"
                    Margin="0,1,0,5"
                    BorderThickness="2"
                    Command="{Binding AppendSelectedNodeCommand}">
                    <Image Margin="3" Source="{StaticResource BlackPlusImage}" />
                </Button>
            </StackPanel>
        </DockPanel>
        <TreeView
            Grid.Column="1"
            Margin="0,5,0,0"
            Background="Transparent"
            BorderBrush="Black"
            BorderThickness="0"
            ItemsSource="{Binding TestsGroupNodes}"
            ScrollViewer.HorizontalScrollBarVisibility="Disabled">
            <TreeView.Resources>
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}">
                    Transparent
                </SolidColorBrush>
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}">
                    Black
                </SolidColorBrush>
                <!--Стиль текстового блока-->
                <Style x:Key="TextBlockStyle" TargetType="{x:Type TextBlock}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding IsSelected}" Value="True">
                            <Setter Property="FontSize" Value="18" />
                            <Setter Property="FontWeight" Value="Bold" />
                        </DataTrigger>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True" />
                                <Condition Binding="{Binding IsSelected}" Value="False" />
                            </MultiDataTrigger.Conditions>
                            <Setter Property="Foreground" Value="Gray" />
                        </MultiDataTrigger>
                    </Style.Triggers>
                </Style>
            </TreeView.Resources>
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate 
                    DataType="{x:Type viewModels:TestingPlanTreeNodeViewModel}"
                    ItemsSource="{Binding Nodes}" >
                    <TextBlock
                        Text="{Binding Title}" 
                        FontSize="14"
                        TextWrapping="Wrap"
                        Padding="3"
                        Style="{StaticResource TextBlockStyle}">
                        <TextBlock.InputBindings>
                            <MouseBinding 
                                MouseAction="LeftDoubleClick"
                                Command="{Binding DataContext.AppendSelectedNodeCommand, 
                                    RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                                />
                        </TextBlock.InputBindings>
                    </TextBlock>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsExpanded" Value="True" />
                    <Setter Property="IsSelected" Value="{Binding IsSelected}" />
                </Style>
            </TreeView.ItemContainerStyle>
        </TreeView>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\Views\TestingPlanView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.TestingPlan.Views.TestingPlanView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.TestingPlan.Views"
             mc:Ignorable="d" 
             xmlns:styles="clr-namespace:Updk7.Wpf.Styles;assembly=Updk7.Wpf"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             styles:TabItemHeader.Text="План тестирования"
             xmlns:keys="clr-namespace:Updk7.Wpf.Master.Modules.TestingPlan">
    <Grid
        Margin="2,5,1,1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*" MinWidth="450" />
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
        <!--Информация о сотруднике и выбор групп тестов-->
        <Grid
            Grid.Column="0">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <!--Информация о сотруднике-->
            <Grid
                Grid.Row="0">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="50" />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <!--Заголовок панели-->
                <Border
                    Grid.Row="0"
                    Grid.Column="0"
                    Grid.ColumnSpan="2"
                    Background="DeepSkyBlue"
                    Padding="10,3,2,3">
                    <TextBlock 
                        FontSize="14"
                        FontWeight="Bold"
                        Text="Сотрудник"
                        />
                </Border>
                <!--Выбор рабочего места-->
                <local:TestingPlanWorkplacesView 
                    Grid.Column="0"
                    Grid.Row="1"
                    Margin="0,3,0,0"
                    VerticalAlignment="Top"
                    />
                <!--Информация о испытуемом-->
                <ContentControl 
                    Grid.Row="1"
                    Grid.Column="1"
                    prism:RegionManager.RegionName="{x:Static keys:RegionKeys.TestingPlanTesteeInfoRegion}"
                    />
            </Grid>
            <!--Выбор групп тестов-->
            <Grid
                Grid.Row="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <!--Заголовок панели-->
                <Border
                    Grid.Row="0"
                    Grid.Column="0"
                    Grid.ColumnSpan="2"
                    Background="DeepSkyBlue"
                    Padding="10,3,2,3">
                    <TextBlock 
                        FontSize="14"
                        FontWeight="Bold"
                        Text="Выбор обследований"
                        />
                </Border>
                <!--Выбор групп тестов-->
                <ContentControl 
                    Grid.Row="1"
                    prism:RegionManager.RegionName="{x:Static keys:RegionKeys.TestingPlanTreeRegion}"
                    />
            </Grid>
        </Grid>
        <!--Выбор тестов в группе-->
        <Grid
            Grid.Column="1"
            Margin="3,0,0,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <!--Заголовок панели-->
            <Border
                Grid.Row="0"
                Background="DeepSkyBlue"
                Padding="10,3,2,3">
                <TextBlock 
                    FontSize="14"
                    FontWeight="Bold"
                    Text="Выбор обследований"
                    />
            </Border>
            <!--Выбор тестов в группе-->
            <ContentControl 
                Grid.Row="1"
                prism:RegionManager.RegionName="{x:Static keys:RegionKeys.TestingPlanTestsRegion}"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\Views\TestingPlanView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.TestingPlan.Views
{
    /// <summary>
    /// Interaction logic for TestingPlanView.xaml
    /// </summary>
    public partial class TestingPlanView : UserControl
    {
        public TestingPlanView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\Views\TestingPlanWorkplacesView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.TestingPlan.Views.TestingPlanWorkplacesView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Wpf.Master.Modules.TestingPlan.Views"
             xmlns:commands="clr-namespace:Updk7.Wpf.Commands;assembly=Updk7.Wpf"
             mc:Ignorable="d" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True">
    <Grid
        Margin="0,0,8,0">
        <Button
            Margin="0,0,0,5"
            Style="{StaticResource FlatButtonStyle}"
            ToolTip="Начать тестирование выбранного сотрудника"
            BorderThickness="2"
            Command="{commands:CommandProvider StartTesting}">
            <Image Margin="3" Source="{StaticResource StartTestImage}" />
        </Button>
        <Popup
            Margin="5"
            Placement="Right"
            HorizontalOffset="5"
            VerticalOffset="0"
            AllowsTransparency="True"
            IsOpen="{Binding CanSelectWorkplace}"
            StaysOpen="False">
            <Border
                BorderBrush="Black"
                BorderThickness="1"
                Padding="3"
                Width="300"
                MaxHeight="500"
                Background="{StaticResource WindowBackground}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition />
                    </Grid.RowDefinitions>
                    <Grid.Resources>
                        <Style x:Key="HeaderTextBlock" TargetType="{x:Type TextBlock}">
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding HasWorkplaces}" Value="False">
                                    <Setter Property="Text" Value="Нет свободных рабочих мест для проведения тестирования" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding HasWorkplaces}" Value="True">
                                    <Setter Property="Text" Value="Выберите рабочее место, на котором будет запущено тестирование испытуемого" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Grid.Resources>
                    <TextBlock 
                        Grid.Row="0"
                        Style="{StaticResource HeaderTextBlock}"
                        Margin="5"
                        FontSize="14"
                        TextWrapping="Wrap"
                        TextAlignment="Center"
                        />
                    <ListView
                        Grid.Row="1"
                        ItemsSource="{Binding Workplaces}"
                        Background="Transparent"
                        BorderBrush="Transparent"
                        BorderThickness="0">
                        <ListView.ItemContainerStyle>
                            <Style TargetType="{x:Type ListViewItem}">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="{x:Type ListViewItem}">
                                            <Button
                                                Command="{Binding DataContext.WorkplaceSelectedCommand, 
                                                    RelativeSource={RelativeSource Mode=FindAncestor, 
                                                        AncestorType={x:Type ListView}}}"
                                                CommandParameter="{Binding DataContext, 
                                                    RelativeSource={RelativeSource Mode=FindAncestor, 
                                                        AncestorType={x:Type ListViewItem}}}">
                                                <Button.Template>
                                                    <ControlTemplate>
                                                        <Border
                                                            x:Name="PART_Border"
                                                            Margin="3"
                                                            Padding="3"
                                                            BorderBrush="Gray"
                                                            BorderThickness="2"
                                                            Background="Transparent">
                                                            <Grid>
                                                                <Grid.ColumnDefinitions>
                                                                    <ColumnDefinition Width="Auto" />
                                                                    <ColumnDefinition />
                                                                </Grid.ColumnDefinitions>
                                                                <Image
                                                                    Grid.Column="0"
                                                                    Source="{StaticResource ComputerImage}"
                                                                    VerticalAlignment="Center"
                                                                    HorizontalAlignment="Center"
                                                                    />
                                                                <TextBlock 
                                                                    Grid.Column="1"
                                                                    x:Name="PART_Text"
                                                                    Margin="5,0,5,0"
                                                                    FontSize="14"
                                                                    FontWeight="Medium"
                                                                    VerticalAlignment="Center"
                                                                    HorizontalAlignment="Center">
                                                                    <Run Text="{Binding Title}" />
                                                                    <LineBreak />
                                                                    <Run Text="{Binding Address}" 
                                                                         FontSize="10" 
                                                                         FontWeight="Bold"
                                                                         />
                                                                </TextBlock>
                                                            </Grid>
                                                        </Border>
                                                        <ControlTemplate.Triggers>
                                                            <EventTrigger RoutedEvent="ListViewItem.MouseEnter">
                                                                <EventTrigger.Actions>
                                                                    <BeginStoryboard>
                                                                        <Storyboard>
                                                                            <DoubleAnimation 
                                                                                Storyboard.TargetName="PART_Text"
                                                                                Storyboard.TargetProperty="FontSize"
                                                                                To="16"
                                                                                Duration="0:0:0.5"
                                                                                />
                                                                        </Storyboard>
                                                                    </BeginStoryboard>
                                                                </EventTrigger.Actions>
                                                            </EventTrigger>
                                                            <EventTrigger RoutedEvent="ListViewItem.MouseLeave">
                                                                <EventTrigger.Actions>
                                                                    <BeginStoryboard>
                                                                        <Storyboard>
                                                                            <DoubleAnimation
                                                                                Storyboard.TargetName="PART_Text"
                                                                                Storyboard.TargetProperty="FontSize"
                                                                                BeginTime="0:0:0.2"
                                                                                Duration="0:0:0.2"
                                                                                />
                                                                        </Storyboard>
                                                                    </BeginStoryboard>
                                                                </EventTrigger.Actions>
                                                            </EventTrigger>
                                                            <Trigger Property="IsMouseOver" Value="True">
                                                                <Setter 
                                                                    TargetName="PART_Border" 
                                                                    Property="BorderBrush" 
                                                                    Value="DeepSkyBlue" 
                                                                    />
                                                            </Trigger>
                                                        </ControlTemplate.Triggers>
                                                    </ControlTemplate>
                                                </Button.Template>
                                            </Button>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </ListView.ItemContainerStyle>
                    </ListView>
                </Grid>
            </Border>
        </Popup>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\Views\TestingPlanWorkplacesView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.TestingPlan.Views
{
    /// <summary>
    /// Interaction logic for TestingPlanWorkplacesView.xaml
    /// </summary>
    public partial class TestingPlanWorkplacesView : UserControl
    {
        public TestingPlanWorkplacesView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\Views\TestsGroupSelectionInfoView.xaml

<UserControl x:Class="Updk7.Wpf.Master.Modules.TestingPlan.Views.TestsGroupSelectionInfoView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Border
            Grid.Row="0"
            Focusable="False"
            Background="LimeGreen">
            <Grid>
                <Grid.Resources>
                    <Style x:Key="CloseButtonStyle" TargetType="{x:Type Button}">
                        <Setter Property="Background" Value="Transparent" />
                        <Setter Property="Foreground" Value="Black" />
                        <Setter Property="FontSize" Value="14" />
                        <Setter Property="FontWeight" Value="Bold" />
                        <Setter Property="BorderThickness" Value="0" />
                        <Setter Property="FontFamily" Value="Webdings" />
                        <Setter Property="Content" Value="r" />
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type Button}">
                                    <ContentPresenter />
                                    <ControlTemplate.Triggers>
                                        <Trigger Property="IsMouseOver" Value="True">
                                            <Setter Property="FontSize" Value="18" />
                                            <Setter Property="FontWeight" Value="ExtraBlack" />
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </Grid.Resources>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition Width="30" />
                </Grid.ColumnDefinitions>
                <TextBlock 
                    Grid.Column="0"
                    Focusable="False"
                    Text="{Binding Title}"
                    FontSize="14"
                    FontWeight="Medium"
                    Margin="3"
                    TextWrapping="Wrap"
                    />
                <Button 
                    Grid.Column="1"
                    Style="{StaticResource CloseButtonStyle}"
                    Margin="3,3,7,3"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Command="{Binding RemoveRequestCommand}"
                    />
            </Grid>
        </Border>
        <ListView
            Grid.Row="1"
            Background="Transparent"
            BorderBrush="Transparent"
            BorderThickness="0"
            ItemsSource="{Binding GroupTests}"
            ScrollViewer.CanContentScroll="False"
            ScrollViewer.HorizontalScrollBarVisibility="Disabled">
            <ListView.Template>
                <ControlTemplate>
                    <ItemsPresenter />
                </ControlTemplate>
            </ListView.Template>
            <ListView.ItemContainerStyle>
                <Style TargetType="{x:Type ListViewItem}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ListViewItem}">
                                <CheckBox 
                                    Style="{StaticResource FlatCheckBoxStyle}"
                                    Focusable="False"
                                    Margin="10,3,3,3"
                                    FontSize="14"
                                    Content="{Binding Title}"
                                    IsEnabled="{Binding IsOptional}"
                                    IsChecked="{Binding IsChecked}"
                                    />
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListView.ItemContainerStyle>
        </ListView>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\Views\TestsGroupSelectionInfoView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.TestingPlan.Views
{
    /// <summary>
    /// Interaction logic for TestsGroupSelectionInfoView.xaml
    /// </summary>
    public partial class TestsGroupSelectionInfoView : UserControl
    {
        public TestsGroupSelectionInfoView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Wpf\Updk7.Wpf.Master.Modules\Source\TestingPlan\Views\TestsGroupSelectionView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Wpf.Master.Modules.TestingPlan.Views
{
    /// <summary>
    /// Interaction logic for TestingPlanTreeView.xaml
    /// </summary>
    public partial class TestingPlanTreeView : UserControl
    {
        public TestingPlanTreeView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\MMPI.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="L80"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Результаты тестирования недостоверны из-за чрезмерного стремления представить себя в лучшем свете.
        </Paragraph>
    </Section>

    <Section 
        x:Key="L60"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремление представить себя в возможно более выгодном свете, продемонстрировав строгое соблюдение
            социальных норм (может быть обусловлено либо ситуацией обследования, либо принадлежностью
            к социальной группе с высоким стандартом поведения и повышенными требованиями к соблюдению конвенциальных 
            норм).
        </Paragraph>
    </Section>

    <Section 
        x:Key="F80"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Результаты тестирования недостоверны из-за случайно или намеренно неверных ответов (симуляция, агравация),
            либо болезненных изменений психической деятельности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="F60"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Неконформность поведения, своеобразие восприятия, логики, затруднённость межличностных контактов, 
            протест против существующих принятых норм, внутренняя напряжённость, недовольство ситуацией, плохо 
            организованная активность; тенденция к усилению тяжести имеющихся затруднений и конфликтов.
        </Paragraph>
    </Section>

    <Section 
        x:Key="K80"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Результаты тестирования сомнительны из-за стремления улучшить впечатление о себе, смягчить
            свою симптоматику,  отрицать наличие проблем.
        </Paragraph>
    </Section>

    <Section 
        x:Key="K75"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Сремление к поведению, ориентированному на одобрение окружающих, озабоченность своим социальным 
            статусом, склонность к отрицанию каких-либо затруднений в межличностных отношениях или в 
            самоконтроле, стремление к соблюдению общепринятых норм, отказ от критики в адрес окружающих
            в той мере, в какой их поведение укладывается в установленные нормы; выраженная отрицательная
            реакция на явно неконформное, отклоняющееся  поведение окружающих.
        </Paragraph>
    </Section>

    <Section 
        x:Key="K66"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремление улучшить впечатление о себе, смягчить свою симптоматику и эмоционально насыщенные 
            проблемы или даже отрицать их наличие.
        </Paragraph>
    </Section>

    <Section 
        x:Key="K60"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Не всегда замечает отрицательные сигналы, идущие от окружающих, считает свои отношения с ними
            более гармоничными, чем они есть на самом деле; как правило, производит на окружающих впечатление
            человека благоразумного, доброжелательного, общительного, имеющего широкий круг интересов; большой
            опыт межличностных контактов и отрицание затруднений обусловливает более или менее высокую
            предприимчивость и умение находить правильную линию поведения в различных ситуациях.
        </Paragraph>
    </Section>

    <Section 
        x:Key="K20"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Понимание своих затруднений, склонность скорее преувеличивать, чем недооценивать степень
            межличностных конфликтов; не скрывает своих слабостей и затруднений, психопатологических
            расстройств (если таковые имеются); скептицизм, неудовлетворённость и склонность преувеличивать 
            значение конфликтов часто ведёт к лёгкой уязвимости и неловкости в межличностных отношениях.
        </Paragraph>
    </Section>

    <Section 
        x:Key="KL20"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Результаты тестирования сомнительны из-за стремления подчеркнуть тяжесть своих симптомов и 
            жизненные трудности, вызвать сочувствие и соболезнование.
        </Paragraph>
    </Section>

    <Section 
        x:Key="FK"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Результат представляется сомнительным.
        </Paragraph>
    </Section>

    <Section 
        x:Key="LFK"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Результаты тестирования достоверны.
        </Paragraph>
    </Section>

    <Section 
        x:Key="LFKMean"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремление к максимальному нивелированию своих проблем и «улучшению» оценки своего поведения 
            в социальных ситуациях.
        </Paragraph>
    </Section>

    <Section 
        x:Key="LFKMax"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Повторите тестирование для получения достоверных результатов.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S1N"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 1.</Bold> Следит за состоянием своего физического здоровья, собственное здоровье 
            может стать объектом тщательного изучения; склонность к озабоченности своим физическим состоянием;
            погружённость в эти проблемы может восприниматься окружающими как несговорчивость и упрямство.

        </Paragraph>
    </Section>

    <Section 
        x:Key="S1H"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 1.</Bold> Выраженное беспокойство за своё физическое здоровье, фиксация на болезненных 
            ощущениях, убеждённость в наличии заболевания, неверие в успешность медицинской помощи.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S1L"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 1.</Bold> Решительность, энергичность, отсутствие озабоченности своим здоровьем, 
            способность успешно разрешать свои проблемы, невысокий уровень самоконтроля и рефлексии.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S2N"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 2.</Bold> Cклонность к уединённости, отгороженности и замкнутости вызваны стремлением 
            избежать разочарования; испытывает потребность привлечь и удержать внимание окружающих, дорожит 
            их оценкой, стремится приобрести и сохранить близость с ними; характерны реакции, сопровождающиеся
            чувством вины, направленным на себя (интрапунитивные реакции).
        </Paragraph>
    </Section>

    <Section 
        x:Key="S2H"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 2.</Bold> Cниженный фон настроения, подавленность, тоска, двигательная и идеамоторная
            заторможенность, высокий уровень тревоги, неспособность справиться с жизненными проблемами и 
            избавиться от психотравмирующих переживаний; высокий уровень тревоги, пессимистическая оценка 
            будущего, высокая и неудовлетворённая потребность в глубоких и прочных контактах с окружающими; 
            высокая опасность суицида.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S2L"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 2.</Bold> Hизкий уровень тревоги, активность, общительность, ощущение своей значимости,
            силы, энергии и бодрости.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S3N"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 3.</Bold> Cклонность к демонстративности и театральности; отмечается склонность к выдумкам и
            к фантазиям, т.к. собственный внутренний мир развит недостаточно, переживания ориентированы на
            внешнего наблюдателя.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S3H"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 3.</Bold> Bыраженная театральность, демонстративность, способность к проигрыванию
            разнообразных социальных ролей; высокая способность к вытеснению тревоги затрудняет формирование
            достаточно устойчивого поведения; из сознания вытесняется всё, что не соответствует сиюминутной
            ситуации и роли, в связи с чем отмечается постоянное возникновение новых ролей, задач и оценок; 
            высокий уровень вытеснения позволяет игнорировать отрицательные сигналы со стороны окружающих,
            сохранять высокую самооценку и обусловливает самолюбование, стремление "играть себя" в соответствии 
            с принятой в данный момент ролью; главным мотивом поведения является стремление любым способом быть в
            центре внимания, поиск признания и поддержки настойчивыми в этом отношении действиями; отмечается
            склонность к выдумкам и к фантазиям; одной из главных отличительных черт является выраженный эгоцентризм;
            эффективная деятельность возможна только при внимании окружающих, либо в игровом варианте; длительные 
            и упорядоченные усилия крайне затруднены, что оправдывается различного рода декларативными заявлениями;
            участие в групповой деятельности, требующей планирования и проведения единой линии, чаще всего 
            оказывается неэффективным, наиболее показана деятельность, требующая разнообразных и относительно
            кратковременных контактов, умения приспособиться к различным людям, благоприятно выглядеть в глазах 
            окружающих, вживаться в роль.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S3L"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 3.</Bold> Интроверсия, скептицизм, недостаточная спонтанность в социальных контактах,
            некоторая прямолинейность поведения и недостаток дипломатических способностей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S4N"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 4.</Bold> Отмечается негативное отношение к существующим морально-этическим нормам,
            правилам поведения и обычаям; это отношение может проявляться в зависимости от настроения либо в
            состоянии гнева и агрессивном поведении, либо относительно пассивно; некоторые затруднения регулирования
            поведения делают поступки плохо предсказуемыми в глазах окружающих; в межличностных отношениях 
            (даже в наиболее интимных) отличается поверхностными и нестойкими контактами, редко ощущает чувство
            глубокой привязанности; при длительном знакомстве окружающие могут легко заметить ненадёжность,
            склонность к колебаниям настроения с преобладанием раздражительного аффекта; в семейной жизни 
            постоянная череда конфликтов и разногласий; отношения с сослуживцами напряжённые.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S4H"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 4.</Bold> Нарушения социальной адаптации, обилие нерешаемых проблем, конфликтность,
            агрессивность в связи со сильнейшим стремлением к немедленному и непосредственному удовлетворению 
            сиюминутных потребностей без достаточного прогноза возможных последствий своих действий, недостаточность
            социального, интеллектуального и волевого контроля поведения; легко возникает склонность к 
            злоупотреблению алкоголем, а при возможности и наркотиками.
        </Paragraph>
    </Section>


    <Section 
        x:Key="S4L"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 4.</Bold> Враженная ориентация в оценках и поведении на существующие морально-этические 
            нормы, высокий уровень идентификации со своим социальным статусом, тенденция к сохранению постоянных 
            установок, интересов и целей, тщательный социальный и волевой контроль поведения.
        </Paragraph>
    </Section>


    <Section 
        x:Key="S6N"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 6.</Bold> Тенденция к обдумыванию действий окружающих, которые часто представляются
            некомпетентными и недобросовестными; полутона практически исключаются; мышление обстоятельное, 
            несколько схематичное, ригидное, со склонностью к детализации; высокий уровень притязаний, честолюбие, 
            выраженное стремление к достижению поставленных целей; стремление к самоутверждению является одной из
            ведущих черт; временами возникает ощущение недостаточности признания со стороны окружающих; в групповой
            деятельности стремится к лидерству, нуждается в реальных достижениях, подтверждающих престиж и значимость.
        </Paragraph>
    </Section>


    <Section 
        x:Key="S6H"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 6.</Bold> Выраженная напряженность, подозрительность, ощущение недоброжелательности и
            преследования со стороны окружающих; застревание на отрицательно окрашенных переживаниях, 
            подозрительность, злопамятность, частое возникновение враждебных реакций, склонность к построению
            трудно корригируемых, аффективно окрашенных концепций и суждений; отношение к окружающим всегда
            пристрастно - они воспринимаются враждебно; цели могут носить сверхценный характер.
        </Paragraph>
    </Section>


    <Section 
        x:Key="S6L"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 6.</Bold> Мягкость, добросердечие, чрезвычайно гибкое мышление, лёгкость в изменении 
            концепций, установок, готовность отказаться от своей точки зрения; поведению не хватает твёрдости,
            целеустремлённости, способности к отстаиванию своей точки зрения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S7N"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 7.</Bold> Низкая способность к вытеснению отрицательных переживаний; стремится удерживать
            в центре внимания даже несущественные факты, учитывать и предвидеть даже маловероятные возможности; 
            круг стимулов, приобретающих эмоциональную значимость, неограниченно расширяется, что обусловливает 
            недостаточную способность к концентрации внимания, сомнения и колебания при необходимости принять
            решение; поскольку каждый новый стимул воспринимается как потенциально угрожающий, возникает стремление
            придерживаться известного, проверенного, представляющегося надёжным; в своей деятельности
            руководствуется главным образом не потребностью достичь успеха, а стремлением избежать неудачи;
            в профессиональной деятельности отмечается добросовестность, пунктуальность, высокий внутренний 
            стандарт поведения, с которым вне зависимости от внешнего контроля сопоставляются действия и 
            результаты; желание уменьшить возможность неудачи заставляет стремиться к максимальной
            информированности о ситуации и к построению на основе этой информации подробных и систематизированных
            планов.
        </Paragraph>
    </Section>


    <Section 
        x:Key="S7H"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 7.</Bold> Выраженные трудности принятия решений (особенно при необходимости самостоятельного
            выбора); выраженная тревога, ведущая в ряде случаев к дезорганизации деятельности, наличие навязчивых
            страхов и сомнений; страх перед возможностью навлечь на себя опасность неверным поступком или потерпеть
            неудачу в результате допущенной ошибки лежит в основе самоограничительного поведения, которое проявляется
            в отказе от деятельности в тех случаях, когда успех не представляется гарантированным; в ситуации с 
            непредсказуемым исходом, при быстрой смене действующих факторов.
        </Paragraph>
    </Section>


    <Section 
        x:Key="S7L"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 7.</Bold> Решительность, гибкость поведения, низкий уровень тревоги, отсутствие сомнений и 
            колебаний при принятии решений, уверенность в правильности своих действий.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S8N"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 8.</Bold> Склонность к отгороженности и замкнутость; своеобразие восприятия и суждений,
            эмоциональная холодность и парадоксальность эмоциональных реакций; способность к интуитивному 
            пониманию окружающих снижена, в связи с чем ослаблены сопереживание (эмпатия) и способность к
            адекватному эмоциональному реагированию в различных ситуациях межличностного общения; характерна 
            ориентация главным образом на внутренние критерии с игнорированием общепринятых оценок, суждений;
            окружающим поведение нередко представляется эксцентричным, лишённым естественной эмоциональной
            окраски или надменным; затруднения в повседневных контактах порождают тенденцию к самоизоляции, 
            поскольку ситуации межличностного общения усиливают ощущение внутренней напряжённости; мышление 
            нестандартное с опорой на малозначимые (по прошлому опыту) признаки предметов и понятий, с
            возможностью нетривиальных решений, с некоторой расплывчатостью суждений и избеганием ясных 
            формулировок.
        </Paragraph>
    </Section>


    <Section 
        x:Key="S8H"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 8.</Bold> Отмечаются чрезмерная чувствительность, ранимость, маскируемые холодностью, 
            "твердокожестью"; стремление ликвидировать свою отгороженность при неспособности преодолеть 
            имеющиеся коммуникативные затруднения порождает амбивалентность в отношениях с окружающими, 
            связанную с ожиданием внимания и боязнью натолкнуться на холодность с их стороны; крайнее
            затруднение межличностных контактов, уход от реальности в свой внутренний мир, нарастающая
            самоизоляция, необычность мыслей и переживаний
        </Paragraph>
    </Section>


    <Section 
        x:Key="S8L"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 8.</Bold> Способность к интуитивному пониманию других людей, включённость в разнообразные
            межличностные связи, реалистичное и конкретное мышление.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S9N"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 9.</Bold> Повышенный фон настроения, оптимистичность, общительность, способность к
            высокой активности, непринуждённость в общении; охотно вступает в контакты и при этом производит 
            на окружающих впечатление человека приятного, весёлого, отличающегося широкими интересами и энтузиазмом,
            охотно высказывающего свое мнение и готового поделиться впечатлениями; характерны такие черты личности,
            как эмоциональная яркость, умение испытывать удовольствие от жизни, реалистическое образное мышление и
            отсутствие приверженности к жёстким схемам; легко становится "душой общества", хорошо приспосабливается 
            к переменам и даже стремится к ним.
        </Paragraph>
    </Section>


    <Section 
        x:Key="S9H"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 9.</Bold> Чрезмерная и плохо организованная активность, эмоциональное возбуждение, раздражительность,
            обидчивость, несдержанность, неэффективность деятельности, в связи с тем, что она не доводится до 
            конца из-за переключения внимания на другие объекты или чрезмерного преувеличения своих возможностей;
            в конечном итоге это ведёт к дезадаптации.
        </Paragraph>
    </Section>


    <Section 
        x:Key="S9L"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 9.</Bold> Недостаток побуждений, неспособность испытывать удовольствие, снижение активности,
            легко возникающее чувство вины, неадекватно низкая оценка собственных возможностей, пессимистичная
            окраска переживаний.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S0N"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 0.</Bold> Экстравертированность, cтремление к межличностным контактам и интерес к людям,
            общительность, эмоциональная отзывчивость, синтонность, хорошо развиты навыки общения; охотно принимает
            на себя общественные обязанности, имеет большое количество межличностных контактов в различных сферах 
            и испытывает от осуществления этих контактов большое удовлетворение.
        </Paragraph>
    </Section>


    <Section 
        x:Key="S0H"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 0.</Bold> Интровертированность, затруднения при осуществлении межличностных контактов, 
            замкнутость, необщительность, стремление к деятельности, не связанной с общением; возникновение
            тревоги в ситуациях вынужденных контактов.
        </Paragraph>
    </Section>


    <Section 
        x:Key="S0L"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 0.</Bold> Выраженная открытость и общительность, наличие такого большого числа контактов, 
            что их осуществление неизбежно сопровождается мимолётностью и поверхностностью общения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S5MH"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 5.</Bold> Внимание к эмоциональным нюансам и оттенкам отношений, сентиментальность, 
            широта интересов, мягкость и утонченность, богатое воображение и артистические наклонности,
            ранимость, добросердечие, низкий уровень гетероагрессивности, склонность к гуманитарным дисциплинам.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S5FH"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 5.</Bold> Непринуждённость и уверенность в себе, предприимчивость и последовательность 
            внутренне мотивированного поведения, доминантность и агрессивные тенденции; дифференцированные 
            интересы, лежащие в области науки и техники; склонность к выбору мужских профессий и занятий, 
            решительность и низкая чувствительность; в ситуациях, требующих исполнения традиционно женской 
            роли (в частности, при гетеросексуальных контактах) может возникать тревога, которая уменьшается,
            если и в этих ситуациях удаётся сохранить привычную доминантность поведения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S5ML"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 5.</Bold> Выраженная идентификация с мужской социальной ролью, большая избирательность и 
            дифференцированность круга интересов, предприимчивость, выраженное стремление к преодолению 
            препятствий, нечувствительность к эстетическим тонкостям и оттенкам человеческих отношений; 
            в межличностных отношениях наблюдаются доминантность и склонность к соперничеству; имеется 
            тенденция к демонстрации нарочито мужественных черт - силы, выносливости, пренебрежения к 
            мелочам; склонность к рефлексии, анализу оттенков своего поведения отсутствует, так как эти 
            моменты считаются несущественными.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S5FL"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 5.</Bold> Повышенная чувствительность к оттенкам эмоций и отношений, любопытство,
            мечтательность, капризность, артистичность, различные эстетические интересы, сентиментальность; 
            выраженная идентификация с женской социальной ролью - жены, любовницы, матери, хозяйки; мягкость,
            сердечность, стремление к защищённости, подчинённости; некоторая пассивность в поведении.
        </Paragraph>
    </Section>

    <Section 
        x:Key="S5MF"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 5.</Bold> Высокая потребность в защищённости, которая почти никогда не бывает полностью
            удовлетворена, в связи с чем легко возникает жалость к ебе или ощущение одиночества, обездоленности, 
            чувствительность к оттенкам в отношениях легко перерастает в придирчивость, а сдержанность в выражении
            положительных эмоций - в ощущение тревоги и вины в ситуациях, при которых такие эмоции от неё ожидаются.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\QSort.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="TendentionsPairs"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Возможно наличие внутриличностного конфликта, обусловленного одинаково выраженными
            противоположными тенденциями в парах:
            <TextBlock 
                Text="{Binding}" 
                />.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Зависимость-Title"
        x:Shared="False">
        <Paragraph>
            <Bold>Зависимость.</Bold>
        </Paragraph>
    </Section>

    <Paragraph
        x:Key="Зависимость"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
         Стремление индивида к принятию групповых норм, стандартов и морально-этических ценностей.
    </Paragraph>

    <Section 
        x:Key="Независимость-Title"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Независимость.</Bold> 
        </Paragraph>
    </Section>

    <Paragraph
        x:Key="Независимость"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        Стремление индивида к отстаиванию своей независимой позиции.
    </Paragraph>

    <Section 
        x:Key="Общительность-Title"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Общительность.</Bold> 
        </Paragraph>
    </Section>

    <Paragraph
        x:Key="Общительность"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        Выраженная контактность в общении, стремление образовывать эмоциональные связи как в своей группе,
        так и за её пределами.
    </Paragraph>

    <Section 
        x:Key="Необщительность-Title"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Необщительность.</Bold> 
        </Paragraph>
    </Section>

    <Paragraph
        x:Key="Необщительность"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        Избегание контактов в общении, недооценка эмоционального состояния партнёров по общению, 
        консерватизм в развитии сложившихся отношений.
    </Paragraph>

    <Section 
        x:Key="Принятие «борьбы»-Title"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Принятие «борьбы».</Bold> 
        </Paragraph>
    </Section>

    <Paragraph
        x:Key="Принятие «борьбы»"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        Активное стремление личности участвовать в групповой жизни, стремление к достижению более высокого 
        статуса в системе межличностных отношений.
    </Paragraph>

    <Section 
        x:Key="Избегание «борьбы»-Title"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Избегание «борьбы».</Bold> 
        </Paragraph>
    </Section>

    <Paragraph
        x:Key="Избегание «борьбы»"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        Уклонение от борьбы. Стремление уйти от взаимодействия, сохранить нейтралитет в групповых спорах 
        и конфликтах, склонность к компромиссным решениям.
    </Paragraph>

    <Paragraph
        x:Key="ScaleWarning"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        По данной тенденции имеются признаки нерешительности или уклончивости в ответах.
    </Paragraph>

    <Section 
        x:Key="Warning"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Имеют место признаки нерешительности, уклончивости. Однако, это может также свидетельствовать об 
            известной избирательности в поведении, о тактической гибкости. Выраженность этих качеств нужно 
            проверить, анализируя их в совокупности с другими личностными особенностями.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Remark"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Для составления полной интерпретации по результатам теста следует учитывать соотношение
            значений баллов по всем шкалам.
        </Paragraph>
    </Section>

    <Section 
        x:Key="TestingFaild"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Результаты тестирования недостоверны из-за  случайно или намеренно неверных ответов.</Bold>
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\ResultsMetadata.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:Updk7.Tests.Reports.Wpf">

    <local:ResultsMetadata
        x:Key="Эмоциональная_устойчивость_ЭУ: Среднеарифметическое время реагирования без помехи (ВР1)"
        Dimension="с"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Эмоциональная_устойчивость_ЭУ: Среднеарифметическое время реагирования с помехой (ВР2)"
        Dimension="с"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Эмоциональная_устойчивость_ЭУ: Разница среднеарифметических времен реагирования (ВР2 - ВР1)"
        Dimension="с"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Распределение_внимания_РВ: Среднее время реагирования в задании №1"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Распределение_внимания_РВ: Разница средних времен реагирования между заданием №2 и заданием №1"
        Dimension="с"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Распределение_внимания_РВ: Разница количества правильных ответов на зрительные стимулы (№1 - №2)"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Распределение_внимания_РВ: Количество ошибочных ответов на зрительные стимулы"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Сложная_двигательная_реакция_СДР_М: Среднее время реагирования в задании №1"
        Dimension="с"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Сложная_двигательная_реакция_СДР_М: Время выбора"
        Dimension="с"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_тестированию_ЭПФС: Среднее время реагирования"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_монотоноустойчивости_ОМУ: Среднее время реагирования в 1-й части теста"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_монотоноустойчивости_ОМУ: Среднее время реагирования во 2-й части теста"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_бдительности_ОБ: Среднеарифметическое время реагирования на экстренные сигналы"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata 
        x:Key="Оценка_бдительности_ОБ: Среднеарифметическое время реагирования на сигналы с предупреждением" 
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_бдительности_ОБ: Среднеарифметическое время реагирования на красные сигналы"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_бдительности_ОБ: Показатель бдительности"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_устойчивости_внимания_УВ: Среднеарифметическое время реагирования"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_глазомера_ОГ: Средний процент соотношения линий"
        Dimension="%"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Тест_Лири: Авторитарный"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_Лири: Независимо - доминирующий"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_Лири: Агрессивный"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_Лири: Недоверчивый - скептический"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_Лири: Покорно - застенчивый"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_Лири: Зависимый"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_Лири: Сотрудничающий"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_Лири: Альтруистический"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_Спилберга: Интерпретация ситуативной тревожности"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_Спилберга: Интерпретация личностной тревожности"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Опроснок_профессиональных_предпочтений_Голланд: Интерпретация"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тип_акцентуации_личности_Шмишека_Леонгарда: Среднее время реагирования на зелёные сигналы"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_скорости_переделки_навыков_СПН: Среднее время реагирования на зелёные сигналы"
        Dimension="c"
        ShowInReport="True" />
    
    <local:ResultsMetadata
        x:Key="Оценка_скорости_переделки_навыков_СПН: Среднее квадратичное отклонение реагирования на зелёные сигналы"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_скорости_переделки_навыков_СПН: Средние времена реагирования"
        Dimension="c"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Проба_на_моторную_согласованность_МС: Общее время выполнения"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Тест_Игра_5: Время выполнения"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Чувство_времени_ЧВ: Среднее время недооцененных интервалов"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Чувство_времени_ЧВ: Среднее время переоцененных интервалов"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Чувство_времени_ЧВ: Среднее время общее"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Чувство_времени_ЧВ: Среднеквадратичное отклонение общее"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_склонности_к_риску_ОСР: Время прохождения круга"
        Dimension="cм/c2"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_склонности_к_риску_ОСР: Средняя скорость движения по кругу"
        Dimension="см/с"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_склонности_к_риску_ОСР: Ускорение торможения"
        Dimension="см/с2"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_склонности_к_риску_ОСР: Расстояние до финиша"
        Dimension="см"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_склонности_к_риску_ОСР: Точность остановки на месте старта"
        Dimension="мм"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_склонности_к_риску_ОСР: Время торможения"
        Dimension="c"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_склонности_к_риску_ОСР: Число пиков максимума скорости"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_стрессоустойчивости_СТР: Задание 1. Cреднеарифметическое T реагирования"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_стрессоустойчивости_СТР: Задание 2. Cреднеарифметическое Т реагирования"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_стрессоустойчивости_СТР: Задание 3. Среднее время реагирования (Т3)"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_стрессоустойчивости_СТР: Задание 4. Среднее время реагирования (Т4)"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_ГЭД: Среднеарифметическое время реагирования на сигналы без предупреждения"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_ГЭД: Среднеарифметическое время реагирования на сигналы с предупреждением"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_ГЭД: Готовность"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Время выполнения 1-го задания"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Время выполнения 2-го задания"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Время выполнения 3-го задания"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Время выполнения 4-го задания"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Время переключения внимания"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Помехоустойчивость"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест: Количество касаний на 1-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест: Количество касаний на 2-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест: Количество касаний на 3-ем временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест: Количество касаний на 4-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест: Количество касаний на 5-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест: Количество касаний на 6-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест: Количество касаний на 7-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест: Количество касаний на 8-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест: Количество касаний на 9-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест: Количество касаний на 10-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест: Количество касаний на 11-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест: Количество касаний на 12-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Критическая_частота_слияния_миганий_КЧСМ: Частота нарастающих мельканий на 1-ом цикле"
        Dimension="Гц."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Критическая_частота_слияния_миганий_КЧСМ: Частота убывающих мельканий на 1-ом цикле"
        Dimension="Гц."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Критическая_частота_слияния_миганий_КЧСМ: Частота нарастающих мельканий на 2-ом цикле"
        Dimension="Гц."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Критическая_частота_слияния_миганий_КЧСМ: Частота убывающих мельканий на 2-ом цикле"
        Dimension="Гц."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Критическая_частота_слияния_миганий_КЧСМ: Частота нарастающих мельканий на 3-ем цикле"
        Dimension="Гц."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Критическая_частота_слияния_миганий_КЧСМ: Частота убывающих мельканий на 3-ем цикле"
        Dimension="Гц."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Критическая_частота_слияния_миганий_КЧСМ: Средняя частота по всем нарастающим мельканиям"
        Dimension="Гц."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Критическая_частота_слияния_миганий_КЧСМ: Средняя частота по всем убывающим мельканиям"
        Dimension="Гц."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Критическая_частота_слияния_миганий_КЧСМ: Средняя частота на 3-ом цикле"
        Dimension="Гц."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Критическая_частота_слияния_миганий_КЧСМ: Средняя частота по всем циклам"
        Dimension="Гц."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Тест_Айзенка_EPQ: Интерпретация"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_MMPI_Березина: Сочетание"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Реакция_на_движущийся_объект_РДО: Cреднеарифметическое время реагирования"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Реакция_на_движущийся_объект_РДО: Cреднеквадратическое отклонение времени реагирования"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Простая_двигательная_реакция_ПДР: Cреднеарифметическое время реагирования"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Простая_двигательная_реакция_ПДР: Cреднеквадратическое отклонение времени реагирования"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Корректурная_проба_КП: Время выполнения теста"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_ГЭД: Таблица_Действие"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_ГЭД: Таблица_Время"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_ГЭД: Сдвиг"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Экспресс_проба_бдительности_УРБ: Среднеарифметическое время реагирования на сигналы без предупреждения"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Экспресс_проба_бдительности_УРБ: Среднеарифметическое время реагирования на сигналы с предупреждением"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Экспресс_проба_бдительности_УРБ: Готовность"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Экспресс_проба_бдительности_УРБ: Времена реагирования на перескоки без предупреждения"
        Dimension="c"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Экспресс_проба_бдительности_УРБ: Времена реагирования на перескоки с предупреждением"
        Dimension="c"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Корректурная_проба_КП: Время просмотра каждой строки"
        Dimension="c"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Q_Сортировка: Внутренняя конфликтность и нерешительность"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-1 Среднеарифметическое время реагирования на сигналы без предупреждения"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-1 Среднеарифметическое время реагирования на сигналы с предупреждением"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-1 Готовность"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-1 Сдвиг"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-2 Ср. арифм. времён реагирования на все перескоки без предупреждения"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-2 Ср. кв. откл. времён реагирования на перескоки превышений сопротивления"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-2 Ср. арифм. времён реагирования на перескоки c предупреждением"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-2 Ср. кв. откл. времён реагирования на перескоки c предупреждением"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-2 Показатель  надёжности  работы в состоянии утомления"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-2 Таблица_Действие"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-2 Таблица_Время"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-2 Таблица_Реакция"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-2 Таблица_Сопротивление"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_ГЭД: Таблица_Реакция"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Опросник_САН: Гистограмма"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Ценностные_ориентации_Рокич: Таблица 1"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Ценностные_ориентации_Рокич: Таблица 2"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_Кетелла: Баллы"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_Кетелла: Стены"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_Уровень_субъективного_контроля_УСК: Саморефлексия"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Тест_Люшера: Пары"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_стрессоустойчивости_СТР: Разница времён реагирования (Т3 - Т4)"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Социометрический_опрос: Patient ID"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Социометрический_опрос: Patient Selection"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Концентрация_внимания_КВ: Время выполнения теста"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Концентрация_внимания_КВ: Время просмотра каждой строки"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Концентрация_внимания_КВ: Количество ошибок в каждой строке"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Корректурная_проба_КП: Количество ошибок в каждой строке"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Распределение_внимания_РВ: Среднее время реагирования в задании №2 на зрительные стимулы"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_устойчивости_внимания_УВ: Среднеарифметические времена реагирования по блокам"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_устойчивости_внимания_УВ: Количество ошибок по блокам"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_устойчивости_внимания_УВ: Количество пропусков по блокам"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_глазомера_ОГ: Длины линий"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_глазомера_ОГ: Длины эталонов"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_монотоноустойчивости_ОМУ: Времена реагирования"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_бдительности_ОБ: Количество неправильных нажатий на сигналы с предупреждением"
        Dimension="шт."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Готовность_к_экстренному_действию_2_ГЭД_2: ГЭД-2 Количество пропусков скачков подряд"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Статический_тремор_ТРЕМ: График амплитуды отклонения"
        Dimension="мм."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Статический_тремор_ТРЕМ: Тремор"
        Dimension="Гц."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Статический_тремор_ТРЕМ: Суммарное время пребывания щупа за уровнем допустимого отклонения"
        Dimension="c"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Сложная_двигательная_реакция_СДР_100: Среднее время реагирования"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Сложная_двигательная_реакция_СДР_100: Все значения времён реагирования на сигналы"
        Dimension="c"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Сложная_двигательная_реакция_СДР_М: Среднее время реагирования в задании №2"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_2_ПВ_2: Время выполнения 1-го задания"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_2_ПВ_2: Время выполнения 2-го задания"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_2_ПВ_2: Время выполнения 3-го задания"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_2_ПВ_2: Время выполнения 4-го задания"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_2_ПВ_2: Время переключения внимания"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_2_ПВ_2: Помехоустойчивость"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_2_ПВ_2: Нажатые числа в 1-ом задании"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_2_ПВ_2: Нажатые числа во 2-ом задании"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_2_ПВ_2: Нажатые числа в 3-ем задании"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_2_ПВ_2: Нажатые числа в 4-ом задании"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Нажатые числа в 1-ом задании"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Нажатые числа во 2-ом задании"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Нажатые числа в 3-ем задании"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Нажатые числа в 4-ом задании"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_стрессоустойчивости_М_СТР_М: Задание 1. Cреднеарифметическое T реагирования"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_стрессоустойчивости_М_СТР_М: Задание 2. Cреднеарифметическое Т реагирования"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_стрессоустойчивости_М_СТР_М: Задание 3. Среднее время реагирования (Т3)"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_стрессоустойчивости_М_СТР_М: Задание 4. Среднее время реагирования (Т4)"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_стрессоустойчивости_М_СТР_М: Разница времён реагирования (Т3 - Т4)"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Оценка_стрессоустойчивости_М_СТР_М: Таблица: тип сигнала"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Оценка_стрессоустойчивости_М_СТР_М: Таблица: реакция"
        Dimension="c"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Простая_двигательная_реакция_ПДР: Все значения времён реагирования на сигналы"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Сложная_двигательная_реакция_30_СДР_30: Среднее время реагирования"
        Dimension="c"
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Сложная_двигательная_реакция_30_СДР_30: Все значения времён реагирования на сигналы"
        Dimension="c"
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест_310_ТЕП_310: Количество касаний на 1-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест_310_ТЕП_310: Количество касаний на 2-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест_310_ТЕП_310: Количество касаний на 3-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест_310_ТЕП_310: Количество касаний на 4-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест_310_ТЕП_310: Количество касаний на 5-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Теппинг_тест_310_ТЕП_310: Количество касаний на 6-ом временном интервале"
        Dimension="кас."
        ShowInReport="True" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Нажатые числа в 1-ом повторном задании"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Нажатые числа во 2-ом повторном задании"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Нажатые числа в 3-ем повторном задании"
        Dimension=""
        ShowInReport="False" />

    <local:ResultsMetadata
        x:Key="Переключение_внимания_и_помехоустойчивость_ПВПУ: Ручной ввод"
        Dimension=""
        ShowInReport="False" />
    
</ResourceDictionary>

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\TestGroupTicket.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="TicketForm"
        x:Shared="False"
        DataContext="{Binding}">
        <!--Организация-->
        <Paragraph>
            <TextBlock 
                FontSize="14"
                TextWrapping="Wrap"
                TextAlignment="Left"
                Text="{Binding Organization}"
                />
        </Paragraph>
        <!--Заголовок-->
        <BlockUIContainer>
            <Border
                HorizontalAlignment="Stretch"
                Background="LightGray"
                BorderBrush="Gray"
                BorderThickness="1">
                <TextBlock 
                    Text="{Binding Title}"
                    FontSize="16"
                    FontWeight="Bold"
                    HorizontalAlignment="Center"
                    Margin="5"
                    TextWrapping="Wrap"
                    TextAlignment="Center"
                />
            </Border>
        </BlockUIContainer>
        <!--Цель обследования-->
        <Paragraph>
            <TextBlock 
                FontSize="14"
                TextWrapping="Wrap">
                <Run Text="Цель обследования: " FontWeight="Bold" />
                <Run Text="{Binding Goal}"  />
            </TextBlock>
        </Paragraph>
        <!--Информация о испытуемом-->
        <Table
            DataContext="{Binding}"
            CellSpacing="0"
            BorderBrush="Black"
            BorderThickness="0.5">
            <Table.Resources>
                <Style TargetType="{x:Type TableCell}">
                    <Setter Property="BorderBrush" Value="Black" />
                    <Setter Property="BorderThickness" Value="0.5" />
                    <Setter Property="TextAlignment" Value="Center" />
                </Style>
                <Style TargetType="{x:Type TextBlock}">
                    <Setter Property="TextWrapping" Value="Wrap" />
                    <Setter Property="Margin" Value="5,0,0,0" />
                    <Setter Property="VerticalAlignment" Value="Center" />
                    <Setter Property="TextAlignment" Value="Center" />
                </Style>
                <Style TargetType="{x:Type Paragraph}">
                    <Setter Property="Margin" Value="5,1,1,1" />
                </Style>
            </Table.Resources>
            <Table.Columns>
                <TableColumn Width="1*" />
                <TableColumn Width="2*" />
            </Table.Columns>
            <TableRowGroup>
                <!--ФИО-->
                <TableRow>
                    <TableCell>
                        <Paragraph FontSize="14">Ф.И.О. обследуемого</Paragraph>
                    </TableCell>
                    <TableCell>
                        <Paragraph>
                            <TextBlock Text="{Binding FullName}" />
                        </Paragraph>
                    </TableCell>
                </TableRow>
                <!--Должность-->
                <TableRow>
                    <TableCell>
                        <Paragraph FontSize="14">Должность</Paragraph>
                    </TableCell>
                    <TableCell>
                        <Paragraph>
                            <TextBlock Text="{Binding Profession}" />
                        </Paragraph>
                    </TableCell>
                </TableRow>
                <!--Тип движения-->
                <TableRow>
                    <TableCell>
                        <Paragraph FontSize="14">Вид движения</Paragraph>
                    </TableCell>
                    <TableCell>
                        <Paragraph>
                            <TextBlock Text="{Binding Traffic}" />
                        </Paragraph>
                    </TableCell>
                </TableRow>
            </TableRowGroup>
        </Table>
        <!--Результаты обследования-->
        <Table
            DataContext="{Binding}"
            CellSpacing="0"
            BorderBrush="Black"
            BorderThickness="0.5">
            <Table.Resources>
                <Style TargetType="{x:Type TableCell}">
                    <Setter Property="BorderBrush" Value="Black" />
                    <Setter Property="BorderThickness" Value="0.5" />
                </Style>
                <Style TargetType="{x:Type TextBlock}">
                    <Setter Property="TextWrapping" Value="Wrap" />
                    <Setter Property="Margin" Value="5,0,0,0" />
                    <Setter Property="VerticalAlignment" Value="Center" />
                </Style>
                <Style TargetType="{x:Type Paragraph}">
                    <Setter Property="Margin" Value="5,1,1,1" />
                </Style>
            </Table.Resources>
            <Table.Columns>
                <TableColumn />
                <TableColumn />
                <TableColumn />
                <TableColumn />
            </Table.Columns>
            <TableRowGroup>
                <TableRow>
                    <TableCell>
                        <Paragraph TextAlignment="Center" FontSize="12">Группа профпригодности</Paragraph>
                    </TableCell>
                    <TableCell>
                        <Paragraph TextAlignment="Center" FontSize="12">Уязвимые качества</Paragraph>
                    </TableCell>
                    <TableCell>
                        <Paragraph TextAlignment="Center" FontSize="12">Дата обследования</Paragraph>
                    </TableCell>
                    <TableCell>
                        <Paragraph TextAlignment="Center" FontSize="12">Дата переосвидетельствования</Paragraph>
                    </TableCell>
                </TableRow>
                <TableRow>
                    <TableCell>
                        <Paragraph TextAlignment="Center">
                            <TextBlock Text="{Binding AssignedGroup}" FontWeight="Medium" />
                        </Paragraph>
                    </TableCell>
                    <TableCell>
                        <Paragraph TextAlignment="Center">
                            <TextBlock Text="{Binding FailedTests}" FontWeight="Medium" />
                        </Paragraph>
                    </TableCell>
                    <TableCell>
                        <Paragraph TextAlignment="Center">
                            <TextBlock Text="{Binding ExaminationDate, StringFormat='{}{0:yyyy.MM.dd}'}" FontWeight="Normal" />
                        </Paragraph>
                    </TableCell>
                    <TableCell>
                        <Paragraph TextAlignment="Center">
                            <TextBlock Text="{Binding NextExamination, StringFormat='{}{0:yyyy.MM.dd}'}" FontWeight="Medium" />
                        </Paragraph>
                    </TableCell>
                </TableRow>
            </TableRowGroup>
        </Table>
        <Paragraph>
            <TextBlock 
                FontSize="14"
                TextWrapping="Wrap">
                <Run Text="Заключение: " FontWeight="Bold" />
                <Run Text="{Binding Conclusion}" />
            </TextBlock>
        </Paragraph>
        <!--Печати-подписи-->
        <Table
            DataContext="{Binding}"
            Margin="0,30,0,0">
            <Table.Resources>
                <Style TargetType="{x:Type TextBlock}">
                    <Setter Property="TextWrapping" Value="Wrap" />
                    <Setter Property="Margin" Value="5,0,0,0" />
                </Style>
                <Style TargetType="{x:Type Paragraph}">
                    <Setter Property="Margin" Value="5,1,1,1" />
                </Style>
            </Table.Resources>
            <Table.Columns>
                <TableColumn />
                <TableColumn Width="Auto" />
                <TableColumn />
            </Table.Columns>
            <TableRowGroup>
                <TableRow>
                    <TableCell>
                        <Paragraph
                            TextAlignment="Right">
                            Тестирование проводил:
                        </Paragraph>
                    </TableCell>
                    <TableCell>
                        <Paragraph
                            TextAlignment="Center">
                            ___________________
                        </Paragraph>
                    </TableCell>
                    <TableCell>
                        <Paragraph
                            TextAlignment="Left">
                            <TextBlock 
                                Text="{Binding UserName}" 
                                />
                        </Paragraph>
                    </TableCell>
                </TableRow>
                <TableRow>
                    <TableCell />
                    <TableCell>
                        <Paragraph
                            TextAlignment="Center">
                            М.П.
                        </Paragraph>
                    </TableCell>
                    <TableCell />
                </TableRow>
                <TableRow>
                    <TableCell>
                        <Paragraph
                            TextAlignment="Right">
                            Ознакомлен:
                        </Paragraph>
                    </TableCell>
                    <TableCell>
                        <Paragraph
                            TextAlignment="Center">
                            ___________________
                        </Paragraph>
                    </TableCell>
                    <TableCell>
                        <Paragraph
                            TextAlignment="Left">
                            (подпись испытуемого)
                        </Paragraph>
                    </TableCell>
                </TableRow>
            </TableRowGroup>
        </Table>
    </Section>
</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\UnknownReport.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Адаптивность.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Адаптивность</sys:String>

    <Section 
        x:Key="Адаптивные способности (в стэнах) - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold></Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Группа высокой и нормальной адаптации. Лица этой группы достаточно легко адаптируются к 
            новым условиям деятельности, быстро входят в новый коллектив, достаточно легко и адекватно 
            ориентируются в ситуации, быстро вырабатывают стратегию своего поведения. Как правило, не 
            конфликтны, обладают высокой эмоциональной устойчивостью.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Адаптивные способности (в стэнах) - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold></Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Группа низкой адаптации. Лица этой группы обладают признаками явных акцентуаций характера и
            некоторыми признаками психопатий, а психическое состояние можно охарактеризовать, как пограничное.
            Возможны нервно-психические срывы. Лица этой группы обладают низкой нервно-психической устойчивостью,
            конфликтны, могут допускать асоциальные поступки. Требуют наблюдения психолога и врача (невропатолога,
            психиатра).
        </Paragraph>
    </Section>

    <Section 
        x:Key="Адаптивные способности (в стэнах) - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold></Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Группа удовлетворительной адаптации. Большинство лиц этой группы обладают признаками 
            различных акцентуаций, которые в привычных условиях частично компенсированы и могут проявляться
            при смене деятельности. Поэтому успех адаптации зависит от внешних условий среды. Эти лица,
            как правило, обладают невысокой эмоциональной устойчивостью. Возможны асоциальные срывы,
            проявление агрессии и конфликтности. Лица этой группы требуют индивидуального подхода,
            коррекционных мероприятий.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Нервно-психическая устойчивость (в стэнах) - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold></Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Высокий уровень нервно-психической устойчивости и поведенческой регуляции, высокая адекватная 
            самооценка и реальное восприятие действительности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Нервно-психическая устойчивость (в стэнах) - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold></Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Низкий уровень поведенческой регуляции, определенная склонность к нервно-психическим срывам,
            отсутствие адекватности самооценки и реального восприятия действительности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Нервно-психическая устойчивость (в стэнах) - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold></Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
        </Paragraph>
    </Section>

    <Section 
        x:Key="Коммуникативные особенности (в стэнах) - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold></Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Высокий уровень развития коммуникативных способностей, легко устанавливает контакты с
            сослуживцами, окружающими, не конфликтен.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Коммуникативные особенности (в стэнах) - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold></Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Низкий уровень развития коммуникативных способностей, затруднение в построении контактов с
            окружающими, проявление агрессивности, повышенная конфликтность.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Коммуникативные особенности (в стэнах) - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold></Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
        </Paragraph>
    </Section>

    <Section 
        x:Key="Моральная нормативность (в стэнах) - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold></Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Реально оценивает свою роль в коллективе, ориентируется на соблюдение общепринятых норм поведения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Моральная нормативность (в стэнах) - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold></Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Не может адекватно оценить свое место и роль в коллективе, не стремится соблюдать общепринятые 
            нормы поведения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Моральная нормативность (в стэнах) - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold></Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Аддикция_и_аддиктивное_поведение.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:system="clr-namespace:System;assembly=mscorlib">
    <system:String x:Key="Addiction_1">Аддиктивное поведение не прослеживается</system:String>
    <system:String x:Key="Addiction_2">Начальная стадия проявления аддиктивного поведения</system:String>
    <system:String x:Key="Addiction_3">Стадия развития аддиктивного поведения</system:String>
    <system:String x:Key="Addiction_4">Стадия интенсивного развития аддикции и деградации личности</system:String>
    <system:String x:Key="Addiction_Level_0">Аддикция не прослеживается</system:String>
    <system:String x:Key="Addiction_Level_1">Начальная стадия аддикции</system:String>
    <system:String x:Key="Addiction_Level_2">Стадия развития аддикции</system:String>
    <system:String x:Key="Addiction_Level_3">Высокая степень аддикции</system:String>

    <Section x:Key="Изменение толерантности"
             x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 1: Изменение толерантности</Bold>
            (<Run Text="{Binding Mode=OneWay}" /> из 12)
            <LineBreak/>
            «Наблюдается количественный и качественный рост интенсивности искусственного воздействия (собственного вмешательства) в процесс получения удовольствия от ЛП».
        </Paragraph>
    </Section>

    <Section x:Key="Синдром отмены"
             x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 2: Синдром отмены (абстинентный синдром)</Bold>
            (<Run Text="{Binding Mode=OneWay}" /> из 15)
            <LineBreak/>
            «Наблюдается изменение в поведении, чувственной сфере, социальной направленности,
            направленности в духовной и ценностно-смысловой сферах личности в результате дефицитарности искусственного вмешательства или отсутствии «дозы ЛП».
        </Paragraph>
    </Section>

    <Section x:Key="Потеря контроля"
         x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 3: Потеря контроля</Bold>
            (<Run Text="{Binding Mode=OneWay}" /> из 15)
            <LineBreak/>
            «Характеризуется навязчивым поведением, имеющим целью активизировать искусственное вмешательство в процесс получения позитивного эмоционального подкрепления».
        </Paragraph>
    </Section>

    <Section x:Key="Неудачные попытки воздержаться"
     x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 4: Безуспешные попытки воздержаться</Bold>
            (<Run Text="{Binding Mode=OneWay}" /> из 12)
            <LineBreak/>
            «Имеются волевые попытки воздержания от «дозы ЛП» с последующими срывами либо переходом от одного вида аддикции к другому».
        </Paragraph>
    </Section>

    <Section x:Key="Употребление «несмотря на…»"
    x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 5: Употребление «несмотря на…»</Bold>
            (<Run Text="{Binding Mode=OneWay}" /> из 15)
            <LineBreak/>
            «Наблюдается осознаваемое, но, тем не менее, продолжающееся саморазрушение, разрушение социальных связей и ценностно-смысловых ориентиров личности».
        </Paragraph>
    </Section>

    <Section x:Key="Отрицание своей зависимости"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 6: Отрицание своей зависимости</Bold>
            (<Run Text="{Binding Mode=OneWay}" /> из 12)
            <LineBreak/>
            «Наблюдаются случаи отрицания своей зависимости от ЛП, что сопряжено с активизацией механизмов психологической защиты».
        </Paragraph>
    </Section>

    <Section x:Key="Резкие изменения в образе жизни"
    x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала 7: Резкие перемены в образе жизни</Bold>
            (<Run Text="{Binding Mode=OneWay}" /> из 15)
            <LineBreak/>
            «Резкие перемены в образе жизни являются интегрирующим показателем разбалансированности реакций головного мозга, 
            когда человек пытается искусственно создавать экстремальные или 
            чувственно-запредельные ситуации в своей жизни и жизни своих близких в поисках сильных эмоций».
        </Paragraph>
    </Section>

    <Section x:Key="ps">
        <Paragraph TextIndent="20" FontSize="14">
            Примечание:
                Особое внимание следует обратить на признак аддиктивного поведения, определённый как «резкие изменения в образе жизни».
                Высокие количественные результаты по этой шкале (даже при условии низких показателей по другими поведенческим проявлениям от данного ЛП)
                показывают серьёзные нарушения в личностной устойчивости,
                что является косвенным свидетельством наличия аддиктивных признаков по другим ЛП.
        </Paragraph>
    </Section>
</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Айзенк.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:controls="clr-namespace:Updk7.Tests.Reports.Wpf.Controls.EysenckChart">

    <Section 
        x:Key="EysenckChartSection"
        x:Shared="False">
        <BlockUIContainer>
            <Grid
                Margin="5"
                DataContext="{Binding}">
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <controls:EysenckChartControl 
                    Grid.Row="0"
                    HorizontalAlignment="Center"
                    Width="420"
                    Height="400"
                    DataContext="{Binding Points}"
                    Tresholds="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
                        AncestorType={x:Type Grid}}, 
                        Path=DataContext.Tresholds}"
                    />
                <controls:EysenckChartLegendControl
                    Grid.Row="1"
                    Margin="0,5,0,0"
                    HorizontalAlignment="Center"
                    DataContext="{Binding Tresholds}" />
            </Grid>
        </BlockUIContainer>
    </Section>

    <Section 
        x:Key="Interpretation-0"
        x:Shared="False">
    </Section>

    <Section 
        x:Key="Interpretation-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Импульсивный, вспыльчивый, легко возбудимый человек. Трудно сдерживает себя, бывает излишне резким и
            прямолинейным. Увлечённо отдаётся делу, но при неудаче быстро теряет интерес. Нравится работа, 
            требующая максимального напряжения сил, но не длительная. Характерен высокий темп движений, 
            разнообразие, необходимость проявлять смекалку и сообразительность. Тип темперамента - холерик. 
            Отличается повышенной возбудимостью, действия прерывисты. Ему свойственны резкость и стремительность 
            движений, сила, импульсивность, яркая выраженность эмоциональных переживаний. Вследствие 
            неуравновешенности, увлекшись делом склонен действовать изо всех сил, истощаться больше, чем следует.
            Имея общественные интересы, темперамент проявляет в инициативности, энергичности, принципиальности.
            При отсутствии духовной жизни холерический темперамент часто проявляется в раздражительности, 
            несдержанности, вспыльчивости, неспособности к самоконтролю при эмоциональных обстоятельствах.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Interpretation-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Тип темперамента - холерик. Отличается повышенной возбудимостью, действия прерывисты. 
            Ему свойственны резкость и стремительность движений, сила, импульсивность, яркая 
            выраженность эмоциональных переживаний. Вследствие неуравновешенности, увлекшись делом 
            склонен действовать изо всех сил, истощаться больше, чем следует. Имея общественные интересы, 
            темперамент проявляет в инициативности, энергичности, принципиальности. При отсутствии духовной 
            жизни холерический темперамент часто проявляется в раздражительности, несдержанности, 
            вспыльчивости, неспособности к самоконтролю при эмоциональных обстоятельствах. Характерные 
            поведенческие реакции холерика обострены состоянием эмоциональной неустойчивости.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Interpretation-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Человек уравновешенный, умеет себя сдерживать. В сложных жизненных ситуациях может иногда 
            "потерять голову", но волевым усилием способен восстановить душевное равновесие. Достаточно 
            легко переносит смену обстановки, схватывает новое, гибок в общении с людьми, склонен к 
            активной деятельности. Предпочитает самостоятельность  в делах и работе, проявляет смекалку, 
            находчивость, энергичность, выдержку. Люди такого типа лучше справляются с деятельностью, 
            требующей чёткого планирования, учёта последовательности событий. Чаще всего они становятся 
            лидерами, организаторами. Тип темперамента - сангвиник. Быстро приспосабливается к новым условиям,
            быстро сходится с людьми, общителен. Чувства легко возникают и сменяются, эмоциональные переживания,
            как правило, неглубоки. Мимика богатая, подвижная, выразительная. Несколько непоседлив, 
            нуждается в новых впечатлениях, недостаточно регулирует свои импульсы, не умеет строго 
            придерживаться выработанного распорядка жизни, системы в работе. В связи с этим не может 
            успешно выполнять дело, требующее ровной затраты сил, длительного и методичного напряжения, 
            усидчивости, устойчивости внимания, терпения. При отсутствии серьёзных целей, творческой 
            деятельности, вырабатываются поверхностность и непостоянство.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Interpretation-4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Человек достаточно живой и общительный, активный, когда видит перед собой ясную задачу и 
            конкретное дело. Неплохо переносит смену обстановки, достаточно быстро к ней приспосабливается, 
            легко схватываете новое. Энергичен, способен много работать не утомляясь. Сдержан, спокоен. 
            Легко может включаться в новую деятельность.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Interpretation-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Человек весьма уравновешенный в своих чувствах и действиях. Остаётся спокойным и ровным в 
            сложных жизненных ситуациях. Очень сдержан, рассудителен, но медлителен. Трудно приспосабливается к 
            изменяющейся обстановке. Требуется время для обдумывания, раскачки, но, начав  дело, обычно 
            старается довести его до конца, малоразговорчив, не любит говорить по пустякам, ему трудно 
            общаться с людьми. Предпочитает работу медленного темпа, не требующую частой смены заданий. 
            Затрудняется самостоятельно принимать решения, поэтому ему лучше не брать на себя ответственность 
            за руководство людьми и организацию деятельности других. Тип темперамента - флегматик. 
            Характеризуется сравнительно низким уровнем активности поведения, новые формы которого вырабатываются 
            медленно, но являются стойкими. Обладает медлительностью и спокойствием в действиях, мимике и речи, 
            ровностью, постоянством, глубиной чувств и настроений. Настойчивый и упорный "труженик жизни", 
            он редко выходит из себя, не склонен к аффектам, рассчитав свои силы, доводит дело до конца, 
            ровен в отношениях, в меру общителен, не любит попусту болтать. Экономит силы, попусту их не 
            тратит. В зависимости от условий, в одних случаях флегматик может характеризоваться "положительными" 
            чертами: выдержка, глубина мыслей, постоянство, основательность и т. д., в других вялость, 
            безучастность к окружающему, лень и безволие, бедность и слабость эмоций, склонность к выполнению 
            одних лишь привычных действий.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Interpretation-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Впечатлительный, стеснительный человек, трудно находящий контакты с другими людьми. Зачастую
            обидчив, чувствует себя уверенно лишь в знакомой, привычной обстановке. Люди, мало знающие его, 
            часто не понимают. Рекомендуется работать в обстановке, не требующей большого нервного напряжения,
            быстрых реакций и частой смены характера деятельности. Тип темперамента - меланхолик. У него реакция 
            часто не соответствует силе раздражителя, присутствует глубина и устойчивость чувств при слабом их
            выражении. Ему трудно долго на чём-то сосредоточиться. Сильные воздействия часто вызывают у меланхолика 
            продолжительную тормозную реакцию ("опускаются руки"). Ему свойственны сдержанность и приглушённость 
            речи, застенчивость, робость, нерешительность. В нормальных условиях меланхолик - человек глубокий, 
            содержательный, может быть хорошим тружеником, успешно справляться с жизненными задачами. При 
            неблагоприятных условиях может превратиться в замкнутого, боязливого, тревожного, ранимого человека,
            склонного к тяжелым внутренним переживаниям таких жизненных обстоятельств, которые вовсе этого не
            заслуживают.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Interpretation-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Тип темперамента - меланхолик. Реакция часто не соответствует силе раздражителя, присутствует глубина 
            и устойчивость чувств при слабом их выражении. Ему трудно долго на чём-то сосредоточиться. Сильные 
            воздействия часто вызывают у меланхолика продолжительную тормозную реакцию ("опускаются руки"). Ему 
            свойственны сдержанность и приглушённость речи, застенчивость, робость, нерешительность. В нормальных 
            условиях меланхолик - человек глубокий, содержательный, может быть хорошим тружеником, успешно 
            справляться с жизненными задачами. При неблагоприятных условиях может превратиться в замкнутого, 
            боязливого, тревожного, ранимого человека, склонного к тяжелым внутренним переживаниям таких жизненных
            обстоятельств, которые вовсе этого не заслуживают. Характерные поведенческие реакции меланхолика 
            обострены состоянием эмоциональной неустойчивости.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Interpretation-8"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Человек необщительный, застенчивый, довольно сдержанный в чувствах, вдумчивый, склонный к рассуждениям. 
            В поступках ориентируетесь в основном на собственные чувства, представления. Сближается только с
            немногими друзьями. Свои действия планирует заранее, не любит сильных впечатлений, стремится к спокойной,
            упорядоченной жизни. Людям этого типа предпочтительны профессии, где требуются усидчивость, 
            пунктуальность, внимательность в работе. Они предпочитают работать в одиночку.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Interpretation-9"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Человек эмоционально неустойчивый, чувствительный. В меру общительный, но в отношениях с людьми не 
            очень выдержанный, легко обижается. Его поступки часто зависят от настроения: то очень энергичен, то 
            становится вялым и апатичным. Непостоянен во взглядах и интересах. У него хорошо развита интуиция. 
            Люди такого типа предпочитают работу интересную, связанную с эмоциями, но непродолжительную и не очень 
            напряженную, больше умственную, чем физическую.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Interpretation-10"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Человек спокойный, постоянный в дружбе и интересах. Эмоции не  мешают ему видеть вещи реалистично, 
            умеет приспосабливаться к действительности, рассудителен, в меру общителен, но в поведении независим 
            от взглядов  других, охотно делится советами, не боится неудач, пытается разобраться в их  причинах. 
            Люди такого типа могут добиться успеха в разнообразных видах деятельности, особенно в тех, где четко
            определены действия и наглядно представлен конечный результат.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Interpretation-11"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Человек в меру общительный, довольно спокойный, но иногда дает волю эмоциям. Достаточно открытый, 
            не очень тревожный, хорошо переносит одиночество, не очень любит шумные компании. Люди такого типа в
            меру активны, универсальны в поведении. Они хорошо работают в разных областях деятельности, но при 
            условии осознания важности работы, наличия перспективы продвижения по службе.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Conclusion-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Патопсихологическая предрасположенность: <Bold>выявлена</Bold>
        </Paragraph>
    </Section>

    <Section 
        x:Key="Conclusion-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Патопсихологическая предрасположенность: <Bold>не выявлена</Bold>
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Аналогии.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Аналогии</sys:String>

    <Section 
        x:Key="Уровень развития вербального интеллекта - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Уровень развития вербального интеллекта»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для оценки особенностей вербального интеллекта,
            способности к пониманию характера логических связей и отношений между понятиями.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Характерны трудности с осмысленным восприятием текстов, пониманием логики изложения,
            расстановкой смысловых акцентов, пониманием отношений. Недостаточно развитые общие 
            аналитические способности, недостаточное умение находить причинно-следственные и другие
            связи между явлениями. Возможно автоматическое освоение конкретных алгоритмов стереотипных 
            видов деятельности без понимания их общих принципов и переноса полученных интеллектуальных 
            навыков на смежные виды деятельности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Уровень развития вербального интеллекта - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Уровень развития вербального интеллекта»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для оценки особенностей вербального интеллекта, способности к пониманию
            характера логических связей и отношений между понятиями.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Характерно недостаточно осмысленное восприятие текстов, недостаточно глубокое понимание логики 
            изложения, недостаточная способность расставлять смысловые акценты, понимать отношения. Общие 
            аналитические способности, умение находить причинно-следственные и другие связи между явлениями 
            развиты на среднем уровне.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Уровень развития вербального интеллекта - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Уровень развития вербального интеллекта»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для оценки особенностей вербального интеллекта, способности к пониманию
            характера логических связей и отношений между понятиями.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Характерно осмысленное восприятие текстов, понимание логики изложения, способность
            расставлять смысловые акценты, понимать отношения. Хорошо развиты общие аналитические 
            способности, умение находить причинно-следственные и другие связи между явлениями.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Антиципационная_состоятельность.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Антиципационная состоятельность (прогностическая компетентность)</sys:String>

    <Paragraph 
        x:Key="Общая АС"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Общая АС»:</Bold> уровень -
        <Run 
            Text="{Binding Mode=OneWay}" 
            FontWeight="Bold" 
            />
        Данная шкала представляет собой интегральный показатель уровня развития антиципационной 
        состоятельности испытуемого.
    </Paragraph>

    <Paragraph 
        x:Key="Личностно-ситуативная АС"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Личностно-ситуативная АС»:</Bold> уровень -
        <Run 
            Text="{Binding Mode=OneWay}" 
            FontWeight="Bold" 
            />
        Ответы по данной шкале отражают коммуникативный уровень антиципации испытуемого, т.е. способность 
        прогнозировать жизненные события и ситуации.
    </Paragraph>

    <Paragraph 
        x:Key="Пространственная АС"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Пространственная АС»:</Bold> уровень -
        <Run 
            Text="{Binding Mode=OneWay}" 
            FontWeight="Bold" 
            />
        Ответы по данной шкале демонстрируют способности испытуемого предвосхищать движение предметов в 
        пространстве, упреждать их, координировать собственные движения, проявляя моторную ловкость
    </Paragraph>

    <Paragraph 
        x:Key="Временная АС"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Временная АС»:</Bold> уровень -
        <Run 
            Text="{Binding Mode=OneWay}" 
            FontWeight="Bold" 
            />
        Ответы по данной шкале  презентуют хроноритмологические особенности испытуемого: способности прогнозировать 
        течение времени и точно распределять время.
    </Paragraph>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Голланд.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="Реалистический тип"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Реалистический тип.</Bold> Профессионалы данного типа склонны заниматься конкретными вещами 
            и их использованием, отдают предпочтение занятиям, требующим применения физической силы, ловкости. 
            Ориентированы в основном на практический труд, быстрый результат деятельности. Способности к общению 
            с людьми, формулировке и изложению мыслей развиты слабее.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чаще люди этого типа выбирают профессии механика, электрика, инженера, агронома, садовода, кондитера,
            повара и другие профессии, которые предполагают решение конкретных задач, наличие подвижности,
            настойчивости, связь с техникой. Общение не является ведущим в структуре деятельности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интеллектуальный тип"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Интеллектуальный тип.</Bold> Профессионалы данного типа отличаются аналитичностью,
            рационализмом, независимостью, оригинальностью, не склонны ориентироваться на социальные нормы.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Обладают достаточно развитыми математическими способностями, хорошей формулировкой и изложением
            мыслей, склонностью к решению логических, абстрактных задач.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Люди этого типа предпочитают профессии научно-исследовательского направления: ботаник, физик, 
            философ, программист и другие, в деятельности которых необходимы творческие способности и
            нестандартное мышление. Общение не является ведущим видом деятельности.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Бухгалтер, патентовед, нотариус, топограф, корректор и другие, направленные на обработку информации,
            предоставленной в виде условных знаков, цифр, формул, текстов.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Сфера общения в таких видах деятельности ограничена и не является ведущей, что вполне устраивает
            данный тип личности. Коммуникативные и организаторские способности развиты слабо, но зато прекрасно 
            развиты исполнительские качества.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Социальный тип"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Социальный тип.</Bold> Профессионалы данного типа гуманны, чувствительны, активны, ориентированы 
            на социальные нормы, способны к сопереживанию, умению понять эмоциональное состояние другого человека.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Обладают хорошими вербальными (словесными) способностями, с удовольствием общаются с людьми.
            Математические способности развиты слабее.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Люди этого типа ориентированы на труд, главным содержанием которого является взаимодействие с 
            другими людьми, возможность решать задачи, предполагающие анализ поведения и обучения людей.
            Возможные сферы деятельности: обучение, лечение, обслуживание и другие, требующие постоянного 
            контакта и общения с людьми, способностей к убеждению.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Артистический тип"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Артистический тип.</Bold> Профессионалы данного типа оригинальны, независимы в принятии решений,
            редко ориентируются на социальные нормы и одобрение, обладают необычным взглядом на жизнь, гибкостью 
            и скоростью мышления, высокой эмоциональной чувствительностью. Отношения с людьми строят, опираясь
            на свои ощущения, эмоции, воображение, интуицию. Обладают хорошей реакцией и обострённым восприятием.
            Любят и умеют общаться.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Профессиональная предрасположенность в наибольшей степени связана с актерско-сценической, музыкальной,
            изобразительной деятельностью.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Предприимчивый тип"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Предприимчивый тип.</Bold> Профессионалы данного типа находчивы, практичны, быстро ориентируются 
            в сложной обстановке, склонны к самостоятельному принятию решений, социальной активности, лидерству;
            имеют тягу к приключениям (возможно, авантюрным). Обладают достаточно развитыми коммуникативными 
            способностями.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Не предрасположены к занятиям, требующим усидчивости, большой и длительной концентрации внимания.
            Предпочитают деятельность, требующую энергии, организаторских способностей. Профессии: предприниматель, 
            менеджер, продюсер и другие, связанные с руководством, управлением и влиянием на разных людей в разных 
            ситуациях.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Конвенциальный тип"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Конвенциальный тип.</Bold> Профессионалы данного типа практичны, конкретны, не любят отступать
            от задуманного, энергичны, ориентированы на социальные нормы.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Предпочитают чётко определенную деятельность, выбирают из окружающей среды цели и задачи, поставленные 
            перед ними обычаями и обществом. В основном выбирают профессии, связанные с канцелярскими и расчетными
            работами, созданием и оформлением документов, установлением количественных соотношений между числами,
            системами условных знаков.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Дигностика_уровня_агрессии_Басс_Дарки.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Диагностика уровня агрессии Басс-Дарки</sys:String>
    
    <Paragraph 
        x:Key="Индекс агрессии"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Индекс Агрессии (ИА)»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Индекс враждебности"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Индекс Враждебности (ИВ)»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold"
            />
    </Paragraph>

    <Paragraph 
        x:Key="Физическая агрессия"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Физическая агрессия»:</Bold> уровень -
        <TextBlock Text="{Binding}" />
        Шкала характеризует испытуемого с точки зрения использования физической силы против другого лица.
    </Paragraph>

    <Paragraph 
        x:Key="Косвенная агрессия"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Косвенная агрессия»:</Bold> уровень -
        <TextBlock Text="{Binding}" />
        Шкала характеризует испытуемого с точки зрения проявления агрессии, окольным путём направленной на 
        другое лицо или ни на кого не направленной.
    </Paragraph>

    <Paragraph 
        x:Key="Раздражение"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Раздражение»:</Bold> уровень -
        <TextBlock Text="{Binding}" />
        Шкала характеризует испытуемого с точки зрения готовности к проявлению негативных чувств при малейшем 
        возбуждении (вспыльчивость, грубость).
    </Paragraph>

    <Paragraph 
        x:Key="Негативизм"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Негативизм»:</Bold> уровень -
        <TextBlock Text="{Binding}" />
        Шкала характеризует испытуемого с точки зрения проявления оппозиционной манеры в поведении: от пассивного 
        сопротивления до активной борьбы против установившихся обычаев и законов.
    </Paragraph>

    <Paragraph 
        x:Key="Обида"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Обида»:</Bold> уровень -
        <TextBlock Text="{Binding}" />
        Шкала характеризует испытуемого с точки зрения склонности к зависти и ненависти к окружающим за их 
        действительные и вымышленные действия.
    </Paragraph>

    <Paragraph 
        x:Key="Подозрительность"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Подозрительность»:</Bold> уровень -
        <TextBlock Text="{Binding}" />
        Шкала характеризует испытуемого с точки зрения склонности к подозрительности: в диапазоне от недоверия 
        и осторожности по отношению к людям до убеждения в том, что другие люди планируют и приносят вред.
    </Paragraph>

    <Paragraph 
        x:Key="Вербальная агрессия"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Вербальная агрессия»:</Bold> уровень -
        <TextBlock Text="{Binding}" />
        Шкала характеризует испытуемого с точки зрения склонности к выражению негативных чувств как через 
        форму (крик, визг), так и через содержание словесных ответов (проклятия, угрозы).
    </Paragraph>

    <Paragraph 
        x:Key="Чувство вины"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Чувство вины»:</Bold> уровень -
        <TextBlock Text="{Binding}" />
        Шкала характеризует испытуемого с точки зрения убеждения субъекта в том, что он является плохим 
        человеком, что поступает зло, также учитываются ощущаемые им угрызения совести.
    </Paragraph>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\ДОРС.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Дифференцированная оценка состояния сниженной работоспособности</sys:String>

    <Section 
        x:Key="Утомление"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Индекс утомления»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            <Bold>Утомление</Bold> – представляет собой состояние истощения и дискоординации в протекании основных
            реализующих деятельность процессов, развивающееся вследствие продолжительного и интенсивного
            воздействия рабочих нагрузок, с доминирующей мотивацией на завершение работы и отдых.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Монотония"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Индекс Монотонии»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            <Bold>Монотония</Bold> – состояние сниженного сознательного контроля за исполнением деятельности,
            возникающее в ситуациях однообразной работы с частым повторением стереотипных действий и 
            обеднённой внешней средой, сопровождающееся переживанием скуки или сонливости и доминирующей 
            мотивацией к смене деятельности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Пресыщение"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Индекс пресыщения»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            <Bold>Психическое пресыщение</Bold> – состояние непринятия слишком простой и субъективно
            неинтересной или малоосмысленной деятельности, которое проявляется в выраженном стремлении 
            прекратить работу (отказ от деятельности)  или внести разнообразие в заданный стереотип исполнения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Стресс"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Индекс стресса»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            <Bold>Стресс</Bold> – состояние повышенной мобилизации психологических и энергетических ресурсов, 
            развивающееся в ответ на повышение сложности или субъективной значимости деятельности, с 
            доминированием мотивации на преодоление затруднений, реализуемой как в продуктивной, так и в 
            деструктивной форме (преобладание процессуальных мотивов - мотивов самосохранения или 
            психологической защиты).
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Зунг.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="Score"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Набранная сумма баллов: </Bold>
            <TextBlock Text="{Binding}" FontWeight="Bold" />
        </Paragraph>
    </Section>

    <Section 
        x:Key="Level - 1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Уровень депрессии:</Bold> в настоящее время отсутствует снижение настроения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Level - 2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Уровень депрессии:</Bold> незначительное, но отчетливо выраженное снижение
            настроения. Легкая депрессия ситуативного или невротического происхождения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Level - 3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Уровень депрессии:</Bold> значительное снижение настроения. Субдепрессивное 
            состояния или замаскированная депрессия.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Level - 4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Уровень депрессии:</Bold> глубокое снижение настроения. Выраженное депрессивное состояние.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Избегание_неудач_Элерс.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="Score"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Набранная сумма баллов:
            <TextBlock Text="{Binding}" />
        </Paragraph>
    </Section>
    
    <Section 
        x:Key="Low"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Уровень мотивации к избеганию неудач: низкий.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Normal"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Уровень мотивации к избеганию неудач: средний.
        </Paragraph>
    </Section>

    <Section 
        x:Key="High"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Уровень мотивации к избеганию неудач: высокий.
        </Paragraph>
    </Section>

    <Section 
        x:Key="VeryHigh"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Уровень мотивации к избеганию неудач: слишком высокий уровень мотивации к избеганию неудач, к защите. 
            Возможны неадекватные импульсивные действия в трудных ситуациях.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Индекс_жизненного_стиля.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Индекс жизненного стиля</sys:String>

    <Paragraph 
        x:Key="Отрицание"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Отрицание»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Вытеснение"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Вытеснение»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Самообладание"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Регрессия»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Компенсация"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Компенсация»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Проекция"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Проекция»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Замещение"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Замещение»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Интеллектуализация"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Интеллектуализация»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Реактивные образования"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Реактивные образования»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
    </Paragraph>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Исследование_волевой_саморегуляции.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Исследование волевой саморегуляции</sys:String>

    <Paragraph 
        x:Key="Общая шкала - Норма"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Волевая саморегуляция»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Общая шкала - Выше нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Волевая саморегуляция»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Такая оценка характерна для лиц эмоционально зрелых, активных, независимых, самостоятельных. 
        Их отличает спокойствие, уверенность внутри себя, устойчивость целей, реалистичность взглядов, 
        развитое чувство собственного долга. Обычно они отлично рефлексируют личные мотивы, планомерно 
        реализуют возникшие намерения, могут распределять усилия и способны держать под контролем свои 
        поступки, владеют выраженной социально-позитивной направленностью. В предельных случаях у их 
        может быть нарастание внутренней напряжённости, связанной с рвением проконтролировать каждый аспект 
        собственного поведения и опаской по поводу мельчайшей его спонтанности.
    </Paragraph>

    <Paragraph 
        x:Key="Общая шкала - Ниже нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Волевая саморегуляция»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Такая оценка характерна для людей чувствительных, эмоционально неуравновешенных, ранимых, неуверенных в 
        себе. Рефлексивность у них невысока, а общий фон активности обычно снижен. Для них характерна 
        импульсивность и неустойчивость целей. Это может быть связано как с незрелостью, так и с выраженной 
        утончённостью натуры, не подкреплённой способностью к рефлексии и самоконтролю.
    </Paragraph>

    <Paragraph 
        x:Key="Настойчивость - Норма"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
    <Bold>Шкала «Настойчивость»:</Bold> уровень -
    <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Настойчивость - Выше нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Настойчивость»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Такая оценка характерна для инициативных, работоспособных людей, интенсивно стремящихся к выполнению 
        намеченного, их мобилизуют преграды на пути к цели. Основная их ценность – начатое дело. Для таких людей 
        характерно почтение к социальным нормам, рвение стопроцентно подчинить им своё поведение. В крайнем 
        выражении настойчивости вероятна утрата гибкости поведения, возникновение маниакальных тенденций.
    </Paragraph>

    <Paragraph 
        x:Key="Настойчивость - Ниже нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Настойчивость»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Низкие значения по данной шкале свидетельствуют о завышенной лабильности, неуверенности, импульсивности, 
        что может приводить к непоследовательности и даже разбросанности поведения. Сниженный фон активности и 
        работоспособности обычно компенсируется у таких лиц завышенной чувствительностью, гибкостью,
        изобретательностью, также тенденцией к свободной трактовке социальных норм.
    </Paragraph>

    <Paragraph 
        x:Key="Самообладание - Норма"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Самообладание»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Самообладание - Выше нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Самообладание»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Такая оценка характерна для людей эмоционально устойчивых, отлично владеющих собой в разных ситуациях. 
        Характерное для них внутреннее спокойствие, уверенность увеличивают готовность к восприятию нового,
        внезапного и обычно смешиваются со свободой взглядов, тенденцией к новшеству и радикализму. Совместно
        с тем рвение к неизменному самоконтролю, чрезмерное сознательное ограничение спонтанности может приводить
        к увеличению внутренней напряжённости, доминированию постоянной озабоченности и утомляемости.
    </Paragraph>

    <Paragraph 
        x:Key="Самообладание - Ниже нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Самообладание»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Такая оценка характеризует людей, которым свойственны спонтанность и импульсивность вкупе с обидчивостью и
        предпочтением обычных взглядов, что ограждает от насыщенных переживаний и внутренних конфликтов.
    </Paragraph>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Кеттелл.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="A-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «А».</Bold> «-» «Замкнутость».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Холодность; жёсткость; эмоциональная невыразительность; формальность в контактах; низкая потребность в
            общении; точность и обязательность в деловых вопросах наряду с недостаточной гибкостью, ригидностью.
            Во время больших психических нагрузок, стрессов могут проявляться замкнутость, отчуждённость, желание 
            «уйти в себя».
        </Paragraph>
    </Section>

    <Section 
        x:Key="A+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «А».</Bold> «+» «Общительность».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Поведение естественное и непринуждённое; богатство, яркость эмоциональных проявлений; активность в
            установлении контактов; чуткое и внимательное отношение к людям; лёгкая адаптация в различных социальных 
            группах; доверчивость, иногда чрезмерная; легкомыслие, которое может приводить к небрежности в деловых 
            вопросах.
        </Paragraph>
    </Section>

    <Section 
        x:Key="B-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «В».</Bold> «-» «Низкий интеллект».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Низкий уровень умственных способностей, неумение оперировать абстрактными понятиями, невыраженные 
            интеллектуальные интересы (данные характеристики могут быть обусловлены либо низким образовательным 
            уровнем, либо эмоциональной дезорганизацией мышления).
        </Paragraph>
    </Section>

    <Section 
        x:Key="B+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «В».</Bold> «+» «Высокий интеллект».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Высокий уровень умственных способностей, широта интеллектуальных интересов, быстрота усвоения новых 
            понятий, хорошее понимание абстракций, развитая интеллектуальная интуиция.
        </Paragraph>
    </Section>

    <Section 
        x:Key="C-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «С».</Bold> «-» «Эмоциональная неустойчивость».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Капризность; беспокойство; переменчивость в отношениях и интересах; внутреннее ощущение беспомощности, 
            усталости, неспособности справиться с жизненными трудностями; желание уклониться от реальности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="C+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «С».</Bold> «+» «Эмоциональная устойчивость».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Постоянство интересов; отсутствие случайных колебаний настроения; способность зрело и реалистично
            оценить обстановку; осознанность собственных недостатков; ощущение хорошей приспособленности к жизни.
        </Paragraph>
    </Section>

    <Section 
        x:Key="E-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «Е».</Bold> «-» «Подчинённость».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Склонность к подчинению, зависимости, покорности; неспособность отстоять свою точку зрения; заниженная 
            самооценка; неуверенность в собственных силах; внешняя конформность; свойственны реакции интрапунитивного 
            и импунитивного типа.
        </Paragraph>
    </Section>

    <Section 
        x:Key="E+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «Е».</Bold> «+» «Доминантность».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Властность; настойчивость; независимость; стремление к самостоятельности (право на самостоятельность
            отстаивает агрессивно), требование самостоятельности от окружающих; в поступках энергичность, активность,
            смелость; стремление к лидерству в социальной группе.
        </Paragraph>
    </Section>

    <Section 
        x:Key="F-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «F».</Bold> «-» «Сдержанность».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Озабоченность; беспокойство о своём будущем; ожидание возможных неудач и неприятностей; склонность 
            всё усложнять; для защиты от предполагаемых неприятностей предпринимает предохранительные меры, 
            тщательно планирует свои действия; возможен педантизм и скрупулёзность.
        </Paragraph>
    </Section>

    <Section 
        x:Key="F+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «F».</Bold> «+» «Экспрессивность».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Беззаботность; беспечность; жизнерадостность; активность; лёгкость восприятия жизни (не задумывается 
            серьёзно над событиями); склонность к путешествиям и переменам, к холостяцкому образу жизни (нередко 
            беспечное отношение к жизни обусловлено уменьшением стремлений).
        </Paragraph>
    </Section>

    <Section 
        x:Key="G-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «G».</Bold> «-» «Подверженность чувствам».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Недобросовестность; непостоянство; неспособность доводить до конца начатое дело; лень; расхлябанность;
            презрительное отношение к моральным ценностям; нежелание прилагать усилия для выполнения социальных
            и культурных норм; небрежность в обыденной жизни.
        </Paragraph>
    </Section>

    <Section 
        x:Key="G+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «G».</Bold> «+» «Высокое нормативность поведения».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Высокое чувство долга; ответственность; обязательность; добросовестность; стойкость моральных принципов;
            порядочность не из-за сиюминутной выгоды, а по убеждению; тщательное соблюдение правил, законов; 
            точность и аккуратность в деловых отношениях.
        </Paragraph>
    </Section>

    <Section 
        x:Key="H-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «Н».</Bold> «-» «Робость».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Робость; нерешительность; сверхчувствительная, симпатическая нервная система; острое реагирование на 
            любую угрозу; необоснованное чувство собственной неполноценности; испытывает смущение в присутствии 
            лиц противоположного пола; медлительность и сдержанность выражения своих чувств; страх к жизни;
            предпочтение близких, дружеских связей (одного-двух друзей) большой компании; неумение поддерживать 
            контакты с широким кругом людей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="H+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «Н».</Bold> «+» «Смелость».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Смелость; решительность; невосприимчивость к угрозе; тяга к риску, острым ощущениям; явный интерес 
            к противоположному полу; лёгкость в общении, установлении контактов; красноречие; быстро забывает
            о неудачах; не делает надлежащих выводов из пережитого.
        </Paragraph>
    </Section>

    <Section 
        x:Key="I-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «I».</Bold> «-» «Жёсткость».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Суровость; жёстокость; независимость; мужественность; эмоциональная зрелость; рассудочность, 
            рациональность (интуицию подменяют расчёты); нечувствительность к боли, физическим недомоганиям; 
            отсутствие иллюзий; склонность к цинизму.
        </Paragraph>
    </Section>

    <Section 
        x:Key="I+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «I».</Bold> «+» «Чувствительность».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Мягкость; утончённость; образное восприятие мира; изысканность в стиле поведения, манере держаться; 
            романтизм; богатое воображение; эстетический вкус; склонность к художественной деятельности; 
            высокоразвитая интуиция; сентиментальность; склонность к ипохондрии (беспокойство о состоянии 
            своего здоровья).
        </Paragraph>
    </Section>

    <Section 
        x:Key="L-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «L».</Bold> «-» «Доверчивость».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Доверчивость; открытость; бескорыстие; терпимость; уступчивость; покладистость; высокая степень 
            откровенности (даже с малознакомыми людьми); некритичность; отсутствие желания выделиться, 
            зависти; уживчивость; несварливость.
        </Paragraph>
    </Section>

    <Section 
        x:Key="L+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «L».</Bold> «+» «Подозрительность».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Подозрительность; недоверчивость; неуступчивость; склонность к ревности, зависти; злопамятность; 
            фиксация на неудачах; скрытность; раздражительность; требовательность к окружающим; высокий
            уровень тревожности и беспокойства; нетерпимость к конкуренции; скептическое отношение к моральным 
            мотивам поведения окружающих.
        </Paragraph>
    </Section>

    <Section 
        x:Key="M-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «М».</Bold> «-» «Практичность».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Практичность; трезвость в оценках обстоятельств и людей; здравомыслие; прозаичность; отрицательное 
            отношение к новому, необычному; недостаточность воображения и находчивости; склонность к устройству 
            личных дел, решению практических вопросов («земные интересы»).
        </Paragraph>
    </Section>

    <Section 
        x:Key="M+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «М».</Bold> «+» «Мечтательность».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Мечтательность; идеалистичность; склонность к фантазиям; эксцентричность и своеобразие поведения;
            неуравновешенность; увлечённость абстрактными проблемами (в т.ч. искусством, религией); поглощённость 
            своими идеями и фантазиями; непрактичность; рассеянность.
        </Paragraph>
    </Section>

    <Section 
        x:Key="N-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «N».</Bold> «-» «Прямолинейность».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Наивность; неловкость поведения (вплоть до бестактности); эмоциональная недисциплинированность; неумение анализировать мотивы поведения окружающих; отсутствие проницательности; отсутствие социальной ловкости, дипломатичности; неумение хитрить; прямолинейность сентиментальность; влечение к большим коллективам; простота вкусов; любовь к естественным человеческим радостям.
        </Paragraph>
    </Section>

    <Section 
        x:Key="N+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «N».</Bold> «+» «Дипломатичность, проницательность».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Проницательность; умение вести себя расчётливо и рационально, не поддаваясь эмоциональным порывам,
            видеть за аффектом логику; социальная ловкость; дипломатичность; склонность к интригам и утончённому 
            коварству; несентиментальность; вежливость и корректность в общении; скептическое отношение к лозунгам 
            и призывам; честолюбие; эстетическая изощренность.
        </Paragraph>
    </Section>

    <Section 
        x:Key="O-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «О».</Bold> «-» «Низкая тревожность».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Уверенность в своих возможностях; самонадеянность; весёлость; умение находить интерес и новизну даже 
            в повседневных, мелких делах; энергичность; активность; вовлечённость в происходящие события;
            нечувствительность к замечаниям и упрёкам.
        </Paragraph>
    </Section>

    <Section 
        x:Key="O+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «О».</Bold> «+» «Высокая тревожность».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Неуверенность в своих силах; заниженная самооценка; доминирование тревожно-депрессивного фона 
            настроения; постоянная подавленность; напряжённость; боязливость; обязательность; чувствительность к
            замечаниям и порицаниям; ранимость; склонность к самоупрёкам и самобичеванию; ипохондрия (беспокойство 
            о состоянии своего здоровья); неуверенность и излишняя скромность в общении.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Q1-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «Q1».</Bold> «-» «Консерватизм».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Ригидность; консерватизм; нелюбовь к переменам, к новому; невосприимчивость к другим точкам зрения; 
            следование устоявшимся принципам и традициям; склонность к нравоучениям, морализации и наставлениям.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Q1+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «Q1».</Bold> «+» «Радикализм».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Гибкость; разнообразие интеллектуальных интересов; скептичность; недоверие к авторитетам; критичность
            мышления; терпимость к противоречиям и неясностям; независимость суждений; восприимчивость к
            новым идеям; склонность к самостоятельному анализу.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Q2-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «Q2».</Bold> «-» «Конформизм».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Зависимость от группы (коллектива); внутренняя конформность; ориентация на мнение в группе; нужда в
            постоянной опоре, поддержке окружающих, совете и одобрении; безынициативность; отсутствие собственной
            линии поведения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Q2+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «Q2».</Bold> «+» «Нонконформизм».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Самодостаточность; самостоятельность принятия решений, ответственность за их исполнение; независимость 
            оценок и суждений; нонконформизм; готовность на жертвы ради сохранения своей независимости; 
            отсутствие потребности в поддержке и одобрении со стороны окружающих.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Q3-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «Q3».</Bold> «-» «Низкий самоконтроль».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Безволие; низкий самоконтроль над желаниями; безалаберность; импульсивность; неумение организовать
            своё время; хаотичность, неупорядоченность и неравномерность действий; небрежность в деловых вопросах.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Q3+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «Q3».</Bold> «+» «Высокий самоконтроль».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Высокий самоконтроль поведения; планомерность и упорядоченность действий; упорство в преодолении
            препятствий; аккуратность; выполнение социальных требований; забота о впечатлении, производимом 
            на окружающих.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Q4-"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «Q4».</Bold> «-» «Расслабленность».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Слабость или отсутствие побуждений и желаний; безразличие к происходящим событиям, к удачам и
            неудачам; невозмутимость; удовлетворённость любым положением дел; отсутствие стремления к 
            достижениям и переменам.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Q4+"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор «Q4».</Bold> «+» «Напряжённость».
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Беспокойство; суетливость; неусидчивость; раздражительность; нетерпеливость; напряжённость; неумение
            остаться без дела даже в обстановке, благоприятной для отдыха («усталость, не ощущающая покоя»);
            активная неудовлетворённость стремлений.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\КОПС.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Комплексная оценка психологического состояния</sys:String>

    <Section 
        x:Key="Работоспособность - Признаки высокой вероятности"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Работоспособность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для выявления симптомов физиологического дискомфорта, снижения общего 
            самочувствия, нарушений в эмоциональной сфере, снижения мотивации к деятельности и изменений 
            в сфере социальных контактов.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Для лиц с высокими оценками характерны высокая активность, стеническая реакция на возникающие
            трудности, субъективные ощущения внутренней собранности, запаса сил, энергии, высокий уровень 
            готовности к работе, в том числе длительной.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Работоспособность - Признаки повышенной склонности"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Работоспособность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для выявления симптомов физиологического дискомфорта, снижения общего 
            самочувствия, нарушений в эмоциональной сфере, снижения мотивации к деятельности и изменений 
            в сфере социальных контактов.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Работоспособность - Признаки тенденции"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Работоспособность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Низкий балл по этой шкале характерен для лиц со сниженной работоспособностью, повышенной утомляемостью,
            отличающихся несобранностью, вялостью, инертностью. Они менее склонны проявлять активность,
            расходовать энергию, ресурс сил у них ограничен. Выражена тенденция проявлять астенические реакции 
            на возникающие трудности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Работоспособность - Низкий уровень"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Работоспособность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Низкий балл по этой шкале характерен для лиц со сниженной работоспособностью, повышенной утомляемостью,
            отличающихся несобранностью, вялостью, инертностью. Они менее склонны проявлять активность,
            расходовать энергию, ресурс сил у них ограничен. Выражена тенденция проявлять астенические реакции 
            на возникающие трудности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Самооценка - Признаки высокой вероятности"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Самооценка»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для оценки рефлексивного компонента общего самочувствия, проявляющегося в 
            субъективной оценке своих сил и возможностей (самоэффективности).
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Работник с позитивной самооценкой (самоотношением) склонен испытывать положительные эмоции от 
            своей деятельности и более успешен в ней; он более удовлетворён работой, взаимоотношениями с 
            коллегами и жизнью в целом. Для таких людей характерна большая готовность к принятию на себя 
            ответственности и преодолению трудностей в реализации своих целей, достаточно высокая оценка 
            личностной успешности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Самооценка - Признаки повышенной склонности"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Самооценка»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для оценки рефлексивного компонента общего самочувствия, проявляющегося в 
            субъективной оценке своих сил и возможностей (самоэффективности).
        </Paragraph>
    </Section>

    <Section 
        x:Key="Самооценка - Признаки тенденции"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Самооценка»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для оценки рефлексивного компонента общего самочувствия, проявляющегося в 
            субъективной оценке своих сил и возможностей (самоэффективности).
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Для лиц с низкой оценкой по шкале характерны неудовлетворённость работой, характером взаимоотношений 
            с окружающими и жизнью в целом, её ходом, процессом самореализации и тем, как именно в настоящее 
            время разворачиваются основные жизненные события, неуверенность в себе, преобладание негативных 
            эмоций. Низкая самооценка личностной успешности часто коррелирует с утратой мотивации к личностному 
            росту и самосовершенствованию, объективными признаками снижения эффективности деятельности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Самооценка - Низкий уровень"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Самооценка»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для оценки рефлексивного компонента общего самочувствия, проявляющегося в 
            субъективной оценке своих сил и возможностей (самоэффективности).
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Для лиц с низкой оценкой по шкале характерны неудовлетворённость работой, характером взаимоотношений 
            с окружающими и жизнью в целом, её ходом, процессом самореализации и тем, как именно в настоящее 
            время разворачиваются основные жизненные события, неуверенность в себе, преобладание негативных 
            эмоций. Низкая самооценка личностной успешности часто коррелирует с утратой мотивации к личностному 
            росту и самосовершенствованию, объективными признаками снижения эффективности деятельности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Социальная желательность - Признаки высокой вероятности"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Социальная желательность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для измерения готовности испытуемого представлять себя в благоприятном, 
            социально одобряемом свете.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Высокие оценки по шкале свидетельствуют о тенденции испытуемого демонстрировать строгое соблюдение
            даже малозначительных социальных норм, умышленное стремление показать себя в лучшем свете, о 
            настороженности по отношению к ситуации обследования. При получении оценки по шкале более 28 баллов
            результаты по другим шкалам должны интерпретироваться с осторожностью (реальные уровни работоспособности
            и самооценки могут оказаться ниже расчетных).
        </Paragraph>
    </Section>

    <Section 
        x:Key="Социальная желательность - Низкий уровень"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Социальная желательность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для измерения готовности испытуемого представлять себя в благоприятном, 
            социально одобряемом свете.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Низкие оценки по шкале (менее 28 баллов) свидетельствуют об отсутствии у  испытуемого тенденции 
            к умышленному стремлению показать себя в лучшем свете.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Атипичность ответов - Признаки высокой вероятности"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Атипичность ответов»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для оценки достоверности полученных результатов.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Высокие значения по этой шкале свидетельствуют о недостоверности полученных результатов. Оценки 
            свыше 15 баллов, скорее всего, указывают на случайный, хаотический характер реагирования; ещё 
            более высокие значения атипичности (25 баллов и выше) могут свидетельствовать либо об аггравации, 
            т.е. нарочитом стремлении представить себя в худшем свете, либо о тяжёлом личностном кризисе, 
            требующем психологической помощи.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Атипичность ответов - Признаки повышенной склонности"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Атипичность ответов»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для оценки достоверности полученных результатов.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Оценки свыше 15 баллов (но не более 25 баллов), скорее всего, указывают на случайный, 
            хаотический характер реагирования.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Атипичность ответов - Низкий уровень"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Шкала «Атипичность ответов»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для оценки достоверности полученных результатов.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Низкие значения по этой шкале (не более 15 баллов) свидетельствуют о достоверности полученных результатов.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Лири.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="Factor1 - 1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Авторитарный".</Bold> Уверенный в себе, упорный, настойчивый, может быть хорошим
            наставником и организатором. Обладает свойствами руководителя.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor1 - 2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Авторитарный".</Bold> Доминантный, энергичный, успешный в делах, любит давать
            советы, требует к себе уважения, может быть нетерпим к критике, ему свойственна переоценка 
            собственных возможностей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor1 - 3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Авторитарный".</Bold> Властный, диктаторский, деспотический характер, всех поучает, 
            дидактический стиль высказываний, не склонен принимать советы других, стремится к лидерству, 
            командованию другими, сильная личность с чертами деспотизма.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor2 - 1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Независимо – доминирующий".</Bold> Уверенный, независимый, ориентированный на себя,
            склонный к соперничеству тип.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor2 - 2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Независимо – доминирующий".</Bold> Самодовольный, нарциссический, с выраженным чувством
            собственного достоинства, превосходства над окружающими, с тенденцией иметь собственное мнение,
            отличное от мнения большинства, и занимать обособленную позицию в группе.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor2 - 3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Независимо – доминирующий".</Bold> Стремится быть над всеми, самовлюблённый, 
            расчётливый. К окружающим относится отчуждённо. Заносчивый, хвастливый.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor3 - 1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Агрессивный".</Bold> Упрямый, упорный, настойчивый в достижении цели, энергичный,
            непосредственный.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor3 - 2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Агрессивный".</Bold> Требовательный, прямолинейный, откровенный, строгий и резкий в
            оценки других, непримиримый, склонный во всём обвинять окружающих, насмешливый, иронический, 
            раздражительный.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor3 - 3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Агрессивный".</Bold> Чрезмерное упорство, недружелюбие, жесткость, враждебность
            по отношению к окружающим, несдержанность, вспыльчивость, агрессивность, доходящая до асоциального 
            поведения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor4 - 1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Недоверчивый - скептический".</Bold> Реалистичен в суждениях и поступках, критичен 
            по отношению к окружающим, скептик, неконформный.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor4 - 2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Недоверчивый - скептический".</Bold> Выраженная склонность к критицизму. Разочарованный 
            в людях, замкнутый, скрытный, обидчивый. Недоверчив к окружающим, испытывает трудности в 
            интерперсональных контактах из – за подозрительности и боязни плохого отношения. Свой негативизм
            проявляет в вербальной агрессии.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor4 - 3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Недоверчивый - скептический".</Bold> Отчуждённый по отношению к враждебному и злобному 
            миру, очень подозрительный, крайне обидчив, склонный к сомнениям во всём, злопамятный, постоянно 
            жалуется на всех (шизоидный тип характера).
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor5 - 1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Покорно - застенчивый".</Bold> Скромный, робкий, уступчивый, эмоционально сдержанный,
            способный подчиняться, не имеет собственного мнения, послушно и честно выполняет свои обязанности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor5 - 2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Покорно - застенчивый".</Bold> Застенчивый, кроткий, легко смущается, склонен
            подчиняться более сильному без учета ситуации.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor5 - 3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Покорно - застенчивый".</Bold> Покорный, склонный к самоуничижению, слабовольный, 
            склонный уступать всем и во всём, всегда ставит себя на последнее место и осуждает себя, приписывает
            себе вину, пассивный, стремится найти опору в ком – либо более сильном.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor6 - 1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Зависимый".</Bold> Конформный, мягкий, ожидает помощи и советов, доверчивый, 
            склонный к восхищению окружающими, вежливый, нуждается в признании.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor6 - 2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Зависимый".</Bold> Послушный, боязливый, беспомощный, не умеет проявить сопротивление,
            искренне считает, что другие всегда правы.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor6 - 3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Зависимый".</Bold> Очень неуверенный в себе, имеет навязчивые страхи, опасения, 
            тревожится по любому поводу, поэтому зависим от других, чужого мнения, сверхконформный.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor7 - 1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Сотрудничающий".</Bold> Склонный к сотрудничеству, кооперации, гибкий и компромиссный
            при решении проблем и в конфликтных ситуациях, стремится быть в согласии с мнением окружающих,
            сознательно конформный, следует условностям, правилам и принципам «хорошего тона» в отношениях с
            людьми, инициативный энтузиаст в достижении целей группы, стремится помогать, чувствовать себя в 
            центре внимания, заслужить внимание и любовь, общительный, проявляет теплоту и дружелюбие в отношениях.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor7 - 2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Сотрудничающий".</Bold> Дружелюбный и любезный со всеми, ориентирован на принятие и с
            оциальное одобрение, стремится удовлетворить требования всех, «быть хорошим» для всех без 
            учёта ситуации, стремится к целям микрогруппы, имеет развитые механизмы вытеснения и подавления, 
            эмоционально лабильный (истероидный тип характера).
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor8 - 1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Альтруистический".</Bold> Ответственный по отношению к людям, деликатный,
            мягкий, добрый, эмоциональное отношение к людям проявляет в сострадании, симпатии, заботе,
            ласке, умеет подбодрить и успокоить окружающих, бескорыстный и отзывчивый.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Factor8 - 2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Фактор "Альтруистический".</Bold> Гиперответственный, всегда приносит в жертву свои интересы,
            стремится помочь и сострадать всем, навязчивый в своей помощи и слишком активный по отношению к
            окружающим, неадекватно принимает на себя ответственность за других (может быть только внешняя «маска», 
            скрывающая личность противоположного типа).
        </Paragraph>
    </Section>

    <Section 
        x:Key="Conclusion"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Для составления полной интерпретации по результатам теста следует учитывать соотношение 
            значений баллов всем шкалам.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Личностный_профиль.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Личностный профиль</sys:String>

    <Section 
        x:Key="Ответственность - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Ответственность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для измерения степени организованности, настойчивости и мотивированности 
            индивида в целенаправленном поведении.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            При высоком уровне выраженности свойства для обследуемого характерны самообладание, упорство, 
            организованность, дисциплинированность, ответственность, направленность на задачу, надёжность, 
            достаточная пунктуальность; обычно такой работник эффективно справляется со своими обязанностями, 
            может быть ориентирован на карьеру.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Ответственность - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Ответственность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для измерения степени организованности, настойчивости и мотивированности 
            индивида в целенаправленном поведении.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Низкий уровень выраженности свойства отличает лиц, для которых свойственны отвлекаемость,
            плохая организованность, непредсказуемость, стремление экспериментировать, несосредоточенность, 
            слабоволие; такой человек часто бросает начатое дело, так как легко увлекается другими идеями, 
            слабо контролирует собственные импульсы.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Ответственность - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Ответственность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для измерения степени организованности, настойчивости и мотивированности 
            индивида в целенаправленном поведении.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Эмоциональная выносливость - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Эмоциональная выносливость»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для измерения эмоциональной выносливости либо эмоциональной нестабильности 
            работника, проявляющихся, в частности, в чувствительности к стрессогенным ситуациям и степени 
            адекватности реакций на них.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            При высоких оценках обследуемый относится к жизни рационально и спокойно; его отличительные 
            черты – невозмутимость, уверенность в себе, расслабленность, значительная нервно-эмоциональная 
            выносливость.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Эмоциональная выносливость - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Эмоциональная выносливость»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для измерения эмоциональной выносливости либо эмоциональной нестабильности 
            работника, проявляющихся, в частности, в чувствительности к стрессогенным ситуациям и степени 
            адекватности реакций на них.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Низкие оценки характеризуют лиц, отличающихся явным беспокойством, нервозностью, неадекватностью 
            реакций, неуверенностью в себе, лёгкостью возникновения отрицательных эмоций, склонностью к ипохондрии, 
            испытывающих большое количество негативных переживаний, тревожных, раздражительных, подверженных 
            плохому настроению, склонных видеть всё в чёрном цвете, неудовлетворённых жизнью.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Эмоциональная выносливость - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Эмоциональная выносливость»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для измерения эмоциональной выносливости либо эмоциональной нестабильности 
            работника, проявляющихся, в частности, в чувствительности к стрессогенным ситуациям и степени 
            адекватности реакций на них.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Активность - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Активность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Результаты по данной шкале не учитываются при вынесении заключения первичного психофизиологического
            отбора и периодического обследования работников! Шкала предназначена для измерения широты и интенсивности 
            уровня общей активности и межличностных взаимоотношений.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Лица с высокими уровнями выраженности свойства, как правило, быстро просыпаются по утрам и рано 
            встают, энергичны, легко переключаются с одной деятельности на другую и имеют разнообразные и
            многочисленные интересы и увлечения, общительны, оптимистичны, уверенны в себе и склонны к 
            доминированию в межличностных отношениях.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Активность - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Активность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Результаты по данной шкале не учитываются при вынесении заключения первичного психофизиологического
            отбора и периодического обследования работников! Шкала предназначена для измерения широты и интенсивности 
            уровня общей активности и межличностных взаимоотношений.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Низкий балл по этой шкале характерен для физически пассивных и быстро утомляемых людей. Они 
            обычно неторопливы, имеют ограниченный круг общения, в компании предпочитают держаться в тени, 
            в межличностных отношениях склонны к зависимости и подчинению.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Активность - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Активность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Результаты по данной шкале не учитываются при вынесении заключения первичного психофизиологического
            отбора и периодического обследования работников! Шкала предназначена для измерения широты и интенсивности 
            уровня общей активности и межличностных взаимоотношений.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Социальная желательность - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Социальная желательность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для измерения готовности испытуемого представлять себя в благоприятном, социально 
            одобряемом свете.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Высокие оценки по шкале свидетельствуют о тенденции испытуемого демонстрировать строгое соблюдение 
            даже малозначительных социальных норм, умышленное стремление показать себя в лучшем свете, о 
            настороженности по отношению к ситуации обследования. При получении оценки по шкале более 28 баллов 
            результаты по другим шкалам должны интерпретироваться с осторожностью (реальные уровни активности, 
            ответственности и эмоциональной стабильности могут оказаться ниже расчётных).
        </Paragraph>
    </Section>

    <Section 
        x:Key="Социальная желательность - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Социальная желательность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для измерения готовности испытуемого представлять себя в благоприятном, социально 
            одобряемом свете.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Низкие оценки по шкале (менее 28 баллов) свидетельствуют об отсутствии у  испытуемого
            тенденции к демонстрации соблюдения малозначительных социальных норм, и умышленному 
            стремлению показать себя в лучшем свете.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Социальная желательность - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Социальная желательность»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для измерения готовности испытуемого представлять себя в благоприятном, социально 
            одобряемом свете.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Атипичность ответов - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Атипичность ответов»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для оценки достоверности полученных результатов.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Высокие значения по этой шкале (25 баллов и выше) могут свидетельствовать либо об аггравации, 
            т.е. нарочитом стремлении представить себя в худшем свете, либо о тяжёлом личностном кризисе, 
            требующем психологической помощи.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Атипичность ответов - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Атипичность ответов»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для оценки достоверности полученных результатов.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Оценки свыше 15 баллов, скорее всего, указывают на случайный, хаотический характер реагирования.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Атипичность ответов - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Атипичность ответов»:</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Шкала предназначена для оценки достоверности полученных результатов.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Низкие значения по этой шкале (не более 15 баллов) свидетельствуют о достоверности полученных результатов.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Люшер.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="Results"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Первый выбор:</Bold>
            <TextBlock Text="{Binding}" FontWeight="Normal" TextWrapping="Wrap" />
        </Paragraph>
    </Section>


    <Section 
        x:Key="+0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Не желает  ни в чем  участвовать и избегает  каких бы то  ни было стимулов. Неприятности, которые 
            ему пришлось перенести, утомили и опустошили его. Сейчас стремится к уединению и не вовлеченности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Жаждет состояния, мирного, гармоничного и способного дать спокойную удовлетворенность и ощущение общности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится выработать решительность и силу воли,  необходимые  для того, чтобы,  несмотря на
            трудность своего положения,  приобрести независимость. Намерен преодолевать преграды и 
            добиться признания.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Активный,  сильный,  энергичный,  живой, получает удовольствие от действий. Стремится к успеху и
            победе, хочет жить полной жизнью.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Испытывает потребность в изменении существующих обстоятельств или отношений, которое бы дало путь 
            к новым возможностям и осуществило его надежды.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+`"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствует потребность в идентификации себя  с кем-либо или с чем-либо  и хочет приобрести сторонников  
            при помощи своего обаяния и приветливости.  Сентиментален,  томится по романтической нежности  отношений.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Ищет  освобождения от проблем и безмятежного состояния физической  легкости, расслабленности, которое
            бы позволило восстановить силы.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Находит существующую ситуацию неприятной, поскольку к нему предъявляются слишком высокие требования.
            Не допускает ничего, что могло бы повлиять на его точку зрения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Определяет барьер между компенсирующими цветами, которые стоят до x0 и всеми остальными, т.е. ту точку,
            в которой произошло "выключение". Таким образом, плюс-группа - это единственный механизм, при помощи 
            которого возможно какое-либо "участие". Подчеркивает значение характеристик плюс - группы и придает
            компульсивный характер.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Действует  спокойно,  с минимумом волнения, не теряя контроля над отношениями  с окружающими. 
            В обществе  своих знакомых и близких  чувствует себя легко и расковано.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Настойчив. Требует того, что, по его мнению, ему причитается и пытается сохранить свой статус.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Активен и энергичен, однако считает, что достигнутый им успех или полученная награда
            недостаточны,  по сравнению с затраченными им усилиями.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Ощущает тягу к новому и увлекательному. Испытывает скуку от однообразности, от всего 
            заурядного или традиционного.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится выразить потребность в идентификации в тонкой, интимной обстановке,  
            которая бы дала выход его эстетической и эмоциональной утонченности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            В  существующей ситуации  чувствует  себя  тревожно и неуверенно. Нуждается в ощущении
            большей безопасности, в большей теплоте  со стороны окружающих, в обстановке, не
            вызывающей такого физического напряжения (плюс-группа является попыткой компенсации 
            этого и других конфликтов).
        </Paragraph>
    </Section>

    <Section 
        x:Key="x7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Конфликт или внутренняя неудовлетворенность вызвали потребность в компенсациях, на которые 
            указывает плюс-группа.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Готов "участвовать" и позволяет вовлечь себя в деятельность,  однако пытается избежать
            конфликтов или волнений.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Не испытывает эмоциональной привязанности даже находясь в близких  отношениях с кем-либо.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Ситуация  не благоприятствует ему, но он считает, что должен наилучшим образом использовать 
            существующие обстоятельства.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Пытается успокоиться  и расслабиться после периода перевозбуждения, которое сделало его вялым 
            и апатичным. Нуждается  в покое и тишине, и если в этом ему отказано становится раздражительным.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Верит в то, что его надежды и замыслы реальны, однако нуждается в подбадривании и поощрении. 
            При выборе  партнера пользуется очень строгими критериями, перестраховывая себя от потерь
            или разочарований.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Эгоцентричен и поэтому обидчив.  Чувствителен и сентиментален, но скрывает это от всех, кроме
            очень близких людей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Способен получать удовлетворение от сексуальной активности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает,  что  на его пути стоят  препятствия, что обстоятельства вынуждают  его 
            идти на компромиссы и временно отказаться от некоторых удовольствий.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Полагает,  что  жизнь  может  предложить гораздо больше и что еще есть вещи, которых стоит 
            добиваться, что жизнь нужно прожить сполна. В итоге,  преследует свои цели с неистовой энергией и 
            не  упускает достигнутого. Не может оставить без внимания ни малейшего события и чувствует, 
            что успокоится лишь тогда, когда наконец  достигнет своей цели. Становится крайне "вовлеченным",  
            и есть риск, что он окажется неспособным смотреть  на  вещи  с достаточной объективностью или с
            достаточным  спокойствием, поэтому ему грозит опасность перевозбуждения и даже истощения нервной 
            системы.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Существующая ситуация или отношения не удовлетворяют его. Считает себя неспособным изменить их.  
            Не хочет,  чтобы другие догадались о его ранимости и продолжает сопротивляться обстоятельствам, 
            однако  чувствует  себя эмоционально зависимым от других.  Это не только угнетает его, но и делает 
            его раздражительным и нетерпеливым,  вызывая  серьезное  беспокойство и стремление избавиться от  
            этой ситуации,  если не на деле, то хотя бы мысленно. Может  появиться неспособность сосредоточиться.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Целеустремленность и сила воли, необходимые для успешной борьбы с  существующими трудностями, 
            стали слабее,  чем раньше.  Чувствует  перенапряжение  и  бесцельность усилий, однако, продолжает 
            стоять на своем.  Ощущает эту неприятную ситуацию как какое-то реальное, осязаемое давление, 
            которое невыносимо для него и от которого он  стремится  избавиться,  но не в состоянии принять 
            необходимое для  этого решение.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            В сложившейся ситуации ощущает  угрозу и опасность.  Возмущается  при мысли о том, что он будет не в 
            состоянии достичь своих целей,  и страдает от ощущения бессилия что-либо с этим поделать. Чувствует 
            себя загнанным в угол, возможно даже нервное истощение.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Несбывшиеся надежды вызвали неуверенность и опасения. Испытывает  потребность в ощущении безопасности,
            стремится избежать разочарований  и  избавиться от страха потерять положение или престиж. Сомневается
            в том, что в будущем ситуация будет хоть сколько-нибудь лучше, и эта негативная установка приводит к
            завышенным требованиям и нежеланию идти на разумные компромиссы.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Получает наслаждение от утонченного, изящного, изысканного, однако сохраняет скептицизм и не позволяет
            себе увлечься, пока полностью  не убедится в том,  что ощущение неподдельно и целостно. Держит эмоции
            под строгим контролем. Требует от других искренности,  что  служит ему защитой от его собственной
            тенденции к излишней доверчивости.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Имеет  неудовлетворенную потребность  объединиться с людьми,  чьи  стандарты так же высоки, как и его. 
            Это желание быть исключительным  приводит к изоляции и мешает ему щедро отдавать себя другим.  Несмотря 
            на неосознанное желание уступить и дать волю чувствам, он считает это слабостью,  с которой надо
            бороться,  и  полагает, что самоограничение поднимает его над общей массой и принесет ему   
            признание как уникальной и неповторимой личности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится  к независимости,  свободе от ограничений  и  запретов, кроме тех, которые он налагает на себя 
            сам, по своему решению.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+0+1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствует, что конфликты и ссоры вымотали его,  и стремится оградить  себя от них.  Нуждается в мирной 
            и спокойной обстановке,  в которой он мог бы расслабиться и набраться сил.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+0+2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствует, что обстоятельства враждебны ему,  вымотан конфликтами  и ссорами. Желает оградить себя от
            конфликтов и скрывает свои намерения,  чтобы ему не смогли воспрепятствовать осуществить их.
            Старательно  избегает  действий,  которые  могли бы повредить его планам.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+0+3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Предъявляет слишком высокие требования, однако скрывает их за показной  рационалистичностью  и  
            осторожным поведением.  Стремится  производить впечатление на  окружающих  своими  достижениями, 
            но маскирует это желание. Склонен к скрытности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+0+4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Хочет избавиться от не удовлетворяющей его ситуации и от бремени, которое ему приходится нести, 
            и то,  и другое угнетает его и кажется невыносимым. Ищет какой-либо выход из ситуации, но не находит 
            его. Старается оградить себя от участия в спорах и конфликтах.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+0+5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Охвачен мечтами об идеальной близости,  нежности и взаимном обаянии.  Ощущает опасение,  что может выдать 
            свои мысли, и поэтому в достижении своей мечты прибегает  к осторожной,  разведывательной  тактике,  
            проверяя, не связал ли он себя бесповоротно какими-либо обязательствами, не разгадали
            ли его намерения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+0+6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Защищается от всего, что может опустошить или утомить его. Жаждет жизни, в которой он был бы в 
            безопасности и чувствовал себя легко  в физическом отношении, жизни свободной от проблем и волнений.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+0+7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что с ним обращаются несправедливо.  Надежды также обманули его. Рассержен и бунтует 
            против сложившихся обстоятельств, в которых он видит незаслуженное личное оскорбление.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1+0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Испытывает потребность освободиться от стресса.  Жаждет мира, покоя, удовлетворенности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1+2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Нуждается в спокойной обстановке.  Хочет освободиться от стресса,  избавиться от конфликтов 
            и внутренних разногласий.  Прилагает все усилия,  чтобы, действуя осторожно, как-то отрегулировать
            сложившуюся ситуацию и связанные с ней проблемы.  Способен тонко  чувствовать, внимателен к деталям.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1+3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Ищет нежных, приятных, гармоничных отношений. Стремится к тесному союзу, в котором была бы любовь,
            самопожертвование и взаимное доверие.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1+4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится к  нежным  отношениям,  которые  принесли  бы  ощущение  счастья и удовлетворение. 
            Способен на сильный эмоциональный подъем. Готов на жертвы, если это необходимо для того, чтобы
            желанный  союз,  основанный на нежности,  стал реальностью. Испытывает потребность во внимании и 
            понимании со стороны других.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1+5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится к нежности и тонкости чувств, в которых он мог бы  раствориться. Мечтает об отношениях,  
            исполненных нежности и сочувствия, об идеальной гармонии. Чуток ко всему,  что эстетично и
            исполнено вкуса.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1+6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Жаждет найти убежище от конфликтов,  где он был бы в безопасности  и где чувствовал себя легко 
            в физическом отношении.  Остро нуждается во внимании,  любви и заботе.  Боится пустоты и одиночества,
            разлуки.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1+7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Крайне нуждается в отдыхе,  расслаблении, покое и теплом участии.  Считает, что к нему относились 
            с недостаточным  уважением.  Это  убеждение сделало его беспокойным и тревожным.  Поскольку его
            потребности не удовлетворены,  полагает,  что существующая ситуация  невыносима.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2+0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится упрочить  свое положение и производить на людей сильное  впечатление, несмотря на неблагоприятные 
            обстоятельства и полное отсутствие признания.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2+1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится производить  приятное  впечатление и получить одобрение  со стороны окружающих.  Нуждается 
            в ощущении,  что его ценят и им восхищаются.  Обидчив  и  легко раним - когда его не замечают или  
            когда он не получает заслуженного признания.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2+3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится к успеху. Хочет преодолевать преграды и сопротивление и принимать решения самостоятельно. 
            Проявляет инициативу. Не желает  чувствовать себя зависимым от расположения других людей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2+4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Нуждается в признании. Честолюбив, хочет производить впечатление,  обладать популярностью и вызывать 
            восхищение.  Стремится ликвидировать разрыв, который, как он чувствует, отделяет его от других.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2+5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится производить приятное впечатление.  Хочет,  чтобы в  нем  видели  необыкновенную  личность,
            поэтому  всегда  контролирует свои  действия и реакцию окружающих.  Чтобы добиться влияния и
            особого  признания, прибегает к различным приемам, пуская их в ход в хорошо продуманном порядке. 
            Восприимчив к эстетическому и оригинальному.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2+6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что  от  него требуют слишком многого,  и чувствует себя  вконец уставшим,  однако все 
            еще надеется одолеть трудности и упрочить свое положение,  несмотря на те последствия, которые 
            будет для него иметь подобная попытка.  Горд,  но безропотен,  занимает  позицию "неохотного
            подчинения".  Нуждается в признании, безопасности и избавлении хотя бы от части проблем.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2+7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Хочет доказать себе и другим,  что его ничем не проймешь,  что он  выше  каких-либо  слабостей. 
            Поэтому  действует жестко и сурово,  вырабатывает у себя диктаторскую и своевольную позицию.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3+0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится смести все,  что стоит у него на пути, следовать только своим импульсам и участвовать
            в необычных и возбуждающих предприятиях.  Таким образом, он рассчитывает притупить остроту 
            конфликтов. Импульсивное поведение заставляет его идти на риск.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3+1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится к жизни,  богатой действиями и впечатлениями,  и к близости, которая доставляет
            и сексуальное, и эмоциональное удовлетворение.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3+2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Упорно преследует  поставленные  перед  собой цели и не позволяет  себе никаких отклонений 
            от намеченного пути. Хочет преодолеть все  трудности, которые встают перед ним, и добиться
            особого признания  и положения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3+4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Жаждет успеха, возбуждения, жизни полной впечатлений. Стремится к  свободной и насыщенной жизни, 
            избавления от неуверенности, победам. Охотно вступает в контакты с людьми,  энтузиаст  по  натуре.  
            Восприимчив ко всему новому,  современному или увлекательному.  У него много интересов, и тем не 
            менее он стремится расширять сферы деятельности. Hа будущее смотрит оптимистично.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3+5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Поглощен вещами,  которые способны сильно волновать, захватывать,  будь то эротическое или иное 
            возбуждение.  Хочет привлекать окружающих  и  интересовать их как личность,  не выпуская их из 
            сферы действия своего обаяния и внушительности. Искусно пользуется различными приемами, чтобы 
            не поставить под угрозу свои шансы на успех и не подорвать доверие к себе других людей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3+6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Приходится отказаться от  желания  добиться  высокого  положения, престижа, поскольку предпочитает 
            ко всему относиться спокойно, не отказывать себе в комфорте и безопасности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3+7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится наверстать то,  что,  как ему кажется, он упустил, живя  насыщенной жизнью так, по его 
            мнению, он сможет освободиться от  угнетающих его ощущений.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4+0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hуждается в избавлении от угнетающих его ощущений, и цепляется за смутные и иллюзорные надежды.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4+1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hадеется, что любовь принесет ему облегчение и удовлетворенность. Потребность в одобрении и приязни 
            со стороны  окружающих,  делает его  готовым  оказать помощь,  в ответ на которую он ждет тепла и 
            понимания.  Одобрительно относится к новым идеям и  возможностям,  на которые он возлагает надежды.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4+2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Отличается бдительностью и острой наблюдательностью.  Ищет  новых путей,  которые  открыли бы перед ним
            большую свободу и позволили  бы ему добиться как можно большего.  Хочет показать себя и завоевать
            признание.  Стремится ликвидировать разрыв,  который, как он  чувствует, отделяет его от других.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4+3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Потребность чувствовать себя в центре внимания и иметь более  широкую сферу влияния делает его 
            беспокойным, движимым собственными  желаниями и надеждами.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4+5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Обладает гипертрофированным воображением,  отдается  фантазиям  и мечтам.  Жаждет интересных 
            и волнующих событий, хочет, чтобы другие восхищались его обаятельностью.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4+6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hаходится в отчаянии,  силы его близки к пределу. Испытывает потребность  в  физическом  облегчении,  
            освобождении  от каких-либо проблем, в безопасности и возможности восстановить силы.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4+7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Пытается уклониться от каких-либо проблем,  сложностей и напряжения. Часто принимает неожиданные,  
            своевольные и необдуманные решения или резко меняет линию поведения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+5+0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Жаждет тонкого и сочувственного понимания и хочет  оградить  себя  от споров, конфликтов или 
            каких-либо обессиливающих стрессов.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+5+4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Жаждет интересных и волнующих событий.  Может очень нравиться окружающим, подкупая их своим 
            явным к ним интересом и искренностью. Слишком развито воображение, любит фантазировать и мечтать.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+5+6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Хочет найти  возбуждение  в атмосфере сладострастия и чувственной  роскоши, комфорта.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+5+7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Испытывает насущную  потребность  в  союзе или тесных отношениях,  которые принесут ему 
            чувственное удовлетворение,  не вступая, однако, в конфликт с его убеждениями или чувством дозволенности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+6+0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Серьезно нуждается в отдыхе и расслаблении, свободе от конфликтов и в возможности восстановить силы.  
            Хочет оградить себя от разрушительных и опустошающих воздействий.  Стремится к безопасности и 
            освобождению от проблем.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+6+2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Строжайшим образом контролирует себя,  пытаясь выдержать тяжестью  существующей ситуации. Нуждается
            в более надежных и спокойных условиях,  в которых он бы чувствовал себя в большей безопасности и 
            имел бы возможность набраться сил.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+6+3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Имеет сильную тенденцию к чувствительности и сентиментальности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+6+4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает маловероятным,  что его надежды сбудутся и предается жизни,  исполненной чувственной
            праздности,  свободной от каких-либо проблем и обязательств.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+6+7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Ставит себе идеальные,  но не реалистичные  цели.  В  результате,  сталкивается с горькими 
            разочарованиями и отворачивается от жизни  с горестным чувством отвращения к самому себе. 
            Хочет все забыть и восстановить силы в спокойной, свободной от проблем обстановке.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7+0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что ситуация безнадежна. Оказывает сильное сопротивление  всему, что ему неприятно. 
            Пытается защититься от всего, что может вызвать у него раздражение или углубить его подавленность.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7+1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Страдает от  любых воздействий и оказывает им сильное сопротивление.  Полагает,  что существующая 
            ситуация невыносима  Хочет  лишь  одного  чтобы его оставили в покое.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7+2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Оказывает агрессивное сопротивление каким бы то ни было ограничениям и несвободам.  
            Упрямо отстаивает свое мнение,  полагая,  что это доказывает его независимость и самостоятельность.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7+3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Страдает от подавляемого сверхвозбуждения, которое может находить  выход во вспышках импульсивного 
            и необузданного поведения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7+4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Пытается избавиться от проблем, трудностей и напряжения, принимая  неожиданные,  своевольные и 
            необдуманные решения.  Отчаянно  ищет  пути к избавлению от проблем.  Есть опасность безрассудного 
            поведения вплоть до попыток суицида.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7+5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Ощущает потребность в том, чтобы мысли и чувства находились в совершенной,  идеальной гармонии.  
            Отказывается идти на  какие-либо уступки или принимать какие-либо уступки от других.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x0x1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Относительно пассивен и находится в статичном состоянии, хотя некий конфликт и мешает 
            душевному покою. Неспособен  добиться отношений, которые могли бы удовлетворить его с точки 
            зрения взаимной  привязанности и взаимопонимания (предшествующая группа цветов характеризует попытку 
            компенсации этого или других конфликтов).
        </Paragraph>
    </Section>

    <Section 
        x:Key="x0x2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hаходится в сложной ситуации,  но упорно пытается  достичь  своих  целей. Из  предосторожности считает 
            необходимым скрывать свои намерения, чтобы обезоружить противника (предшествующая группа цветов  
            характеризует попытку компенсации этого и других конфликтов).
        </Paragraph>
    </Section>

    <Section 
        x:Key="x0x3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Пытаясь добиться успеха,  наталкивается на трудности. Несмотря на то,  что  он  старается скрыть 
            свою импульсивность,  его действия приводят к осложнениям и неопределенности,  и он становится  тревожным 
            и раздражительным (предшествующая группа цветов характеризует попытку компенсации этого и других 
            конфликтов).
        </Paragraph>
    </Section>

    <Section 
        x:Key="x0x4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Крушение надежд  и  неспособность определить,  какие меры следует  принять,  все это  вызвало  
            значительный  стресс  (предшествующая  группа  цветов  характеризует  попытку компенсации 
            этого и других  конфликтов).
        </Paragraph>
    </Section>

    <Section 
        x:Key="x0x5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Боязнь встретить  отказ  и  крайняя  осторожность  подхода делают  трудным достижение желанной 
            степени близости и идентификации.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x0x6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hеспособен на какие-либо усилия для достижения своих целей.  Считает, что им пренебрегают, 
            жаждет ощущения большей безопасности и  теплоты со стороны окружающих и хочет избежать 
            проблем (предшествующая группа характеризует попытку компенсации  этого  и  других  конфликтов).
        </Paragraph>
    </Section>

    <Section 
        x:Key="x0x7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hаходится в состоянии стресса,  вызванного требованиями, предъявляемыми ему сложившейся ситуацией.
            Пытается освободиться от того, что как-то ограничивает или стесняет его  (предшествующая  группа  
            цветов  характеризует  попытку компенсации этого или других конфликтов).
        </Paragraph>
    </Section>

    <Section 
        x:Key="x1x0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствителен и отзывчив, однако, в нем ощущается некоторая напряженность. Нуждается в покое, который он 
            может обрести в  обществе близкого человека.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x1x2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            x1x2Действует упорядоченно,  методично и самостоятельно,  нуждается в  сочувственном понимании, признании 
            и одобрении со стороны близкого человека.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x1x3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            x1x3В сотрудничестве с другими работает хорошо,  однако брать на себя  роль лидера не склонен.
            Стремится к тому, чтобы личная жизнь была  основана на взаимопонимании, чтобы в ней не было 
            никаких конфликтов и ссор.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x1x4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Легко приспосабливается.  Чувствует  себя  спокойно только тогда, когда тесно связан с каким-либо 
            человеком,  группой или организацией, на которых он может положиться.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x1x5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствителен, нуждается в эстетическом окружении или в  таком  же  чувствительном и понимающем
            партнере, с которым он будет в теплых  и близких отношениях.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x1x6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Избегает прилагать какие-либо чрезмерные усилия и испытывает пот6ребность в "корнях", безопасности
            и спокойном дружеском окружении.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x1x7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hуждается в теплых товарищеских отношениях, однако не выносит ситуаций,  когда близкие выказывают 
            недостаточно уважения  к  нему. Если он не может и в будущем рассчитывать на желаемые отношения к 
            себе, то обычно прекращает общение с такими людьми.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x2x0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Занимает оборонительную позицию.  Считает,  что его положение недостаточно прочно или что над ним
            нависла какая-то  угроза. Полон  решимости достичь своих целей, несмотря на тревогу, которую вызывает 
            конфликт.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x2x1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Аккуратен, методичен,  самостоятелен. Нуждается в уважении, признании и понимании со стороны близких ему людей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x2x3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится к  власти или обладает властью,  занимает высокое социальное положение,  однако  склонен  
            полагать,  что  существующие трудности ставят дальнейшие его успехи под сомнение. Упорно преодолевает 
            трудности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x2x4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Пытается улучшить свое положение и повысить престиж.  Не удовлетворен существующим положением дел 
            и полагает, что некоторое улучшение их существенно важно для его самоуважения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x2x5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Пытается улучшить представление о  себе  со  стороны  окружающих, стремится найти у них благожелательное
            отношение, уступчивость.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x2x6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Пытается создать надежный фундамент для спокойного, свободного от существующих проблем будущего, в котором 
            он хочет получить уважение и призвание.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x2x7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Преследует свои цели и свои интересы с упрямой решимостью,  отказывается идти на уступки и компромиссы.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x3x0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Импульсивен и раздражителен. Его желания и связанные с ними действия являются для него главным, 
            последствиям же этих действий он уделяет недостаточно внимания. Это приводит к стрессу и конфликту.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x3x2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Проявляет решительность  и  инициативу  в  преодолении  преград и трудностей.  Или занимает,  
            или хочет занять положение, в котором  он был бы обеспечен властью, управлял бы ходом событий.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x3x4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hепостоянен и экстравертен. Ему необходимо чувствовать, что события развиваются в желаемом направлении, 
            в противном случае раздражение может привести к хаотичности или поверхностности в  действиях.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x3x5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Готовностью участвует во всем,  что может возбуждать или волновать. Стремится к активной жизни.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x3x6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hе желает растрачивать себя или затрачивать какие-либо чрезмерные  усилия (возможно,  за исключением
            сексуальной активности).  Чувствует,  что дальнейшее продвижение потребуют от него больше,  
            чем  он  хочет  или  может  дать.  Умеренный комфорт и безопасность он  предпочел бы тем благам,
            которые дает большое честолюбие.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x3x7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что  на  пути  к желанным целям ему чинят преграды - это является для него существенно важным.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x4x0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Ищет какого-либо  разрешения существующих проблем и забот, однако  склонен полагать, что выбор 
            правильного курса - дело трудное.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x4x1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Легко поддается влиянию окружающих, чувства других людей сразу же находят в нем отклик. Ищет близких 
            взаимоотношений и занятий, которые бы способствовали установлению таких отношений.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x4x2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hадеется добиться лучшего положения  и  завоевать  более  высокий  престиж, чтобы он мог обеспечить 
            себя многими вещами, без которых  раньше ему приходилось обходиться.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x4x3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Активный, экстравертный и неугомонный. Разочарован тем, что события разворачиваются в желаемом 
            направлении так медленно.  Это вызывает  раздражительность,  переменчивость и недостаток 
            настойчивости в достижении цели.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x4x5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Одарен богатым воображением и чувствителен,  ищет выхода этим качествам  особенно в обществе 
            кого-нибудь столь же чувствительного.  Что-либо необычное или связанное с приключениями быстро 
            пробуждает в нем заинтересованность и энтузиазм.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x4x6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Ведет себя неуверенно.  Ищет устойчивости, эмоциональной безопасности и окружения,  в котором 
            было бы меньше проблем  и в котором  он чувствовал бы себя свободнее.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x4x7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Оценивает ситуацию как критическую или угрожающую,  поэтому мучительно  ищет какой-нибудь выход.  
            Это может привести к внезапным, даже опрометчивым решениям. Своеволен и отказывается слушать
            чьи-либо  советы  (предшествующая группа цветов характеризует попытку  компенсировать этот и другие 
            конфликты).
        </Paragraph>
    </Section>

    <Section 
        x:Key="x5x0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится к союзу,  основанному на понимании,  нежности, интимной  близости - это послужило бы 
            ля него защитой от тревоги  и  конфликтов.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x5x6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувственен. Склонен к наслаждению вещами, доставляющими чувственное удовлетворение, отвергает 
            все безвкусное, пошлое и грубое.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x5x7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hуждается в отношениях, исполненных близости и понимания, или хотя бы в каком-нибудь средстве
            удовлетворить компульсивное желание ощутить свою идентификацию с кем-либо.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x6x1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Избегает затрачивать чрезмерные усилия.  Испытывает потребность в "корнях", безопасности и спокойном 
            дружеском общении. Может находится в плохом физическом состоянии, нуждается в мягком обращении
            и внимательной помощи.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x6x2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            С трудом выполняет  требования,  которые  к  нему  предъявляются. Находит, что это стоит ему больших 
            усилий и хочет, чтобы ситуация  стала легче.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x6x4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hеуверен в себе. Ищет "корней", устойчивости, эмоциональной безопасности и окружения, в котором 
            было бы меньше проблем, и в котором бы он чувствовал себя свободнее, однако не хочет или 
            не может затрачивать для этого какие-либо усилия.
        </Paragraph>
    </Section>

    <Section 
        x:Key="x6x7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Физический недуг, перенапряжение или эмоциональные мучения приняли  серьезную  форму.  
            Самоуважение понизилось и сейчас для того,  чтобы исцеление стало возможным,  ему необходима 
            спокойная обстановка  и заботливая помощь (предшествующая группа цветов характеризует попытку
            компенсации этого или других конфликтов).
        </Paragraph>
    </Section>

    <Section 
        x:Key="x7x0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Ощущает неудовлетворенность.  Потребность как-то  положить  конец его  постоянной "вовлеченности"
            в сложившуюся ситуацию делает необходимыми поиски какого-либо выхода  (предыдущая  группа  цветов 
            указывает на тип компенсации, который, по-видимому, будет выбран).
        </Paragraph>
    </Section>

    <Section 
        x:Key="x7x1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Испытывает потребность в тишине и покое.  Хочет  иметь  близкого,  преданного партнера, от которого
            он мог бы требовать особого внимания и несомненной привязанности.  Если это требование не 
            удовлетворено, он склонен прекратить общение и полностью замкнуться в себе. (Hа требования, 
            которые предъявляет он своему окружению или  своему партнеру, указывает предшествующая группа).
        </Paragraph>
    </Section>

    <Section 
        x:Key="x7x2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что его требования чрезвычайно скромны,  а также  что они  требуют  немедленного 
            удовлетворения.  Упрямо стоит на этих требованиях и не пойдет ни на какие уступки (предшествующая
            группа  цветов  характеризует  попытку  компенсации  этого или других  конфликтов).
        </Paragraph>
    </Section>

    <Section 
        x:Key="x7x3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что на пути к желанным целям ему ставят преграды,  и это мешает ему получить то,  что
            он считает для себя существенно важным  (предшествующая группа цветов характеризует попытку 
            компенсации этого или других конфликтов).
        </Paragraph>
    </Section>

    <Section 
        x:Key="=0=1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Взаимоотношения с окружающими редко отвечают его высоким  эмоциональным  ожиданиям  и его 
            потребности в чрезмерном внимании - это  приводит к разочарованию (часто бывает характерным 
            для фиксации, сильной  привязанности или обиды).  Всегда делает "мысленные оговорки" и склонен 
            оставаться эмоционально обособленным и свободным  от каких-либо связей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=0=2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что получает меньше,  чем заслуживает, но мирится с этим  и  старается наилучшим 
            образом использовать сложившуюся ситуацию.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=0=3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Испытывает апатию и тревогу. Считает, что попал во враждебное окружение, но обстоятельства
            вынуждают его сдерживать свои желания. Хочет избежать открытого конфликта с другими,
            обрести спокойствие.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=0=4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            В своих  отношениях с партнерами или близкими людьми требователен и привередлив,  однако 
            открытых  конфликтов  старается  избегать,  поскольку  они могут понизить шансы на осуществление
            его надежд и замыслов.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=0=5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Эгоцентричен,  поэтому легко обижается, что часто приводит к изоляции от окружающих.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=0=6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Хочет чувствовать себя "эмоционально вовлеченным". Способен получать удовлетворение  от 
            сексуальной  активности,  пытаясь в то же избежать конфликтов.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=0=7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            В силу сложившихся обстоятельств считает себя вынужденным времен- но идти на компромиссы, 
            поскольку не хочет лишить себя некоторой  привязанности или "полного участия".
        </Paragraph>
    </Section>

    <Section 
        x:Key="=1=0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Имеет высокие  эмоциональные  притязания.  Хочет чувствовать себя "вовлеченным" в какие-либо 
            близкие взаимоотношения,  не требующие  от него, однако, особой глубины чувства.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=1=2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Полагает, что он получает меньше того, что заслуживает, что его и понимают недостаточно 
            хорошо, и ценят недостаточно высоко. Чувствует, что ему приходится приноравливаться к другим.
            Близкие взаимоотношения не вызывают чувства эмоциональной "вовлеченности".
        </Paragraph>
    </Section>

    <Section 
        x:Key="=1=3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствует себя отделенным от других и несчастным в силу  трудностей  в достижении необходимой
            ему степени сотрудничества или гармонии, к которым он стремится.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=1=4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Взыскателен в  своих эмоциональных требованиях и весьма привередлив в выборе партнера.  
            Потребность в эмоциональной независимости  делает для него невозможной какую-либо глубокую 
            "вовлеченность".
        </Paragraph>
    </Section>

    <Section 
        x:Key="=1=5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Эгоцентричен и поэтому обидчив.  Чувствителен и сентиментален, но скрывает это от всех, кроме
            очень близких людей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=1=6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Способен получать физическое удовлетворение от сексуальной активности,  но  склонен  к замкнутости, 
            и это не позволяет ему стать "глубоко вовлеченным" в его взаимоотношениях.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=1=7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Эмоционально заторможен. Чувствует себя вынужденным идти на  компромиссы, и поэтому ему трудно сохранить
            уступчивость  и  эмоциональную привязанность.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=2=0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hедостаток уверенности,  в котором он не  признается  даже  себе, заставляет его избегать открытых 
            конфликтов. Считает, что должен  наилучшим образом использовать те обстоятельства, какие есть.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=2=3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствует себя несчастным из-за сопротивления,  с которым сталкивается всякий раз, когда пытается 
            отстаивать свои права. Эти препятствия вызывают у него негодование и возмущение, однако он вяло 
            уступает и согласен как угодно приспосабливаться, лишь бы достичь  мира и покоя.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=2=4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что ему выпало больше всяких трудностей, чем другим. Тем не менее упорно преследует  свои  
            цели  и  пытается  преодолевать трудности, проявляя гибкость и приспосабливаясь.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=2=5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что он получает меньше того,  что ему  причитается,  что  нет ни одного человека, 
            у которого бы он мог бы найти сочувствие и понимание.  Сдерживаемые эмоции делают  его  обидчивым,
            но  он осознает, что должен как можно лучше использовать  те обстоятельства, какие есть.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=2=6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что при существующих трудностях не может рассчитывать на многое,  поэтому стремится
            использовать те обстоятельства,  какие есть. Способен получать удовлетворение от сексуальной 
            активности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=2=7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Обстоятельства вынуждают его идти на компромиссы, сдерживать свои требования  и надежды и временно 
            отказываться от желаемого.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=3=0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Страдает из-за препятствий, с которыми он сталкивается, и не расположен ни к какому виду деятельности. 
            Нуждается только в тишине,  покое и избавлении от всего, что его еще может расстроить.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=3=4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится расширить  сферы деятельности и настаивает на том,  что его надежды и желания реальны. 
            Страдает от опасений, что ему могут помешать сделать то, что он хочет, нуждается в спокойной
            обстановке,  а также в мягком подбадривании,  которое вернуло бы ему  уверенность.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=3=5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Страдает из-за того,  что его нужды или желания  понимают  неправильно и не на кого положиться. 
            Эгоцентричен и поэтому обидчив.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=3=6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствует, что попал в бедственное или неудобное положение, и ищет  какого-либо  пути к облегчению. 
            Способен получать удовлетворение от сексуальной активности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=3=7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Обстоятельства стесняют  и  ограничивают его действия,  заставляя  его временно отказаться
            от всех радостей и удовольствий.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=4=1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            В своих эмоциональных требованиях крайне взыскателен, особенно, в  минуты близости, поэтому
            часто остается разочарованным из-за  несоответствия реальности идеалу.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=4=5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hастаивает на  том,  что  его надежды и замыслы реальны,  однако, нуждается в подбадривании 
            и  поощрении.  Эгоцентричен  и  поэтому обидчив.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=4=6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            В выборе партнера пользуется очень строгими критериями,  в сексу6альной сфере ищет далекого 
            от реальности совершенства.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=4=7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hастаивает на том, что его цели реальны и упрямо к ним стремится, несмотря на то,  что
            обстоятельства вынуждают его идти на компромиссы. В выборе партнера пользуется очень 
            строгими критериями.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=5=0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится к ощущению "эмоциональной вовлеченности", т.к. в настоящий момент чувствует себя 
            изолированным  от  других,  одиноким. Эгоцентричен и поэтому обидчив, хотя и избегает 
            открытых конфликтов.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=5=1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствует себя  изолированным  от  других,  одиноким,  но слишком сдержан,  чтобы позволить 
            себе какую-либо глубокую привязанность. Эгоцентричен и поэтому обидчив.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=5=6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Эгоцентричен и  поэтому  обидчив.  Способен  получать  физическое удовлетворение от сексуальной
            активности,  но в эмоциональном отношении остается равнодушным.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=5=7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Обстоятельства таковы,  что он не позволяет себе интимной  вовлеченности без каких-либо мысленных оговорок.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=6=3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствует, что попал в бедственное и неудобное положение,  и ищет какого-либо  пути к облегчению. 
            Способен получать удовлетворение от сексуальной активности при условии,  что это не принесет 
            никакого беспокойства и от него не потребуется никакого эмоционального переживания.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=6=7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Обстоятельства вынуждают его идти на компромиссы и временно отказаться  от  некоторых удовольствий.  
            Способен получать физическое удовлетворение от сексуальной активности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="=7=1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Эмоционально заторможен. Чувствует себя вынужденным идти на компромиссы, сдерживать свои 
            требования и надежды, на время отказаться от желаемого.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-0-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Существующая ситуация или отношения не удовлетворяют его. Считает себя неспособным изменить их.  
            Не хочет,  чтобы другие догадались о его ранимости и продолжает сопротивляться обстоятельствам, 
            однако  чувствует  себя эмоционально зависимым от других.  Это не только угнетает его, но и делает 
            его раздражительным и нетерпеливым,  вызывая  серьезное  беспокойство и стремление избавиться от  
            этой ситуации,  если не на деле, то хотя бы мысленно. Может  появиться неспособность сосредоточиться.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-0-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Целеустремленность и сила воли, необходимые для успешной борьбы с  существующими трудностями, 
            стали слабее,  чем раньше.  Чувствует  перенапряжение  и  бесцельность усилий, однако, продолжает 
            стоять на своем.  Ощущает эту неприятную ситуацию как какое-то реальное, осязаемое давление, 
            которое невыносимо для него и от которого он  стремится  избавиться,  но не в состоянии принять 
            необходимое для  этого решение.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-0-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Видит в сложившейся ситуации угрозу и опасность.  Возмущается примысли,  что  будет  не в
            состоянии достичь своих целей и страдает  от ощущения своего бессилия. Перенапряжение заставляет 
            его чувствовать себя загнанным в угол, возможно даже нервное истощение.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-0-4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Несбывшиеся надежды вызвали неуверенность и опасения. Испытывает потребность в ощущении безопасности,
            стремится избежать разочарований  и  избавиться от страха потерять положение или престиж. Сомневается
            в том, что в будущем ситуация будет хоть сколько-нибудь лучше, и эта негативная установка приводит 
            к завышенным требованиям и нежеланию идти на разумные компромиссы.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-0-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Получает наслаждение от утонченного, изящного, изысканного, однако сохраняет скептицизм и не позволяет
            себе увлечься, пока полностью  не убедится в том,  что ощущение неподдельно и целостно. Держит эмоции
            под строгим контролем. Требует от других искренности,  что  служит ему защитой от его собственной
            тенденции к излишней доверчивости.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-0-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Существующая ситуация  неприятна.  Испытывает  неудовлетворенную потребность объединения с людьми, 
            имеющими такие же высокие стандарты,  как и он,  хочет выделяться из толпы.  Контроль над
            чувственными инстинктами ограничивает его способность отдавать себя,  однако  изоляция, к которой 
            это приводит, вызывает у него желание сдаться и позволить себе слиться  с другими.  Это его  
            беспокоит,  т.к.  он  рассматривает подобные инстинкты как слабость,  которую  следует преодолеть, 
            считает,  что только сохраняя умение владеть собой он может надеяться сохранить свое превосходство 
            как личности.  Хочет,  чтобы его любили и восхищались им бескорыстно,  ради  него самого, нуждается во 
            внимании, признании, уважении.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-0-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится преодолеть ощущение пустоты  и  ликвидировать  разрыв,  который,  как он чувствует,
            отделяет его от других. Стремится узнать жизнь со всех  сторон,  испытать  все  возможности,  
            прожить  жизнь сполна. Поэтому он негодует, когда его как-либо ограничивают или стесняют,  и 
            требует,  чтобы ему не чинили  препятствий  и предоставили свободу.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-1-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствует, что он не может добиться ощущения общности с окружающими, в котором он нуждается. 
            Оказывает сопротивление обстоятельствам или отношениям, накладывающим на него ответственность, 
            считает,  что  жизнь  может  предложить  гораздо  больше  и  склонен  оставаться нетерпеливым и 
            раздражительным, до тех пор пока не получит всего того,  что, по его мнению, ему еще не хватает.
            Стремление  выбраться  из не удовлетворяющего его состояния приводит к  беспокойству и неустойчивости. 
            Может появиться неспособность сосредоточиться.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-1-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Несмотря на  жизнестойкость  и целеустремленность,  перенапряжен  непрерывным преодолением  
            существующих  трудностей.  Стремится  к достижению целей, однако чувствует невыносимое давление.
            Считает,  что изменить ситуацию так, чтобы в ней нашли место сотрудничество  и взаимное доверие,
            невозможно и поэтому желает вообще от нее освободиться.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-1-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Не удовлетворяющие взаимоотношения с каким-то человеком причиняют  ему сильную боль.  Считает себя
            бессильным вернуть былую близость и хотя бы какое-то подобие взаимного доверия, поэтому  рассматривает
            ситуацию  как несчастное состояние,  которое он должен продолжать терпеть. Чувствует себя загнанным в
            угол, возможно даже нервное истощение.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-1-4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Какая-то эмоциональная связь перестала развиваться  гладко,  она  глубоко разочаровала его,  и теперь 
            угнетает его.  Хотя,  с одной  стороны,  он и хотел бы совершенно освободиться от этой привязанности,
            но, с другой стороны, он не хочет ни чтобы то ни было терять,  ни подвергать себя опасности новых 
            разочарований. Эти противоречивые  эмоции до такой степени ухудшают его состояние,  что он пытается, 
            подавляя их, вырабатывать у себя отношения равнодушное и суровое.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-1-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Существующая ситуация  неудовлетворительна,  но он чувствует, что сам он без помощи окружающих, 
            исправить ее не в состоянии.  Потребность в понимании,  в благожелательных взаимных уступках остается 
            не удовлетворенной, он чувствует себя связанным, это вызывает нетерпеливость и раздражительность.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-1-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что прежде чем существующее положение  дел  может  быть улучшено, он должен заручиться 
            сотрудничеством других. Недостаток  понимания и признания заставляет его думать, что невозможен никакой 
            "настоящий" союз,  а неудовлетворенность делает его повышенно чувствительным,  он хочет чувствовать себя
            более свободно и  уверенно. Он хотел бы освободиться от того, что сейчас представляется ему как 
            угнетение, и восстановить свою индивидуальность. Самоограничения в сфере чувств мешают ему отдавать 
            себя. Однако  изоляция,  к которой это приводит, вызывает у него желание сдаться и  позволить себе 
            слиться с другими. Это беспокоит его, т.к. он рассматривает подобные инстинкты как слабость, которую 
            следует  преодолеть - он считает,  что  утвердить  себя как личность он может  только путем постоянного
            самоконтроля,  что только  это  позволит  ему, несмотря на настоящие трудности, сохранить свои позиции.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-1-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Существующая ситуация  или отношения неудовлетворительны,  но он  чувствует, что сам он без помощи
            других, исправить их не в состоянии.  Он не хочет,  чтобы другие догадывались о его ранимости, и  
            поэтому считает неразумным проявлять свои привязанности  или быть  чрезмерно откровенным. Сложившиеся 
            отношения он рассматривает как  тягостные путы,  однако,  хотя он и хочет обрести независимость и 
            очистить свой путь от препятствий,  он не желает идти на риск все потерять.  Это обуславливает 
            повышенные,  нетерпеливые реакции, в то время как стремление избавиться от всего этого приводит 
            к значительному беспокойству. Может появиться неспособность сосредоточиться.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-2-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            У тратил жизнестойкость и силу воли, необходимые для борьбы с существующими трудностями. Чувствует, 
            что силы истратил и ничего не добился - но продолжает отстаивать свои позиции и все еще преследует
            свои цели с неистовой энергией.  Из-за  этого ему приходится терпеть невыносимое давление, от
            которого он хочет избавиться, но не может заставить себя принять необходимое решение.  В результате,
            он  оказывается  настолько глубоко "вовлеченным" в проблему, что не может ни посмотреть на нее 
            объективно,  ни  избавиться  от нее -  он  не может оставить ее и чувствует,  что успокоится лишь
            тогда, когда достигнет цели.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-2-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Утратил жизнестойкость,  силу воли,  необходимые для того, чтобы бороться с существующими 
            трудностями,  которые представляются ему как преднамеренное противодействие.  Отстаивает
            свои позиции, однако испытывает невыносимое давление.  Нуждается в сотрудничестве и
            эмоциональной удовлетворенности и чувствует,  что пока их  нет,  он не может сделать
            ничего, что улучшило бы сложившуюся ситуацию.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-2-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Страдает из-за   кажущейся  враждебности  окружения.  Испытывает чувство принуждения и невыносимое 
            давление. Негодует и возмущается требованиями,  предъявляемые к нему,  которые он считает неразумными, 
            однако чувствует себя бессильным повлиять на ситуацию и  неспособным защититься.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-2-4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Разочарование и  несбывшиеся  надежды привели к тревожной неуверенности,  в то время как сомнения
            в том, что в будущем положение будет хоть сколько-нибудь лучше, ведут к тому, что все важные решения 
            откладываются на "потом".  Этот конфликт между  надеждой  и необходимостью  создает значительное 
            давление.  Вместо того чтобы поставить себя перед необходимостью принять важное решение  и тем 
            самым разрешить этот конфликт,  склонен заниматься пустяками, ища в этом средство избавления.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-2-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Положение, в  котором он находится,  кажется ему оскорбительным:  ни в ком он не встречает доверия, 
            расположения к себе и  понимания,  с ним обращаются с унижающим его неуважением.  Считает, что ему 
            отказано в признании,  которое существенно  важно  для  того, чтобы  он  сам мог себя уважать,  
            и что он ничего не может с этим поделать.  Сломлен борьбой с трудностями, которую ему приходиться
            вести в одиночку, не получая никакой поддержки. Чувствует, что он  ничего не добился,  что вместо
            восхищения,  которое ему нужно, он  постоянно встречает непонимание.  Хочет избавиться от этой
            ситуации,  но не хватает силы духа, чтобы принять необходимое решение.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-2-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Испытывает неудовлетворенную потребность объединения  с  людьми, имеющими такие же высокие
            стандарты,  как и он,  хочет выделиться из толпы.  Это вызывает у него значительный стресс,
            однако он остается  верен  своим позициям,  несмотря на недостаток признания. Находит ситуацию 
            неприятной и хотел бы покончить с ней,  но отказывается ради компромиссов жертвовать своими 
            убеждениями.  Неспособен найти выход из этой ситуации,  т.к.  постоянно  откладывает  принятие 
            необходимого решения из-за сомнений,  сумеет ли он дать отпор сопротивлению,  которое вызывает
            это решение.  Нуждается  в признании со стороны других  его желаний и уважении  его взглядов - без
            этого он не может почувствовать себя легко.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-2-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Ищет независимости и свободы от каких-либо  ограничений, поэтому  избегает  всяких  обязанностей и 
            вообще всего,  что могло бы оказаться помехой.  Испытывает значительное давление и  хочет  
            избавиться от него, чтобы иметь возможность получить то, что ему нужно,  однако,  как правило, 
            ему не хватает целеустремленности, для того чтобы преуспеть в этом.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-3-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            В сложившейся ситуации видит  угрозу  и  опасность.  Возмущается  тем,  что то, к чему он так сильно 
            стремится, находится под угрозой,  и впадает в отчаяние из-за бессилия как-то  это  предотвратить, 
            боится, что он вообще всего лишится. Не способен взглянуть на ситуацию объективно,  чрезмерно 
            возбужден и не дает себе  отвлечься  от попыток устранить угрозу,  нависшую над его желаниями.
            Перенапряжен и чувствует себя загнанным  в  угол,  возможно  даже  нервное истощение.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-3-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Страдает из-за  неудовлетворительного состояния каких-либо  близких взаимоотношений.  Считает
            себя неспособным сделать  что-либо, что могло бы вернуть близость и взаимное доверие и чувствует, 
            что  он запутался в этой ситуации.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-3-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Остро переживает кажущуюся враждебностью  окружения.  Чувствует, что он является объектом 
            невыносимого давления и что какая-то сила движет им против его воли.  Негодует и возмущается 
            требованиями,  предъявляемыми к нему,  которые он воспринимает как неразумные,  однако чувствует 
            себя не в силах  повлиять  на  ситуацию  и  неспособным защитить себя самого.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-3-4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится производить хорошее впечатление,  однако сомневается в возможности успеха. Считает, что он
            вправе получать то, на что он  питает надежды. Чувствует себя беспомощным и страдает, когда
            обстоятельства оборачиваются против него.  Даже  возможность  неудач чрезвычайно его огорчает,
            и это может привести к нервному истощению.  Видит в себе жертву, которую ввели в заблуждение и 
            чьим доверием злоупотребляли, и пытается убедить себя, что в его неудаче  достичь признания виноваты 
            другие.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-3-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствителен к внешним раздражителям и жаждет все  испытать все, однако  находит  существующую  
            ситуацию  крайне разочаровывающей.  Нуждается в сочувственном понимании и  в  ощущении 
            безопасности. Страдает от своего явного бессилия достичь целей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-3-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что  его недооценивают,  и находит сложившуюся ситуацию неприятной. Ищет признания и уважения 
            со стороны окружающих, чтобы  это  компенсировало нехватку единомышленников,  с которыми он  мог
            объединиться и упрочить  свою  безопасность.  Самоконтроль  в сфере чувств не позволяет ему отдавать 
            себя, но изоляция, к которой это приводит,  вызывает у него желание сдаться  и  слиться  с другими,
            что  беспокоит его,  поскольку он считает подобные инстинкты слабостью,  которую нужно преодолеть. 
            Хочет,  чтобы в нем видели союзника и восхищались его личными качествами.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-3-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствует, что  попал  в неприятную ситуацию и бессилен как-либо исправить это положение. Рассержен 
            и раздражен, т.к. сомневается, что сможет достичь своих целей, и фрустрирован почти до состояния 
            нервного истощения.  Стремится избавиться от этой ситуации и  обрести свободу принимать решения 
            самостоятельно.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-4-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что  жизнь должна давать ему больше,  что его надежды и желания должны каким-то образом 
            претворяться в жизнь - что их исполнение  должно быть ему обеспечено.  Испытываемая неуверенность 
            причиняет ему серьезное беспокойство,  и он напряженно следит  за тем,  чтобы не упустить какую-нибудь 
            возможность. Стремится избегать дальнейших неудач и опасности уронить престиж. Старается позаботиться 
            о  том,  чтобы им не пренебрегали,  очень нуждается в ощущении безопасности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-4-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Подавляет свой  природный  энтузиазм  и художественную натуру из опасения, что они заставят его забыть
            об опасности обмана. Считает,  что его обманули и злоупотребили его доверием, поэтому замкнулся в
            себе и тщательно следит за тем,  чтобы держаться подальше  от других.  Мотивы других людей,  их 
            отношение к нему, подвергает тщательной критической оценке - искренни  ли  они  -  настороженность,
            которая легко превращается в подозрительность и недоверие.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-4-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Существующая ситуация неприятна,  т.к. его потребность объединения с другими,  чьи стандарты были бы такими 
            же высокими,  как  и  его,  остается не удовлетворенной. Стремится выделиться из толпы. Ощущение
            изолированности превращает это стремление в  непреодолимую  побудительную  силу,  которая  тем более 
            неприятна для него, поскольку обычно он контролирует свои желания.  Пытаясь  доказать другим
            уникальность своего характера, он подавляет в себе потребность в других людях и,  чтобы скрыть страх
            неполноценности,  напускает на себя вид беспечной самоуверенности, выражая свое признание тем,  кто
            критикует  его  поведение.  Однако  за  напускным  безразличием скрывается жажда одобрения и успеха 
            у окружающих.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-4-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Несбывшиеся надежды привели к неуверенности и напряженной настороженности.  Требует свободы действия и 
            отвергает какой бы то  ни  было контроль,  кроме самоконтроля. Не желает в чем-либо себе отказывать 
            и требует гарантии безопасности от дальнейших неудач или  потери положения и престижа. Сомневается в 
            том, что в будущем положение будет хоть сколько-нибудь лучше,  и эта отрицательная установка  приводит 
            к тому,  что он лишь повышает свои притязания и  отказывается от разумных компромиссов.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-5-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Получает наслаждение от утонченного, изящного, изысканного, однако сохраняет скептицизм и не позволяет
            себе увлечься, пока полностью  не убедится в том,  что ощущение неподдельно и целостно. Держит эмоции
            под строгим контролем. Требует от других искренности,  что  служит ему защитой от его собственной
            тенденции к излишней доверчивости.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-5-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Существуют какие-то отношения, которые его не удовлетворяют, однако он чувствует, что без помощи
            и сотрудничества их изменить не  в состоянии. Потребность в понимании, в благожелательных взаимных 
            уступках остается неудовлетворенной.  Это не только угнетает его,  но  и  делает его раздражительным 
            и нетерпеливым,  вызывая беспокойство и стремление избавиться от этой ситуации,  если не на деле, то
            хотя бы мысленно. Может появиться неспособность сосредоточиться.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-5-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствителен к внешним раздражителям и жаждет все испытать,  однако находит существующую ситуацию
            крайне разочаровывающей. Страдает от своего явного бессилия и невозможности достичь целей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-5-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Хочет найти партнера,  с которым возможна жизнь в атмосфере полного  спокойствия,  однако 
            компульсивное желание демонстрировать свою индивидуальность заставляет его вырабатывать у с
            ебя  отношение критическое и взыскательное.  Это вносит в отношения разлад и  вызывает чередование 
            периода сближения и отдаления,  так  что  то  идеальное состояние,  к которому он стремится, не
            может стать реальностью. Несмотря на стремление удовлетворять свои естественные  желания,  он 
            сам накладывает на свои инстинкты значительное ограничение, полагая, что это доказывает его 
            превосходство и поднимает его над толпой.  Проницателен, критичен и взыскателен, разборчив, 
            имеет  тонкий  вкус.  Эти  качества  в  сочетании   с   его склонностью к независимым воззрением,  
            позволяют ему давать вещам  самостоятельную оценку и авторитетно высказывать  свои  суждения. 
            Оригинальное,  искусное  и утонченное доставляет ему наслаждение,  он стремится объединиться 
            с другими людьми, имеющими сходные вкусы, которые могли бы способствовать его интеллектуальному 
            расцвету. Хочет получать восхищение и уважение.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-5-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится к откровенным взаимоотношениям, основанным на взаимном  доверии и понимании.  Хочет действовать 
            только в согласии со своими убеждениями.  Требует свободы принимать решения самостоятельно, без
            всякого вмешательства извне и без необходимости  идти  на компромиссы.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-6-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствителен и  отзывчив на мягкость и тонкость чувства,  жаждет  мистического слияния,  эротической 
            гармонии.  Однако, это желание остается неудовлетворенным из-за отсутствия подходящего для этого 
            партнера или благоприятных обстоятельств. Держит свои эмоциональные отношения под строгим контролем.
            Эстет; обладает воспитанным  и утонченным вкусом,  позволяющим ему  формулировать  и  выражать  свои 
            пристрастия и мнения,  особенно, в области искусства и художественного творчества.  Стремится
            объединиться с теми, кто может  способствовать его интеллектуальному или художественному росту.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-6-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Хочет свободно следовать своим убеждениям и принципам, хочет добиться  уважения  к  себе как к
            личности.  Стремится использовать  каждую возможность не подчиняться никаким ограничениям и 
            запретам.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-7-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Хочет преодолеть ощущение пустоты и  изолированности.  Полагает, что  жизнь  может  предложить
            еще многое и что он может упустить  свой шанс в жизни,  если не будет наилучшим образом  использовать 
            каждый случай. Поэтому он преследует свои цели с неистовой напряженностью и легко становится "глубоко
            вовлеченным".  Чем бы он не занимался, считает себя вполне компетентным в этой области, иногда его 
            находят надоедливым и назойливым.
        </Paragraph>
    </Section>

    <Section 
        x:Key="-7-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чувствителен и впечатлителен, стремится к захватывающим увлечениям.  Ищет идеализированной и еще пока 
            не осуществленной ситуации, в  которой он мог бы разделить с другим отношения полной гармонии и 
            глубокого взаимопонимания.  Считает,  что если он будет слишком  легко доверять другим,  то появится 
            риск, что они будут использовать его в своих интересах,  поэтому он требует доказательств  их 
            искренности. В своих взаимоотношениях должен точно знать на каком он "месте".
        </Paragraph>
    </Section>

    <Section 
        x:Key="-7-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Сопротивляется какому бы то ни было давлению со стороны других и требует личной независимости. 
            Стремится сам, без чьего-либо вмешательства искать и принимать решения.  Ненавидит  однообразие 
            и  заурядность.  Поскольку  он  хочет,  чтобы в нем видели человека, мнение которого является
            авторитетным, ему трудно признавать свою  -неправоту, иногда он лишь с неохотой принимает и с 
            трудом понимает точку зрения другого.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+0-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Тревога и  беспокойное недовольство - либо обстоятельствами, либо неудовлетворенными эмоциональными 
            потребностями - вызвали  напряжение и стресс.  Способ спасения видит в том,  чтобы не позволить 
            себе "стать вовлеченным", создавая тем самым хотя бы внешне подобие покоя.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+0-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Неудачные попытки упрочить  свое  положение  в  соответствии  его собственным  высоким мнением о 
            своих достоинствах,  в сочетании с  непрерывным усилием "показать себя",  не имея к тому 
            достаточных  оснований  -  привели к значительному стрессу.  Пытается избежать  чрезмерных 
            запросов при своих ограниченных возможностях,  вырабатывая защитную установку - он отказывается,
            брать на себя какую бы то было ответственность или быть "вовлеченным".
        </Paragraph>
    </Section>

    <Section 
        x:Key="+0-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Истощение жизненной энергии сделало непереносимым дальнейшее возбуждение и какие-либо требования
            к нему.  Ощущение бессилия вызывает у него беспокойство,  раздражение причиняет острую боль,
            от  которой он пытается избавиться,  отказываясь от какого-либо дальнейшего прямого участия. 
            Он ограничивает себя, надеясь при помощи  осторожности, в конце концов, добиться своего.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+0-4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Разочарование тем,  что его надежды не сбылись и страх, что новые цели приведут только к новым 
            неудачам, породили значительную тревогу. Он пытается избавиться от нее, замыкаясь в себе и ведя
            себя  осмотрительно. Угрюм и подавлен.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+0-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Испытывает потребность  как-то защитить себя от своей тенденции к  чрезмерной доверчивости, т.к. он
            находит, что другие часто неправильно  ее  истолковывают или же используют в своих интересах.  
            В результате,  он вырабатывает у себя отношение критическое и высокомерное  и  готов "участвовать" 
            лишь тогда,  когда имеет возможность убедиться в искренности и надежности других.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+0-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Неудовлетворенное желание  добиться  уважения,  выделяться  среди  других, вызывает у него
            некоторую тревогу. В результате он подавляет  в  себе  нормальную общительность и не позволяет 
            себе стать "вовлеченным" или участвовать с другими в их обычной деятельности. Боится, что  если 
            он не оградит себя от внешних воздействий,  его независимость окажется под угрозой или будет  
            сильно  ограничена. Hе хочет, чтобы его беспокоили.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится достичь устойчивого и спокойного состояния,  в  котором  .он мог бы освободиться от
            тревоги и опасений.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Напряжение и стремление,  вызванное попыткой справиться с некоторыми обстоятельствами,  которые
            на самом деле ему не по силам или не по способностям, вызвали значительную тревогу и ощущение 
            своей  неполноценности,  в которой он не признается.  Стремится спастись  от этого в какой-либо
            более спокойной и свободной от проблем обстановке, в которой бы ему больше не пришлось отстаивать
            свои права или сопротивляться такому сильному давлению.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Истощение жизненной  энергии сделало невыносимым какое-либо дальнейшее возбуждение или какие-либо 
            требования  к  нему.  Ощущение бессилия  вызывает  у  него беспокойство и причиняет острую боль. 
            Пытается найти спасение от всего этого в отказе от борьбы и в поисках доброжелательной,  спокойной 
            и мирной обстановки, в которой  он мог бы прийти в себя.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1-4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Разочарование из-за  несбывшихся  надежд и страх,  что новые цели  приведут только к новым неудачам,  
            вызвали значительную  тревогу. Он пытается найти спасение от этого в спокойных и гармоничных 
            отношениях,  которые оградили бы его от разочарований и  недостатка  признания.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Испытывает потребность как-то защититься от  своей  склонности  к чрезмерной доверчивости, т.к. находит, 
            что другие зачастую неправильно истолковывают ее или используют в своих интересах. Поэтому 
            он ищет отношений,  в которых бы царила спокойная и сочувственная  близость и каждый доподлинно 
            знал позицию другого.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Хочет, чтобы его ценили и уважали, и пытается найти это в близком  и спокойном общении, основанном на 
            взаимном уважении.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+1-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hе хочет  быть  втянутым  в  разногласия из-за каких-либо мнений,  воззрений или суждений, 
            предпочитая, чтобы его оставили в покое.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Опасается, что ему могут помешать добиться того,  что он хочет, и поэтому он требует, чтобы 
            другие признали его права.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Тревога и  беспокойное  недовольство либо обстоятельствами,  либо неудовлетворенными эмоциональными 
            потребностями,  вызвали стресс. Он  пытается  избавиться  от  тревоги,  отрицая ее существование, 
            скрывая свою неудовлетворенность за гордым,  но неадекватным  утверждением своей независимости и 
            самостоятельности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Истощение жизненной  энергии  сделало  непереносимым   какое-либо  дальнейшее возбуждение или какие-либо 
            требования к нему. Это ощущение бессилия в сочетании с разочарованностью тем, что он не может
            управлять ходом событий,  вызывает в нем возбуждение, раздражение и острую боль.  Он пытается  
            избавиться  от  этого,  упрямо  отстаивая свою точку зрения, однако общее состояние беспомощности  
            часто определяет безуспешность этих попыток.  Поэтому он чувствителен к критике и обидчив.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2-4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Разочарование и опасение,  что определять какие-либо  новые  цели было бы бессмысленно, породили 
            тревогу. Жаждет признания и уважения,  однако ожидающее его будущее вызывает у него  беспокойство.
            Реагирует на него протестом против какой-либо критики и сопротивляется всякой попытке повлиять 
            на него.  Стремясь  укрепить  свою позицию, пытается утвердить себя, педантично применяя свою власть 
            в мелочах.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Старается укрепить  свою позицию и повысить чувство самоуважения,  анализируя свои достижения и достижения
            других людей критически и  с научной тщательностью.  Требует,  чтобы все было ясно и недвусмысленно.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чтобы повысить самоуважение и ощутить свою ценность как личность, ему нужно,  чтобы другие ценили его 
            и уважали как  исключительную  личность. Протестует против посредственности, ставит себе высокие 
            стандарты.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+2-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Оказывает сильное сопротивление внешним воздействиям и каким-либо  посягательствам на его свободу
            самостоятельно принимать решения и  строить планы. Старается создать положение себе и упрочить его. 
            Опасение, что ему могут помешать добиться  того,  что  он  хочет,  заставляет его действовать с 
            лихорадочной напряженностью.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Напряжённая активность, направленная на достижение цели в связи с опасением препятствий и помех в 
            её осуществлении.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Тревога и беспокойное недовольство,  вызванные либо обстоятельствами,  либо неудовлетворенными 
            эмоциональными потребностями, привели к стрессу. Он пытается избавиться от него посредством напряженной 
            деятельности,  направленной  либо  на достижение  личного  успеха, либо на приобретение  опыта.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hапряжение, вызванное попыткой справиться с обстоятельствами, которые на самом деле выше его сил и
            возможностей,  породило значительную тревогу и ощущение неполноценности, в которых он не признается.
            Он пытается избавиться от этого,  отдаваясь  напряженной  деятельности  и требуя право действовать 
            по-своему.  Неправильный  самоконтроль может привести к неуправляемым вспышкам гнева.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3-4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Разочарование и  боязнь,  что определять какие-либо новые цели не  имеет смысла, вызвали стресс и тревогу. 
            Он хочет близкого общения  с другими людьми и возможность свободно развиваться,  но чувствует,  
            что его взаимоотношения пусты,  а его развитию препятствуют. Он реагирует на это рьяной, напряженной 
            деятельностью, направленной на то, чтобы любой ценой достичь своих целей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Хочет действовать свободно и беспрепятственно,  однако, ограничен  своей потребностью все возводить 
            на рациональную,  твердую, четко определенную основу.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Действие доставляет ему удовольствие и он хочет,  чтобы за личные достижения его уважали и почитали.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+3-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Борется с ограничениями и запретами и  требует,  чтобы  ему  дали  возможность развиваться свободно.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Боязнь, что ему могут помешать достичь того,  чего он хочет, приводит  его к беспокойным поискам ощущения
            удовлетворенности в какой-либо "иллюзорной" или бессмысленной деятельности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Тревога и беспокойное  недовольство, либо обстоятельствами,  либо неудовлетворенными эмоциональными 
            потребностями, вызвали  стресс. Чувствует себя непонятым,  сбитым с толку,  испытывает беспокойство.
            Это заставляет его искать новых условий или новых взаимоотношений в надежде, что они смогут принести 
            ему большую удовлетворенность и душевный покой.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hапряжение, вызванное попыткой одолеть обстоятельства, которые на самом деле выше его сил  и  
            возможностей,  породило  значительную  тревогу  и ощущение неполноценности,  в котором он не
            признается.  Он реагирует на это поисками внешних доказательств своих  способностей и своей
            ценности, чтобы повысить самоуважение.  Склонен обвинять других - это позволяет ему снимать вину 
            с себя. Беспокойно  ищет  выход  и склонен к компульсивным поражениям и компульсивным  желаниям.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Возбужденность, непредсказуемость поведения и раздражительность в  сочетании с истощением жизненной 
            энергии и  неприятием каких-либо требований  к  нему - все это вместе поставило бы в положение,  
            в котором он чувствует, что сложившиеся обстоятельства таят для него какую-то угрозу.  Ощущая 
            свое бессилие,  он отчаянно надеется,  что какое-нибудь решение натолкнет его на путь к спасению.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Яростно критикует существующие условия,  которые,  по его мнению, недостаточно определенны или оказывают 
            дезорганизующее  воздействие.  Поэтому  ищет какого-нибудь выхода,  который принес бы ясность в эту
            ситуацию и сделал бы ее более упорядоченной  и  методичной.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится добиться признания и уважения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+4-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Считает, что его рост ограничивают и стесняют,  ищет  какого-либо выхода, который бы избавил его от
            этих ограничений.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+5-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Опасается, что ему могут помешать достичь того, что он хочет. Это заставляет  его,  когда  он  имеет
            дело с другими,  пускать в ход  сильное личное обаяние, надеясь, так будет легче достичь целей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+5-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Тревога и беспокойное недовольство,  вызванное либо обстоятельствами,  либо неудовлетворенными
            эмоциональными потребностями, вызвали значительный стресс.  Он пытается искать спасение от этого 
            в идеализированной обстановке сочувствия и понимания  или в замещающей ее атмосфере эстетизма и 
            красоты.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+5-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Hапряжение, вызванное попыткой справиться с обстоятельствами, которые на самом деле выше его сил
            и возможностей,  породило значительную тревогу и ощущение неполноценности, в которых он не 
            признается.  Он хочет спастись от этого бегством в искусственный мир,  в котором вещи больше 
            приближаются к тому,  какими бы он хотел их  видеть.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+5-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Упадок жизненных  сил сделал непереносимыми какое-либо дальнейшее возбуждение или какие-либо
            требования к нему.  Он хочет  спастись  от этого бегством, в иллюзорный искусственный мир, 
            в котором вещи  больше приближаются к тому, какими бы он хотел их видеть.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+5-4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Разочарование и  боязнь,  что определять какие-либо новые цели не имеет смысла,  вызвали тревогу 
            и он страдает  от  недостатка  каких-либо близких и сочувственных отношений.  Он пытается спастись
            от этого бегством в искусственный мир,  в котором эти разочарования отсутствуют и вещи более 
            приближаются к тому, какими он хотел бы их видеть.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+5-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Все необычное, оригинальное, в том числе люди с какими-либо выдающимися свойствами, производят на него
            сильное впечатление. Пытается перенимать те качества, которыми он восхищается, и проявляет 
            оригинальность своей собственной личности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+5-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Стремится избегать критики, предотвращать ограничения свободы самостоятельно определять свою судьбу. Когда 
            он имеет дело с другими, пускает в ход личное обаяние.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+6-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Страх, что ему могут помешать достичь  желаемого,  усиливает  его потребность  в  безопасности и
            свободе от конфликтов.  Поэтому он  ищет устойчивости и окружения,  в котором он мог бы 
            расслабиться.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+6-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Тревога и  беспокойное недовольство - либо обстоятельствами, либо  неудовлетворенными эмоциональными 
            потребностями - вызвали  значительный  стресс.  Он пытается искать спасение от этого в безопасной,  
            свободной от конфликтов ситуации, в которой он мог бы  расслабиться и прийти в норму.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+6-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Напряжение, вызванное попыткой справиться с обстоятельствами, которые на самом деле выше его сил и 
            возможностей,  породило значительную тревогу и ощущение неполноценности, в котором он не
            признается.  Он  пытается  искать  от  этого  спасения  в  обстановке  устойчивости и безопасности, 
            в которой он мог бы расслабиться  и  прийти в себя.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+6-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Истощение жизненной  энергии  сделало  непереносимым   какое-либо дальнейшее возбуждение или какие-либо
            требования к нему. Это ощущение бессилия,  в сочетании с разочарованностью тем,  что он  не может
            управлять ходом событий,  вызывает в нем возбуждение, раздражение и острую боль. Он пытается искать 
            спасение от этого в обстановке устойчивости и безопасности, в которой он мог бы расслабиться и прийти 
            в себя.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+6-4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Разочарование и  страх,  что  определять какие-либо новые цели не  имеет смысла,  вызвали тревогу;  
            он страдает  от  недостатка  каких-либо  близких и сочувственных отношений или от того,  что его 
            не понимают и недооценивают.  Он пытается найти спасение в обстановке устойчивости, в которой он
            мог бы расслабиться и почувствовать себя более удовлетворенным.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+6-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Хочет быть  гарантированным  от критики или конфликтов и укрепить положение устойчивости и безопасности, 
            однако сам склонен  относиться к окружающим критически, ему трудно угодить.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+6-7"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Ищет безопасности и такого положения,  в котором бы его больше не беспокоили никакими требованиями.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Боязнь, что ему могут помешать добиться того, чего он хочет, заставляет его категорически отрицать, 
            что хоть одна из сторон жизни  представляет  собой  какую-нибудь  ценность.  Это  разрушительное 
            глумление становится для него средством скрыть свою беспомощность  и глубокое чувство разочарования.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Тревога и  беспокойное недовольство - либо обстоятельствами, либо эмоциональными потребностями - вызвали 
            значительный  стресс.  Он  реагирует  на это,  объясняя все отсутствием понимания со стороны 
            окружающих и вырабатывая у себя отношение презрительное и вызывающее.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Напряжение, вызванное попыткой справиться с обстоятельствами, которые на самом деле выше его сил 
            и возможностей,  породило значительную тревогу и ощущение неполноценности, в котором он не
            признается.  Неспособность  как-то  укрепить свою волю заставляет его  переходить все границы и
            во всех своих неудачах винить окружающих.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Истощение жизненной   энергии  сделало  непереносимым  какое-либо  дальнейшее возбуждение или
            какие-либо требования к нему. Он хочет  спастись от этого бегством в иллюзорный, искусственный мир,
            в котором вещи больше приближаются к тому,  какими бы он хотел их видеть. Он уверен, что из него 
            сделали жертву, он требует с негодованием,  возмущением  и  вызовом,  чтобы  ему  дали   возможность 
            поступать по-своему.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7-4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Разочарование и боязнь,  что определять какие-либо новые цели  не имеет смысла, вызвали тревогу,
            ощущение пустоты и презрения к самому себе,  в которых он не признается.  Его отказ признать  это,  
            заставляет его выработать своевольное и вызывающее отношение.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7-5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Его природная способность ко всему подходить с критических  позиций  приняла  искаженную форму и 
            превратилась в резкое критиканство; он все отрицает и надо всем глумится, несмотря на факты.
        </Paragraph>
    </Section>

    <Section 
        x:Key="+7-6"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Потребность в уважении, которое позволило бы ему играть какую-нибудь выдающуюся роль и сделать
            себе имя,  стала императивной.  Он требует,  чтобы он был центром внимания, отказывается играть 
            второстепенную роль.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\МДУ_Элерс.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Диагностика мотивации к достижению успеха</sys:String>

    <Paragraph 
        x:Key="Мотивация к достижению успеха - Низкий уровень"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Мотивация к достижению успеха»:</Bold> уровень -
        <Run 
            Text="{Binding Mode=OneWay}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Мотивация к достижению успеха - Признаки тенденции"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Мотивация к достижению успеха»:</Bold> уровень -
        <Run 
            Text="{Binding Mode=OneWay}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Мотивация к достижению успеха - Признаки повышенной склонности"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Мотивация к достижению успеха»:</Bold> уровень -
        <Run 
            Text="{Binding Mode=OneWay}" 
            FontWeight="Bold" 
            />
        Люди, имеющие умеренно высокий уровень мотивации к достижению успеха, предпочитают средний уровень риска. 
        Те же, кто боится неудач, предпочитают малый или, наоборот, слишком большой уровень риска. Чем выше 
        мотивация человека к успеху - достижению цели, тем ниже готовность к риску. При этом мотивация к успеху 
        влияет и на надежду на успех: при сильной мотивации к успеху надежды на успех обычно скромнее, чем при 
        слабой мотивации к успеху. К тому же людям, мотивированным на успех и имеющим большие надежды на него, 
        свойственно избегать высокого риска.
    </Paragraph>

    <Paragraph 
        x:Key="Мотивация к достижению успеха - Признаки высокой вероятности"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Мотивация к достижению успеха»:</Bold> уровень -
        <Run 
            Text="{Binding Mode=OneWay}" 
            FontWeight="Bold" 
            />
        Люди, имеющие слишком высокий уровень мотивации к достижению успеха, имеют высокую готовность к риску, 
        но реже попадают в несчастные случаи, чем те, которые имеют высокую готовность к риску, но высокую 
        мотивацию к избеганию неудач (защиту). И наоборот, когда у человека имеется высокая мотивация к 
        избеганию неудач (защита), то это препятствует мотиву к успеху - достижению цели.
    </Paragraph>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Оценка_нервно_психической_устойчивости_НПУ.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Оценка нервно-психической устойчивости</sys:String>

    <Section 
        x:Key="Неискренность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Неискренность» (от 0 до 15):</Bold> -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
        </Paragraph>
    </Section>

    <Section 
        x:Key="Нервно-психическая устойчивость - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Нервно-психическая устойчивость»: степень - НИЗКАЯ</Bold>
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Высокая вероятность нервно-психических срывов. Необходимо дополнительное медобследование психиатра, невропатолога или психолога. Прогноз – неблагоприятный.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Нервно-психическая устойчивость - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Нервно-психическая устойчивость»: степень - СРЕДНЯЯ</Bold>
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Нервно-психические срывы вероятны, особенно в экстремальных условиях.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Нервно-психическая устойчивость - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Шкала «Нервно-психическая устойчивость»: степень - ВЫСОКАЯ</Bold>
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Нервно-психические срывы маловероятны. Прогноз – благоприятный.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Потребность_в_достижении.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="Low"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>
                Низкий уровень (<TextBlock Text="{Binding}" /> стен).
            </Bold>
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            У Вас не очень высокий уровень потребности достижений. Вы придерживаетесь точки зрения, что
            слишком трудно достичь успеха в любом начатом деле. В своей работе Вы предпочитаете использовать
            проверенные методы, дающие не лучший, но надёжный результат. Вы, очевидно, испытываете чувство
            удовлетворения, когда успех пришёл легко. Вам явно не хватает целеустремлённости, настойчивости,
            работоспособности. Это может отрицательно сказываться на Ваших деловых, организаторских качествах.
            В то же время Вам присущи многие другие положительные качества, такие как мягкость, доброта, 
            чувствительность. Вы способны сопереживать, сочувствовать. Hо если Вы хотите достигнуть успеха в 
            работе, Вам следует быть более настойчивым в достижении своих целей, не удовлетворяться достигнутым.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Normal"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>
                Удовлетворительный  уровень (<TextBlock Text="{Binding}" /> стен).
            </Bold>
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            У Вас удовлетворительный уровень потребности достижений. Вам не откажешь в упорстве, целеустремлённости,
            хотя нельзя сказать, что Вы полностью подчиняете свои интересы стремлению достичь цели. Вы можете
            успешно справляться с работой, не требующей значительных организаторских усилий. Иногда Вам даже может 
            не хватать настойчивости. Вы привыкли рассчитывать скорее на себя, чем на поддержку коллектива. У Вас
            не силён дух соперничества. Вас больше привлекает спокойная, размеренная деятельность.
        </Paragraph>
    </Section>

    <Section 
        x:Key="High"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>
                Высокий уровень (<TextBlock Text="{Binding}" /> стен).
            </Bold>
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            У Вас достаточно высокий уровень потребности достижений. Вы можете успешно справляться с большей частью
            организационной и руководящей деятельности. Вы целеустремлённы и настойчивы. Вам присуща потребность
            изобретать новые приёмы работы в исполнении самых обычных дел, творческое отношения к любому виду
            деятельности. Вы хорошо работаете в коллективе, готовы принять помощь и помогать другим, чтобы совместно
            испытать удовлетворение от конечного результата деятельности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="VeryHigh"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>
                Очень высокий уровень (<TextBlock Text="{Binding}" /> стен).
            </Bold>
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            У Вас очень высокий уровень потребности достижений. Это характеризует Вас как отличного руководителя и 
            организатора. Вы крайне настойчивы в достижении своих целей, никогда не удовлетворяетесь достигнутым, 
            постоянно стремитесь улучшить своё социальное и материальное положение. Вас не удовлетворяет легкий 
            успех. Вы не боитесь трудных задач. Вам доставляет удовольствие преодолевать их. Вы хорошо работаете в
            коллективе, но Ваше чрезмерное стремление к успеху может вызывать негативное отношение со стороны Ваших 
            коллег и приводить к некоторой напряжённости, отрицательно сказываться на других сторонах Вашей жизни.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Рокич.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="Terminal-Title"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Терминальные ценности.</Bold>
        </Paragraph>
    </Section>

    <Section 
        x:Key="Instrumental-Title"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Инструментальные ценности.</Bold>
        </Paragraph>
    </Section>
    
    <!--Терминальные ценности-->

    <ListItem
        x:Key="Terminal-1"
        x:Shared="False">
        <Paragraph>
            Активная деятельная жизнь (полнота и эмоциональная насыщенность жизни)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-2"
        x:Shared="False">
        <Paragraph>
            Жизненная мудрость (зрелость суждений и здравый смысл, достигаемые жизненным опытом)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-3"
        x:Shared="False">
        <Paragraph>
            Здоровье (физическое и психическое)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-4"
        x:Shared="False">
        <Paragraph>
            Интересная работа
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-5"
        x:Shared="False">
        <Paragraph>
            Красота природы и искусства (переживание прекрасного в природе и в искусстве)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-6"
        x:Shared="False">
        <Paragraph>
            Любовь (духовная и физическая близость с любимым человеком)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-7"
        x:Shared="False">
        <Paragraph>
            Материально обеспеченная жизнь (отсутствие материальных затруднений)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-8"
        x:Shared="False">
        <Paragraph>
            Наличие хороших и верных друзей
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-9"
        x:Shared="False">
        <Paragraph>
            Общественное признание (уважение окружающих, коллектива, товарищей по работе)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-10"
        x:Shared="False">
        <Paragraph>
            Познание (возможность расширения своего образования, кругозора, общей культуры, интеллектуальное развитие)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-11"
        x:Shared="False">
        <Paragraph>
            Продуктивная жизнь (максимально полное использование своих возможностей, сил и способностей)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-12"
        x:Shared="False">
        <Paragraph>
            Развитие (работа над собой, постоянное физическое и духовное совершенствование)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-13"
        x:Shared="False">
        <Paragraph>
            Развлечения (приятное, необременительное времяпрепровождение, отсутствие обязанностей)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-14"
        x:Shared="False">
        <Paragraph>
            Свобода (самостоятельность, независимость в суждениях поступках)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-15"
        x:Shared="False">
        <Paragraph>
            Счастливая семейная жизнь
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-16"
        x:Shared="False">
        <Paragraph>
            Счастье других (благосостояние, развитие и совершенствование других людей, всего народа, человечества в целом)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-17"
        x:Shared="False">
        <Paragraph>
            Творчество (возможность творческой деятельности)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Terminal-18"
        x:Shared="False">
        <Paragraph>
            Уверенность в себе (внутренняя гармония, свобода от внутренних противоречий, сомнений)
        </Paragraph>
    </ListItem>

    <!--Инструментальные ценности-->
    
    <ListItem
        x:Key="Instrumental-1"
        x:Shared="False">
        <Paragraph>
            Аккуратность (чистоплотность), умение содержать в порядке вещи, порядок в делах
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-2"
        x:Shared="False">
        <Paragraph>
            Воспитанность (хорошие манеры)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-3"
        x:Shared="False">
        <Paragraph>
            Высокие запросы (высокие требования к жизни и высокие притязания)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-4"
        x:Shared="False">
        <Paragraph>
            Жизнерадостность (чувство юмора)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-5"
        x:Shared="False">
        <Paragraph>
            Исполнительность (дисциплинированность)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-6"
        x:Shared="False">
        <Paragraph>
            Независимость (способность действовать самостоятельно, решительно)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-7"
        x:Shared="False">
        <Paragraph>
            Непримиримость к недостаткам в себе и других
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-8"
        x:Shared="False">
        <Paragraph>
            Образованность (широта знаний, высокая общая культура)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-9"
        x:Shared="False">
        <Paragraph>
            Ответственность (чувство долга, умение держать свое слово)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-10"
        x:Shared="False">
        <Paragraph>
            Рационализм (умение здраво и логично мыслить, принимать обдуманные, рациональные решения)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-11"
        x:Shared="False">
        <Paragraph>
            Самоконтроль (сдержанность, самодисциплина)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-12"
        x:Shared="False">
        <Paragraph>
            Смелость в отстаиваниях своего мнения, взглядов
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-13"
        x:Shared="False">
        <Paragraph>
            Твердая воля (умение настоять на своем, не отступать перед трудностями)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-14"
        x:Shared="False">
        <Paragraph>
            Терпимость (к взглядам и мнениям других, умение прощать другим их ошибки и заблуждения)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-15"
        x:Shared="False">
        <Paragraph>
            Широта взглядов (умение понять чужую точку зрения, уважать иные вкусы, обычаи, привычки)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-16"
        x:Shared="False">
        <Paragraph>
            Честность (правдивость, искренность)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-17"
        x:Shared="False">
        <Paragraph>
            Эффективность в делах (трудолюбие, продуктивность в работе)
        </Paragraph>
    </ListItem>

    <ListItem
        x:Key="Instrumental-18"
        x:Shared="False">
        <Paragraph>
            Чуткость (заботливость)
        </Paragraph>
    </ListItem>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\САН.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <!--Низкий уровень-->
    
    <Section 
        x:Key="Самочувствие-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Самочувствие.</Bold> Сегодня у Вас плохое самочувствие. Вы утомлены. Необходим отдых.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Активность-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Активность.</Bold> Сегодня у Вас низкая активность. Избегайте физических нагрузок. Будьте осторожны.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Настроение-0"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Настроение.</Bold> Сегодня Вы недовольны собой и окружением. Вам лучше ограничить контакты с людьми и воздержаться от принятия решений.
        </Paragraph>
    </Section>

    <!--Средний уровень-->
    
    <Section 
        x:Key="Самочувствие-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Самочувствие.</Bold> Сегодня у Вас удовлетворительное самочувствие.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Активность-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Активность.</Bold> Сегодня Вы достаточно активны, но, тем не менее, лучше воздержаться от 
            интенсивных и длительных нагрузок.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Настроение-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Настроение.</Bold> Сегодня Вы достаточно уравновешены.
        </Paragraph>
    </Section>
    
    <!--Нормальный уровень-->

    <Section 
        x:Key="Самочувствие-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Самочувствие.</Bold> Сегодня у Вас нормальное самочувствие. Вы бодры и полны сил.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Активность-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Активность.</Bold> Сегодня у Вас нормальная активность. Вы готовы к действиям.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Настроение-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Настроение.</Bold> Сегодня Вы смотрите на мир реалистично и с оптимизмом.
        </Paragraph>
    </Section>

    <!--Высокий уровень-->
    
    <Section 
        x:Key="Самочувствие-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Самочувствие.</Bold> Сегодня у Вас отличное самочувствие. У Вас сегодня может получиться почти всё.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Активность-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Активность.</Bold> Сегодня у Вас высокая активность. Вы собранны и увлечены предстоящими делами.
            Хороший темп мышления.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Настроение-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Настроение.</Bold> Сегодня у Вас прекрасное настроение. Вы можете увлечь других людей своим 
            задором и настроением.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Remark"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Для составления полной интерпретации по результатам теста следует учитывать соотношение значений 
            баллов по всем шкалам.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Склонность_к_зависимому_поведению.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Склонность к зависимому поведению</sys:String>

    <Paragraph 
        x:Key="Наркозависимость"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Наркозависимость»:</Bold> уровень -
        <Run 
            Text="{Binding Mode=OneWay}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Алкогольная зависимость - Низкий уровень"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Алкогольная зависимость»:</Bold> уровень -
        <Run 
            Text="{Binding Mode=OneWay}" 
            FontWeight="Bold" 
            />
    </Paragraph>

    <Paragraph 
        x:Key="Алкогольная зависимость - Признаки тенденции"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Алкогольная зависимость»:</Bold> уровень -
        <Run 
            Text="{Binding Mode=OneWay}" 
            FontWeight="Bold" 
            />
        Испытуемые, отнесённые к данной группе, имеют склонность к зависимому поведению средней выраженности. 
        Это говорит о том, что у испытуемых при определённых социальных условиях имеется риск развития 
        алкогольной зависимости.
    </Paragraph>

    <Paragraph 
        x:Key="Алкогольная зависимость - Признаки повышенной склонности"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Алкогольная зависимость»:</Bold> уровень -
        <Run 
            Text="{Binding Mode=OneWay}" 
            FontWeight="Bold" 
            />
        У испытуемых данной группы склонность к зависимому поведению выше средней, то есть у них преобладают 
        те качества, которые в большей степени свидетельствуют об их направленности на употребление спиртных
        напитков и риск развития психологической зависимости от алкоголя.
    </Paragraph>

    <Paragraph 
        x:Key="Алкогольная зависимость - Признаки высокой вероятности"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Алкогольная зависимость»:</Bold> уровень -
        <Run 
            Text="{Binding Mode=OneWay}" 
            FontWeight="Bold" 
            />
        Данная категория характеризуется высокой склонностью к зависимому поведению. У испытуемых наблюдается 
        высокая направленность на употребление спиртных напитков, позитивное отношение к зависимости и черты
        личности, которые значительно увеличивают риск зависимого алкогольного проблемного поведения.
    </Paragraph>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Склонность_к_риску_Шуберт.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Методика диагностики степени готовности (склонности) к риску</sys:String>

    <Paragraph 
        x:Key="Склонность к риску - Слишком низкий"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Склонность к риску»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Испытуемый слишком осторожен.
    </Paragraph>

    <Paragraph 
        x:Key="Склонность к риску - Низкий"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Склонность к риску»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Испытуемый осторожен.
    </Paragraph>

    <Paragraph 
        x:Key="Склонность к риску - Средний"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Склонность к риску»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Испытуемому свойственно нормальное ситуативное поведение.
    </Paragraph>

    <Paragraph 
        x:Key="Склонность к риску - Умеренный"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Склонность к риску»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Испытуемый склонен к риску.
    </Paragraph>

    <Paragraph 
        x:Key="Склонность к риску - Высокий"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Склонность к риску»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Испытуемый склонен к безудержному риску.
    </Paragraph>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\СМОЛ.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Cокращённый многопрофильный опросник личности</sys:String>

    <Paragraph 
        x:Key="Lie"
        x:Shared="False"
        TextIndent="20">
        Умышленное искажение результатов обследования. Стремление приукрасить свой 
        облик. Необходимо повторить обследование.
    </Paragraph>

    <Paragraph 
        x:Key="Secrecy"
        x:Shared="False"
        TextIndent="20">
        Неоткровенность, выраженная защита. Желательно повторное или дополнительное обследование. 
        Диссимуляция. Отрицание психологических трудностей, тревоги и внутреннего напряжения.
    </Paragraph>

    <Paragraph 
        x:Key="Incrimination"
        x:Shared="False"
        TextIndent="20">
        Стремление наговорить на себя, исказить результаты обследования, акцентировать тяжесть состояния, 
        или аггравация имеющихся симптомов (симуляция), либо трудности в осмыслении содержания утверждений 
        методики. Дезинтеграция психических процессов.
    </Paragraph>

    <Paragraph 
        x:Key="NoResults"
        x:Shared="False"
        TextIndent="20">
        Рзультаты по всем шкалам теста в норме.
    </Paragraph>

    <Paragraph 
        x:Key="1 (Hs) - Выше нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Иппохондрия»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Близость испытуемого к астено-невротическому типу. Испытуемые с высокими оценками медлительны, пассивны, 
        принимают всё на веру, покорны власти, медленно приспосабливаются, плохо переносят смену обстановки, 
        легко теряют равновесие в социальных конфликтах.
    </Paragraph>

    <Paragraph 
        x:Key="2 (D) - Выше нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Депрессия »:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Высокие оценки имеют чувствительные, сензитивные лица, склонные к тревогам, робкие, застенчивые. В
        делах они старательны, добросовестны, высокоморальны и обязательны, но неспособны принять 
        решение самостоятельно, нет уверенности в себе, при малейших неудачах они впадают в отчаяние.
    </Paragraph>

    <Paragraph 
        x:Key="3 (Hy) - Выше нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Истерия»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Выявляет лиц, склонных к неврологическим защитным реакциям конверсионного типа. Они используют симптомы 
        соматического заболевания как средство избегания ответственности. Все проблемы разрешаются уходом в 
        болезнь. Главными особенностями таких людей являются стремление казаться больше, значительнее, чем есть 
        на самом деле, стремление обратить на себя внимание во что бы то ни стало, жажда восхищения. Чувства 
        таких людей поверхностны и интересы неглубоки.
    </Paragraph>

    <Paragraph 
        x:Key="4 (Pd) - Выше нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Психопатия»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Высокие оценки по этой шкале свидетельствуют о социальной дезадаптации. Такие люди агрессивны, конфликтны, 
        пренебрегают социальными нормами и ценностями. Настроение у них неустойчивое, они обидчивы, возбудимы и 
        чувствительны. Возможен временный подъём по этой шкале, вызванный какой-нибудь причиной.
    </Paragraph>

    <Paragraph 
        x:Key="6 (Pa) - Выше нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Паранойяльность»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Основная черта людей с высокими показателями по этой шкале — склонность к формированию сверхценных идей. 
        Это люди односторонние, агрессивные и злопамятные. Кто не согласен с ними, кто думает иначе, тот или глупый 
        человек, или враг. Свои взгляды они активно насаждают, поэтому имеют частые конфликты с окружающими. 
        Собственные малейшие удачи они всегда переоценивают.
    </Paragraph>

    <Paragraph 
        x:Key="7 (Pt) - Выше нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Психостения»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Диагностирует лиц с тревожно-мнительным типом характера, которым свойственны тревожность, боязливость, 
        нерешительность, постоянные сомнения.
    </Paragraph>

    <Paragraph 
        x:Key="8 (Se) - Выше нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Шизоидность»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Лицам с высокими показателями по этой шкале свойствен шизоидный тип поведения. Они способны тонко 
        чувствовать и воспринимать абстрактные образы, но повседневные радости и горести не вызывают у них 
        эмоционального отклика. Таким образом, общей чертой шизоидного типа является сочетание повышенной 
        чувствительности с эмоциональной холодностью и отчуждённостью в межличностных отношениях.
    </Paragraph>

    <Paragraph 
        x:Key="9 (Ma) - Выше нормы"
        x:Shared="False"
        TextIndent="20"
        FontSize="14">
        <Bold>Шкала «Гипомания»:</Bold> уровень -
        <TextBlock 
            Text="{Binding}" 
            FontWeight="Bold" 
            />
        Для лиц с высокими оценками по этой шкале характерно приподнятое настроение независимо от обстоятельств. 
        Они активны, деятельны, энергичны и жизнерадостны. Они любят работу с частыми переменами, охотно 
        контактируют с людьми, однако интересы их поверхностны и неустойчивы, им не хватает выдержки и настойчивости.
    </Paragraph>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Спилбергер.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="Ситуативная тревожность - 1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Ситуативная тревожность. Низкий уровень.</Bold> Ваше состояние в настоящее время можно оценить как спокойное. 
            Вы удовлетворены происходящими вокруг событиями. У Вас получается осуществление задуманного.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Ситуативная тревожность - 2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Ситуативная тревожность. Средний уровень.</Bold> В настоящее время Вы способны замечать даже
            незначительные изменения в отношении к Вам других людей. Испытываете волнение задолго до наступления 
            неприятной ситуации. Сейчас Вы придаёте слишком большое значение происходящим событиям, переоценивая 
            их отрицательные последствия для себя.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Ситуативная тревожность - 3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Ситуативная тревожность. Высокий уровень.</Bold> Вы испытываете в настоящее время довольно 
            сильное беспокойство. Вас захлёстывают эмоции, Вы сомневаетесь в правильности своих действий. 
            Вам хочется от всех сбежать. Сейчас Вам необходимо сосредоточиться, чтобы разобраться в происходящих
            или ожидаемых событиях. Желательна консультация у психолога.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Личностная тревожность - 1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Личностная тревожность. Низкий уровень.</Bold> Уравновешенность. Хороший эмоциональный контроль, 
            высокая самооценка. Уверенность в себе. Лёгкость в принятии решений. Самостоятельность и  независимость 
            в решении проблем. Беспокойства и опасения не для Вас.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Личностная тревожность - 2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Личностная тревожность. Низкий уровень.</Bold> Уравновешенность, хороший эмоциональный контроль, 
            высокая самооценка. Самостоятельно справляетесь с беспокойством. Не склонны от кого-то зависеть. 
            Достаточно уверены в себе, но предпочитаете посоветоваться перед принятием решений.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Личностная тревожность - 3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Личностная тревожность. Средний уровень.</Bold> Неустойчивая самооценка, затруднения при принятии 
            самостоятельного решения, особенно в неопределённых условиях. Порой возникает немотивированное 
            беспокойство и приходится прикладывать усилия, чтобы «держать себя в руках», не впадать в депрессию и 
            не проявлять излишней раздражительности.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Личностная тревожность - 4"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Личностная тревожность. Средний уровень.</Bold> Личность, склонная к тревожности, с неустойчивой 
            самооценкой, затруднениями в принятии самостоятельных решений. Лабильность вегетативной нервной системы.
            Возможны проявления излишней раздражительности, депрессии.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Личностная тревожность - 5"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Личностная тревожность. Высокий уровень.</Bold> Склонность к сильному беспокойству без видимой 
            причины. Чувство собственной неполноценности, затруднения в принятии самостоятельных решений.
            Вегетативная лабильность, неуверенность в себе, в правильности своих поступков, постоянные страхи и
            сомнения. Унылое настроение. Приходится прикладывать немало усилий, чтобы не терять контроль над собой.
            Желательна консультация у психолога.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Стиль_руководства.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="Авторитарный стиль руководства"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Авторитарный стиль руководства.</Bold>
        </Paragraph>
        <Paragraph>
            Ориентация на собственное мнение и оценки. Стремление к власти, уверенность в себе, склонность к 
            жёсткой формальной дисциплине, большая дистанция с подчинёнными, нежелание признавать свои ошибки.
            Игнорирование инициативы, творческой активности людей. Единоличное принятие решений. Контроль за 
            действиями подчинённых.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Демократический стиль руководства"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Демократический стиль руководства.</Bold>
        </Paragraph>
        <Paragraph>
            Требовательность и контроль сочетается с инициативой и творческим подходом к выполняемой
            работе и сознательным соблюдением дисциплины. Стремление делегировать полномочия и разделить 
            ответственность. Демократичность в принятии решения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Либеральный стиль руководства"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Либеральный стиль руководства.</Bold>
        </Paragraph>
        <Paragraph>
            Снисходительность к работникам. Отсутствие требовательности и строгой дисциплины, контроля, 
            либеральность, панибратство с подчиненными. Склонность перекладывать ответственность в
            принятии решений.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Смешанный стиль руководства"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Смешанный стиль руководства.</Bold>
        </Paragraph>
        <Paragraph>
            Ни один из принятых стилей руководства не является доминирующим. С одной стороны, это позволяет 
            в зависимости от ситуации перестраивать свою работу, с другой стороны, высокая подверженность 
            обстоятельствам. С одной стороны, гибкость в отношениях с подчинёнными, с другой - непредсказуемость.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Структура_темперамента_Смирнов.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:controls="clr-namespace:Updk7.Tests.Reports.Wpf.Controls.SmirnovChart">

    <sys:String x:Key="Title">Структура темперамента по Смирнову</sys:String>

    <Section
        x:Key="SmirnovChartSection"
        x:Shared="False">
        <BlockUIContainer>
            <Grid DataContext="{Binding}">
                <controls:SmirnovChartControl 
                    HorizontalAlignment="Center"
                    Width="640"
                    DataContext="{Binding}"
                    />
            </Grid>
        </BlockUIContainer>
    </Section>

    <Section
        x:Key="Искренность - 1"
        x:Shared="False">
        <Paragraph 
            TextIndent="20"
            FontSize="14">
            <Bold>Уровень по шкале «Искренность»: Низкий</Bold>
            <TextBlock 
                Text="{Binding}"
                />
            Низкий уровень достоверности ответов.  Необходимо повторить тестирование, отвечая искренне.
        </Paragraph>
    </Section>

    <Section
        x:Key="Искренность - 2"
        x:Shared="False">
        <Paragraph 
            TextIndent="20"
            FontSize="14">
            <Bold>Уровень по шкале «Искренность»: Средний</Bold>
            <TextBlock 
                Text="{Binding}"
                />
            Полученные результаты достоверны.
        </Paragraph>
    </Section>

    <Section
        x:Key="Искренность - 3"
        x:Shared="False">
        <Paragraph 
            TextIndent="20"
            FontSize="14">
            <Bold>Уровень по шкале «Искренность»: Высокий</Bold>
            <TextBlock 
                Text="{Binding}"
                />
            Полученные результаты достоверны.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Тейлор.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="Conclusion-1"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Степень тревожности: НИЗКАЯ
        </Paragraph>
    </Section>

    <Section 
        x:Key="Conclusion-2"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Степень тревожности: СРЕДНЯЯ
        </Paragraph>
    </Section>

    <Section 
        x:Key="Conclusion-3"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Степень тревожности: ВЫСОКАЯ
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Тест_Томаса.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Тест Томаса</sys:String>

    <Section 
        x:Key="Conclusion"
        x:Shared="False">
        <Paragraph 
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Количество баллов, набранных индивидом по каждой шкале, даёт представление о выраженности у него 
            тенденции к проявлению соответствующих форм поведения в конфликтных ситуациях.
        </Paragraph>
        <Paragraph 
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            Для анализа результатов теста следует учитывать шкалы с доминирующими значениями в баллах и 
            соотношение значений баллов по остальным шкалам
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\УСК.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <sys:String x:Key="Title">Уровень субъективного контроля</sys:String>

    <Section 
        x:Key="Общая интернальность - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Общая интернальность</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Низкий уровень субъективного контроля. Не видит прямой связи между своими действиями и значимыми
            событиями своей жизни, полагая, что большая часть этих событий является результатом действий 
            других людей или воли случая. Hизкий уровень способности контролировать развитие действий и 
            своё поведение в связи с этими действиями.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Общая интернальность - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Общая интернальность</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Средний уровень субъективного контроля. Склонен видеть случай во всём, что с ним происходит, 
            недооценивая роль собственных действий в происходящих с ним событиях и ситуациях. Нет уверенности 
            в своей способности контролировать ход событий.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Общая интернальность - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Общая интернальность</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Высокий уровень субъективного контроля в любых значимых ситуациях. Склонен к мнению, что большинство
            важных жизненных событий было результатом собственных усилий. Чувствует, что может управлять 
            ходом событий, держать под контролем ситуацию. Обладает чувством ответственности за то, как 
            складывается его жизнь в целом.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в области достижений - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в области достижений</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Склонен приписывать свои успехи и достижения внешним обстоятельствам - счастливому случаю, 
            удаче, судьбе, помощи хороших людей и т.д. Полагаясь на случай, не планирует должным образом
            свою деятельность, чтобы достичь успеха. Недооценивает собственные усилия и их влияние на деловые
            достижения.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в области достижений - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в области достижений</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Не всегда должным образом оценивает влияние собственных усилий и поступков на свои достижения и 
            успехи, считая их удачей, подарком судьбы. По этой причине не склонен к тщательному планированию 
            своих действий, может пускать всё на самотёк, надеясь «на авось» вместо того, чтобы активно
            воздействовать на обстоятельства.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в области достижений - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в области достижений</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Достаточно высокий уровень контроля над эмоционально положительными событиями, ситуациями успеха.
            Считает, что всего лучшего в своей жизни добился самостоятельно, собственными усилиями. 
            Обладает уверенностью в том, что и в будущем способен с успехом достигать поставленные цели.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в ситуации неудачи - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в ситуации неудачи</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Склонен приписывать ответственность за собственные неудачи стечению внешних обстоятельств и 
            окружающим людям, считая их виновниками своего невезения. Отсутствует критичное отношение
            к себе, к своим действиям.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в ситуации неудачи - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в ситуации неудачи</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Недооценивает роль собственных ошибок в ситуациях неуспеха. Склонен обвинять в своих просчётах 
            других людей или стечение обстоятельств. Недостаточно критично относится к причинам своих неудач.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в ситуации неудачи - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в ситуации неудачи</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Высокий уровень субъективного контроля в ситуации неуспеха. Склонен обвинять только самого 
            себя во всех неприятных событиях жизни. Всегда полностью берёт на себя ответственность за 
            собственные неудачи.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в семейных отношениях - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в семейных отношениях</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Не чувствует ответственности за происходящие в семейной жизни события. Недостаточно оценивает 
            свою роль в установлении семейных отношений. Склонен к обвинению близких в возникающих внутрисемейных
            конфликтах.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в семейных отношениях - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в семейных отношениях</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Склонен перекладывать ответственность за события, происходящие в семье, на плечи близких. Часто 
            не признаёт своей вины в возникающих внутрисемейных неурядицах.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в семейных отношениях - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в семейных отношениях</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Смело берёт на себя ответственность при решении серьёзных внутрисемейных вопросов.
            Не уклоняется от сложностей семейной жизни, при возникновении проблем не ищет виноватых 
            среди близких, критично относится к себе в ходе возникающих семейных конфликтов.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в области производственных отношений - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в области производственных отношений</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Не склонен видеть за собой ответственности за действия коллектива, в котором он находится, 
            приписывая главную роль в этом вопросе руководству, стечению обстоятельств, влиянию других
            членов коллектива.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в области производственных отношений - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в области производственных отношений</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Недооценивает роль собственных усилий в формировании своих отношений в коллективе, организации
            собственной трудовой деятельности, достижений и неудач. Склонен приписывать большое значение 
            роли руководства, сотрудников, внешних обстоятельств для собственных деловых успехов и неудач.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в области производственных отношений - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в области производственных отношений</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Достаточно высоко оценивает собственную роль в организации своей трудовой деятельности. 
            Считает себя ответственным за собственные деловые успехи и неудачи. Настойчив в реализации 
            своих планов, связанных с работой. Полагает, что успешность продвижения по служебной лестнице
            в большей степени зависит от него самого, нежели от внешних обстоятельств.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в межличностных отношениях - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в межличностных отношениях</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Не способен активно формировать свой круг общения, устанавливать новые контакты. В социальном 
            взаимодействии пассивен, предоставляя другим активно действовать и регулировать ход взаимоотношений.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в межличностных отношениях - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в межличностных отношениях</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Обладает достаточно сильным стремлением к социальным контактам, однако склонен считать 
            свои межличностные отношения результатом активности партнёров. Прилагает недостаточно усилий
            для формирования желаемого круга общения, полагаясь на случай.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в межличностных отношениях - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в межличностных отношениях</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Считает себя способным целиком и полностью контролировать свои неформальные отношения с людьми,
            вызывая к себе уважение и симпатию. Активно влияет на свои взаимоотношения с окружающими, добиваясь
            внимания и поддержки.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в отношении здоровья - Ниже нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в отношении здоровья</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Считает состояние своего здоровья результатом воздействия внешних обстоятельств. В случае
            болезни надеется, что выздоровление придёт в результате действия других людей, прежде всего
            врачей, или что болезнь «пройдёт сама собой». Практически не прилагает собственных усилий для 
            профилактики и восстановления состояния своего здоровья.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в отношении здоровья - Норма"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в отношении здоровья</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            В случае болезни надеется прежде всего на медицинскую помощь со стороны врачей, часто не прилагая
            собственных усилий для профилактики и восстановления состояния своего здоровья. Недооценивает 
            собственную роль в поддержании своего хорошего самочувствия.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Интернальность в отношении здоровья - Выше нормы"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            Margin="0,5,0,5">
            <Bold>Интернальность в отношении здоровья</Bold> уровень -
            <TextBlock 
                Text="{Binding}" 
                FontWeight="Bold" 
                />
            Стремится активно воздействовать на состояние собственного здоровья (профилактика, спорт,
            рациональное питание и т.п). Чувствует себя ответственным за своё здоровье. В случае болезни
            ищет вину прежде всего за собой, в выздоровлении надеется на собственные силы.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Warning"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14"
            FontWeight="Bold">
            Сниженный уровень саморефлексии.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Remark"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Для составления полной интерпретации по результатам теста следует  учитывать соотношение значений 
            стенов по всем шкалам.
        </Paragraph>
    </Section>


</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Resources\Шмишек.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Section 
        x:Key="Гипертимность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Гипертимный тип акцентуации.</Bold> Людей этого типа отличает большая подвижность, общительность,
            выразительность жестов и мимики, разговорчивость, чрезмерная самостоятельность, недостаток чувства
            дистанции в отношениях с другими. Они любят компании, отличаются стремлением к лидерству. Почти всегда 
            имеют хорошее настроение, хорошее самочувствие, высокий жизненный тонус, хороший аппетит, здоровый сон,
            склонность к чревоугодию и иным радостям жизни. Это люди с повышенной самооценкой, веселые, 
            легкомысленные, несколько поверхностные и, вместе с тем, деловитые, изобретательные, блестящие
            собеседники. Представители данного типа энергичные, деятельные, инициативные, умеющие увлекать и
            “заражать” эмоциями других, отличающиеся оптимизмом и чувством нового.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Чертами, способствующими конфликтам с окружающими, являются раздражительность, вспышки гнева, склонность 
            к аморальным поступкам, “прожектерство”, недостаточно серьезное отношение к своим обязанностям, 
            отсутствие способности доводить начатое дело до конца. Они трудно переносят ситуации вынужденного 
            одиночества, условия жесткой дисциплины, монотонную деятельность. Более часто, чем у других людей,
            у представителей этого типа отмечаются случаи заболевания МДП.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Для них характерно предпочтение работы, связанной с постоянным общением (организаторская деятельность,
            служба быта, театр и т.п.), склонность к игровым видам спорта и к частой смене профессий и места работы.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Демонстративность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Демонстративный тип акцентуации.</Bold> Люди этого типа характеризуются повышенной способностью
            к вытеснению, демонстративностью поведения, подвижностью, легкостью в установлении контактов. 
            Они склонны к фантазерству и притворству, направленными на приукрашивание своей персоны, к авантюризму, 
            артистизму и позерству. Ими движет стремление к лидерству, потребность в признании, жажда постоянного 
            внимания к своей персоне, жажда похвалы и восхищения, а также жажда власти. Представители данного типа 
            демонстрируют высокую приспосабливаемость к людям и различным ситуациям, обходительность, неординарность
            в суждениях и в поведении, способность увлечь других. Их стремление к компаниям обычно связано с 
            потребностью ощутить себя лидером, занять исключительное положение. Для них характерна высокая
            лабильность, легкая смена настроения при отсутствии действительно глубоких чувств и переживаний.
            Они склонны к интригам при внешней мягкости манеры поведения и общения, отличаются беспредельным
            эгоцентризмом, жаждой сочувствия, почитания, удивления; перспектива быть незамеченными окружающими 
            их крайне отягощает и нередко ведет к конфликтам. В семье и в кругу знакомых они нередко претендуют
            на роль кумира, всеобщего баловня. Самооценка, как правило, далека от объективности. Обладая 
            самоуверенностью и высокими притязаниями, люди этого типа нередко сами провоцируют конфликты,
            при этом склонны активно защищаться, однако, они способны и предупреждать конфликты, благодаря 
            умению “подстраиваться” под партнера. Для них также свойственны некоторый формализм в общении и в
            выполнении своих обязанностей, “крючкотворство”, стремление переложить принятие важных решений на 
            других. Обладая повышенной способностью к вытеснению, представители демонстративного типа могут полностью
            забывать то, о чем не желают знать, а поэтому они “раскованы” во лжи: то, что говорят в данный момент,
            является для них правдой; собственная ложь не вызывает заметных угрызений совести.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            “Угрожающими”, конфликтными ситуациями для этих людей являются ситуации, в которых недооцениваются их
            заслуги, и ситуации личной ответственности за важное дело. Поэтому предпочитаемые сферы профессиональной
            деятельности - профессии, не связанные с большой ответственностью, особенно за других людей.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Возбудимость"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Возбудимый тип акцентуации.</Bold> Для людей данного типа характерна недостаточная управляемость,
            ослабление контроля над влечениями и побуждениями, сочетающиеся с властью физиологических влечений.
            Им свойственны повышенная импульсивность, инстинктивность, низкая контактность, угрюмость, молчаливость, 
            медлительность в разговоре (“тягучая занудливость”), склонность к хамству и брани, но возможны также 
            льстивость и услужливость. Представители этого типа склонны к постоянным трениям и конфликтам с 
            окружающими, в которых сами, как правило, являются активной, провоцирующей стороной, но при этом обычно
            избегают ссор с начальством.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Они неуживчивы в коллективе, деспотичны и жестоки в семье, раздражительны и вспыльчивы, склонны к 
            аморальному поведению, асоциальным поступкам, к злоупотреблению алкоголем. Вспышки импульсивности и
            гнева гасятся с трудом и могут быть опасны для окружающих. Но вне приступов возбуждения и раздражения 
            они добросовестны, аккуратны, отличаются любовью к детям и животным. Равнодушны к будущему, целиком 
            живут настоящим, желая извлечь из него массу развлечений. Могут быть очень властными, выбирая для 
            общения, как правило, наиболее слабых. Для людей данного типа никакой труд не становится привлекательным,
            они работают лишь по мере необходимости, проявляют такое же нежелание учиться. Отдают предпочтение 
            физическому труду и занятиям атлетическими видами спорта. Из-за неуживчивости часто меняют место работы.
            Подвержены невротическим срывам.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Застревание"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Застревающий тип акцентуации.</Bold> Люди данного типа характеризуются умеренной общительностью 
            (с индивидуальными колебаниями), склонностью к нравоучениям, “застревающей занудливостью”,
            несговорчивостью. На работе и в быту склонны к затяжным склокам, поскольку часто страдают от мнимой 
            несправедливости к ним, поэтому отличаются настороженностью и недоверчивостью по отношению к людям, 
            подозрительностью, мнительностью, чувствительностью к обидам и огорчениям, долго переживают происшедшее,
            неспособны “легко отходить” от реальных и мнимых обид. Они склонны к мстительности, ревнивости, проявляют
            непомерную требовательность к окружающим, особенно к близким и подчиненным, отличаются раздутым до 
            фанатизма чувством справедливости, “правдолюбия”. Представителям застревающего типа свойственна
            самонадеянность, жесткость взглядов и установок, сильно выраженное честолюбие, которые приводят 
            к настойчивому и весьма энергичному утверждению своих интересов. По отношению к себе они также проявляют
            высокую требовательность, стремятся добиться высоких показателей в любом деле и проявляют большое 
            упорство в достижении своих целей.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Ситуации, в которых задето самолюбие, нанесена “несправедливая” обида, появилось чувство ревности или 
            возникли препятствия к достижению честолюбивых целей, воспринимаются как “угрожающие” и приводят к 
            конфликтам, в которых они сами нередко выступают инициаторами. Характерными особенностями в проблемных 
            ситуациях являются склонность к аффектам, инертность в проявлении аффектов, в поведении, в мышлении,
            в моторике. Для людей данного типа наиболее предпочтительна работа, дающая ощущение независимости и
            возможность проявить себя.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Педантичность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Педантичный тип акцентуации.</Bold> Представители этого типа характеризуются ригидностью,
            инертностью психических процессов, тяжелы на подъем, долго переживают травмирующие события. Они 
            добросовестны, аккуратны, серьезны, надежны в делах и чувствах, обычно склонны жестко следовать 
            намеченному плану, в выполнении любой деятельности неторопливы, пунктуальны, усидчивы, ориентированы 
            на высокое качество работы, отличаются ровным настроением. В конфликты вступают редко, выступая скорее 
            пассивной, чем активной стороной. В то же самое время очень сильно реагируют на любое проявление
            нарушения порядка, на службе и в семье способны замучить партнеров, предъявляя им множество формальных 
            требований и проявляя “занудливость” и чрезмерную аккуратность. Охотно уступают лидерство другим, 
            стремятся переложить принятие важных решений на окружающих. Такие люди склонны к частым самопроверкам, 
            сомнениям в правильности выполненной работы, к долгим колебаниям в ситуациях выбора.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            “Угрожающими” и конфликтогенными для них являются ситуации личной ответственности за важное дело и 
            ситуации, в которых недооценивают их заслуги. В экстремальных обстоятельствах проявляется склонность 
            к навязчивости и к ипохондрии. Для представителей данного типа противопоказаны профессии, связанные 
            с большой ответственностью, особенно за других людей. Обычно они предпочитают и хорошо выполняют 
            “бумажную работу” (бухгалтерское, библиотечное дело и т.п.), место работы меняют редко.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Дистимность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Дистимический тип акцентуации.</Bold> Люди этого типа отличаются серьезностью, медлительностью, 
            некоторой слабостью волевой сферы, преобладанием сниженного настроения. Для них характерны
            пессимистическое отношение к будущему, заниженная самооценка, склонность фиксироваться на
            теневых сторонах жизни. Они мало контактны, пассивны в общении, замкнуты и немногословны. 
            Как правило, являются домоседами, индивидуалистами, ведут замкнутый образ жизни, избегают общества 
            и шумных компаний, не склонны близко сходиться с сослуживцами. Очень ценят тех, кто одаривает их
            дружбой, и охотно подчиняются им. Чертами, привлекательными для окружающих, являются серьезность, 
            высокая нравственность, добросовестность, справедливость.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Представители этого типа редко вступают в конфликты, являясь в них скорее пассивной стороной.
            Наиболее вероятно, что конфликты могут возникнуть в ситуациях, требующих бурной и активной 
            деятельности, а также при смене привычного образа жизни. При неблагоприятных жизненных
            обстоятельствах может появиться склонность к невротической депрессии, отмечаются и случаи МДП.
            В плане профессиональной деятельности наиболее предпочтительной для них является работа, не 
            требующая широкого круга общения и быстроты в принятии решений.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Тревожность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Тревожный тип акцентуации.</Bold> Представителям данного типа свойственны низкая контактность,
            робость, неуверенность в себе, преобладание минорного настроения. Дети тревожного типа часто боятся 
            животных, темноты, не любят шумных игр, избегают общения со сверстниками, тяжело переживают контрольные,
            экзамены и различные проверки. Они охотно подчиняются опеке старших, нередко становятся зависимы от
            их оценок. У них рано формируется чувство долга и ответственности, самокритичность и исполнительность.
            Присущие людям тревожного типа с детства обидчивость, чувствительность и застенчивость мешают им 
            сблизиться с теми, с кем хочется, особо слабым звеном является реакция на отношение к ним окружающих.
            Непереносимость насмешек, подозрения сопровождаются неумением постоять за себя, отстоять правду при 
            несправедливых обвинениях. Вследствие своей беззащитности нередко становятся “козлами отпущения”, 
            мишенями для шуток. В конфликты вступают редко, играя в них в основном пассивную роль, в трудных 
            ситуациях обычно ищут поддержки и опоры. Они обычно дружелюбны, не склонны брать на себя функции лидера,
            избегают ответственных должностей. Чувство собственной неполноценности стараются замаскировать в 
            самоутверждении через те виды деятельности, где они в большей мере могут раскрыть свои способности. 
            Предпочитают работу, не требующую широкого круга общения и не связанную с риском. Ситуации страха, 
            боязни наказания, постоянных насмешек и несправедливых обвинений могут способствовать развитию 
            неврастении, ипохондрии, язвенных болезней желудочно-кишечного тракта.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Эмотивность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Эмотивный тип акцентуации.</Bold> Для данного типа людей характерны эмоциональность,
            чувствительность, повышенная тревожность, глубокие эмоциональные реакции. Они очень впечатлительны,
            любые жизненные события воспринимают серьезнее, чем другие люди, склонны к сентиментальности и даже к
            слезливости, отличаются лабильностью эмоциональной сферы, их настроение может легко меняться по самым 
            незначительным и незаметным для окружающим поводам. Контактность представителей этого типа ниже среднего 
            уровня, они предпочитают узкий круг друзей и близких, которые понимают их “с полуслова”. В конфликты 
            вступают редко, играя в них в основном пассивную роль, обиды носят в себе, “не выплескивая” их наружу. 
            Чертами, наиболее привлекательными для окружающих, являются гуманность, способность к состраданию, к
            сопереживанию, отзывчивость, мягкосердечность, умение радоваться чужим удачам, а также обостренное 
            чувство долга и исполнительность. На службе они обычно очень добросовестны, хорошие семьянины, бережно
            относятся к природе, любят выращивать растения, заботиться о животных. Предпочитают работать в сфере
            искусств, в области медицины и биологии.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Наиболее тяжелыми для данного типа людей являются ситуации конфликта с близкими людьми, смерть
            или болезнь родных, пребывание в окружении грубых людей и проявление несправедливости. Угрожающие 
            болезни: инфаркт миокарда, гипертоническая болезнь и склонность к невротической депрессии.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Экзальтированность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Экзальтированный тип акцентуации.</Bold> Аффективно-экзальтированные люди реагируют на жизнь более 
            бурно, чем остальные. Темп нарастания реакций, их внешние проявления отличаются большой интенсивностью.
            Представители данного типа одинаково легко приходят в восторг от радостных событий и в отчаяние - от
            печальных (“ от радостного ликования до смертельной тоски”). Экзальтация в незначительной степени связана 
            с эгоистическими стимулами, гораздо чаще она мотивирована альтруистическими побуждениями: радость за
            близких и друзей, любовь к музыке, искусству, природе, переживания религиозного порядка, поиски 
            мировоззрения и др. Другой полюс реакций - крайняя впечатлительность по поводу печальных событий:
            жалость, сострадание к несчастным людям, больным животным, которые могут доводить до отчаяния. Людей
            этого типа отличают высокая коммуникабельность, словоохотливость, патетичность, влюбчивость. Они обычно 
            часто спорят по любым поводам, но до открытых конфликтов дело доходит редко.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            В конфликтных ситуациях они могут быть как активной, так и пассивной стороной. Как правило, обладают 
            артистической одаренностью, художественным вкусом, яркостью и искренностью проявления чувств. 
            Критические, угрожающие жизненные ситуации, такие, как столкновение с “прозой” жизни, неудачи,
            горестные события и т.п., ведут к обострению чувствительности и эмоциональной возбудимости, формируют
            “готовность к отчаянию”, склонность к невротической депрессии.
        </Paragraph>
        <Paragraph
            TextIndent="20"
            FontSize="14">
            В профессиональной деятельности представители данного типа отдают предпочтение сфере искусств 
            (особенно поэзии и музыке), художественным видам спорта, а также профессиям, связанным с 
            близостью к природе.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Циклотимность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Циклоидный тип акцентуации.</Bold> Циклотимические личности - это люди, для которых характерна 
            смена гипертимических и дистимических состояний. На передний план выступает то один, то другой из этих
            полюсов, иногда без видимых внешних мотивов, а иногда в связи с теми или иными конкретными жизненными 
            событиями. Радостные события вызывают у них не только положительные эмоции, но и сопровождаются общей 
            картиной гипертимии: жаждой деятельности, повышенной контактностью, говорливостью, скачкой идей.
            Печальные события вызывают подавленность, замедленность реакций и мышления, снижение коммуникабельности. 
            Настроение и жизненный цикл оказывают влияние и на работоспособность, и на творческую активность, и на
            самооценку. Обычно выделяют типичных и лабильных циклоидов. Типичные циклоиды в детстве производят
            впечатление гипертимных, но затем проявляется вялость, упадок сил, глубокая депрессия, снижение
            потребности в контактах, отвращение к учебе, не исключены и суицидальные попытки. Постепенно ситуация
            меняется, к концу подросткового возраста вновь появляется оптимизм, повышается самооценка и т.п. Но 
            периодически на протяжении всей жизни у таких личностей отмечаются смены “циклов”, которые продолжаются 
            сравнительно длительный период времени. У лабильных циклоидов фазы смены настроения обычно короче, 
            чем у типичных циклоидов (несколько дней или даже часов). “Плохие дни” отмечаются скорее плохим
            настроением, чем вялостью и снижением активности и работоспособности. Периоды подъема характеризуются,
            прежде всего, ростом контактности и потребности в общении, в друзьях, в компании.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Tendention - Демонстративность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Демонстративность.</Bold> Характерно стремление быть постоянно в центре внимания («пусть
            ненавидят, лишь бы не были равнодушными).
        </Paragraph>
    </Section>

    <Section 
        x:Key="Tendention - Застревание"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Застревание.</Bold> Могут долго помнить обиды. Устремлённые, «упёртые в одно». Могут проявлять 
            агрессию.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Tendention - Педантичность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Педантичность.</Bold> Трудно переключаться с одной эмоции на другую. Любят, чтобы всё было 
            на своих местах. Идея порядка и аккуратности иногда становится главным смыслом жизни. Порою всё их
            раздражает.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Tendention - Возбудимость"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Возбудимость.</Bold> Иногда проявляется ослабление контроля над своими желаниями и суждениями.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Tendention - Гипертимность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Гипертимность.</Bold> Оптимисты, быстро переключаются с одного дела на другое, часто не доводят 
            начатое до конца, не любят, когда их опекают. Предприимчивы. Могут брать на себя лидерство.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Tendention - Дистимность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Дистимность.</Bold> Склонен к пессимизму, утомляемости, заторможенности. Предпочитает одиночество.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Tendention - Тревожность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Тревожность.</Bold> Повышенная робость и пугливость, не уверен в себе. Недооценивают, 
            преуменьшают свои способности. Пугается ответственности, опасается всевозможных неприятностей
            для себя и своих родных.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Tendention - Экзальтированность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Экзальтированность.</Bold> Большой диапазон эмоциональных состояний: от восторга на радостные 
            события до отчаяния на печальные события.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Tendention - Эмотивность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Эмотивность.</Bold> Проявляется повышенная эмоциональная чувствительность. От настроения 
            зависит и работоспособность, и самочувствие. Тонко организована эмоциональная сфера: способны
            глубоко чувствовать и переживать. Склонен к добрым отношениям с окружающими. Болезненно воспринимает
            грубость, расстраивается, если происходит разрыв или ухудшение отношений с близкими людьми.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Tendention - Циклотимность"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            <Bold>Циклотимность.</Bold> Впечатлителен. Перепады настроения. Хорошее настроение коротко, 
            плохое длительно. При хорошем настроении активны, деятельны.
        </Paragraph>
    </Section>

    <Section 
        x:Key="Remark"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Для составления полной интерпретации по результатам теста следует учитывать соотношение значений 
            баллов по всем шкалам.
        </Paragraph>
    </Section>

    <Section 
        x:Key="NoResults"
        x:Shared="False">
        <Paragraph
            TextIndent="20"
            FontSize="14">
            Акцентуации личности не выявлены.
        </Paragraph>
    </Section>

</ResourceDictionary>
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ITestReportGenerator.cs


using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf
{
    public interface ITestReportGenerator
    {
        TestType TestType { get; }

        FlowDocument CreateReport(ReportInfo headerInfo, TestResults results);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ITestReportsFactory.cs


using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf
{
    public interface ITestReportsFactory
    {
        FlowDocument CreateReport(ReportInfo headerInfo, TestResults results);

        void SetReportGenerator(ITestReportGenerator generator);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ITestsGroupReportGenerator.cs


using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf
{
    public interface ITestsGroupReportGenerator
    {
        bool CanGenerate(Data.TestGroupResults results);

        FlowDocument CreateReport(ReportInfo headerInfo, Data.TestGroupResults results);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ITestsGroupReportsFactory.cs


using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf
{
    public interface ITestsGroupReportsFactory
    {
        FlowDocument CreateReport(ReportInfo headerInfo, Data.TestGroupResults results);

        void SetReportGenerator(ITestsGroupReportGenerator generator);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportHelper.cs


using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf
{
    public static class ReportHelper
    {
        private const double Scale = 96.0 / 25.4;

        public static FlowDocument CreateBlankDocument()
        {
            var document = new FlowDocument();
            document.Foreground = Brushes.Black;
            document.Background = Brushes.White;
            document.PageWidth = 210 * Scale;
            document.PageHeight = 297 * Scale;
            document.PagePadding = new Thickness(20 * Scale, 15 * Scale, 20 * Scale, 15 * Scale);
            document.ColumnWidth = document.PageWidth - document.PagePadding.Left - document.PagePadding.Right;

            return document;
        }

        public static Block CreateHeader(UIElement header)
        {
            // В составе заголовка есть графики, поэтому их размеры необходимо задать явно 
            // перед сохранением документа в xps
            var padding = 20 * Scale;
            var size = new Size(210 * Scale - 2.0 * padding, double.PositiveInfinity);
            header.Measure(size);
            header.Arrange(new Rect(header.DesiredSize));
            header.UpdateLayout();

            return new BlockUIContainer(header);
        }

        public static Block CreateFooter(UIElement footer)
        {
            // Футер всегда находится внизу печатного листа
            var footerBlock = new BlockUIContainer(footer);
            var figure = new Figure(footerBlock);
            figure.HorizontalAnchor = FigureHorizontalAnchor.PageCenter;
            figure.VerticalAnchor = FigureVerticalAnchor.PageBottom;
            figure.Padding = new Thickness(0, 0, 0, 10 * Scale);

            return new Paragraph(figure);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportInfo.cs


using System;
using System.Windows.Media.Imaging;

namespace Updk7.Tests.Reports.Wpf
{
    public class ReportTesteeInfo
    {
        public string Name { get; set; }

        public DateTime BirthDate { get; set; }

        public BitmapImage Photo { get; set; }

        public string Comment { get; set; }

        public object Entitie { get; set; }
    }

    public class ReportInfo
    {
        public ReportTesteeInfo Testee { get; set; } = new ReportTesteeInfo();

        public string UserName { get; set; }

        public string OrganizationTitle { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportsFactory.cs


namespace Updk7.Tests.Reports.Wpf
{
    public class ReportsFactory
    {
        public ReportsFactory()
        {
        }
        
        public ITestReportsFactory Tests { get; } = new TestReportsFactory();

        public ITestsGroupReportsFactory TestsGroups { get; } = new TestsGroupReportsFactory();
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ResultsMetadata.cs


using System;
using System.Windows;

namespace Updk7.Tests.Reports.Wpf
{
    internal class ResultsMetadata
    {
        private static readonly string ResourcesPath = "/Updk7.Tests.Reports.Wpf;component/Resources/ResultsMetadata.xaml";
        private static Lazy<ResourceDictionary> _resources = new Lazy<ResourceDictionary>(() =>
            new ResourceDictionary() { Source = new Uri(ResourcesPath, UriKind.Relative) });

        public ResultsMetadata()
        {
        }

        public string Dimension { get; internal set; }

        public bool ShowInReport { get; internal set; }

        public static ResultsMetadata Get(TestType test, string resultsKey)
        {
            var resourceKey = $"{test.ToString()}: {resultsKey}";
            return _resources.Value[resourceKey] as ResultsMetadata;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\TestReportsFactory.cs


using System;
using System.Linq;
using System.Collections.Generic;
using System.Windows.Documents;
using Updk7.Tests.Reports.Wpf.ReportGenerators;

namespace Updk7.Tests.Reports.Wpf
{
    internal class TestReportsFactory : ITestReportsFactory
    {
        private ITestReportGenerator _genericReportGenerator = new GenericReportGenerator();
        private Lazy<List<ITestReportGenerator>> _reportGererators;

        public TestReportsFactory()
        {
            _reportGererators = new Lazy<List<ITestReportGenerator>>(() => createGeneratorsList());
        }

        public bool CanGenerate(TestType test)
        {
            return _reportGererators.Value.Any(g => g.TestType == test);
        }

        public FlowDocument CreateReport(ReportInfo reportInfo, TestResults results)
        {
            var generator = _reportGererators.Value.FirstOrDefault(g => g.TestType == results.Test);
            if (generator == null)
                generator = _genericReportGenerator;

            return generator.CreateReport(reportInfo, results);
        }

        public void SetReportGenerator(ITestReportGenerator generator)
        {
            var defaultGenerator = _reportGererators.Value.FirstOrDefault(g => g.TestType == generator.TestType);
            if (defaultGenerator != null)
                _reportGererators.Value.Remove(defaultGenerator);

            _reportGererators.Value.Add(generator);
        }

        private List<ITestReportGenerator> createGeneratorsList()
        {
            var results = new List<ITestReportGenerator>();

            results.Add(new ReportGenerators.Questionnaires.Дигностика_уровня_агрессии_Басс_Дарки());
            results.Add(new ReportGenerators.Questionnaires.Исследование_волевой_саморегуляции());
            results.Add(new ReportGenerators.Questionnaires.Индекс_жизненного_стиля());
            results.Add(new ReportGenerators.Questionnaires.СМОЛ());
            results.Add(new ReportGenerators.Questionnaires.Склонность_к_зависимому_поведению());
            results.Add(new ReportGenerators.Questionnaires.Антиципационная_состоятельность());
            results.Add(new ReportGenerators.Questionnaires.МДУ_Элерс());
            results.Add(new ReportGenerators.Questionnaires.Склонность_к_риску_Шуберт());
            results.Add(new ReportGenerators.Questionnaires.Аналогии());
            results.Add(new ReportGenerators.Questionnaires.Личностный_профиль());
            results.Add(new ReportGenerators.Questionnaires.КОПС());
            results.Add(new ReportGenerators.Questionnaires.ДОРС());
            results.Add(new ReportGenerators.Questionnaires.Оценка_нервно_психической_устойчивости_НПУ());
            results.Add(new ReportGenerators.Questionnaires.Тест_Томаса());

            results.Add(new ReportGenerators.Questionnaires.Айзенк());
            results.Add(new ReportGenerators.Questionnaires.Тейлор());
            results.Add(new ReportGenerators.Questionnaires.Спилбергер());
            results.Add(new ReportGenerators.Questionnaires.Лири());
            results.Add(new ReportGenerators.Questionnaires.Голланд());
            results.Add(new ReportGenerators.Questionnaires.Шмишек());
            results.Add(new ReportGenerators.Questionnaires.MMPI());
            results.Add(new ReportGenerators.Questionnaires.QSort());
            results.Add(new ReportGenerators.Questionnaires.САН());
            results.Add(new ReportGenerators.Questionnaires.Рокич());
            results.Add(new ReportGenerators.Questionnaires.Потребность_в_достижении());
            results.Add(new ReportGenerators.Questionnaires.Стиль_руководства());
            results.Add(new ReportGenerators.Questionnaires.Кеттелл());
            results.Add(new ReportGenerators.Questionnaires.Избегание_неудач_Элерс());
            results.Add(new ReportGenerators.Questionnaires.Люшер());
            results.Add(new ReportGenerators.Questionnaires.УСК());
            results.Add(new ReportGenerators.Questionnaires.Зунг());
            results.Add(new ReportGenerators.Questionnaires.Адаптивность());
            results.Add(new ReportGenerators.Questionnaires.Структура_темперамента_Смирнов());

            results.Add(new ReportGenerators.Questionnaires.Аддикция_и_аддиктивное_поведение());

            results.Add(new ReportGenerators.Psychophysiological.Оценка_устойчивости_внимания_УВ());
            results.Add(new ReportGenerators.Psychophysiological.Оценка_глазомера_ОГ());
            results.Add(new ReportGenerators.Psychophysiological.Концентрация_внимания_КВ());
            results.Add(new ReportGenerators.Psychophysiological.Переключение_внимания_ПВПУ());
            results.Add(new ReportGenerators.Psychophysiological.ГЭД1());
            results.Add(new ReportGenerators.Psychophysiological.ГЭД1М());
            results.Add(new ReportGenerators.Psychophysiological.ГЭД2());
            results.Add(new ReportGenerators.Psychophysiological.ГЭД2М());
            results.Add(new ReportGenerators.Psychophysiological.Теппинг310());
            results.Add(new ReportGenerators.Psychophysiological.ТеппингТест());

            return results;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\TestsGroupReportsFactory.cs


using System.Collections.Generic;
using System.Linq;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf
{
    internal class TestsGroupReportsFactory : ITestsGroupReportsFactory
    {
        private List<ITestsGroupReportGenerator> _generators = new List<ITestsGroupReportGenerator>();

        public TestsGroupReportsFactory()
        {
        }

        public FlowDocument CreateReport(ReportInfo reportInfo, Data.TestGroupResults results)
        {
            var generator = _generators.FirstOrDefault(g => g.CanGenerate(results));
            if (generator != null)
                return generator.CreateReport(reportInfo, results);

            return null;
        }

        public void SetReportGenerator(ITestsGroupReportGenerator generator)
        {
            _generators.Add(generator);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\BarChartDataGrid\BarChartDataCollection.cs


using System.Linq;
using System.Collections.ObjectModel;
using System.Collections.Generic;

namespace Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid
{
    public class BarChartDataGridDataCollection : Prism.Mvvm.BindableBase
    {
        public BarChartDataGridDataCollection()
        {
        }

        public ObservableCollection<BarChartDataGridData> Items { get; } = new ObservableCollection<BarChartDataGridData>();

        public BarChartDataGridData this[string name] => Items.First(d => d.Name == name);

        public BarChartDataGridData AddData(string name)
        {
            var data = new BarChartDataGridData() { Name = name };
            Items.Add(data);
            return data;
        }

        public BarChartDataGridData AddData(BarChartDataGridData data)
        {
            Items.Add(data);
            return data;
        }

        public void AddRange(IEnumerable<BarChartDataGridData> data)
        {
            foreach (var item in data)
                AddData(item);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\BarChartDataGrid\BarChartDataGridControl.xaml

<UserControl x:Class="Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid.BarChartDataGridControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid"
             mc:Ignorable="d"
             FontSize="14">
    <Grid
        DataContext="{Binding}">
        <DataGrid
            FontSize="{Binding FontSize, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"
            Grid.Row="1"
            ColumnWidth="*"
            IsHitTestVisible="False"
            AutoGenerateColumns="False"
            HeadersVisibility="None"
            BorderBrush="Transparent"
            BorderThickness="1"
            Background="Transparent"
            RowBackground="Transparent"
            Focusable="False"
            HorizontalGridLinesBrush="Transparent"
            VerticalGridLinesBrush="Transparent"
            VerticalScrollBarVisibility="Disabled"
            HorizontalScrollBarVisibility="Disabled"
            ItemsSource="{Binding Items}">
            <DataGrid.Resources>
                <!--Стиль текстовых блоков для DataGridTextColumn-->
                <Style x:Key="TextChartElementValueStyle" TargetType="{x:Type TextBlock}">
                    <Setter Property="VerticalAlignment" Value="Center" />
                    <Setter Property="HorizontalAlignment" Value="Center" />
                    <Setter Property="Padding" Value="5,1,5,1" />
                    <Setter Property="Focusable" Value="False" />
                    <Setter Property="TextWrapping" Value="NoWrap" />
                </Style>
                <Style 
                    x:Key="TextChartElementNameStyle" TargetType="{x:Type TextBlock}"
                    BasedOn="{StaticResource TextChartElementValueStyle}">
                    <Setter Property="VerticalAlignment" Value="Center" />
                    <Setter Property="HorizontalAlignment" Value="Left" />
                    <Setter Property="FontWeight" Value="{Binding DataContext.FontWeight, 
                                RelativeSource={RelativeSource Mode=FindAncestor, 
                                    AncestorType={x:Type DataGridCell}}}" />
                </Style>
                <Style 
                    x:Key="TextChartElementBarTitleStyle" 
                    TargetType="{x:Type TextBlock}"
                    BasedOn="{StaticResource TextChartElementValueStyle}">
                    <Setter Property="FontWeight" Value="Bold" />
                    <Setter Property="HorizontalAlignment" Value="Right" />
                </Style>
                <BooleanToVisibilityConverter x:Key="boolConverter" />
            </DataGrid.Resources>
            <!--Стиль ячейки таблицы - убираем рамку выделения ячейки-->
            <DataGrid.CellStyle>
                <Style TargetType="DataGridCell">
                    <Setter Property="BorderThickness" Value="0" />
                    <Style.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Background" Value="Transparent" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </DataGrid.CellStyle>
            <DataGrid.Columns>
                <DataGridTextColumn 
                    IsReadOnly="True"
                    Width="Auto"
                    Binding="{Binding Name, Mode=OneWay}"
                    ElementStyle="{StaticResource TextChartElementNameStyle}" 
                    />
                <DataGridTextColumn 
                    IsReadOnly="True"
                    Width="Auto"
                    Binding="{Binding ValueFormat, Mode=OneWay}"
                    ElementStyle="{StaticResource TextChartElementValueStyle}" 
                    />
                <DataGridTemplateColumn
                    IsReadOnly="True">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <DataTemplate.Resources>
                                
                            </DataTemplate.Resources>
                            <Grid>
                                <ProgressBar 
                                    Focusable="False"
                                    Margin="1,3,1,3"
                                    HorizontalAlignment="Stretch"
                                    VerticalAlignment="Center"
                                    Height="10"
                                    Maximum="{Binding Maximum, Mode=OneWay}"
                                    Value="{Binding Value, Mode=OneWay}"
                                    Foreground="{Binding Foreground}"
                                    Visibility="{Binding IsBarVisible, Converter={StaticResource boolConverter}}"
                                    Background="Transparent"
                                    BorderBrush="Transparent"
                                    BorderThickness="0"
                                    />
                            </Grid>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTextColumn 
                    IsReadOnly="True"
                    Width="Auto"
                    Binding="{Binding BarTitle, Mode=OneWay}"
                    ElementStyle="{StaticResource TextChartElementBarTitleStyle}" 
                    />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\BarChartDataGrid\BarChartDataGridControl.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid
{
    /// <summary>
    /// Interaction logic for BarChartDataGridControl.xaml
    /// </summary>
    public partial class BarChartDataGridControl : UserControl
    {
        public BarChartDataGridControl()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\BarChartDataGrid\BarChartDataGridData.cs


using System.Windows.Media;
using Prism.Mvvm;
using System.Windows;

namespace Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid
{
    public class BarChartDataGridData : BindableBase
    {
        public BarChartDataGridData()
        {
        }

        private string _name;

        public string Name
        {
            get { return _name; }
            set { SetProperty(ref _name, value); }
        }

        private Brush _Foreground;

        public Brush Foreground
        {
            get { return _Foreground; }
            set { SetProperty(ref _Foreground, value); }
        } 

        private double _value;

        public double Value
        {
            get { return _value; }
            set { SetProperty(ref _value, value); }
        }

        private double _maximum;

        public double Maximum
        {
            get { return _maximum; }
            set { SetProperty(ref _maximum, value); }
        }

        private string _valueFormat;

        public string ValueFormat
        {
            get { return _valueFormat; }
            set { SetProperty(ref _valueFormat, value); }
        }

        private string _barTitle;

        public string BarTitle
        {
            get { return _barTitle; }
            set { SetProperty(ref _barTitle, value); }
        } 

        private bool _isBarVisible = true;

        public bool IsBarVisible
        {
            get { return _isBarVisible; }
            set { SetProperty(ref _isBarVisible, value); }
        }

        private FontWeight _fontWeight = FontWeights.Normal;

        public FontWeight FontWeight
        {
            get { return _fontWeight; }
            set { SetProperty(ref _fontWeight, value); }
        } 
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\EysenckChart\EysenckChartControl.cs


using System.Windows;
using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.Controls.EysenckChart
{
    public class EysenckChartControl : FrameworkElement
    {
        private VisualCollection _visuals;

        public EysenckChartControl()
        {
            initializeControl();
        }

        public EysenckXScale XScale { get; } = new EysenckXScale();

        public EysenckYScale YScale { get; } = new EysenckYScale();

        public EysenckDataGrid DataGrid { get; } = new EysenckDataGrid();

        public EysenckTresholdAreasCollection Tresholds
        {
            get { return (EysenckTresholdAreasCollection)GetValue(TresholdsProperty); }
            set { SetValue(TresholdsProperty, value); }
        }

        public static readonly DependencyProperty TresholdsProperty =
            DependencyProperty.Register(
                nameof(Tresholds),
                typeof(EysenckTresholdAreasCollection),
                typeof(EysenckChartControl),
                new FrameworkPropertyMetadata(null, onTresholdsPropertyChanged));

        protected override int VisualChildrenCount => _visuals.Count;

        protected override Size MeasureOverride(Size availableSize)
        {
            var infinitySize = new Size(double.PositiveInfinity, double.PositiveInfinity);
            XScale.Measure(infinitySize);
            YScale.Measure(infinitySize);

            return availableSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            var xHeight = XScale.DesiredSize.Height;
            var yWidth = YScale.DesiredSize.Width;

            var xRect = new Rect(
                yWidth,
                0.0,
                finalSize.Width - yWidth,
                XScale.DesiredSize.Height);
            XScale.Arrange(xRect);

            var yRect = new Rect(
                0.0,
                xHeight,
                YScale.DesiredSize.Width,
                finalSize.Height - xHeight);
            YScale.Arrange(yRect);

            var dataRect = new Rect(
                yWidth,
                xHeight,
                xRect.Width,
                yRect.Height);
            DataGrid.Arrange(dataRect);

            return finalSize;
        }

        protected override Visual GetVisualChild(int index)
        {
            return _visuals[index];
        }

        private void onDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue is EysenckDataPointsCollection)
                DataGrid.DataPoints = e.NewValue as EysenckDataPointsCollection;
        }

        private static void onTresholdsPropertyChanged(DependencyObject d, 
            DependencyPropertyChangedEventArgs e)
        {
            var chart = d as EysenckChartControl;
            chart.DataGrid.Tresholds = e.NewValue as EysenckTresholdAreasCollection;
        }

        private void initializeControl()
        {
            _visuals = new VisualCollection(this);
            _visuals.Add(DataGrid);
            _visuals.Add(XScale);
            _visuals.Add(YScale);

            DataGrid.XScale = XScale;
            DataGrid.YScale = YScale;

            DataContextChanged += onDataContextChanged;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\EysenckChart\EysenckChartLegendControl.xaml

<UserControl x:Class="Updk7.Tests.Reports.Wpf.Controls.EysenckChart.EysenckChartLegendControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Tests.Reports.Wpf.Controls.EysenckChart"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <ItemsControl
            ItemsSource="{Binding}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid
                        Margin="5,1,1,1">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Rectangle 
                            Grid.Column="0"
                            Width="15"
                            Height="15"
                            Fill="{Binding Foreground}"
                            Stroke="Black"
                            StrokeThickness="1"
                            />
                        <TextBlock 
                            Grid.Column="1"
                            Margin="5,0,0,0"
                            FontSize="14"
                            Text="{Binding Title}" 
                            />
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\EysenckChart\EysenckChartLegendControl.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Tests.Reports.Wpf.Controls.EysenckChart
{
    /// <summary>
    /// Interaction logic for EysenckChartLegendControl.xaml
    /// </summary>
    public partial class EysenckChartLegendControl : UserControl
    {
        public EysenckChartLegendControl()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\EysenckChart\EysenckChartScale.cs


using System.Windows;

namespace Updk7.Tests.Reports.Wpf.Controls.EysenckChart
{
    public class EysenckChartScale : FrameworkElement
    {
        public EysenckChartScale()
        {
        }

        public double TitleFontSize
        {
            get { return (double)GetValue(TitleFontSizeProperty); }
            set { SetValue(TitleFontSizeProperty, value); }
        }

        public static readonly DependencyProperty TitleFontSizeProperty =
            DependencyProperty.Register(
                nameof(TitleFontSize),
                typeof(double),
                typeof(EysenckChartScale),
                new FrameworkPropertyMetadata(12.0,
                    FrameworkPropertyMetadataOptions.AffectsMeasure |
                    FrameworkPropertyMetadataOptions.AffectsArrange |
                    FrameworkPropertyMetadataOptions.AffectsRender));

        public double TicksFontSize
        {
            get { return (double)GetValue(TicksFontSizeProperty); }
            set { SetValue(TicksFontSizeProperty, value); }
        }

        public static readonly DependencyProperty TicksFontSizeProperty =
            DependencyProperty.Register(
                nameof(TicksFontSize),
                typeof(double),
                typeof(EysenckChartScale),
                 new FrameworkPropertyMetadata(10.0,
                    FrameworkPropertyMetadataOptions.AffectsMeasure |
                    FrameworkPropertyMetadataOptions.AffectsArrange |
                    FrameworkPropertyMetadataOptions.AffectsRender));
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\EysenckChart\EysenckChartUtils.cs


using System.Windows;
using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.Controls.EysenckChart
{
    internal static class EysenckChartUtils
    {
        public static FormattedText CreateFormattedText(string text, double fontSize)
        {
            var typeface = new Typeface(new FontFamily("Consolas"), FontStyles.Normal,
                FontWeights.Normal, FontStretches.Normal);

            // Коэффициент 1.0 - отношение DPI монитора к 96 (DPI по-умолчанию). Обратить внимание,
            // возможно некоторые визуальные элементы могут изменить свой размер
            return new FormattedText(text, System.Globalization.CultureInfo.InvariantCulture,
                FlowDirection.LeftToRight, typeface, fontSize, Brushes.Black, 1.0);
        }

        public static void DrawControlBox(FrameworkElement visual, DrawingContext drawingContext, Brush brush)
        {
            var pen = new Pen(brush, 1.0);
            var rect = new Rect(0.0, 0.0, visual.ActualWidth, visual.ActualHeight);

            drawingContext.DrawRectangle(null, pen, rect);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\EysenckChart\EysenckDataGrid.cs


using System;
using System.Windows;
using System.Windows.Media;
using System.Collections.Specialized;

namespace Updk7.Tests.Reports.Wpf.Controls.EysenckChart
{
    public class EysenckDataGrid : FrameworkElement
    {
        private Pen _gridPen = new Pen(Brushes.Gray, 1.0);

        public EysenckDataGrid()
        {
        }

        public EysenckDataPointsCollection DataPoints
        {
            get { return (EysenckDataPointsCollection)GetValue(DataPointsProperty); }
            set { SetValue(DataPointsProperty, value); }
        }

        public static readonly DependencyProperty DataPointsProperty =
            DependencyProperty.Register(
                nameof(DataPoints),
                typeof(EysenckDataPointsCollection),
                typeof(EysenckDataGrid),
                new FrameworkPropertyMetadata(
                    null,
                    FrameworkPropertyMetadataOptions.AffectsRender,
                    onDataCollectionPropertyChanged<EysenckDataPointsCollection>));

        public EysenckTresholdAreasCollection Tresholds
        {
            get { return (EysenckTresholdAreasCollection)GetValue(TresholdsProperty); }
            set { SetValue(TresholdsProperty, value); }
        }

        public static readonly DependencyProperty TresholdsProperty =
            DependencyProperty.Register(
                nameof(Tresholds),
                typeof(EysenckTresholdAreasCollection),
                typeof(EysenckDataGrid),
                new FrameworkPropertyMetadata(
                    null,
                    FrameworkPropertyMetadataOptions.AffectsRender,
                    onDataCollectionPropertyChanged<EysenckTresholdAreasCollection>));

        public EysenckXScale XScale
        {
            get { return (EysenckXScale)GetValue(XScaleProperty); }
            set { SetValue(XScaleProperty, value); }
        }

        public static readonly DependencyProperty XScaleProperty =
            DependencyProperty.Register(
                nameof(XScale),
                typeof(EysenckXScale),
                typeof(EysenckDataGrid),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));

        public EysenckYScale YScale
        {
            get { return (EysenckYScale)GetValue(YScaleProperty); }
            set { SetValue(YScaleProperty, value); }
        }

        public static readonly DependencyProperty YScaleProperty =
            DependencyProperty.Register(
                nameof(YScale),
                typeof(EysenckYScale),
                typeof(EysenckDataGrid),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));

        protected override void OnRender(DrawingContext drawingContext)
        {
            if (XScale != null && YScale != null)
            {
                var clipRect = new RectangleGeometry(new Rect(0.0, 0.0, ActualWidth, ActualHeight));
                drawingContext.PushClip(clipRect);

                try
                {
                    drawTresholds(drawingContext);
                    drawHorizontalGrid(drawingContext);
                    drawVerticalGrid(drawingContext);
                    drawDataPoints(drawingContext);
                }
                finally
                {
                    drawingContext.Pop();
                }
            }

            EysenckChartUtils.DrawControlBox(this, drawingContext, Brushes.Black);
        }

        private void drawTresholds(DrawingContext drawingContext)
        {
            if (Tresholds == null)
                return;

            var rect = new Rect();
            foreach (var treshold in Tresholds)
            {
                rect.X = XScale.GetTickCoordinate(treshold.X);
                rect.Y = YScale.GetTickCoordinate(treshold.Y);
                rect.Width = XScale.GetTickCoordinate(treshold.Width);
                rect.Height = YScale.GetTickCoordinate(0) - YScale.GetTickCoordinate(treshold.Height);

                drawingContext.DrawRectangle(treshold.Foreground, null, rect);
            }
        }

        private void drawHorizontalGrid(DrawingContext drawingContext)
        {
            var point0 = new Point();
            var point1 = new Point();

            for (int i = 0; i < YScale.TicksCount; i++)
            {
                point0.X = 0;
                point0.Y = YScale.GetTickCoordinate(i);
                point1.X = ActualWidth;
                point1.Y = point0.Y;

                drawingContext.DrawLine(_gridPen, point0, point1);
            }
        }

        private void drawVerticalGrid(DrawingContext drawingContext)
        {
            var point0 = new Point();
            var point1 = new Point();

            for (int i = 0; i < XScale.TicksCount; i++)
            {
                point0.X = XScale.GetTickCoordinate(i);
                point0.Y = 0.0;
                point1.X = point0.X;
                point1.Y = ActualHeight;

                drawingContext.DrawLine(_gridPen, point0, point1);
            }
        }

        private void drawDataPoints(DrawingContext drawingContext)
        {
            if (DataPoints == null)
                return;

            var rect = new Rect();
            var pointWidth = Math.Abs(XScale.GetTickCoordinate(1) - XScale.GetTickCoordinate(0));
            var pointHeight = Math.Abs(YScale.GetTickCoordinate(1) - YScale.GetTickCoordinate(0));

            foreach (var point in DataPoints)
            {
                rect.X = XScale.GetTickCoordinate(point.X);
                rect.Y = YScale.GetTickCoordinate(point.Y) - pointHeight;
                rect.Width = pointWidth;
                rect.Height = pointHeight;

                drawingContext.DrawRectangle(Brushes.Black, null, rect);
            }
        }

        private void onDataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            InvalidateVisual();
        }

        private static void onDataCollectionPropertyChanged<T>(DependencyObject d,
            DependencyPropertyChangedEventArgs e)
            where T : class, INotifyCollectionChanged
        {
            var grid = d as EysenckDataGrid;

            var oldValue = e.OldValue as T;
            if (oldValue != null)
                oldValue.CollectionChanged -= grid.onDataCollectionChanged;

            var newValue = e.NewValue as T;
            if (newValue != null)
                newValue.CollectionChanged += grid.onDataCollectionChanged;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\EysenckChart\EysenckDataPoint.cs


namespace Updk7.Tests.Reports.Wpf.Controls.EysenckChart
{
    public class EysenkDataPoint
    {
        public EysenkDataPoint()
        {
        }

        public EysenkDataPoint(int x, int y)
        {
            X = x;
            Y = y;
        }

        public int X { get; set; }

        public int Y { get; set; }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\EysenckChart\EysenckDataPointsCollection.cs


using System.Collections.ObjectModel;

namespace Updk7.Tests.Reports.Wpf.Controls.EysenckChart
{
    public class EysenckDataPointsCollection : ObservableCollection<EysenkDataPoint>
    {
        public void Add(int x, int y)
        {
            Add(new EysenkDataPoint(x, y));
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\EysenckChart\EysenckTresholdArea.cs


using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.Controls.EysenckChart
{
    public class EysenckTresholdArea
    {
        public EysenckTresholdArea()
        {
        }

        public EysenckTresholdArea(string title, Brush foreground, int x, int y, int width, int height)
        {
            Title = title;
            Foreground = foreground;
            X = x;
            Y = y;
            Width = width;
            Height = height;
        }

        public string Title { get; set; }

        public Brush Foreground { get; set; } = Brushes.DeepSkyBlue;

        public int X { get; set; }

        public int Y { get; set; }

        public int Width { get; set; }

        public int Height { get; set; }

    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\EysenckChart\EysenckTresholdAreasCollection.cs


using System.Collections.ObjectModel;

namespace Updk7.Tests.Reports.Wpf.Controls.EysenckChart
{
    public class EysenckTresholdAreasCollection : ObservableCollection<EysenckTresholdArea>
    {
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\EysenckChart\EysenckXScale.cs


using System;
using System.Windows;
using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.Controls.EysenckChart
{
    public class EysenckXScale : EysenckChartScale
    {
        private static readonly string Title = "Экстраверсия/Интроверсия";
        private const double Padding = 3.0;
        private const double TicksHeight = 6.0;
        private Pen _pen = new Pen(Brushes.Black, 1.0);

        public EysenckXScale()
        {
        }

        public double TicksCount { get; } = 26;

        public double TicksStep { get; private set; }

        public double GetTickCoordinate(int tickValue)
        {
            return tickValue * TicksStep;
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            var titleSize = EysenckChartUtils.CreateFormattedText(Title, TitleFontSize);
            var ticksSize = EysenckChartUtils.CreateFormattedText("D", TicksFontSize);
            var desiredHeight = Math.Ceiling(titleSize.Height + ticksSize.Height + 3 * Padding);

            return new Size(0, desiredHeight);
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            drawTitle(drawingContext);
            drawTicks(drawingContext);
        }

        private void drawTitle(DrawingContext drawingContext)
        {
            var formattedText = EysenckChartUtils.CreateFormattedText(Title, TitleFontSize);
            var originX = Math.Max((ActualWidth - formattedText.Width) / 2.0, 0.0);
            var originPoint = new Point(originX, Padding);
            drawingContext.DrawText(formattedText, originPoint);
        }

        private void drawTicks(DrawingContext drawingContext)
        {
            TicksStep = ActualWidth / TicksCount;
            var point0 = new Point(0.0, ActualHeight);
            var point1 = new Point(ActualWidth, ActualHeight);

            drawingContext.DrawLine(_pen, point0, point1);

            for (var i = 0; i < TicksCount + 1; i++)
            {
                point0.X = GetTickCoordinate(i);
                point0.Y = ActualHeight - TicksHeight;
                point1.X = point0.X;
                point1.Y = ActualHeight;

                drawingContext.DrawLine(_pen, point0, point1);

                if (i < TicksCount)
                    drawTickLabel(drawingContext, point0, TicksStep, i.ToString());
            }
        }

        private void drawTickLabel(DrawingContext drawingContext, Point leftTick,
            double tickWidth, string label)
        {
            var text = EysenckChartUtils.CreateFormattedText(label, TicksFontSize);
            var originX = leftTick.X + (tickWidth - text.Width) / 2.0;
            var originY = ActualHeight - Padding - text.Height;
            drawingContext.DrawText(text, new Point(originX, originY));
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\EysenckChart\EysenckYScale.cs


using System;
using System.Windows;
using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.Controls.EysenckChart
{
    public class EysenckYScale : EysenckChartScale
    {
        private static readonly string Title = "Нейротизм";
        private const double Padding = 3.0;
        private const double TicksWidth = 6.0;
        private Pen _pen = new Pen(Brushes.Black, 1.0);

        public EysenckYScale()
        {
        }

        public int TicksCount { get; } = 26;

        public double TicksStep { get; private set; }

        public double GetTickCoordinate(int tickValue)
        {
            return ActualHeight - tickValue * TicksStep;
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            var titleSize = EysenckChartUtils.CreateFormattedText(Title, TitleFontSize);
            var ticksSize = EysenckChartUtils.CreateFormattedText(TicksCount.ToString(), TicksFontSize);
            var desiredWidth = Math.Ceiling(titleSize.Height + ticksSize.Width + 3 * Padding);

            return new Size(desiredWidth, 0.0);
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            drawTitle(drawingContext);
            drawTicks(drawingContext);
        }

        private void drawTitle(DrawingContext drawingContext)
        {
            var formattedText = EysenckChartUtils.CreateFormattedText(Title, TitleFontSize);
            var originPoint = new Point(-(ActualHeight + formattedText.Width) / 2.0, Padding);

            var transform = new RotateTransform(-90.0);
            drawingContext.PushTransform(transform);
            drawingContext.DrawText(formattedText, originPoint);
            drawingContext.Pop();
        }

        private void drawTicks(DrawingContext drawingContext)
        {
            TicksStep = ActualHeight / TicksCount;

            var point0 = new Point(ActualWidth, 0.0);
            var point1 = new Point(ActualWidth, ActualHeight);

            drawingContext.DrawLine(_pen, point0, point1);

            for (var i = 0; i < TicksCount + 1; i++)
            {
                point0.X = ActualWidth;
                point0.Y = GetTickCoordinate(i);
                point1.X = point0.X - TicksWidth;
                point1.Y = point0.Y;

                drawingContext.DrawLine(_pen, point0, point1);

                if (i < TicksCount)
                    drawTickLabel(drawingContext, point0, TicksStep, i.ToString());
            }
        }

        private void drawTickLabel(DrawingContext drawingContext, Point bottomPoint, double tickHeight,
            string label)
        {
            var text = EysenckChartUtils.CreateFormattedText(label, TicksFontSize);
            var originX = bottomPoint.X - text.Width - Padding;
            var originY = bottomPoint.Y - (tickHeight + text.Height) / 2.0;
            drawingContext.DrawText(text, new Point(originX, originY));
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\SmirnovChart\SmirnovChartControl.xaml

<UserControl x:Class="Updk7.Tests.Reports.Wpf.Controls.SmirnovChart.SmirnovChartControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Tests.Reports.Wpf.Controls.SmirnovChart"
             mc:Ignorable="d">
    <Grid
        Margin="3"
        DataContext="{Binding}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="83" />
            <ColumnDefinition Width="83" />
            <ColumnDefinition Width="83" />
            <ColumnDefinition Width="83" />
            <ColumnDefinition Width="83" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.Resources>
            <Style x:Key="AxisTitleLeftStyle" TargetType="{x:Type TextBlock}">
                <Setter Property="FontSize" Value="12" />
                <Setter Property="FontWeight" Value="Medium" />
                <Setter Property="VerticalAlignment" Value="Center"/>
                <Setter Property="HorizontalAlignment" Value="Right" />
                <Setter Property="Margin" Value="0,0,3,0" />
            </Style>
            <Style x:Key="AxisTitleRightStyle" TargetType="{x:Type TextBlock}">
                <Setter Property="FontSize" Value="12" />
                <Setter Property="FontWeight" Value="Medium" />
                <Setter Property="VerticalAlignment" Value="Center"/>
                <Setter Property="HorizontalAlignment" Value="Left" />
                <Setter Property="Margin" Value="3,0,0,0" />
            </Style>
            <Style x:Key="AxisValueStyle" TargetType="{x:Type TextBlock}">
                <Setter Property="FontSize" Value="10" />
                <Setter Property="FontWeight" Value="Bold" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <Setter Property="HorizontalAlignment" Value="Center" />
            </Style>
            <Style TargetType="{x:Type Rectangle}" >
                <Setter Property="Stroke" Value="DarkGray" />
                <Setter Property="StrokeThickness" Value="1" />
                <Setter Property="VerticalAlignment" Value="Stretch" />
                <Setter Property="HorizontalAlignment" Value="Stretch" />
                <Setter Property="Margin" Value="-0.5" />
            </Style>
        </Grid.Resources>
        <!--Левые оси-->
        <TextBlock />
        <TextBlock Grid.Row="1" Grid.Column="0" Text="Экстраверсия" Style="{StaticResource AxisTitleLeftStyle}" />
        <TextBlock Grid.Row="2" Grid.Column="0" Text="Ригидность" Style="{StaticResource AxisTitleLeftStyle}" />
        <TextBlock Grid.Row="3" Grid.Column="0" Text="Возбудимость" Style="{StaticResource AxisTitleLeftStyle}" />
        <TextBlock Grid.Row="4" Grid.Column="0" Text="Быстрота реакции" Style="{StaticResource AxisTitleLeftStyle}" />
        <TextBlock Grid.Row="5" Grid.Column="0" Text="Активность" Style="{StaticResource AxisTitleLeftStyle}" />
        <!--Правые оси-->
        <TextBlock Grid.Row="1" Grid.Column="6" Text="Интроверсия" Style="{StaticResource AxisTitleRightStyle}" />
        <TextBlock Grid.Row="2" Grid.Column="6" Text="Пластичность" Style="{StaticResource AxisTitleRightStyle}" />
        <TextBlock Grid.Row="3" Grid.Column="6" Text="Устойчивость" Style="{StaticResource AxisTitleRightStyle}" />
        <TextBlock Grid.Row="4" Grid.Column="6" Text="Медлительность" Style="{StaticResource AxisTitleRightStyle}" />
        <TextBlock Grid.Row="5" Grid.Column="6" Text="Пассивность" Style="{StaticResource AxisTitleRightStyle}" />
        <!--Подписи осей-->
        <TextBlock Grid.Row="0" Grid.Column="1" Text="Очень высокий" Style="{StaticResource AxisValueStyle}" />
        <TextBlock Grid.Row="0" Grid.Column="2" Text="Высокий" Style="{StaticResource AxisValueStyle}" />
        <TextBlock Grid.Row="0" Grid.Column="3" Text="Средний" Style="{StaticResource AxisValueStyle}" />
        <TextBlock Grid.Row="0" Grid.Column="4" Text="Высокий" Style="{StaticResource AxisValueStyle}" />
        <TextBlock Grid.Row="0" Grid.Column="5" Text="Очень высокий" Style="{StaticResource AxisValueStyle}" />
        <!--Шкалы-->
        <!--Экстраверсия-->
        <Rectangle Grid.Row="1" Grid.Column="1" Fill="{Binding Extraversion, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Red,MatchValue=-2}}" />
        <Rectangle Grid.Row="1" Grid.Column="2" Fill="{Binding Extraversion, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Orange,MatchValue=-1}}" />
        <Rectangle Grid.Row="1" Grid.Column="3" Fill="{Binding Extraversion, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Yellow,MatchValue=0}}" />
        <Rectangle Grid.Row="1" Grid.Column="4" Fill="{Binding Extraversion, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Orange,MatchValue=1}}" />
        <Rectangle Grid.Row="1" Grid.Column="5" Fill="{Binding Extraversion, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Red,MatchValue=2}}" />
        <!--Ригидность-->
        <Rectangle Grid.Row="2" Grid.Column="1" Fill="{Binding Rigidity, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Red,MatchValue=-2}}" />
        <Rectangle Grid.Row="2" Grid.Column="2" Fill="{Binding Rigidity, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Orange,MatchValue=-1}}" />
        <Rectangle Grid.Row="2" Grid.Column="3" Fill="{Binding Rigidity, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Yellow,MatchValue=0}}" />
        <Rectangle Grid.Row="2" Grid.Column="4" Fill="{Binding Rigidity, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Orange,MatchValue=1}}" />
        <Rectangle Grid.Row="2" Grid.Column="5" Fill="{Binding Rigidity, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Red,MatchValue=2}}" />
        <!--Эмоциональная возбудимость-->
        <Rectangle Grid.Row="3" Grid.Column="1" Fill="{Binding Emotional, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Red,MatchValue=-2}}" />
        <Rectangle Grid.Row="3" Grid.Column="2" Fill="{Binding Emotional, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Orange,MatchValue=-1}}" />
        <Rectangle Grid.Row="3" Grid.Column="3" Fill="{Binding Emotional, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Yellow,MatchValue=0}}" />
        <Rectangle Grid.Row="3" Grid.Column="4" Fill="{Binding Emotional, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Orange,MatchValue=1}}" />
        <Rectangle Grid.Row="3" Grid.Column="5" Fill="{Binding Emotional, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Red,MatchValue=2}}" />
        <!--Быстрота реакции-->
        <Rectangle Grid.Row="4" Grid.Column="1" Fill="{Binding Reaction, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Red,MatchValue=-2}}" />
        <Rectangle Grid.Row="4" Grid.Column="2" Fill="{Binding Reaction, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Orange,MatchValue=-1}}" />
        <Rectangle Grid.Row="4" Grid.Column="3" Fill="{Binding Reaction, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Yellow,MatchValue=0}}" />
        <Rectangle Grid.Row="4" Grid.Column="4" Fill="{Binding Reaction, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Orange,MatchValue=1}}" />
        <Rectangle Grid.Row="4" Grid.Column="5" Fill="{Binding Reaction, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Red,MatchValue=2}}" />
        <!--Активность-->
        <Rectangle Grid.Row="5" Grid.Column="1" Fill="{Binding Activity, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Red,MatchValue=-2}}" />
        <Rectangle Grid.Row="5" Grid.Column="2" Fill="{Binding Activity, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Orange,MatchValue=-1}}" />
        <Rectangle Grid.Row="5" Grid.Column="3" Fill="{Binding Activity, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Yellow,MatchValue=0}}" />
        <Rectangle Grid.Row="5" Grid.Column="4" Fill="{Binding Activity, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Orange,MatchValue=1}}" />
        <Rectangle Grid.Row="5" Grid.Column="5" Fill="{Binding Activity, Converter={local:SmirnovValueToBackgroundConverter MatchBrush=Red,MatchValue=2}}" />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\SmirnovChart\SmirnovChartControl.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Tests.Reports.Wpf.Controls.SmirnovChart
{
    /// <summary>
    /// Interaction logic for SmirnovChartControl.xaml
    /// </summary>
    public partial class SmirnovChartControl : UserControl
    {
        public SmirnovChartControl()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\SmirnovChart\SmirnovChartViewModel.cs


namespace Updk7.Tests.Reports.Wpf.Controls.SmirnovChart
{
    public class SmirnovChartViewModel
    {
        public SmirnovChartViewModel()
        {
        }

        public int Extraversion { get; set; } = -2;

        public int Rigidity { get; set; } = -1;

        public int Emotional { get; set; } = 0;

        public int Reaction { get; set; } = 1;

        public int Activity { get; set; } = 2;
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Controls\SmirnovChart\SmirnovValueToBackgroundConverter.cs


using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.Controls.SmirnovChart
{
    public class SmirnovValueToBackgroundConverter : MarkupExtension, IValueConverter
    {
        public SmirnovValueToBackgroundConverter()
        {
        }

        public Brush DefaultBrush = Brushes.Transparent;

        public Brush MatchBrush { get; set; } = Brushes.Red;

        public int MatchValue { get; set; } = 0;

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is int && (int)value == MatchValue)
                return MatchBrush;

            return DefaultBrush;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Extensions\BlockExtensions.cs


using System.Collections.Generic;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf
{
    internal static class BlockExtensions
    {
        public static IEnumerable<Block> AsEnumerable(this Block block)
        {
            yield return block;
        }

        public static IEnumerable<Block> Append(this Block first, Block second)
        {
            yield return first;
            yield return second;
        }

        public static IEnumerable<Block> Append(this Block head, IEnumerable<Block> tail)
        {
            yield return head;
            foreach (var block in tail)
                yield return block;
        }

        public static IEnumerable<Block> Append(this IEnumerable<Block> sequence, Block value)
        {
            foreach (var item in sequence)
                yield return item;

            yield return value;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Extensions\DocumentsExtensions.cs


using System.IO;
using System.IO.Packaging;
using System.Windows.Threading;
using System.Windows.Xps.Packaging;
using System.Windows.Xps.Serialization;

namespace System.Windows.Documents
{
    public static class DocumentsExtensions
    {
        public static void BindDataContext(this FixedDocument document, object dataSource)
        {
            document.DataContext = dataSource;

            var dispatcher = Dispatcher.CurrentDispatcher;
            dispatcher.Invoke(
               DispatcherPriority.SystemIdle,
               new DispatcherOperationCallback(delegate { return null; }),
               null);
        }

        public static void BindDataContext(this FlowDocument document, object dataSource)
        {
            document.DataContext = dataSource;

            var dispatcher = Dispatcher.CurrentDispatcher;
            dispatcher.Invoke(
               DispatcherPriority.SystemIdle,
               new DispatcherOperationCallback(delegate { return null; }),
               null);
        }

        public static void SaveToXps(this FixedDocument document, string xpsFilename)
        {
            using (var stream = new FileStream(xpsFilename, FileMode.Create))
            using (var package = Package.Open(stream, FileMode.Create, FileAccess.ReadWrite))
            using (var xpsDoc = new XpsDocument(package, CompressionOption.NotCompressed))
            {
                var rsm = new XpsSerializationManager(new XpsPackagingPolicy(xpsDoc), false);
                var paginator = ((IDocumentPaginatorSource)document).DocumentPaginator;
                rsm.SaveAsXaml(paginator);
                rsm.Commit();
            }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Generic\TableGenerator.cs


using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.Generic
{
    public class TableGenerator
    {
        public class ColumnProperties
        {
            public ColumnProperties()
            {
            }

            public FontWeight FontWeight { get; set; } = FontWeights.Normal;

            public double FontSize { get; set; } = 12;

            public TextAlignment TextAlignment { get; set; } = TextAlignment.Center;

            public GridLength Width { get; set; } = new GridLength(1, GridUnitType.Star);
        }

        private List<Paragraph[]> _rowCells = new List<Paragraph[]>();

        public TableGenerator(int columnsCount)
        {
            Columns = createColumnProperties(columnsCount);
        }

        public ColumnProperties[] Columns { get; }

        public double FontSize { get; set; } = 12;

        public TableGenerator AddRow(params string[] cellsText)
        {
            var row = new Paragraph[cellsText.Length];
            for (int i = 0; i < cellsText.Length; i++)
            {
                var paragraph = new Paragraph(new Run(cellsText[i]));
                paragraph.TextAlignment = Columns[i].TextAlignment;
                paragraph.FontSize = Columns[i].FontSize;
                paragraph.FontWeight = Columns[i].FontWeight;
                paragraph.Margin = new Thickness(3, 2, 1, 2);

                row[i] = paragraph;
            }

            _rowCells.Add(row);
            return this;
        }

        public TableGenerator AddRow(params Paragraph[] cells)
        {
            _rowCells.Add(cells);
            return this;
        }

        public Table CreateTable()
        {
            var table = new Table();
            table.FontSize = FontSize;
            table.CellSpacing = 0;
            table.BorderBrush = Brushes.Black;
            table.BorderThickness = new Thickness(0.5);

            var tableRowGroup = new TableRowGroup();
            table.RowGroups.Add(tableRowGroup);

            for (var i = 0; i < Columns.Length; i++)
                table.Columns.Add(new TableColumn() { Width = Columns[i].Width });

            foreach (var row in _rowCells)
            {
                var tableRow = new TableRow();
                for (var i = 0; i < row.Length; i++)
                {
                    var cell = new TableCell();
                    cell.Blocks.Add(row[i]);
                    cell.BorderThickness = new Thickness(0.5);
                    cell.BorderBrush = Brushes.Black;
                    tableRow.Cells.Add(cell);
                }

                tableRowGroup.Rows.Add(tableRow);
            }

            return table;
        }

        private ColumnProperties[] createColumnProperties(int columnsCount)
        {
            var columns = new ColumnProperties[columnsCount];
            for (var i = 0; i < columnsCount; i++)
                columns[i] = new ColumnProperties();

            return columns;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Generic\TestGroupTicketGenerator.cs


using System;
using System.Windows;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.Generic
{
    public class TestGroupTicketGenerator
    {
        private Lazy<ResourceDictionary> _resources;

        public TestGroupTicketGenerator()
        {
            _resources = new Lazy<ResourceDictionary>(() =>
            {
                var path = $"/Updk7.Tests.Reports.Wpf;component/Resources/TestGroupTicket.xaml";
                return new ResourceDictionary() { Source = new Uri(path, UriKind.Relative) };
            });
        }

        public FlowDocument CreateTicket(TestGroupTicketInfo info)
        {
            var section = _resources.Value["TicketForm"] as Section;

            var document = ReportHelper.CreateBlankDocument();
            document.Blocks.Add(section);
            document.BindDataContext(info);

            return document;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Generic\TestGroupTicketInfo.cs


using System;

namespace Updk7.Tests.Reports.Wpf.Generic
{
    public class TestGroupTicketInfo
    {
        public TestGroupTicketInfo()
        {
        }

        public string Title { get; set; } = "ТАЛОН ПСИХОФИЗИОЛОГИЧЕСКОГО ОБСЛЕДОВАНИЯ";

        public string Goal { get; set; } = "периодическое психофизиологическое обследование";

        public string FullName { get; set; } = "Бубулька Б.Б.";

        public string Profession { get; set; } = "Нажиматель педальки";

        public string Traffic { get; set; } = "Ползучие самокаты";

        public string AssignedGroup { get; set; } = "Нет";

        public string FailedTests { get; set; }

        public DateTime? ExaminationDate { get; set; }

        public DateTime? NextExamination { get; set; }

        public string UserName { get; set; } = "Шлёп Нога Пяткова";

        public string Conclusion { get; set; } = "продолжать заключать";

        public string Organization { get; set; } = "Общество зеленых мандаринов";
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\GenericReportGenerator.cs


using System.Collections.Generic;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators
{
    internal class GenericReportGenerator : TestReportGenerator
    {
        public GenericReportGenerator(TestType testType = TestType.Готовность_к_тестированию_ЭПФС) 
            : base(string.Empty)
        {
            TestType = testType;
        }

        public override FlowDocument CreateReport(ReportInfo reportInfo, TestResults results)
        {
            var viewModel = new ViewModels.GenericReportTemplateViewModel();
            viewModel.Title = GetTestName(results.Test);
            viewModel.TesteeInfo.SetInfo(reportInfo);
            viewModel.TestDate = results.BeginTime;
            viewModel.OrganizationTitle = reportInfo.OrganizationTitle;
            viewModel.UserName = reportInfo.UserName;

            var header = CreateHeader(new Views.GenericReportHeaderView());
            var bodyBlocks = CreateReportBody(reportInfo, results);
            var footer = CreateFooter(new Views.GenericReportFooterView());

            var reportBlocks = header.Append(bodyBlocks).Append(footer);
            var document = CreateDocument(viewModel, reportBlocks);
            
            return document;
        }

        protected virtual IEnumerable<Block> CreateReportBody(ReportInfo info, TestResults results)
        {
            var resultsTable = new ReportBlocks.ResultsTable();
            resultsTable.Results = results;

            return resultsTable.CreateDocumentBlock().AsEnumerable();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\ReportColors.cs


using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators
{
    internal static class ReportColors
    {
        public static readonly Brush DefaultScale = new SolidColorBrush(Colors.LimeGreen);
        public static readonly Brush NormalityLow = new SolidColorBrush(Colors.Blue);
        public static readonly Brush NormalityNormal = new SolidColorBrush(Colors.Gray);
        public static readonly Brush NormalityHigh = new SolidColorBrush(Colors.Red);

        public static readonly Brush TendentionLow = new SolidColorBrush(Colors.Blue);
        public static readonly Brush TendentionNormal = new SolidColorBrush(Colors.Gray);
        public static readonly Brush TendentionMedium = new SolidColorBrush(Colors.Yellow);
        public static readonly Brush TendentionHigh = new SolidColorBrush(Colors.Red);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\ReportStrings.cs


namespace Updk7.Tests.Reports.Wpf.ReportGenerators
{
    internal static class ReportStrings
    {
        public static readonly string NormalityLow = "Ниже нормы";
        public static readonly string NoralityNormal = "Норма";
        public static readonly string NormalityHigh = "Выше нормы";

        public static readonly string TendentionLow = "Низкий уровень";
        public static readonly string TendentionNormal = "Признаки тенденции";
        public static readonly string TendentionMedium = "Признаки повышенной склонности";
        public static readonly string TendentionHigh = "Признаки высокой вероятности";

        public static readonly string BaseScales = "Основные шкалы";
        public static readonly string OptionalScales = "Дополнительные шкалы";
        public static readonly string EstimationScales = "Оценочные шкалы";
        public static readonly string ControlScales = "Контрольные шкалы";
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\TestReportGenerator.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators
{
    internal abstract class TestReportGenerator : ITestReportGenerator
    {
        private static readonly Thickness TableCellMargin = new Thickness(3);
        private static readonly Thickness ZeroThickness = new Thickness(0);

        protected Lazy<ResourceDictionary> Resources;

        public TestReportGenerator(string resourcesName)
        {
            Resources = createResourceDictionary(resourcesName);
        }

        public TestType TestType { get; protected set; }

        public abstract FlowDocument CreateReport(ReportInfo headerInfo, TestResults results);

        protected static FlowDocument CreateDocument<THeaderView>(object dataContext, params Block[] blocks)
            where THeaderView : UIElement, new()
        {
            var document = ReportHelper.CreateBlankDocument();
            var header = new THeaderView();
            var container = CreateHeader(header);

            document.Blocks.Add(container);
            document.Blocks.AddRange(blocks.Where(b => b != null));
            document.BindDataContext(dataContext);

            return document;
        }

        protected static FlowDocument CreateDocument(object dataContext, params Block[] blocks)
        {
            var document = ReportHelper.CreateBlankDocument();
            document.Blocks.AddRange(blocks.Where(b => b != null));
            document.BindDataContext(dataContext);

            return document;
        }

        protected static FlowDocument CreateDocument(object dataContext, IEnumerable<Block> blocks)
        {
            return CreateDocument(dataContext, blocks.ToArray());
        }

        protected static Block CreateHeader(UIElement header)
        {
            return ReportHelper.CreateHeader(header);
        }

        protected static Block CreateFooter(UIElement footer)
        {
            return ReportHelper.CreateFooter(footer);
        }

        protected static string GetTestName(TestType test)
        {
            var metadata = Metadata.GetTestInfo(test);
            return metadata.TestName;
        }
       
        private Lazy<ResourceDictionary> createResourceDictionary(string resourcesName)
        {
            if (string.IsNullOrEmpty(resourcesName))
                resourcesName = GetType().Name;

            var uri = new Uri($"/Updk7.Tests.Reports.Wpf;component/Resources/{resourcesName}.xaml",
                UriKind.Relative);
            
            return new Lazy<ResourceDictionary>(() => new ResourceDictionary() { Source = uri });
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\UnknownReportGenerator.cs


using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators
{
    internal class UnknownReportGenerator : TestReportGenerator
    {
        public UnknownReportGenerator() : base("UnknownReport")
        {
            TestType = TestType.Готовность_к_тестированию_ЭПФС;
        }

        public override FlowDocument CreateReport(ReportInfo headerInfo, TestResults results)
        {
            return CreateDocument<Views.UnknownReportHeader>(null);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Compatibility\CompatibilityReportGenerator.cs


using System;
using System.Linq;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Compatibility
{
    internal class CompatibilityReportGenerator : GenericReportGenerator
    {
        private class TableRowInfo
        {
            public TableRowInfo(string fullName, ResultParsers.Compatibility.AnswerInfo answer)
            {
                Answer = answer;
                FullName = fullName;
            }

            public ResultParsers.Compatibility.AnswerInfo Answer { get; set; }

            public string FullName { get; set; }
        }

        private static readonly Thickness TableCellMargin = new Thickness(3);
        private static readonly Thickness ZeroThickness = new Thickness(0);

        private ITesteeNameFactory _testeeNameFactory;

        public CompatibilityReportGenerator(ITesteeNameFactory testeeNameFactory)
            : base(TestType.Опрос_совместимости)
        {
            _testeeNameFactory = testeeNameFactory;
        }

        protected override IEnumerable<Block> CreateReportBody(ReportInfo info, TestResults results)
        {
            var answers = ResultParsers.Compatibility.Parser.ParseResults(results).ToArray();
            if (answers == null || answers.Length == 0)
            {
                yield return new Paragraph(new Run("Нет подходящих кандидатур для опроса"))
                { 
                    FontSize = 14.0,
                    FontWeight = FontWeights.Medium
                };
            }

            var table = createTable();
            var background = Brushes.White;

            foreach (var answer in answers)
            {
                var testeeName = _testeeNameFactory.GetName(answer.TesteeId);
                if (testeeName == null)
                    continue;

                var rowInfo = new TableRowInfo(testeeName, answer);
                addTableRow(table, rowInfo, background);

                background = background == Brushes.White
                    ? Brushes.LightGray
                    : Brushes.White;
            }

            yield return table;
        }
        
        private void addTableRow(Table table, TableRowInfo rowInfo, Brush background)
        {
            Func<ResultParsers.Compatibility.PollResult, ResultParsers.Compatibility.PollResult, string> map = (value, cellValue) =>
                value == cellValue ? "Да" : "";

            var row = new TableRow();
            row.FontWeight = FontWeights.Normal;
            row.Background = background;

            row.Cells.Add(createTableCell(rowInfo.FullName, TextAlignment.Left, ZeroThickness));
            row.Cells.Add(createTableCell(map(rowInfo.Answer.AnswerValue, ResultParsers.Compatibility.PollResult.Agree), TextAlignment.Center, ZeroThickness));
            row.Cells.Add(createTableCell(map(rowInfo.Answer.AnswerValue, ResultParsers.Compatibility.PollResult.Disagree), TextAlignment.Center, ZeroThickness));
            row.Cells.Add(createTableCell(map(rowInfo.Answer.AnswerValue, ResultParsers.Compatibility.PollResult.NoMatter), TextAlignment.Center, ZeroThickness));

            table.RowGroups[0].Rows.Add(row);
        }

        private Table createTable()
        {
            var table = new Table();
            table.FontSize = 12;
            table.CellSpacing = 0;
            table.Columns.Add(new TableColumn() { Width = new GridLength(3, GridUnitType.Star) });
            table.Columns.Add(new TableColumn() { Width = new GridLength(1, GridUnitType.Star) });
            table.Columns.Add(new TableColumn() { Width = new GridLength(1, GridUnitType.Star) });
            table.Columns.Add(new TableColumn() { Width = new GridLength(1, GridUnitType.Star) });

            var rowGroup = new TableRowGroup();
            table.RowGroups.Add(rowGroup);

            createColumnHeaders(table);

            return table;
        }

        private void createColumnHeaders(Table table)
        {
            var thickness = new Thickness(0, 1, 0, 1);
            var row = new TableRow();
            row.Background = Brushes.LightGray;
            row.FontWeight = FontWeights.Bold;

            row.Cells.Add(createTableCell("Ф.И.О.", TextAlignment.Left, thickness));
            row.Cells.Add(createTableCell("Согласен", TextAlignment.Center, thickness));
            row.Cells.Add(createTableCell("Не согласен", TextAlignment.Center, thickness));
            row.Cells.Add(createTableCell("Не возражаю", TextAlignment.Center, thickness));

            table.RowGroups[0].Rows.Add(row);
        }

        private static TableCell createTableCell(string title, TextAlignment alignment,
            Thickness borderThickness)
        {
            var header = new TableCell();
            header.BorderBrush = Brushes.Black;
            header.BorderThickness = borderThickness;
            var paragraph = new Paragraph(new Run(title))
            {
                Margin = TableCellMargin,
                TextAlignment = alignment
            };

            header.Blocks.Add(paragraph);
            return header;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Compatibility\ITesteeNameFactory.cs


namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Compatibility
{
    public interface ITesteeNameFactory
    {
        string GetName(int testeeId);
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Compatibility\ReportGeneratorFactory.cs


namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Compatibility
{
    public static class ReportGeneratorFactory
    {
        public static ITestReportGenerator Create(ITesteeNameFactory nameFactory)
        {
            return new CompatibilityReportGenerator(nameFactory);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Psychophysiological\ГЭД1.cs


using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Psychophysiological
{
    internal class ГЭД1 : GenericReportGenerator
    {
        public ГЭД1()
        {
            TestType = TestType.Готовность_к_экстренному_действию_ГЭД;
        }
        
        protected override IEnumerable<Block> CreateReportBody(ReportInfo info, TestResults results)
        {
            foreach (var block in base.CreateReportBody(info, results))
                yield return block;

            yield return new Paragraph(new Run("Таблица реагирований"))
            {
                FontSize = 16.0,
                FontWeight = FontWeights.Bold,
                Margin = new Thickness(0.0, 5.0, 0.0, 5.0)
            };

            var table = new ReportBlocks.GedPressTable()
            {
                Actions = results["Таблица_Действие"].IntArray,
                TimeSamples = results["Таблица_Время"].IntArray,
                Reactions = results["Таблица_Реакция"].FloatArray
            };

            yield return table.CreateDocumentBlock();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Psychophysiological\ГЭД1М.cs


namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Psychophysiological
{
    internal class ГЭД1М : ГЭД1
    {
        public ГЭД1М()
        {
            TestType = TestType.Готовность_к_экстренным_действиям_ГЭД_1М;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Psychophysiological\ГЭД2.cs


using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Psychophysiological
{
    internal class ГЭД2 : GenericReportGenerator
    {
        public ГЭД2()
        {
            TestType = TestType.Готовность_к_экстренному_действию_2_ГЭД_2;
        }

        protected override IEnumerable<Block> CreateReportBody(ReportInfo info, TestResults results)
        {
            foreach (var block in base.CreateReportBody(info, results))
                yield return block;

            yield return createTableTitle("Таблица реагирований за 1-й час");
            var table1 = new ReportBlocks.GedPressTable()
            {
                Actions = results["ГЭД-1 Таблица_Действие"].IntArray,
                TimeSamples = results["ГЭД-1 Таблица_Время"].FloatArray.Select(v => (int)v).ToArray(),
                Reactions = results["ГЭД-1 Таблица_Реакция"].FloatArray
            };
            yield return table1.CreateDocumentBlock();

            yield return createTableTitle("Таблица реагирований за 2-й час");
            var table2 = new ReportBlocks.GedPressTable()
            {
                Actions = results["ГЭД-2 Таблица_Действие"].IntArray,
                TimeSamples = results["ГЭД-2 Таблица_Время"].FloatArray.Select(v => (int)v).ToArray(),
                Reactions = results["ГЭД-2 Таблица_Реакция"].FloatArray,
                Resistance = results["ГЭД-2 Таблица_Сопротивление"].IntArray
            };
            yield return table2.CreateDocumentBlock();
        }

        private Block createTableTitle(string title)
        {
            return new Paragraph(new Run(title))
            {
                FontSize = 16.0,
                FontWeight = FontWeights.Bold,
                Margin = new Thickness(0.0, 5.0, 0.0, 5.0)
            };
        }
    }
}
 
*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Psychophysiological\ГЭД2М.cs


namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Psychophysiological
{
    internal class ГЭД2М : ГЭД2
    {
        public ГЭД2М()
        {
            TestType = TestType.Готовность_к_экстренным_действиям_ГЭД_2М;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Psychophysiological\Концентрация_внимания_КВ.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Psychophysiological
{
    internal class Концентрация_внимания_КВ : Оценка_устойчивости_внимания_УВ
    {
        private static readonly string TimesTag = "Время просмотра каждой строки";
        private static readonly string ErrorsTag = "Количество ошибок в каждой строке";

        private static readonly string LinesHeader = "Строки";
        private static readonly string TimesHeader = "Время просмотра строк, с";
        private static readonly string ErrorsHeader = "Ошибки в строках";

        public Концентрация_внимания_КВ()
        {
            TestType = TestType.Концентрация_внимания_КВ;
        }

        protected override Block CreateErrorsTable(TestResults results)
        {
            var section = new Section();

            if (isResultsCorrect(results))
            {
                foreach (var table in createErrorsTables(results))
                    section.Blocks.Add(table);
            }

            return section;
        }

        private bool isResultsCorrect(TestResults results)
        {
            // При быстром завершении теста (в отладочном режиме) получаем один нуль и показывать нечего
            return results[TimesTag].IsTypeOf<float[]>()
                && results[ErrorsTag].IsTypeOf<float[]>();
        }

        private IEnumerable<Block> createErrorsTables(TestResults results)
        {
            var dataCount = results[TimesTag].FloatArray.Length;
            var columnsPerTable = 16;
            var skipCount = 0;
            var data = results[TimesTag].FloatArray.Zip(results[ErrorsTag].FloatArray,
                (t, e) => new { Time = t, Error = e });

            Func<string, IEnumerable<string>, ReportBlocks.ResultsArrayTableRow> createRow = 
                (h, v) => new ReportBlocks.ResultsArrayTableRow() { Header = h, Values = v };

            do
            {
                var tableDataLength = Math.Min(dataCount - skipCount, columnsPerTable);
                var tableData = data.Skip(skipCount).Take(tableDataLength);
                var table = new ReportBlocks.ResultsArrayTable();
                var lineNumbers = Enumerable.Range(skipCount + 1, tableDataLength).Select(v => v.ToString());
                table.Rows.Add(createRow(LinesHeader, lineNumbers));
                table.Rows.Add(createRow(TimesHeader, tableData.Select(d => d.Time.ToString("f1"))));
                table.Rows.Add(createRow(ErrorsHeader, tableData.Select(d => d.Error.ToString("f0"))));
                skipCount += tableDataLength;

                yield return table.CreateDocumentBlock();
            }
            while (skipCount < dataCount);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Psychophysiological\Оценка_глазомера_ОГ.cs


using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Psychophysiological
{
    internal class Оценка_глазомера_ОГ : Оценка_устойчивости_внимания_УВ
    {
        private static readonly string MeasuredTag = "Длины линий";
        private static readonly string ReferenceTag = "Длины эталонов";

        public Оценка_глазомера_ОГ()
        {
            TestType = TestType.Оценка_глазомера_ОГ;
        }

        protected override Block CreateErrorsTable(TestResults results)
        {
            var errorsTable = new ReportBlocks.ResultsArrayTable();

            errorsTable.Rows.Add(CreateArrayTableRow("Длины линий", results[MeasuredTag], "f0"));
            errorsTable.Rows.Add(CreateArrayTableRow("Длины эталонов", results[ReferenceTag], "f0"));

            return errorsTable.CreateDocumentBlock();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Psychophysiological\Оценка_устойчивости_внимания_УВ.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Psychophysiological
{
    internal class Оценка_устойчивости_внимания_УВ : GenericReportGenerator
    {
        private static readonly string ErrorsTag = "Количество ошибок по блокам";
        private static readonly string MissingsTag = "Количество пропусков по блокам";
        private static readonly string TimesTag = "Среднеарифметические времена реагирования по блокам";

        public Оценка_устойчивости_внимания_УВ()
        {
            TestType = TestType.Оценка_устойчивости_внимания_УВ;
        }

        protected override IEnumerable<Block> CreateReportBody(ReportInfo info, TestResults results)
        {
            var dataTable = base.CreateReportBody(info, results);
            var errorsTable = CreateErrorsTable(results);

            return dataTable.Append(errorsTable);
        }

        protected virtual Block CreateErrorsTable(TestResults results)
        {
            var errorsTable = new ReportBlocks.ResultsArrayTable();

            var blocksRow = new ReportBlocks.ResultsArrayTableRow();
            blocksRow.Header = "Блоки";
            blocksRow.Values = Enumerable.Range(1, 12).Select(v => v.ToString());
            errorsTable.Rows.Add(blocksRow);

            errorsTable.Rows.Add(CreateArrayTableRow("Ошибок", results[ErrorsTag]));
            errorsTable.Rows.Add(CreateArrayTableRow("Пропусков", results[MissingsTag]));
            errorsTable.Rows.Add(CreateArrayTableRow("Ср. время реакции, с", results[TimesTag]));

            return errorsTable.CreateDocumentBlock();
        }

        protected ReportBlocks.ResultsArrayTableRow CreateArrayTableRow(string header, 
            TestResultValue resultsValue, string formatter = "f3")
        {
            var row = new ReportBlocks.ResultsArrayTableRow();
            row.Header = header;

            if (resultsValue.IsTypeOf<int[]>())
                row.Values = resultsValue.IntArray.Select(v => v.ToString()).ToArray();
            else if (resultsValue.IsTypeOf<float[]>())
                row.Values = resultsValue.FloatArray.Select(v => v.ToString(formatter)).ToArray();
            else
                throw new ArgumentException();

            return row;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Psychophysiological\Переключение_внимания_ПВПУ.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Xps.Packaging;
using TestKeys = Updk7.Tests.Metadata.Tests.Переключение_внимания_и_помехоустойчивость_ПВПУ;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Psychophysiological
{
    internal class Переключение_внимания_ПВПУ : GenericReportGenerator
    {
        public Переключение_внимания_ПВПУ()
        {
            TestType = TestType.Переключение_внимания_и_помехоустойчивость_ПВПУ;
        }

        protected override IEnumerable<Block> CreateReportBody(ReportInfo info, TestResults results)
        {
            yield return resultsTable(results);
            yield return createPressTableHeader();
            yield return createPressTable(results);
        }

        private Block resultsTable(TestResults results)
        {
            var shownResults = new TestResults();
            shownResults.AddValue(results[TestKeys.Keys.Result1]);
            shownResults.AddValue(results[TestKeys.Keys.Result2]);
            shownResults.AddValue(results[TestKeys.Keys.Result3]);
            shownResults.AddValue(results[TestKeys.Keys.Result4]);
            shownResults.AddValue(results[TestKeys.Keys.Result5]);
            shownResults.AddValue(results[TestKeys.Keys.Result6]);
            shownResults.AddValue(results[TestKeys.Keys.Result7]);
            shownResults.AddValue(results[TestKeys.Keys.Result8]);
            shownResults.AddValue(results[TestKeys.Keys.Result9]);
            shownResults.AddValue(results[TestKeys.Keys.Result10]);
            shownResults.AddValue(results[TestKeys.Keys.Result11]);

            var resultsTable = new ReportBlocks.ResultsTable();
            resultsTable.Results = shownResults;
            return resultsTable.CreateDocumentBlock();
        }

        private Block createPressTableHeader()
        {
            return new Paragraph(new Run("Таблица фактических нажатий, участвующих в оценке результата"))
            {
                FontSize = 16.0,
                FontWeight = FontWeights.Bold,
                Margin = new Thickness(0.0, 5.0, 0.0, 5.0)
            };
        }

        private Block createPressTable(TestResults results)
        {
            var table = new ReportBlocks.PvPuPressTable();
            var keys = getTableKeys(results);
            var column = 0;

            foreach (var key in keys)
            {
                var data = results[key];
                if (data != null)
                {
                    var parsedData = parseResults(data.String);
                    foreach (var p in parsedData)
                        table[column].Values.Add(p);
                }

                ++column;
            }

            return table.CreateDocumentBlock();
        }

        private IEnumerable<ReportBlocks.PvPuPressTable.ColumnCellValue> parseResults(string data)
        {
            var parsedData = new List<ReportBlocks.PvPuPressTable.ColumnCellValue>();

            try
            {
                var splitData = data.Split(' ');
                for (int i = 0; i < splitData.Length; i += 2)
                {
                    parsedData.Add(new ReportBlocks.PvPuPressTable.ColumnCellValue()
                    {
                        Number = int.Parse(splitData[i]),
                        Color = splitData[i + 1].Trim() == "Красное"
                            ? ReportBlocks.PvPuPressTable.Color.Red
                            : ReportBlocks.PvPuPressTable.Color.Black
                    });
                }
            }
            catch (Exception)
            {
            }

            return parsedData;
        } 

        private IEnumerable<string> getTableKeys(TestResults results)
        {
            var keys = new List<string>();

            Func<string, string, string> selectKey = (key1, key2) =>
                !string.IsNullOrEmpty(results[key2].String)
                    ? key2 : key1;

            keys.Add(selectKey(TestKeys.Keys.Result12, TestKeys.Keys.Result16));
            keys.Add(selectKey(TestKeys.Keys.Result13, TestKeys.Keys.Result17));
            keys.Add(selectKey(TestKeys.Keys.Result14, TestKeys.Keys.Result18));
            keys.Add(TestKeys.Keys.Result15);

            return keys;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Psychophysiological\Теппинг310.cs


using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Psychophysiological
{
    internal class Теппинг310 : GenericReportGenerator
    {
        public Теппинг310()
        {
            TestType = TestType.Теппинг_тест_310_ТЕП_310;
        }

        protected override IEnumerable<Block> CreateReportBody(ReportInfo info, TestResults results)
        {
            foreach (var block in base.CreateReportBody(info, results))
                yield return block;

            var total = 0;
            for (var i = 1; i <= 6; i++)
            {
                var key = $"Количество касаний на {i}-ом временном интервале";
                total += results[key].Int;
            }

            yield return new Paragraph(new Run($"Среднее количество касаний: {total / 6}"))
            {
                FontSize = 14.0,
                FontWeight = FontWeights.Medium,
                Margin = new Thickness(3.0, 0.0, 0.0, 5.0)
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Psychophysiological\ТеппингТест.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Documents;
using System.Windows;
using Updk7.Tests.Reports.Wpf.ReportGenerators;
using Updk7.Tests.Reports.Wpf.ReportGenerators.ReportBlocks;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Psychophysiological
{
    internal class ТеппингТест : GenericReportGenerator
    {
        public ТеппингТест()
        {
            TestType = TestType.Теппинг_тест;
        }

        protected override IEnumerable<Block> CreateReportBody(ReportInfo info, TestResults results)
        {
            var table = new ReportBlocks.ResultsArrayTable();
            table.HeaderColumnWidth = new GridLength(220);
            string key = "Касания каждые 5 секунд";
            var results5seconds = results[key].IntArray;
            var blocks = new List<Block>();

            string result = "";
            for (int i = 0; i < 6; i++)
            {
                if (i != 5)
                    result += $"{results5seconds[i]}, ";
                else
                    result += $"{results5seconds[i]}";
            }

            var resultRow = new ResultsArrayTableRow() { Header = key, Values = new List<string>() { result } };
            table.Rows.Add(resultRow);
            blocks.Add(table.CreateDocumentBlock());
            return blocks;
        }

    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\BarChartScalesFactory.cs


using System.Windows;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal static class BarChartScalesFactory
    {
        public static BarChartDataGridData Create(TestResultValue result, QuestionnaireMetadata metadata)
        {
            var data = new BarChartDataGridData();
            data.Name = metadata.GetResultName(result);
            data.Value = result.Int;
            data.Maximum = metadata.GetMaxValue(result);
            data.ValueFormat = metadata.FormatValue(result);
            data.Foreground = ReportColors.DefaultScale;

            if (metadata.IsMainScale(result.Key) && metadata.NormalityLow.ContainsKey(result.Key))
            {
                var normality = metadata.GetNormalityRang(result);
                data.BarTitle = metadata.GetNormalityName(normality);
                data.Foreground = metadata.GetScaleBrush(result);
            }

            return data;
        }

        public static BarChartDataGridData CreateSeparator(string title)
        {
            var data = new BarChartDataGridData();
            data.Name = title;
            data.IsBarVisible = false;
            data.FontWeight = FontWeights.Bold;

            return data;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\MMPI.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Documents;
using System.Windows.Media;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    // TODO: Добавить корректную обработку пола испытуемого
    internal class MMPI : QuestionnaireReportGenerator
    {
        private class ScaleKeys
        {
            public static readonly string L = "Шкала L";
            public static readonly string F = "Шкала F";
            public static readonly string K = "Шкала K";
            public static readonly string S1 = "Шкала 1";
            public static readonly string S2 = "Шкала 2";
            public static readonly string S3 = "Шкала 3";
            public static readonly string S4 = "Шкала 4";
            public static readonly string S5 = "Шкала 5";
            public static readonly string S6 = "Шкала 6";
            public static readonly string S7 = "Шкала 7";
            public static readonly string S8 = "Шкала 8";
            public static readonly string S9 = "Шкала 9";
            public static readonly string S0 = "Шкала 0";
            public static readonly string Reliability = "Достоверность";
            public static readonly string Combination = "Сочетание";
        }

        private const int MaxScaleValue = 100;
        private Func<int, string> FormatValue = v => $"({v}/{MaxScaleValue})";
        private List<Func<TestResults, Section>> _estimationScalesInterpretations;

        public MMPI()
        {
            TestType = TestType.Тест_MMPI_Березина;
            _estimationScalesInterpretations = new List<Func<TestResults, Section>>()
            {
                getInterpretationL,
                getinterpretationF,
                getInterpretationK,
                getInterpretationFK,
                getInterpretationLFK,
                getInterpretationLFKMean,
                getInterpretationLFKMax
            };
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
            chartData.AddData(BarChartScalesFactory.CreateSeparator(ReportStrings.EstimationScales));
            chartData.AddData(createScale(results[ScaleKeys.L], isEstimationScale: true));
            chartData.AddData(createScale(results[ScaleKeys.F], isEstimationScale: true));
            chartData.AddData(createScale(results[ScaleKeys.K], isEstimationScale: true));

            chartData.AddData(BarChartScalesFactory.CreateSeparator(ReportStrings.BaseScales));
            chartData.AddData(createScale(results[ScaleKeys.S1]));
            chartData.AddData(createScale(results[ScaleKeys.S2]));
            chartData.AddData(createScale(results[ScaleKeys.S3]));
            chartData.AddData(createScale(results[ScaleKeys.S4]));
            chartData.AddData(createScale(results[ScaleKeys.S5]));
            chartData.AddData(createScale(results[ScaleKeys.S6]));
            chartData.AddData(createScale(results[ScaleKeys.S7]));
            chartData.AddData(createScale(results[ScaleKeys.S8]));
            chartData.AddData(createScale(results[ScaleKeys.S9]));
            chartData.AddData(createScale(results[ScaleKeys.S0]));
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata();
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            var interpretations = new List<Section>();
            foreach (var estimationInterpretation in _estimationScalesInterpretations)
            {
                var section = estimationInterpretation(results);
                if (section != null)
                    interpretations.Add(section);
            }

            return interpretations;
        }

        private IEnumerable<Section> getBaseScalesInterpretations(TestResults results)
        {
            foreach (var scale in results.Values.Where(r => !IsEstimationResult(r)))
            {
                var key = getInterpretationKey(scale);
                if (Resources.Value.Contains(key))
                    yield return Resources.Value[key] as Section;
            }
        }

        private string getInterpretationKey(TestResultValue result)
        {
            var value = result.Int;
            var keySuffix = "N";
            if (value < 30)
                keySuffix = "L";
            else if (value > 70)
                keySuffix = "H";

            if (result.Key != ScaleKeys.S5)
                return $"S{result.Key.Split(' ')[1]}{keySuffix}";
            else
            {
                if (keySuffix == "N")
                    return string.Empty;

                if (value < 25 || value > 80)
                    return "S5MF";

                //var gender = CurrentReportInfo.Testee.Gender == Entities.GenderType.Male ? "M" : "F";
                var gender = "M";       // ИСПРАВИТЬ!
                return $"S5{gender}{keySuffix}";
            }
        }

        private Section getInterpretationL(TestResults testResults)
        {
            var l = testResults[ScaleKeys.L].Int;
            if (l > 80)
                return Resources.Value["L80"] as Section;
            else if (l > 60)
                return Resources.Value["L60"] as Section;

            return null;
        }

        private Section getinterpretationF(TestResults testResults)
        {
            var f = testResults[ScaleKeys.F].Int;
            if (f > 80)
                return Resources.Value["F80"] as Section;
            else if (f > 60)
                return Resources.Value["F60"] as Section;

            return null;
        }

        private Section getInterpretationK(TestResults testResults)
        {
            var k = testResults[ScaleKeys.K].Int;
            if (k > 80)
                return Resources.Value["K80"] as Section;
            else if (k >= 75 && k <= 80)
                return Resources.Value["K75"] as Section;
            else if (k >= 66 && k <= 74)
                return Resources.Value["K66"] as Section;
            else if (k >= 60 && k <= 65)
                return Resources.Value["K60"] as Section;
            else if (k >= 20 && k < 30)
                return Resources.Value["K20"] as Section;
            else if (k < 20)
                return Resources.Value["KL20"] as Section;

            return null;
        }

        private Section getInterpretationFK(TestResults testResults)
        {
            var f = testResults[ScaleKeys.F].Int;
            var k = testResults[ScaleKeys.K].Int;
            if (f > 70 || k > 70)
                return null;

            var index = f - k;
            var showInReport = false;
            // ИСПРАВИТЬ!
            //if (CurrentReportInfo.Testee.Gender == Data.Entities.GenderType.Male)
                showInReport = index < -18 || index > 5;
            //else
            //    showInReport = index < -23 || index > 8;

            return showInReport
                ? Resources.Value["FK"] as Section
                : null;
        }

        private Section getInterpretationLFK(TestResults testResults)
        {
            var l = testResults[ScaleKeys.L].Int;
            var f = testResults[ScaleKeys.F].Int;
            var k = testResults[ScaleKeys.K].Int;

            return (l >= 30 && l <= 70)  && (f >= 30 && f <= 70) && (k >= 30 && k <= 70)
                ? Resources.Value["LFK"] as Section
                : null;
        }

        private Section getInterpretationLFKMean(TestResults testResults)
        {
            var baseScales = testResults.Values.Where(r => IsBaseScale(r)).Select(r => r.Int);
            var mean = baseScales.Sum() / baseScales.Count();
            var l = (testResults[ScaleKeys.L].Int - mean) > 15;
            var f = (testResults[ScaleKeys.F].Int - mean) > 15;
            var k = (testResults[ScaleKeys.K].Int - mean) > 15;

            return l && f && k
                ? Resources.Value["LFKMean"] as Section
                : null;
        }

        private Section getInterpretationLFKMax(TestResults testResults)
        {
            var l = testResults[ScaleKeys.L].Int > 80;
            var f = testResults[ScaleKeys.F].Int > 80;
            var k = testResults[ScaleKeys.K].Int > 80 || testResults[ScaleKeys.K].Int < 20;

            return l && f && k
                ? Resources.Value["LFKMax"] as Section
                : null;
        }

        private BarChartDataGridData createScale(TestResultValue resultValue, bool isEstimationScale = false)
        {
            var value = resultValue.Int;
            var scale = new BarChartDataGridData();
            scale.Name = resultValue.Key;
            scale.Value = value;
            scale.Maximum = MaxScaleValue;
            scale.ValueFormat = FormatValue(value);

            if (isEstimationScale)
                scale.Foreground = Brushes.Black;
            else
            {
                if (value < 30)
                    scale.Foreground = Brushes.DeepSkyBlue;
                else if (value > 70)
                    scale.Foreground = Brushes.Red;
                else
                    scale.Foreground = Brushes.Gray;
            }

            return scale;
        }

        private bool IsEstimationResult(TestResultValue result)
        {
            return result.Key == ScaleKeys.L
                || result.Key == ScaleKeys.F
                || result.Key == ScaleKeys.K;
        }

        private bool IsBaseScale(TestResultValue result)
        {
            return !IsEstimationResult(result)
                && result.Key != ScaleKeys.Reliability
                && result.Key != ScaleKeys.Combination;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\QSort.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Documents;
using System.Windows.Media;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class QSort : QuestionnaireReportGenerator
    {
        protected class ScaleKeys
        {
            public static readonly string Зависимость = "Зависимость";
            public static readonly string Независимость = "Независимость";
            public static readonly string Общительность = "Общительность";
            public static readonly string Необщительность = "Необщительность";
            public static readonly string Боротьба = "Принятие «борьбы»";
            public static readonly string Неборотьба = "Избегание «борьбы»";
            public static readonly string Флаги = "Внутренняя конфликтность и нерешительность";
        }

        private const int MaxScaleValue = 20;
        private Dictionary<string, int> _flagIndex = new Dictionary<string, int>()
        {
            [ScaleKeys.Зависимость] = 3,
            [ScaleKeys.Независимость] = 4,
            [ScaleKeys.Общительность] = 5,
            [ScaleKeys.Необщительность] = 6,
            [ScaleKeys.Боротьба] = 7,
            [ScaleKeys.Неборотьба] = 8
        };

        public QSort()
        {
            TestType = TestType.Q_Сортировка;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new Questionnaires.QuestionnaireMetadata();
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            yield return checkTendentionsPairs(results);
            foreach (var interpretation in getInterpretations(results))
                yield return interpretation;
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
            chartData.AddData(createScale(results[ScaleKeys.Зависимость], Brushes.Yellow));
            chartData.AddData(createScale(results[ScaleKeys.Независимость], Brushes.Yellow));
            chartData.AddData(createScale(results[ScaleKeys.Общительность], Brushes.LimeGreen));
            chartData.AddData(createScale(results[ScaleKeys.Необщительность], Brushes.LimeGreen));
            chartData.AddData(createScale(results[ScaleKeys.Боротьба], Brushes.DeepSkyBlue));
            chartData.AddData(createScale(results[ScaleKeys.Неборотьба], Brushes.DeepSkyBlue));
        }

        private Section checkTendentionsPairs(TestResults results)
        {
            var pairs = new List<string>();
            var flags = getFlagsString(results);

            if (flags[0] == '1')
                pairs.Add($"{ScaleKeys.Зависимость}-{ScaleKeys.Независимость}");
            if (flags[1] == '1')
                pairs.Add($"{ScaleKeys.Общительность}-{ScaleKeys.Необщительность}");
            if (flags[2] == '1')
                pairs.Add($"{ScaleKeys.Боротьба}-{ScaleKeys.Неборотьба}");

            if (pairs.Count == 0)
                return new Section();

            var section = Resources.Value["TendentionsPairs"] as Section;
            section.DataContext = pairs.Aggregate(string.Empty, (acc, v) => acc + $"{v}, ").TrimEnd(' ', ',');
            return section;
        }

        private IEnumerable<Section> getInterpretations(TestResults results)
        {
            var flags = getFlagsString(results);
            var hasTestWarning = false;

            foreach (var key in GetScaleKeys().Where(k => k != ScaleKeys.Флаги))
            {
                var result = results[key].Int;
                var hasInterpretation = result >= 10;
                var hasWarning = flags[_flagIndex[key]] == '1';
                hasTestWarning |= hasWarning;

                if (!hasInterpretation && !hasWarning)
                    continue;

                var section = Resources.Value[$"{key}-Title"] as Section;
                if (hasInterpretation)
                    section.Blocks.Add(Resources.Value[key] as Paragraph);

                if (hasWarning)
                    section.Blocks.Add(Resources.Value["ScaleWarning"] as Paragraph);

                yield return section;
            }

            if (hasTestWarning)
                yield return Resources.Value["Warning"] as Section;

            var lessThen10 = results.Values.Where(v => v.Key != ScaleKeys.Флаги).All(v => v.Int < 10);
            var noWarnings = flags == "000000000";
            if (lessThen10 && noWarnings)
                yield return Resources.Value["TestingFaild"] as Section;

            yield return Resources.Value["Remark"] as Section;
        }

        private string getFlagsString(TestResults results)
        {
            return results[ScaleKeys.Флаги].String;
        }

        private BarChartDataGridData createScale(TestResultValue resultValue, Brush foreground)
        {
            Func<int, string> formatValue = v => $"({v}/{MaxScaleValue})";

            var value = resultValue.Int;
            var scale = new BarChartDataGridData();
            scale.Name = resultValue.Key;
            scale.Value = value;
            scale.Maximum = MaxScaleValue;
            scale.ValueFormat = formatValue(value);
            scale.Foreground = foreground;

            return scale;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\QuestionnaireMetadata.cs


using System.Collections.Generic;
using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class QuestionnaireMetadata
    {
        public enum ValueDifferentiationType
        {
            ThreeRanges,
            FourRanges
        }

        public QuestionnaireMetadata()
        {
        }

        public ValueDifferentiationType ValueDifferentiation { get; set; }

        public Dictionary<string, string> KeysAliases { get; set; }

        public Dictionary<string, string> NormalityAliases { get; set; }

        public Dictionary<string, int> Max { get; set; }

        public Dictionary<string, int> NormalityLow { get; set; }

        public Dictionary<string, int> NormalityHigh { get; set; }

        public Dictionary<string, int> NormalityMedium { get; set; }

        public Dictionary<string, Brush> NormalityBrushes { get; set; }

        public Dictionary<string, bool> SubScales { get; set; }

        public bool IsSubScale(string key)
        {
            return SubScales != null && SubScales.ContainsKey(key);
        }

        public bool IsMainScale(string key)
        {
            return !IsSubScale(key);
        }

        public string FormatValue(TestResultValue result)
        {
            return Max != null && Max.ContainsKey(result.Key)
                ? $"({result.Int}/{Max[result.Key]})"
                : $"({result.Int})";
        }

        public int GetMaxValue(TestResultValue result)
        {
            return Max != null && Max.ContainsKey(result.Key)
                ? Max[result.Key]
                : result.Int;
        }

        public string GetResultName(TestResultValue result)
        {
            return KeysAliases != null && KeysAliases.ContainsKey(result.Key)
               ? KeysAliases[result.Key]
               : result.Key;
        }

        public string GetNormalityRang(TestResultValue result)
        {
            return ValueDifferentiation == ValueDifferentiationType.ThreeRanges
                ? getNormalityRangThreeRanges(result)
                : getNormalityRangFourRanges(result);
        }

        public string GetNormalityName(string normality)
        {
            return NormalityAliases != null && NormalityAliases.ContainsKey(normality)
                ? NormalityAliases[normality]
                : normality;
        }

        public Brush GetScaleBrush(TestResultValue result)
        {
            return ValueDifferentiation == ValueDifferentiationType.ThreeRanges
                ? getScaleBrushThreeRanges(result)
                : getScaleBrushFourRanges(result);
        }

        private Brush getScaleBrushThreeRanges(TestResultValue result)
        {
            var key = result.Key;
            if (!NormalityLow.ContainsKey(key))
                return ReportColors.DefaultScale;

            var normalMin = NormalityLow[key];
            var normalMax = NormalityHigh[key];

            if (result.Int < normalMin)
                return getBrush(ReportStrings.NormalityLow,  ReportColors.NormalityLow);
            if (result.Int >= normalMin && result.Int <= normalMax)
                return getBrush(ReportStrings.NoralityNormal, ReportColors.NormalityNormal);

            return getBrush(ReportStrings.NormalityHigh, ReportColors.NormalityHigh);
        }

        private Brush getBrush(string normalityTag, Brush defaultBrush)
        {
            if (NormalityBrushes == null || !NormalityBrushes.ContainsKey(normalityTag))
                return defaultBrush;

            return NormalityBrushes[normalityTag];
        }

        private Brush getScaleBrushFourRanges(TestResultValue result)
        {
            var key = result.Key;
            if (!NormalityLow.ContainsKey(key))
                return ReportColors.DefaultScale;

            var low = NormalityLow[key];
            var medium = NormalityMedium[key];
            var high = NormalityHigh[key];

            if (result.Int < low)
                return ReportColors.TendentionLow;
            if (result.Int >= low && result.Int < medium)
                return ReportColors.TendentionNormal;
            if (result.Int >= medium && result.Int < high)
                return ReportColors.TendentionMedium;

            return ReportColors.TendentionHigh;
        }

        private string getNormalityRangThreeRanges(TestResultValue result)
        {
            var key = result.Key;
            if (!NormalityLow.ContainsKey(key))
                return string.Empty;

            var normalMin = NormalityLow[key];
            var normalMax = NormalityHigh[key];

            if (result.Int < normalMin)
                return ReportStrings.NormalityLow;
            if (result.Int >= normalMin && result.Int <= normalMax)
                return ReportStrings.NoralityNormal;

            return ReportStrings.NormalityHigh;
        }

        private string getNormalityRangFourRanges(TestResultValue result)
        {
            var key = result.Key;
            if (!NormalityLow.ContainsKey(key))
                return string.Empty;

            var low = NormalityLow[key];
            var medium = NormalityMedium[key];
            var high = NormalityHigh[key];

            if (result.Int < low)
                return ReportStrings.TendentionLow;
            if (result.Int >= low && result.Int < medium)
                return ReportStrings.TendentionNormal;
            if (result.Int >= medium && result.Int < high)
                return ReportStrings.TendentionMedium;

            return ReportStrings.TendentionHigh;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\QuestionnaireReportGenerator.cs


using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows.Documents;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal abstract class QuestionnaireReportGenerator : TestReportGenerator
    {
        private static readonly List<Block> EmptyInterpretations = new List<Block>();
        protected QuestionnaireMetadata Metadata;
        private string[] _mainScaleKeys;
        private string[] _subScaleKeys;

        protected ReportInfo CurrentReportInfo = null;

        public QuestionnaireReportGenerator() : base(string.Empty)
        {
        }

        public override FlowDocument CreateReport(ReportInfo reportInfo, TestResults results)
        {
            CurrentReportInfo = reportInfo;

            var viewModel = new ViewModels.QuestionnaireReportTemplateViewModel();
            viewModel.Title = GetTestName(results.Test);
            viewModel.TestDate = results.BeginTime;
            viewModel.UserName = reportInfo.UserName;
            viewModel.OrganizationTitle = reportInfo.OrganizationTitle;

            if (Metadata == null)
            {
                Metadata = CreateMetadata();
                var scaleKeys = GetScaleKeys();
                Debug.Assert(Metadata != null);
                Debug.Assert(scaleKeys != null);
                _mainScaleKeys = scaleKeys.Where(k => Metadata.IsMainScale(k)).ToArray();
                _subScaleKeys = scaleKeys.Where(k => Metadata.IsSubScale(k)).ToArray();
            }

            viewModel.TesteeInfo.SetInfo(reportInfo);
            FillBarChart(results, viewModel.BarChartData);
            viewModel.IsBarChartVisible = viewModel.BarChartData.Items.Count != 0;

            var header = CreateHeader(new Views.QuestionnaireReportTemplate());
            var interpretations = CreateInterpretations(results).ToArray();
            var footer = CreateFooter(new Views.GenericReportFooterView());

            UpdateHeaderViewModel(viewModel);
            var reportBlocks = header.Append(interpretations).Append(footer);
            var document = CreateDocument(viewModel, reportBlocks);

            return document;
        }

        protected virtual void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
            if (_mainScaleKeys.Length != 0)
            {
                chartData.AddData(BarChartScalesFactory.CreateSeparator(ReportStrings.BaseScales));
                foreach (var key in _mainScaleKeys)
                    AddBarChartScale(key, results, chartData);
            }

            if (_subScaleKeys.Length != 0)
            {
                chartData.AddData(BarChartScalesFactory.CreateSeparator(ReportStrings.OptionalScales));
                foreach (var key in _subScaleKeys)
                    AddBarChartScale(key, results, chartData);
            }
        }

        protected virtual IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            var interpretations = new List<Block>();
            foreach (var key in _mainScaleKeys.Concat(_subScaleKeys))
                AddInterpretationBlock(key, results, interpretations);

            return interpretations;
        }

        protected abstract QuestionnaireMetadata CreateMetadata();

        protected abstract IEnumerable<string> GetScaleKeys();

        protected virtual void UpdateHeaderViewModel(ViewModels.QuestionnaireReportTemplateViewModel viewModel)
        {
        }

        protected void AddBarChartScale(string key, TestResults results, BarChartDataGridDataCollection chartData)
        {
            chartData.AddData(BarChartScalesFactory.Create(results[key], Metadata));
        }

        protected void AddInterpretationBlock(string commonKey, TestResults data, List<Block> blocks)
        {
            blocks.Add(LoadInterpretationBlock(commonKey, data));
        }

        protected Block LoadInterpretationBlock(string commonKey, TestResults data)
        {
            var testResult = data[commonKey];
            var valueFormat = Metadata.FormatValue(testResult);
            var resourceKey = commonKey; 
            var context = valueFormat;

            if (Metadata.NormalityLow.ContainsKey(commonKey))
            {
                var normality = Metadata.GetNormalityRang(testResult);
                var specialKey = $"{commonKey} - {normality}";
                context = $"{Metadata.GetNormalityName(normality)} {valueFormat}";
                resourceKey = Resources.Value.Contains(specialKey)
                    ? specialKey
                    : commonKey;
            }

            Block block = null;
            if (Resources.Value.Contains(resourceKey))
            {
                block = Resources.Value[resourceKey] as Block;
                block.DataContext = context;
            }

            return block;
        }

        protected static IEnumerable<string> GetScaleKeys<T>() where T : class
        {
            var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static);

            foreach (var field in fields)
                yield return field.GetValue(null) as string;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Адаптивность.cs


using System.Collections.Generic;
using System.Windows.Media;
using ScaleKeys = Updk7.Tests.Metadata.Tests.Адаптивность.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Адаптивность : QuestionnaireReportGenerator
    {
        public Адаптивность()
        {
            TestType = TestType.Адаптивность;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            yield return ScaleKeys.Neuropsychic_resistance_stens;
            yield return ScaleKeys.Communicative_features_stens;
            yield return ScaleKeys.Moral_normativity_stens;
            yield return ScaleKeys.Adaptive_abilities_stens;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                ValueDifferentiation = QuestionnaireMetadata.ValueDifferentiationType.ThreeRanges,
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Neuropsychic_resistance_stens] = 10,
                    [ScaleKeys.Communicative_features_stens] = 10,
                    [ScaleKeys.Moral_normativity_stens] = 10,
                    [ScaleKeys.Adaptive_abilities_stens] = 10
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.Neuropsychic_resistance_stens] = 4,
                    [ScaleKeys.Communicative_features_stens] = 4,
                    [ScaleKeys.Moral_normativity_stens] = 4,
                    [ScaleKeys.Adaptive_abilities_stens] = 3
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.Neuropsychic_resistance_stens] = 6,
                    [ScaleKeys.Communicative_features_stens] = 6,
                    [ScaleKeys.Moral_normativity_stens] = 6,
                    [ScaleKeys.Adaptive_abilities_stens] = 4
                },
                NormalityAliases = new Dictionary<string, string>()
                {
                    [ReportStrings.NormalityLow] = "Низкий",
                    [ReportStrings.NoralityNormal] = "Средний",
                    [ReportStrings.NormalityHigh] = "Высокий"
                },
                KeysAliases = new Dictionary<string, string>()
                {
                    [ScaleKeys.Neuropsychic_resistance_stens] = "Нервно-психическая устойчивость",
                    [ScaleKeys.Communicative_features_stens] = "Коммуникативные особенности",
                    [ScaleKeys.Moral_normativity_stens] = "Моральная нормативность",
                    [ScaleKeys.Adaptive_abilities_stens] = "Адаптивные способности"
                },
                NormalityBrushes = new Dictionary<string, System.Windows.Media.Brush>()
                {
                    [ReportStrings.NormalityLow] = Brushes.Blue,
                    [ReportStrings.NoralityNormal] = Brushes.Gray,
                    [ReportStrings.NormalityHigh] = Brushes.Red
                }
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Аддикция_и_аддиктивное_поведение.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;
using Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires;
using Updk7.Tests.Reports.Wpf.ViewModels;
using ScaleKeys = Updk7.Tests.Metadata.Tests.Аддикция_и_аддиктивное_поведение.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Аддикция_и_аддиктивное_поведение : QuestionnaireReportGenerator
    {
        public Аддикция_и_аддиктивное_поведение()
        {
            TestType = TestType.Аддикция_и_аддиктивное_поведение;
        }

        private string[] _mainScaleKeys;
        private string[] _subScaleKeys;
        public override FlowDocument CreateReport(ReportInfo reportInfo, TestResults results)
        {
            CurrentReportInfo = reportInfo;

            var viewModel = new Аддикция_и_аддиктивное_поведение_ReportTemplateViewModel
            {
                Title = GetTestName(results.Test),
                TestDate = results.BeginTime,
                UserName = reportInfo.UserName,
                OrganizationTitle = reportInfo.OrganizationTitle
            };

            if (Metadata == null)
            {
                Metadata = CreateMetadata();
                var scaleKeys = GetScaleKeys();
                Debug.Assert(Metadata != null);
                Debug.Assert(scaleKeys != null);
                _mainScaleKeys = scaleKeys.Where(k => Metadata.IsMainScale(k)).ToArray();
                _subScaleKeys = scaleKeys.Where(k => Metadata.IsSubScale(k)).ToArray();
            }

            viewModel.TesteeInfo.SetInfo(reportInfo);
            viewModel.SummaryResults = GetAddictionViewModel(results);
            FillBarChart(results, viewModel.BarChartData);
            viewModel.IsBarChartVisible = viewModel.BarChartData.Items.Count != 0;

            var header = CreateHeader(new Views.Аддикция_и_аддиктивное_поведение_ReportTemplate());
            var interpretations = CreateInterpretations(results).ToArray();
            var footer = CreateFooter(new Views.GenericReportFooterView());

            UpdateHeaderViewModel(viewModel);
            var reportBlocks = header.Append(interpretations).Append(footer);
            var document = CreateDocument(viewModel, reportBlocks);

            return document;
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
            if (_mainScaleKeys.Length != 0)
            {
                chartData.AddData(BarChartScalesFactory.CreateSeparator(ReportStrings.BaseScales));
                foreach (var key in _mainScaleKeys)
                    AddBarChartScale(key, results, chartData);
            }

            if (_subScaleKeys.Length != 0)
            {
                chartData.AddData(BarChartScalesFactory.CreateSeparator(ReportStrings.OptionalScales));
                foreach (var key in _subScaleKeys)
                    AddBarChartScale(key, results, chartData);
            }
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Изменение_толерантности] = 12,
                    [ScaleKeys.Синдром_отмены] = 15,
                    [ScaleKeys.Потеря_контроля] = 15,
                    [ScaleKeys.Неудачные_попытки_воздержаться] = 12,
                    [ScaleKeys.Употребление_несмотря_на] = 15,
                    [ScaleKeys.Отрицание_своей_зависимости] = 12,
                    [ScaleKeys.Резкие_изменения_в_образе_жизни] = 15
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.Изменение_толерантности] = 5,
                    [ScaleKeys.Синдром_отмены] = 5,
                    [ScaleKeys.Потеря_контроля] = 5,
                    [ScaleKeys.Неудачные_попытки_воздержаться] = 5,
                    [ScaleKeys.Употребление_несмотря_на] = 5,
                    [ScaleKeys.Отрицание_своей_зависимости] = 5,
                    [ScaleKeys.Резкие_изменения_в_образе_жизни] = 5
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.Изменение_толерантности] = 6,
                    [ScaleKeys.Синдром_отмены] = 6,
                    [ScaleKeys.Потеря_контроля] = 6,
                    [ScaleKeys.Неудачные_попытки_воздержаться] = 6,
                    [ScaleKeys.Употребление_несмотря_на] = 6,
                    [ScaleKeys.Отрицание_своей_зависимости] = 6,
                    [ScaleKeys.Резкие_изменения_в_образе_жизни] = 6
                },
            };
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>().Where(w => w != ScaleKeys.Уровень_проявления_аддикции
                                                     && w != ScaleKeys.Степень_выраженности_аддикции);
        }
        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            var res = results.Values.Where(w => w.Key != ScaleKeys.Уровень_проявления_аддикции && w.Key != ScaleKeys.Степень_выраженности_аддикции);
            foreach (var resValueItem in res)
            {
                if (resValueItem.Int >= 6)
                {
                    var scale = (Block)Resources.Value[resValueItem.Key];
                    scale.DataContext = resValueItem.Int;
                    yield return scale;
                }
            }

            yield return getPS();
        }

        private AddictionModel GetAddictionViewModel(TestResults results)
        {
            var addiction = results[ScaleKeys.Уровень_проявления_аддикции].Int;
            var addictionLevel = results[ScaleKeys.Степень_выраженности_аддикции].Int;
           
            var model = new AddictionModel
            {
                Score = addiction,
                AddictionLevel = addictionLevel,
                AddictionText = GetAddictionText(addiction),
                AddictionLevelText = GetAddictionLevelText(addictionLevel)
            };
            return model;
        }

        private string GetAddictionText(int addiction)
        {
            if (addiction >= 0 && addiction <= 30)
                return (string)Resources.Value["Addiction_1"];
            else if (addiction > 30 && addiction <= 50)
                return (string)Resources.Value["Addiction_2"];
            else if (addiction > 50 && addiction <= 70)
                return (string)Resources.Value["Addiction_3"];
            else if (addiction > 70)
                return (string)Resources.Value["Addiction_4"];
            return "";
        }
        private string GetAddictionLevelText(int addictionLevel)
        {
            if (addictionLevel < 0 || addictionLevel > 4) return "";
            return (string)Resources.Value[$"Addiction_Level_{addictionLevel}"];
        }

        private Block getPS()
        {
            return Resources.Value["ps"] as Section;
        }

        private class AddictionModel
        {
            public int Score { get; set; }
            public string AddictionText { get; set; }
            public int AddictionLevel { get; set; }
            public string AddictionLevelText { get; set; }
        }
    }

    class Аддикция_и_аддиктивное_поведение_ReportTemplateViewModel : QuestionnaireReportTemplateViewModel
    {
        private object _summaryResults;

        public object SummaryResults
        {
            get { return _summaryResults; }
            set { SetProperty(ref _summaryResults, value); }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Айзенк.cs


using System.Collections.Generic;
using System.Windows.Documents;
using System.Windows.Media;
using Updk7.Tests.Reports.Wpf.Controls.EysenckChart;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Айзенк : QuestionnaireReportGenerator
    {
        private class ScaleKeys
        {
            public static readonly string Психотизм = "Психотизм";
            public static readonly string Экстраверсия = "Экстраверсия/Интроверсия";
            public static readonly string Нейротизм = "Нейротизм";
            public static readonly string Искренность = "Искренность";
        }

        private class ResourceKeys
        {
            public static readonly string EysenckChartSection = "EysenckChartSection";
        }

        private class ChartDataModel
        {
            public EysenckDataPointsCollection Points { get; set; } = new EysenckDataPointsCollection();

            public EysenckTresholdAreasCollection Tresholds { get; set; } = new EysenckTresholdAreasCollection();
        }

        public Айзенк()
        {
            TestType = TestType.Тест_Айзенка_EPQ;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Психотизм] = 25,
                    [ScaleKeys.Экстраверсия] = 25,
                    [ScaleKeys.Нейротизм] = 25,
                    [ScaleKeys.Искренность] = 25
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.Психотизм] = 5,
                    [ScaleKeys.Экстраверсия] = 7,
                    [ScaleKeys.Нейротизм] = 8,
                    [ScaleKeys.Искренность] = 0
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.Психотизм] = 12,
                    [ScaleKeys.Экстраверсия] = 15,
                    [ScaleKeys.Нейротизм] = 16,
                    [ScaleKeys.Искренность] = 10
                }
            };
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            yield return getEysenckChart(results);
            yield return getInterpretation(results);
            yield return getConclusion(results);
        }

        private Block getEysenckChart(TestResults results)
        {
            var x = results[ScaleKeys.Экстраверсия].Int;
            var y = results[ScaleKeys.Нейротизм].Int;

            var dataModel = new ChartDataModel();
            dataModel.Points.Add(x, y);
            fillTresholdes(dataModel.Tresholds);

            var section = Resources.Value[ResourceKeys.EysenckChartSection] as Section;
            section.DataContext = dataModel;

            return section;
        }

        private Block getConclusion(TestResults results)
        {
            var x = results[ScaleKeys.Психотизм].Int;
            var index = x >= 13 ? 1 : 2;
            var key = $"Conclusion-{index}";
            return Resources.Value[key] as Section;
        }

        private Block getInterpretation(TestResults results)
        {
            var index = getInterpretationIndex(results);
            var key = $"Interpretation-{index}";
            return Resources.Value[key] as Section;
        }

        private void fillTresholdes(EysenckTresholdAreasCollection tresholds)
        {
            var color1 = new Color() { A = 128, R = 140, G = 159, B = 106 };
            var treshold1 = new EysenckTresholdArea("Флегматик", new SolidColorBrush(color1), 0, 8, 8, 8);
            tresholds.Add(treshold1);

            var color2 = new Color() { A = 128, R = 255, G = 0, B = 0 };
            var treshold2 = new EysenckTresholdArea("Сангвиник", new SolidColorBrush(color2), 17, 8, 9, 8);
            tresholds.Add(treshold2);

            var color3 = new Color() { A = 128, R = 0, G = 191, B = 255 };
            var treshold3 = new EysenckTresholdArea("Меланхолик", new SolidColorBrush(color3), 0, 26, 8, 9);
            tresholds.Add(treshold3);

            var color4 = new Color() { A = 128, R = 255, G = 255, B = 0 };
            var treshold4 = new EysenckTresholdArea("Холерик", new SolidColorBrush(color4), 17, 26, 9, 9);
            tresholds.Add(treshold4);
        }

        private int getInterpretationIndex(TestResults results)
        {
            var x = results[ScaleKeys.Экстраверсия].Int;
            var y = results[ScaleKeys.Нейротизм].Int;

            if (x > 16 && x < 23 && y > 16)
                return 1;
            else if (x >= 23 && y > 16)
                return 2;
            else if (x > 16 && y < 8)
                return 3;
            else if (x > 16 && y >= 8 && y <= 16)
                return 4;
            else if (x < 8 && y < 8)
                return 5;
            else if (x < 8 && y > 16 && y < 23)
                return 6;
            else if (x < 8 && y >= 23)
                return 7;
            else if (x < 8 && y >= 8 && y <= 16)
                return 8;
            else if (x >= 8 && x <= 16 && y > 16)
                return 9;
            else if (x >= 8 && x <= 16 && y < 8)
                return 10;
            else if (x >= 8 && x <= 16 && y >= 8 && y <= 16)
                return 11;
            else
                return 0;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Аналогии.cs


using System.Collections.Generic;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Методика_Аналогии.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Аналогии : QuestionnaireReportGenerator
    {
        public Аналогии()
        {
            TestType = TestType.Методика_Аналогии;
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
            // График не выводится
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.VerbalIntelligence] = 30
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.VerbalIntelligence] = 13
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.VerbalIntelligence] = 16
                },
                NormalityAliases = new Dictionary<string, string>()
                {
                    [ReportStrings.NormalityLow] = "Низкий",
                    [ReportStrings.NoralityNormal] = "Средний",
                    [ReportStrings.NormalityHigh] = "Высокий"
                }
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Антиципационная_состоятельность.cs


using System.Collections.Generic;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Тест_антиципационной_состоятельности_АС.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Антиципационная_состоятельность : QuestionnaireReportGenerator
    {
        public Антиципационная_состоятельность()
        {
            TestType = TestType.Тест_антиципационной_состоятельности_АС;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Common] = 405,
                    [ScaleKeys.Personal] = 275,
                    [ScaleKeys.Spatial] = 70,
                    [ScaleKeys.Temporary] = 60
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.Common] = 241,
                    [ScaleKeys.Personal] = 166,
                    [ScaleKeys.Spatial] = 52,
                    [ScaleKeys.Temporary] = 42
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.Common] = 405,
                    [ScaleKeys.Personal] = 275,
                    [ScaleKeys.Spatial] = 70,
                    [ScaleKeys.Temporary] = 60
                }
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Голланд.cs


using System.Collections.Generic;
using System.Linq;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Голланд : QuestionnaireReportGenerator
    {
        private class ScaleKeys
        {
            public static readonly string Type1 = "Реалистический тип";
            public static readonly string Type2 = "Интеллектуальный тип";
            public static readonly string Type3 = "Социальный тип";
            public static readonly string Type4 = "Конвенциальный тип";
            public static readonly string Type5 = "Предприимчивый тип";
            public static readonly string Type6 = "Артистический тип";
        }

        private static readonly string _skipValue = "Интерпретация";

        public Голланд()
        {
            TestType = TestType.Опроснок_профессиональных_предпочтений_Голланд;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Type1] = 14,
                    [ScaleKeys.Type2] = 14,
                    [ScaleKeys.Type3] = 14,
                    [ScaleKeys.Type4] = 14,
                    [ScaleKeys.Type5] = 14,
                    [ScaleKeys.Type6] = 14,
                },
                NormalityLow = new Dictionary<string, int>()
            };
        }
        
        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            TestResultValue maxValue = results.Values.First();
            foreach (var result in results.Values.Where(v => v.Key != _skipValue))
            {
                if (result.Int > maxValue.Int)
                    maxValue = result;
            }

            yield return Resources.Value[maxValue.Key] as Section;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Дигностика_уровня_агрессии_Басс_Дарки.cs


using System.Collections.Generic;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Методика_диагностики_уровня_агрессии_Басс_Дарки.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Дигностика_уровня_агрессии_Басс_Дарки : QuestionnaireReportGenerator
    {
        public Дигностика_уровня_агрессии_Басс_Дарки()
        {
            TestType = TestType.Методика_диагностики_уровня_агрессии_Басс_Дарки;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.AgressionIndex] = 34,
                    [ScaleKeys.HostilityIndex] = 18,
                    [ScaleKeys.PhysicalAgression] = 10,
                    [ScaleKeys.IndirectAgression] = 9,
                    [ScaleKeys.Irritation] = 11,
                    [ScaleKeys.Negativism] = 5,
                    [ScaleKeys.Offense] = 8,
                    [ScaleKeys.Suspicion] = 10,
                    [ScaleKeys.VerbalAgrgression] = 13,
                    [ScaleKeys.Guilt] = 9
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.AgressionIndex] = 17,
                    [ScaleKeys.HostilityIndex] = 4
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.AgressionIndex] = 25,
                    [ScaleKeys.HostilityIndex] = 10
                },
                SubScales = new Dictionary<string, bool>()
                {
                    [ScaleKeys.PhysicalAgression] = true,
                    [ScaleKeys.IndirectAgression] = true,
                    [ScaleKeys.Irritation] = true,
                    [ScaleKeys.Negativism] = true,
                    [ScaleKeys.Offense] = true,
                    [ScaleKeys.Suspicion] = true,
                    [ScaleKeys.VerbalAgrgression] = true,
                    [ScaleKeys.Guilt] = true
                }
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\ДОРС.cs


using System.Collections.Generic;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Методика_ДОРС.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class ДОРС : QuestionnaireReportGenerator
    {
        public ДОРС()
        {
            TestType = TestType.Методика_ДОРС;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                ValueDifferentiation = QuestionnaireMetadata.ValueDifferentiationType.FourRanges,
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Fatigue] = 40,
                    [ScaleKeys.Monotonically] = 36,
                    [ScaleKeys.Satiation] = 40,
                    [ScaleKeys.Stress] = 40
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.Fatigue] = 16,
                    [ScaleKeys.Monotonically] = 16,
                    [ScaleKeys.Satiation] = 17,
                    [ScaleKeys.Stress] = 17
                },
                NormalityMedium = new Dictionary<string, int>()
                {
                    [ScaleKeys.Fatigue] = 26,
                    [ScaleKeys.Monotonically] = 26,
                    [ScaleKeys.Satiation] = 25,
                    [ScaleKeys.Stress] = 25
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.Fatigue] = 32,
                    [ScaleKeys.Monotonically] = 31,
                    [ScaleKeys.Satiation] = 31,
                    [ScaleKeys.Stress] = 31
                },
                NormalityAliases = new Dictionary<string, string>()
                {
                    [ReportStrings.TendentionLow] = "Низкий",
                    [ReportStrings.TendentionNormal] = "Умеренный",
                    [ReportStrings.TendentionMedium] = "Выраженный",
                    [ReportStrings.TendentionHigh] = "Высокий"
                },
                KeysAliases = new Dictionary<string, string>()
                {
                    [ScaleKeys.Fatigue] = "Индекс утомления",
                    [ScaleKeys.Monotonically] = "Индекс монотонии",
                    [ScaleKeys.Satiation] = "Индекс пресыщения",
                    [ScaleKeys.Stress] = "Индекс стресса"
                }
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Зунг.cs


using System.Collections.Generic;
using System.Windows.Documents;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;
using Updk7.Tests.Reports.Wpf.ViewModels;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Зунг : QuestionnaireReportGenerator
    {
        private class ScaleKeys
        {
            public static readonly string Score = "Баллы";
        }

        public Зунг()
        {
            TestType = TestType.Тест_Зунга;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new Questionnaires.QuestionnaireMetadata();
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override void UpdateHeaderViewModel(QuestionnaireReportTemplateViewModel viewModel)
        {
            viewModel.IsInterpretetaionTitleVisible = false;
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            var value = results[ScaleKeys.Score].Int;

            var scoresSection = Resources.Value["Score"] as Section;
            scoresSection.DataContext = value;
            yield return scoresSection;

            var levelSectionKey = "Level - 1";
            if (value >= 70)
                levelSectionKey = "Level - 4";
            else if (value >= 60)
                levelSectionKey = "Level - 3";
            else if (value >= 50)
                levelSectionKey = "Level - 2";

            yield return Resources.Value[levelSectionKey] as Block;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Избегание_неудач_Элерс.cs


using System.Collections.Generic;
using System.Windows.Documents;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Избегание_неудач_Элерс : QuestionnaireReportGenerator
    {
        private class ScaleKeys
        {
            public static readonly string Scores = "Баллы";
        }

        public Избегание_неудач_Элерс()
        {
            TestType = TestType.Диагностики_мотивации_к_избеганию_неудач_Элерса_МИН;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata();
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            var score = results[ScaleKeys.Scores].Int;

            var scoreSection = Resources.Value["Score"] as Section;
            scoreSection.DataContext = score;
            yield return scoreSection;

            var resultSectionKey = "Low";
            if (score > 20)
                resultSectionKey = "VeryHigh";
            else if (score >= 17)
                resultSectionKey = "High";
            else if (score >= 11)
                resultSectionKey = "Normal";

            yield return Resources.Value[resultSectionKey] as Block;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Индекс_жизненного_стиля.cs


using System.Collections.Generic;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Тест_опросник_Индекс_жизненного_стиля_ИЖС.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Индекс_жизненного_стиля : QuestionnaireReportGenerator
    {
        public Индекс_жизненного_стиля()
        {
            TestType = TestType.Тест_опросник_Индекс_жизненного_стиля_ИЖС;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Negation] = 13,
                    [ScaleKeys.Extrusion] = 12,
                    [ScaleKeys.Regression] = 14,
                    [ScaleKeys.Compensation] = 10,
                    [ScaleKeys.Projection] = 13,
                    [ScaleKeys.Replacement] = 13,
                    [ScaleKeys.Intellectualization] = 12,
                    [ScaleKeys.Reactive] = 10
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.Negation] = 3,
                    [ScaleKeys.Extrusion] = 2,
                    [ScaleKeys.Regression] = 3,
                    [ScaleKeys.Compensation] = 2,
                    [ScaleKeys.Projection] = 4,
                    [ScaleKeys.Replacement] = 3,
                    [ScaleKeys.Intellectualization] = 4,
                    [ScaleKeys.Reactive] = 2
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.Negation] = 7,
                    [ScaleKeys.Extrusion] = 7,
                    [ScaleKeys.Regression] = 7,
                    [ScaleKeys.Compensation] = 5,
                    [ScaleKeys.Projection] = 8,
                    [ScaleKeys.Replacement] = 6,
                    [ScaleKeys.Intellectualization] = 8,
                    [ScaleKeys.Reactive] = 5
                },
                KeysAliases = new Dictionary<string, string>()
                {
                    [ScaleKeys.Regression] = "Регрессия"
                }
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Исследование_волевой_саморегуляции.cs


using System.Collections.Generic;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Тест_опросник_на_исследование_волевой_саморегуляции_ИВС.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Исследование_волевой_саморегуляции : QuestionnaireReportGenerator
    {
        public Исследование_волевой_саморегуляции()
        {
            TestType = TestType.Тест_опросник_на_исследование_волевой_саморегуляции_ИВС;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.CommonScale] = 24,
                    [ScaleKeys.Perseverance] = 16,
                    [ScaleKeys.SelfControl] = 13
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.CommonScale] = 11,
                    [ScaleKeys.Perseverance] = 7,
                    [ScaleKeys.SelfControl] = 5
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.CommonScale] = 13,
                    [ScaleKeys.Perseverance] = 9,
                    [ScaleKeys.SelfControl] = 7
                }
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Кеттелл.cs


using System.Collections.Generic;
using System.Windows.Documents;
using System.Windows.Media;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Кеттелл : QuestionnaireReportGenerator
    {
        private class ScaleKeys
        {
            public static readonly string Высокие = "Высокие";
            public static readonly string Низкие = "Низкие";
            public static readonly string Баллы = "Баллы";
            public static readonly string Стены = "Стены";
        }

        private string[] _scaleNames = new string[] { "A", "B", "C", "E", "F", "G", "H", "I", "L", "M", "N", "O", "Q1", "Q2", "Q3", "Q4" };

        public Кеттелл()
        {
            TestType = TestType.Тест_Кетелла;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata();
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
            chartData.AddData(BarChartScalesFactory.CreateSeparator("Баллы"));
            var scores = normalizeResultArray(results[ScaleKeys.Баллы].IntArray);
            for (int i = 0; i < scores.Length; i++)
                chartData.AddData(createScoreScale(_scaleNames[i], scores[i]));

            chartData.AddData(BarChartScalesFactory.CreateSeparator("Стены"));
            var walls = normalizeResultArray(results[ScaleKeys.Стены].IntArray);
            var highScales = results[ScaleKeys.Высокие].String;
            var lowScales = results[ScaleKeys.Низкие].String;
            for (int i = 0; i < walls.Length; i++)
                chartData.AddData(createWallScale(_scaleNames[i], walls[i], highScales, lowScales));
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            var highScales = results[ScaleKeys.Высокие].String.Split(' ');
            foreach (var scale in highScales)
            {
                var key = $"{scale.Trim()}+";
                if (Resources.Value.Contains(key))
                    yield return Resources.Value[key] as Block;
            }

            var lowScales = results[ScaleKeys.Низкие].String.Split(' ');
            foreach (var scale in lowScales)
            {
                var key = $"{scale.Trim()}-";
                if (Resources.Value.Contains(key))
                    yield return Resources.Value[key] as Block;
            }
        }

        private int[] normalizeResultArray(int[] source)
        {
            var result = new int[source.Length];
            for (int i = 0; i < result.Length; i++)
                result[i] = source[i] - 100 * i;

            return result;
        }

        private BarChartDataGridData createWallScale(string title, int value, string highScores, 
            string lowScores)
        {
            var foreground = Brushes.Gray;
            if (highScores.Contains(title))
                foreground = Brushes.Red;
            else if (lowScores.Contains(title))
                foreground = Brushes.DeepSkyBlue;

            return createScale(title, value, 10, foreground);
        }

        private BarChartDataGridData createScoreScale(string title, int value)
        {
            return createScale(title, value, 26, Brushes.LimeGreen);
        }

        private BarChartDataGridData createScale(string title, int value, int max, Brush foreground)
        {
            var scale = new BarChartDataGridData();
            scale.Name = title;
            scale.Value = value;
            scale.Maximum = max;
            scale.ValueFormat = $"({value}/{max})";
            scale.Foreground = foreground;

            return scale;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\КОПС.cs


using System.Collections.Generic;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Комплексная_оценка_психологического_состояния_КОПС.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class КОПС : QuestionnaireReportGenerator
    {
        public КОПС()
        {
            TestType = TestType.Комплексная_оценка_психологического_состояния_КОПС;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
            chartData.AddData(BarChartScalesFactory.CreateSeparator(ReportStrings.BaseScales));
            AddBarChartScale(ScaleKeys.Operability, results, chartData);
            AddBarChartScale(ScaleKeys.SelfConcept, results, chartData);

            chartData.AddData(BarChartScalesFactory.CreateSeparator(ReportStrings.ControlScales));
            AddBarChartScale(ScaleKeys.SocialDesirability, results, chartData);
            AddBarChartScale(ScaleKeys.Atypically, results, chartData);
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                ValueDifferentiation = QuestionnaireMetadata.ValueDifferentiationType.FourRanges,
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Operability] = 220,
                    [ScaleKeys.SelfConcept] = 145,
                    [ScaleKeys.SocialDesirability] = 45,
                    [ScaleKeys.Atypically] = 83
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.Operability] = 115,
                    [ScaleKeys.SelfConcept] = 76,
                    [ScaleKeys.SocialDesirability] = 29,
                    [ScaleKeys.Atypically] = 16
                },
                NormalityMedium = new Dictionary<string, int>()
                {
                    [ScaleKeys.Operability] = 136,
                    [ScaleKeys.SelfConcept] = 86,
                    [ScaleKeys.SocialDesirability] = 29,
                    [ScaleKeys.Atypically] = 16
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.Operability] = 191,
                    [ScaleKeys.SelfConcept] = 116,
                    [ScaleKeys.SocialDesirability] = 29,
                    [ScaleKeys.Atypically] = 25
                },
                NormalityAliases = new Dictionary<string, string>()
                {
                    [ReportStrings.TendentionLow] = "Низкий",
                    [ReportStrings.TendentionNormal] = "Ниже среднего",
                    [ReportStrings.TendentionMedium] = "Средний",
                    [ReportStrings.TendentionHigh] = "Высокий"
                },
                SubScales = new Dictionary<string, bool>()
                {
                    [ScaleKeys.SocialDesirability] = true,
                    [ScaleKeys.Atypically] = true
                }
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Лири.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Лири : QuestionnaireReportGenerator
    {
        protected class ScaleKeys
        {
            public static readonly string Factor1 = "Фактор \"Авторитарный\"";
            public static readonly string Factor2 = "Фактор \"Независимо - доминирующий\"";
            public static readonly string Factor3 = "Фактор \"Агрессивный\"";
            public static readonly string Factor4 = "Фактор \"Недоверчивый - скептический\"";
            public static readonly string Factor5 = "Фактор \"Покорно - застенчивый\"";
            public static readonly string Factor6 = "Фактор \"Зависимый\"";
            public static readonly string Factor7 = "Фактор \"Сотрудничающий\"";
            public static readonly string Factor8 = "Фактор \"Альтруистический\"";
        }

        private Dictionary<string, Tuple<int, int>> _factorRanges = new Dictionary<string, Tuple<int, int>>()
        {
            [ScaleKeys.Factor1] = Tuple.Create(9, 12),
            [ScaleKeys.Factor2] = Tuple.Create(9, 12),
            [ScaleKeys.Factor3] = Tuple.Create(9, 12),
            [ScaleKeys.Factor4] = Tuple.Create(9, 12),
            [ScaleKeys.Factor5] = Tuple.Create(9, 12),
            [ScaleKeys.Factor6] = Tuple.Create(9, 12),
            [ScaleKeys.Factor7] = Tuple.Create(9, 16),
            [ScaleKeys.Factor8] = Tuple.Create(9, 16)
        };

        private Dictionary<string, string> _keysDictionary;

        public Лири()
        {
            TestType = TestType.Тест_Лири;
            _keysDictionary = createKeysDictionary();
        }       

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new Questionnaires.QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Factor1] = 16,
                    [ScaleKeys.Factor2] = 16,
                    [ScaleKeys.Factor3] = 16,
                    [ScaleKeys.Factor4] = 16,
                    [ScaleKeys.Factor5] = 16,
                    [ScaleKeys.Factor6] = 16,
                    [ScaleKeys.Factor7] = 16,
                    [ScaleKeys.Factor8] = 16
                },
                NormalityLow = new Dictionary<string, int>()
            };
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            foreach (var fieldName in _keysDictionary.Keys)
            {
                var scaleKey = _keysDictionary[fieldName];
                var range = _factorRanges[scaleKey];
                var value = results.Values.FirstOrDefault(v => v.Key == scaleKey);
                if (value == null)
                    continue;

                var block = getInterpretationBlock(fieldName, value.Int, range.Item1, range.Item2);
                if (block != null)
                    yield return block;
            }

            yield return Resources.Value["Conclusion"] as Section;
        }

        private Section getInterpretationBlock(string scaleKeyField, int value, int normalLow, int normalHigh)
        {
            var index = 1;
            if (value >= normalLow && value <= normalHigh)
                index = 2;
            else if (value > normalHigh)
                index = 3;

            var resourceKey = $"{scaleKeyField} - {index}";
            if (Resources.Value.Contains(resourceKey))
                return Resources.Value[resourceKey] as Section;

            return null;
        }

        private Dictionary<string, string> createKeysDictionary()
        {
            var dictionary = new Dictionary<string, string>();
            var fields = typeof(ScaleKeys).GetFields(BindingFlags.Public | BindingFlags.Static);
            foreach (var field in fields)
                dictionary[field.Name] = field.GetValue(null) as string;

            return dictionary;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Личностный_профиль.cs


using System.Collections.Generic;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Опросник_Личностный_профиль_ЛП.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Личностный_профиль : QuestionnaireReportGenerator
    {
        public Личностный_профиль()
        {
            TestType = TestType.Опросник_Личностный_профиль_ЛП;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
            chartData.AddData(BarChartScalesFactory.CreateSeparator(ReportStrings.BaseScales));
            AddBarChartScale(ScaleKeys.Responsibility, results, chartData);
            AddBarChartScale(ScaleKeys.EmotionalEndurance, results, chartData);

            chartData.AddData(BarChartScalesFactory.CreateSeparator(ReportStrings.OptionalScales));
            AddBarChartScale(ScaleKeys.Activity, results, chartData);

            chartData.AddData(BarChartScalesFactory.CreateSeparator(ReportStrings.ControlScales));
            AddBarChartScale(ScaleKeys.SocialDesirability, results, chartData);
            AddBarChartScale(ScaleKeys.Atypically, results, chartData);
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Activity] = 54,
                    [ScaleKeys.Responsibility] = 48,
                    [ScaleKeys.EmotionalEndurance] = 87,
                    [ScaleKeys.SocialDesirability] = 33,
                    [ScaleKeys.Atypically] = 69
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.Activity] = 23,
                    [ScaleKeys.Responsibility] = 21,
                    [ScaleKeys.EmotionalEndurance] = 40,
                    [ScaleKeys.SocialDesirability] = 28,
                    [ScaleKeys.Atypically] = 16
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.Activity] = 27,
                    [ScaleKeys.Responsibility] = 25,
                    [ScaleKeys.EmotionalEndurance] = 48,
                    [ScaleKeys.SocialDesirability] = 28,
                    [ScaleKeys.Atypically] = 24
                },
                NormalityAliases = new Dictionary<string, string>()
                {
                    [ReportStrings.NormalityLow] = "Низкий",
                    [ReportStrings.NoralityNormal] = "Средний",
                    [ReportStrings.NormalityHigh] = "Высокий"
                },
                SubScales = new Dictionary<string, bool>()
                {
                    [ScaleKeys.Activity] = true,
                    [ScaleKeys.SocialDesirability] = true,
                    [ScaleKeys.Atypically] = true
                }
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Люшер.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Documents;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;
using Updk7.Tests.Reports.Wpf.ViewModels;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Люшер : QuestionnaireReportGenerator
    {
        private class ScaleKeys
        {
            public static readonly string Select1 = "Первый выбор";
            public static readonly string Intensity1 = "Интенсивность тревоги в первом выборе";
            public static readonly string Select2 = "Второй выбор";
            public static readonly string Intensity2 = "Интенсивность тревоги во втором выборе";
            public static readonly string Pairs = "Пары";
            public static readonly string Coefficient = "Коэффициент вегетативного баланса";
            public static readonly string Index = "Индекс суммарного отклонения";
        }

        private string[] _colors = new string[] 
        {
            "серый", "синий", "зеленый", "красный", "желтый", "фиолетовый", "коричневый", "черный"
        };

        private Dictionary<string, string> _sectionTitles = new Dictionary<string, string>()
        {
            ["+"] = "Желаемые цели и поведение, продиктованное желаемыми целями",
            ["x"] = "Существующая ситуация и поведение, уместное в этой ситуации",
            ["="] = "Сдерживаемые свойства и поведение, неуместное в данной ситуации",
            ["-"] = "Отрицаемые, подавляемые или несущие тревогу свойства",
            ["+-"] = "Актуальные проблемы и поведение, вызванное стрессом"
        };

        public Люшер()
        {
            TestType = TestType.Тест_Люшера;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata();
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            yield return getTitleSection("Результаты тестирования");
            yield return getResultsSection("Первый выбор", getColorsString(results[ScaleKeys.Select1]));
            yield return getResultsSection("Индекс тревоги в первом выборе", results[ScaleKeys.Intensity1].Int.ToString());
            yield return getResultsSection("Второй выбор", getColorsString(results[ScaleKeys.Select2]));
            yield return getResultsSection("Индекс тревоги во втором выборе", results[ScaleKeys.Intensity2].Int.ToString());
            yield return getResultsSection("Пары", results[ScaleKeys.Pairs].String);
            yield return getResultsSection("Коэффициент вегетативного баланса", results[ScaleKeys.Coefficient].Float.ToString());

            yield return getTitleSection("Интерпретация результатов");

            var pairs = results[ScaleKeys.Pairs].String;
            var interpretations = createInterpretations(pairs);
            foreach (var interpretationKey in interpretations.Keys)
            {
                var interpretationSections = interpretations[interpretationKey];
                if (interpretationSections.Count == 0)
                    continue;

                yield return getTitleSection(_sectionTitles[interpretationKey]);
                foreach (var section in interpretationSections)
                    yield return section;
            }
        }

        protected override void UpdateHeaderViewModel(QuestionnaireReportTemplateViewModel viewModel)
        {
            viewModel.IsInterpretetaionTitleVisible = false;
        }

        private Dictionary<string, List<Section>> createInterpretations(string pairsValue)
        {
            var results = new Dictionary<string, List<Section>>();
            foreach (var pair in pairsValue.Split(' '))
            {
                var pairKey = getPairKeys(pair);
                var pairSectionKey = getPairSectionKey(pairKey.Item1);

                if (!_sectionTitles.ContainsKey(pairSectionKey))
                    continue;

                if (!results.ContainsKey(pairSectionKey))
                    results[pairSectionKey] = new List<Section>();

                if (Resources.Value.Contains(pairKey.Item1))
                    results[pairSectionKey].Add(Resources.Value[pairKey.Item1] as Section);
                else if (Resources.Value.Contains(pairKey.Item2))
                    results[pairSectionKey].Add(Resources.Value[pairKey.Item2] as Section);
            }

            return results;
        }

        private Tuple<string, string> getPairKeys(string pairValue)
        {
            var parts = pairValue.Trim(' ', '(', ')').Split(';');
            return parts.Length == 2
                ? Tuple.Create($"{parts[0]}{parts[1]}", $"{parts[1]}{parts[0]}")
                : Tuple.Create(parts[0], parts[0]);
        }

        private string getPairSectionKey(string pairKey)
        {
            var signs = new string(pairKey.Where(c => !char.IsDigit(c)).ToArray());
            if (signs.Length == 2 && signs[0] == signs[1])
                signs = signs[0].ToString();

            return signs;
        }

        private string getColorsString(TestResultValue resultValue)
        {
            var normalSource = resultValue.String;
            return normalSource
                .Select(c => _colors[int.Parse(c.ToString())])
                .Aggregate(string.Empty, (acc, v) => string.IsNullOrEmpty(acc) ? v : $"{acc}, {v}");
        }

        private Section getResultsSection(string sectionTitle, string resultValue)
        {
            var section = new Section();
            var paragraph = new Paragraph();
            paragraph.TextIndent = 0.0;
            paragraph.FontSize = 14.0;
            paragraph.Inlines.Add(new Bold(new Run($"{sectionTitle}: ")));
            paragraph.Inlines.Add(new Run(resultValue));
            section.Blocks.Add(paragraph);

            return section;
        }

        protected Section getTitleSection(string title)
        {
            var section = new Section();
            var paragraph = new Paragraph();
            paragraph.TextIndent = 20.0;
            paragraph.FontSize = 16.0;
            paragraph.Inlines.Add(new Bold(new Run(title)));
            section.Blocks.Add(paragraph);

            return section;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\МДУ_Элерс.cs


using System.Collections.Generic;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Диагностики_мотивации_к_достижению_успеха_Элерса_МДУ.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class МДУ_Элерс : QuestionnaireReportGenerator
    {
        public МДУ_Элерс()
        {
            TestType = TestType.Диагностики_мотивации_к_достижению_успеха_Элерса_МДУ;
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
            // График не выводится
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                ValueDifferentiation = QuestionnaireMetadata.ValueDifferentiationType.FourRanges,
                NormalityAliases = new Dictionary<string, string>()
                {
                    [ReportStrings.TendentionLow] = "Низкий",
                    [ReportStrings.TendentionNormal] = "Средний",
                    [ReportStrings.TendentionMedium] = "Умеренно высокий",
                    [ReportStrings.TendentionHigh] = "Слишком высокий"
                },
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Motivation] = 32
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.Motivation] = 11
                },
                NormalityMedium = new Dictionary<string, int>()
                {
                    [ScaleKeys.Motivation] = 17
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.Motivation] = 21
                }
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Оценка_нервно_психической_устойчивости_НПУ.cs


using System.Collections.Generic;
using System.Windows.Documents;
using System.Windows.Media;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Оценка_нервно_психической_устойчивости_НПУ.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Оценка_нервно_психической_устойчивости_НПУ : QuestionnaireReportGenerator
    {
        private const int StabilityMax = 69;
        private const int FalsityMax = 15;

        public Оценка_нервно_психической_устойчивости_НПУ()
        {
            TestType = TestType.Оценка_нервно_психической_устойчивости_НПУ;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
            chartData.AddData(createFalsityScale(results[ScaleKeys.Falsity]));
            chartData.AddData(createStabilityScale(results[ScaleKeys.Stability]));
        }

        private BarChartDataGridData createFalsityScale(TestResultValue testResult)
        {
            var value = testResult.Int;

            var scale = new BarChartDataGridData();
            scale.Name = "Неискренность";
            scale.Value = value;
            scale.Maximum = FalsityMax;
            scale.ValueFormat = $"({value}/{FalsityMax})";
            scale.Foreground = Brushes.Green;

            return scale;
        }
        
        private BarChartDataGridData createStabilityScale(TestResultValue testResult)
        {
            var value = testResult.Int;

            var scale = new BarChartDataGridData();
            scale.Name = "Нервно-психическая устойчивость";
            scale.Value = value;
            scale.Maximum = StabilityMax;
            scale.ValueFormat = $"({value}/{StabilityMax})";

            var normality = getNormality(value);
            if (normality == ReportStrings.NormalityLow)
            {
                scale.Foreground = Brushes.Red;
                scale.BarTitle = "Низкая";
            }
            else if (normality == ReportStrings.NoralityNormal)
            {
                scale.Foreground = Brushes.Gray;
                scale.BarTitle = "Средняя";
            }
            else
            {
                scale.Foreground = Brushes.Blue;
                scale.BarTitle = "Высокая";
            }

            return scale;
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            var block1 = Resources.Value[ScaleKeys.Falsity] as Block;
            block1.DataContext = $"{results[ScaleKeys.Falsity].Int} баллов";

            var value = results[ScaleKeys.Stability].Int;
            var key = $"{ScaleKeys.Stability} - {getNormality(value)}";
            var block2 = Resources.Value[key] as Block;
            block2.DataContext = $" ({value}/{StabilityMax} баллов)";

            return new Block[] { block1, block2 };
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata();
        }

        private string getNormality(int value)
        {
            if (value >= 29)
                return ReportStrings.NormalityLow;
            else if (value >= 14)
                return ReportStrings.NoralityNormal;
            else
                return ReportStrings.NormalityHigh;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Потребность_в_достижении.cs


using System.Collections.Generic;
using System.Windows.Documents;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Потребность_в_достижении : QuestionnaireReportGenerator
    {
        private class ScaleKeys
        {
            public static readonly string Key = "Потребность в достижении";
        }

        public Потребность_в_достижении()
        {
            TestType = TestType.Тест_Потребность_в_достижении_ПД;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata();
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            var value = results[ScaleKeys.Key].Int;
            if (value == 0)
                value = 1;

            var key = "Low";
            if (value >= 9)
                key = "VeryHigh";
            else if (value >= 4)
                key = "High";
            else if (value >= 2)
                key = "Normal";

            var section = Resources.Value[key] as Block;
            section.DataContext = value.ToString();

            yield return section;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Рокич.cs


using System.Collections.Generic;
using System.Windows.Documents;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Рокич : QuestionnaireReportGenerator
    {
        protected class ScaleKyes
        {
            public static readonly string Шкала1 = "Приоритетные терминальные ценности";
            public static readonly string Шкала2 = "Неприоритетные терминальные ценности";
            public static readonly string Шкала3 = "Приоритетные инструментальные ценности";
            public static readonly string Шкала4 = "Неприоритетные инструментальные ценности";
            public static readonly string Таблица1 = "Таблица 1";
            public static readonly string Таблица2 = "Таблица 2";
        }

        public Рокич()
        {
            TestType = TestType.Ценностные_ориентации_Рокич;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKyes>();
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            var tables = new List<Block>();
            tables.AddRange(createTable(results[ScaleKyes.Таблица1], "Terminal"));
            tables.AddRange(createTable(results[ScaleKyes.Таблица2], "Instrumental"));

            return tables;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata();
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
        }

        private IEnumerable<Block> createTable(TestResultValue result, string resourceKeyPrefix)
        {
            yield return Resources.Value[$"{resourceKeyPrefix}-Title"] as Block;

            var list = new List();
            list.MarkerStyle = System.Windows.TextMarkerStyle.Decimal;
            list.MarkerOffset = 15;

            foreach (var item in result.IntArray)
            {
                var key = $"{resourceKeyPrefix}-{item}";
                list.ListItems.Add(Resources.Value[key] as ListItem);
            }

            yield return list;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\САН.cs


using System.Collections.Generic;
using System.Linq;
using System.Windows.Documents;
using System.Windows.Media;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class САН : QuestionnaireReportGenerator
    {
        private class ScaleKeys
        {
            public static readonly string Самочувствие = "Самочувствие";
            public static readonly string Активность = "Активность";
            public static readonly string Настроение = "Настроение";
            public static readonly string Гистограмма = "Гистограмма";
        }

        public САН()
        {
            TestType = TestType.Опросник_САН;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata();
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            foreach (var result in results.Values.Where(v => v.Key != ScaleKeys.Гистограмма))
            {
                var value = result.Float;
                var level = 0;
                if (value > 5.5)
                    level = 3;
                else if (value >= 5.0)
                    level = 2;
                else if (value >= 4.0)
                    level = 1;

                var key = $"{result.Key}-{level}";
                yield return Resources.Value[key] as Section;
            }

            yield return Resources.Value["Remark"] as Section;
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
            chartData.AddData(BarChartScalesFactory.CreateSeparator("Основные шкалы"));
            chartData.AddRange(createBaseScales(results));

            //chartData.AddData(BarChartScalesFactory.CreateSeparator("Выбор ответов"));
            //chartData.AddRange(createFreqScales(results));
        }

        private IEnumerable<BarChartDataGridData> createBaseScales(TestResults results)
        {
            var max = results.Values.Where(v => v.Key != ScaleKeys.Гистограмма).Select(v => v.Float).Max();
            var foreground = Brushes.DeepSkyBlue;
            yield return createScale(ScaleKeys.Самочувствие, results[ScaleKeys.Самочувствие].Float, max, "f1", foreground);
            yield return createScale(ScaleKeys.Активность, results[ScaleKeys.Активность].Float, max, "f1", foreground);
            yield return createScale(ScaleKeys.Настроение, results[ScaleKeys.Настроение].Float, max, "f1", foreground);
        }

        private IEnumerable<BarChartDataGridData> createFreqScales(TestResults results)
        {
            var data = results[ScaleKeys.Гистограмма].IntArray;
            var max = data.Max();
            for (var i = 1; i < data.Length; i++)
                yield return createScale($"Вариант {i.ToString()}", data[i], max, "f0", Brushes.LimeGreen);
        }

        private BarChartDataGridData createScale(string title, double value, double max, string formatter,
            Brush foreground)
        {
            var scale = new BarChartDataGridData();
            scale.Name = title;
            scale.Value = value;
            scale.Maximum = max;
            scale.ValueFormat = $"({value.ToString(formatter)})";
            scale.Foreground = foreground;

            return scale;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Склонность_к_зависимому_поведению.cs


using System.Collections.Generic;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Тест_опросник_Склонность_к_зависимому_поведению_ЗП.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Склонность_к_зависимому_поведению : QuestionnaireReportGenerator
    {
        public Склонность_к_зависимому_поведению()
        {
            TestType = TestType.Тест_опросник_Склонность_к_зависимому_поведению_ЗП;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                ValueDifferentiation = QuestionnaireMetadata.ValueDifferentiationType.FourRanges,
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.DrugAddiction] = 205,
                    [ScaleKeys.AlcoholAddiction] = 175
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.DrugAddiction] = 98,
                    [ScaleKeys.AlcoholAddiction] = 99
                },
                NormalityMedium = new Dictionary<string, int>()
                {
                    [ScaleKeys.DrugAddiction] = 107,
                    [ScaleKeys.AlcoholAddiction] = 105
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.DrugAddiction] = 116,
                    [ScaleKeys.AlcoholAddiction] = 111
                }
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Склонность_к_риску_Шуберт.cs


using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Documents;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Диагностика_степени_готовности_склонности_к_риску_Шуберт.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Склонность_к_риску_Шуберт : QuestionnaireReportGenerator
    {
        private class ResourceTagse
        {
            public static readonly string TooLow = "Слишком низкий";
            public static readonly string Low = "Низкий";
            public static readonly string Medium = "Средний";
            public static readonly string Moderate = "Умеренный";
            public static readonly string High = "Высокий";
        }

        public Склонность_к_риску_Шуберт()
        {
            TestType = TestType.Диагностика_степени_готовности_склонности_к_риску_Шуберт;
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
            // График не выводится
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            var blocks = new List<Block>();

            var conclusionKey = getConclusionKey(results[ScaleKeys.RiskAppetite]);
            var resourceKey = $"{ScaleKeys.RiskAppetite} - {conclusionKey}";
            var block = Resources.Value[resourceKey] as Block;
            block.DataContext = $"{conclusionKey} ({results[ScaleKeys.RiskAppetite].Int})";
            blocks.Add(block);

            return blocks;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata();
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        private string getConclusionKey(TestResultValue result)
        {
            Debug.Assert(result.Key == ScaleKeys.RiskAppetite);

            var conclusion = string.Empty;

            if (result.Int < -30)
                conclusion = ResourceTagse.TooLow;
            else if (result.Int <= -11)
                conclusion = ResourceTagse.Low;
            else if (result.Int <= 10)
                conclusion = ResourceTagse.Medium;
            else if (result.Int <= 20)
                conclusion = ResourceTagse.Moderate;
            else
                conclusion = ResourceTagse.High;

            return conclusion;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\СМОЛ.cs


using System.Collections.Generic;
using System.Linq;
using System.Windows.Documents;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Сокращенный_многопрофильный_опросник_личности_СМОЛ.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class СМОЛ : QuestionnaireReportGenerator
    {
        private static readonly string LieTag = "Lie";
        private static readonly string SecrecyTag = "Secrecy";
        private static readonly string IncriminationTag = "Incrimination";
        private static readonly string NoResultsTag = "NoResults";

        public СМОЛ()
        {
            TestType = TestType.Сокращенный_многопрофильный_опросник_личности_СМОЛ;
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
            chartData.AddData(BarChartScalesFactory.CreateSeparator(ReportStrings.EstimationScales));

            AddBarChartScale(ScaleKeys.L, results, chartData);
            AddBarChartScale(ScaleKeys.F, results, chartData);
            AddBarChartScale(ScaleKeys.K, results, chartData);

            chartData.AddData(BarChartScalesFactory.CreateSeparator(ReportStrings.BaseScales));

            AddBarChartScale(ScaleKeys.S1, results, chartData);
            AddBarChartScale(ScaleKeys.S2, results, chartData);
            AddBarChartScale(ScaleKeys.S3, results, chartData);
            AddBarChartScale(ScaleKeys.S4, results, chartData);
            AddBarChartScale(ScaleKeys.S6, results, chartData);
            AddBarChartScale(ScaleKeys.S7, results, chartData);
            AddBarChartScale(ScaleKeys.S8, results, chartData);
            AddBarChartScale(ScaleKeys.S9, results, chartData);

            var maximum = results.Values.Select(s => s.Int).Max();
            foreach (var data in chartData.Items)
                data.Maximum = maximum;
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            var interpretations = new List<Block>();
            var L = results[ScaleKeys.L];
            var F = results[ScaleKeys.F];
            var K = results[ScaleKeys.K];

            if ((L.Int >= 70) && ((L.Int - F.Int) >= 7))
                interpretations.Add(Resources.Value[LieTag] as Block);

            if (K.Int > 70 && F.Int < 70)
                interpretations.Add(Resources.Value[SecrecyTag] as Block);

            if ((F.Int - K.Int) > 20)
                interpretations.Add(Resources.Value[IncriminationTag] as Block);

            AddInterpretationBlock(ScaleKeys.S1, results, interpretations);
            AddInterpretationBlock(ScaleKeys.S2, results, interpretations);
            AddInterpretationBlock(ScaleKeys.S3, results, interpretations);
            AddInterpretationBlock(ScaleKeys.S4, results, interpretations);
            AddInterpretationBlock(ScaleKeys.S6, results, interpretations);
            AddInterpretationBlock(ScaleKeys.S7, results, interpretations);
            AddInterpretationBlock(ScaleKeys.S8, results, interpretations);
            AddInterpretationBlock(ScaleKeys.S9, results, interpretations);

            if (interpretations.Count == 0)
                interpretations.Add(Resources.Value[NoResultsTag] as Block);

            return interpretations;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                KeysAliases = new Dictionary<string, string>()
                {
                    [ScaleKeys.L] = "Ложь",
                    [ScaleKeys.F] = "Достоверность",
                    [ScaleKeys.K] = "Коррекция",
                    [ScaleKeys.S1] = "Иппохондрия",
                    [ScaleKeys.S2] = "Депрессия",
                    [ScaleKeys.S3] = "Истерия",
                    [ScaleKeys.S4] = "Психопатия",
                    [ScaleKeys.S6] = "Паранойяльность",
                    [ScaleKeys.S7] = "Психастения",
                    [ScaleKeys.S8] = "Шизоидность",
                    [ScaleKeys.S9] = "Гипомания"
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.L] = 40,
                    [ScaleKeys.F] = 40,
                    [ScaleKeys.K] = 40,
                    [ScaleKeys.S1] = 40,
                    [ScaleKeys.S2] = 40,
                    [ScaleKeys.S3] = 40,
                    [ScaleKeys.S4] = 40,
                    [ScaleKeys.S6] = 40,
                    [ScaleKeys.S7] = 40,
                    [ScaleKeys.S8] = 40,
                    [ScaleKeys.S9] = 40
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.L] = 70,
                    [ScaleKeys.F] = 70,
                    [ScaleKeys.K] = 70,
                    [ScaleKeys.S1] = 70,
                    [ScaleKeys.S2] = 70,
                    [ScaleKeys.S3] = 70,
                    [ScaleKeys.S4] = 70,
                    [ScaleKeys.S6] = 70,
                    [ScaleKeys.S7] = 70,
                    [ScaleKeys.S8] = 70,
                    [ScaleKeys.S9] = 70
                }
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Спилбергер.cs


using System.Collections.Generic;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Спилбергер : QuestionnaireReportGenerator
    {
        private class ScaleKeys
        {
            public static readonly string СитуативнаяТревожность = "Ситуативная тревожность";
            public static readonly string ЛичностнаяТревожность = "Личностная тревожность";
        }

        public Спилбергер()
        {
            TestType = TestType.Тест_Спилберга;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new Questionnaires.QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.СитуативнаяТревожность] = 80,
                    [ScaleKeys.ЛичностнаяТревожность] = 80
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.СитуативнаяТревожность] = 31,
                    [ScaleKeys.ЛичностнаяТревожность] = 31
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.СитуативнаяТревожность] = 45,
                    [ScaleKeys.ЛичностнаяТревожность] = 45
                },
                NormalityAliases = new Dictionary<string, string>()
                {
                    [ReportStrings.NormalityLow] = "Низкий",
                    [ReportStrings.NoralityNormal] = "Средний",
                    [ReportStrings.NormalityHigh] = "Высокий"
                }
            };
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            yield return getInterpretation1(results);
            yield return getInterpretation2(results);
        }

        private Block getInterpretation1(TestResults results)
        {
            var value = results[ScaleKeys.СитуативнаяТревожность].Int;
            var index = 1;
            if (value >= 31 && value <= 45)
                index = 2;
            else if (value > 45)
                index = 3;

            var key = $"{ScaleKeys.СитуативнаяТревожность} - {index}";
            return Resources.Value[key] as Section;
        }

        private Block getInterpretation2(TestResults results)
        {
            var value = results[ScaleKeys.ЛичностнаяТревожность].Int;
            var index = 1;
            if (value >= 26 && value <= 30)
                index = 2;
            else if (value >= 31 && value <= 37)
                index = 3;
            else if (value >= 38 && value <= 45)
                index = 4;
            else if (value >= 46)
                index = 5;

            var key = $"{ScaleKeys.ЛичностнаяТревожность} - {index}";
            return Resources.Value[key] as Section;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Стиль_руководства.cs


using System.Collections.Generic;
using System.Linq;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Стиль_руководства : QuestionnaireReportGenerator
    {
        private class ScaleKeys
        {
            public static readonly string Style1 = "Авторитарный стиль руководства";
            public static readonly string Style2 = "Демократический стиль руководства";
            public static readonly string Style3 = "Либеральный стиль руководства";
        }

        private static readonly string MixedStyleKey = "Смешанный стиль руководства";

        public Стиль_руководства()
        {
            TestType = TestType.Тест_Стиль_руководства_СР;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Style1] = 97,
                    [ScaleKeys.Style2] = 99,
                    [ScaleKeys.Style3] = 100
                },
                NormalityLow = new Dictionary<string, int>()
            };
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            var values = results.Values;
            var maxResult = values.Aggregate(values.First(), (acc, v) => v.Int > acc.Int ? v : acc);
            var diffs = values.Where(v => v.Key != maxResult.Key).Select(v => maxResult.Int - v.Int);
            var isMixed = diffs.Min() <= 1;
            var key = isMixed ? MixedStyleKey : maxResult.Key;

            yield return Resources.Value[key] as Block;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Структура_темперамента_Смирнов.cs


using System.Collections.Generic;
using System.Windows.Documents;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Структура_темперамента_Смирнов.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Структура_темперамента_Смирнов : QuestionnaireReportGenerator
    {
        public Структура_темперамента_Смирнов()
        {
            TestType = TestType.Структура_темперамента_Смирнов;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata();
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            yield return getChartSection(results);
            yield return getSincerity(results);
            yield return getScale1(results);
            yield return getScale2(results);
            yield return getScale3(results);
            yield return getScale4(results);
        }

        private Block getChartSection(TestResults results)
        {
            var viewModel = new Controls.SmirnovChart.SmirnovChartViewModel();
            viewModel.Extraversion = results[ScaleKeys.Extraversion_Introversion_Level].Int;
            viewModel.Rigidity = results[ScaleKeys.Rigidity_Plasticity_Level].Int;
            viewModel.Emotional = results[ScaleKeys.Emotional_excitability_Poise_Level].Int;
            viewModel.Reaction = results[ScaleKeys.Reaction_tempo_Level].Int;
            viewModel.Activity = results[ScaleKeys.Activity_Level].Int;

            var section = Resources.Value["SmirnovChartSection"] as Section;
            section.DataContext = viewModel;

            return section;
        }

        private Block getSincerity(TestResults results)
        {
            var scaleValue = results[ScaleKeys.Sincerity];
            Section section = null;
            if (scaleValue.Int <= 7)
                section = Resources.Value["Искренность - 1"] as Section;
            else if (scaleValue.Int <= 12)
                section = Resources.Value["Искренность - 2"] as Section;
            else
                section = Resources.Value["Искренность - 3"] as Section;

            section.DataContext = $"({scaleValue.Int}/20)";
            
            return section;
        }

        private Block getScale1(TestResults results)
        {
            var scaleValue = results[ScaleKeys.Extraversion_Introversion];
            var scaleLevel = results[ScaleKeys.Extraversion_Introversion_Level];
            var title = scaleLevel.Int > 0 ? "Интроверсия" : "Экстраверсия";

            return createConclusionBlock(title, scaleLevel.Int, scaleValue.Int, 27);
        }

        private Block getScale2(TestResults results)
        {
            var scaleValue = results[ScaleKeys.Rigidity_Plasticity];
            var scaleLevel = results[ScaleKeys.Rigidity_Plasticity_Level];
            var title = scaleLevel.Int > 0 ? "Пластичность" : "Ригидность";

            return createConclusionBlock(title, scaleLevel.Int, scaleValue.Int, 25);
        }

        private Block getScale3(TestResults results)
        {
            var scaleValue = results[ScaleKeys.Emotional_excitability_Poise];
            var scaleLevel = results[ScaleKeys.Emotional_excitability_Poise_Level];
            var title = scaleLevel.Int > 0 ? "Эмоциональная устойчивость" : "Эмоциональная возбудимость";

            return createConclusionBlock(title, scaleLevel.Int, scaleValue.Int, 20);
        }

        private Block getScale4(TestResults results)
        {
            var scaleValue = results[ScaleKeys.Reaction_tempo];
            var scaleLevel = results[ScaleKeys.Reaction_tempo_Level];
            var title = scaleLevel.Int > 0 ? "Медлительность" : "Быстрота реакции";

            return createConclusionBlock(title, scaleLevel.Int, scaleValue.Int, 22);
        }

        private Block getScale5(TestResults results)
        {
            var scaleValue = results[ScaleKeys.Activity];
            var scaleLevel = results[ScaleKeys.Activity_Level];
            var title = scaleLevel.Int > 0 ? "Пассивность" : "Активность";

            return createConclusionBlock(title, scaleLevel.Int, scaleValue.Int, 26);
        }

        private Block createConclusionBlock(string title, int level, int value, int maxValue)
        {
            var paragraph = new Paragraph();
            paragraph.TextIndent = 20.0;
            paragraph.FontSize = 14.0;
            paragraph.Inlines.Add(new Bold(new Run($"Уровень по шкале «{title}»: {getLevelTitle(level)}")));
            paragraph.Inlines.Add(new Run($" ({value}/{maxValue})."));

            return paragraph;
        }

        private string getLevelTitle(int levelValue)
        {
            if (levelValue <= -2)
                return "ОЧЕНЬ ВЫСОКИЙ";
            else if (levelValue == -1)
                return "ВЫСОКИЙ";
            else if (levelValue == 0)
                return "СРЕДНИЙ";
            else if (levelValue == 1)
                return "ВЫСОКИЙ";
            else
                return "ОЧЕНЬ ВЫСОКИЙ";
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Тейлор.cs


using System.Collections.Generic;
using System.Text;
using System.Windows.Documents;
using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Тейлор : QuestionnaireReportGenerator
    {
        private class ScaleKeys
        {
            public static readonly string Тревожность = "Степень тревожности";
        }

        public Тейлор()
        {
            TestType = TestType.Тест_уровня_тревожности_Тейлор;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override void FillBarChart(TestResults results, BarChartDataGridDataCollection chartData)
        {
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            // Это для клиента УПДК5
            //var bytes = Encoding.ASCII.GetBytes(results[ScaleKeys.Тревожность].String);
            //var result = Encoding.Unicode.GetString(bytes);

            var scale = results[ScaleKeys.Тревожность].Get<int>();
            var index = 1;

            if (scale >= 6 && scale <= 20)
                index = 2;
            else if (scale > 20)
                index = 3;

            var key = $"Conclusion-{index}";

            yield return Resources.Value[key] as Section;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new Questionnaires.QuestionnaireMetadata();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Тест_Томаса.cs


using System.Collections.Generic;
using System.Windows.Documents;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Тест_Томаса_Конфликтность.Keys;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Тест_Томаса : QuestionnaireReportGenerator
    {
        protected static readonly string ConclusionKey = "Conclusion";

        public Тест_Томаса()
        {
            TestType = TestType.Тест_Томаса_Конфликтность;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            return (Resources.Value[ConclusionKey] as Block).AsEnumerable();
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Rivalry] = 12,
                    [ScaleKeys.Cooperation] = 12,
                    [ScaleKeys.Compromise] = 12,
                    [ScaleKeys.Avoidance] = 12,
                    [ScaleKeys.Adaptation] = 12
                },
                NormalityLow = new Dictionary<string, int>()
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\УСК.cs



using System.Linq;
using System.Collections.Generic;
using System.Windows.Media;

using ScaleKeys = Updk7.Tests.Metadata.Tests.Тест_Уровень_субъективного_контроля_УСК.Keys;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class УСК : QuestionnaireReportGenerator
    {
        private enum Normality
        {
            Low,
            Medium,
            High
        }

        public УСК()
        {
            TestType = TestType.Тест_Уровень_субъективного_контроля_УСК;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>().Where(k => k != ScaleKeys.Selfreflection);
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            foreach (var interpretation in base.CreateInterpretations(results))
                yield return interpretation;

            var selfreflection = results[ScaleKeys.Selfreflection];
            if (selfreflection != null && selfreflection.Int >= 15)
                yield return Resources.Value["Warning"] as Block;

            yield return Resources.Value["Remark"] as Block;
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new Questionnaires.QuestionnaireMetadata()
            {
                ValueDifferentiation = QuestionnaireMetadata.ValueDifferentiationType.ThreeRanges,
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.General_internality_Stens] = 9,
                    [ScaleKeys.Internality_in_arhievement_field_Stens] = 9,
                    [ScaleKeys.Internality_in_a_situation_of_failure_Stens] = 9,
                    [ScaleKeys.Internality_in_family_relations_Stens] = 9,
                    [ScaleKeys.Internality_in_industrial_relations_Stens] = 9,
                    [ScaleKeys.Internality_in_interpersonal_relations_Stens] = 9,
                    [ScaleKeys.Health_internality_Stens] = 9
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.General_internality_Stens] = 3,
                    [ScaleKeys.Internality_in_arhievement_field_Stens] = 3,
                    [ScaleKeys.Internality_in_a_situation_of_failure_Stens] = 3,
                    [ScaleKeys.Internality_in_family_relations_Stens] = 3,
                    [ScaleKeys.Internality_in_industrial_relations_Stens] = 3,
                    [ScaleKeys.Internality_in_interpersonal_relations_Stens] = 3,
                    [ScaleKeys.Health_internality_Stens] = 3
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.General_internality_Stens] = 5,
                    [ScaleKeys.Internality_in_arhievement_field_Stens] = 5,
                    [ScaleKeys.Internality_in_a_situation_of_failure_Stens] = 5,
                    [ScaleKeys.Internality_in_family_relations_Stens] = 5,
                    [ScaleKeys.Internality_in_industrial_relations_Stens] = 5,
                    [ScaleKeys.Internality_in_interpersonal_relations_Stens] = 5,
                    [ScaleKeys.Health_internality_Stens] = 6
                },
                NormalityAliases = new Dictionary<string, string>()
                {
                    [ReportStrings.NormalityLow] = "Низкий",
                    [ReportStrings.NoralityNormal] = "Средний",
                    [ReportStrings.NormalityHigh] = "Высокий"
                },
                NormalityBrushes = new Dictionary<string, System.Windows.Media.Brush>()
                {
                    [ReportStrings.NormalityLow] = Brushes.Blue,
                    [ReportStrings.NoralityNormal] = Brushes.Gray,
                    [ReportStrings.NormalityHigh] = Brushes.Red
                }
            };
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\Questionnaires\Шмишек.cs


using System.Collections.Generic;
using System.Linq;
using System.Windows.Documents;
using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.Questionnaires
{
    internal class Шмишек : QuestionnaireReportGenerator
    {
        private class ScaleKeys
        {
            public static readonly string Type1 = "Демонстративность";
            public static readonly string Type2 = "Застревание";
            public static readonly string Type3 = "Педантичность";
            public static readonly string Type4 = "Возбудимость";
            public static readonly string Type5 = "Гипертимность";
            public static readonly string Type6 = "Дистимность";
            public static readonly string Type7 = "Тревожность";
            public static readonly string Type8 = "Экзальтированность";
            public static readonly string Type9 = "Эмотивность";
            public static readonly string Type10 = "Циклотимность";
        }

        private static readonly string _skipType = "Интерпретация";
        private const int _normalityLow = 15;
        private const int _normalityHigh = 19;

        public Шмишек()
        {
            TestType = TestType.Тип_акцентуации_личности_Шмишека_Леонгарда;
        }

        protected override IEnumerable<string> GetScaleKeys()
        {
            return GetScaleKeys<ScaleKeys>();
        }

        protected override QuestionnaireMetadata CreateMetadata()
        {
            return new QuestionnaireMetadata()
            {
                Max = new Dictionary<string, int>()
                {
                    [ScaleKeys.Type1] = 24,
                    [ScaleKeys.Type2] = 24,
                    [ScaleKeys.Type3] = 24,
                    [ScaleKeys.Type4] = 24,
                    [ScaleKeys.Type5] = 24,
                    [ScaleKeys.Type6] = 24,
                    [ScaleKeys.Type7] = 24,
                    [ScaleKeys.Type8] = 24,
                    [ScaleKeys.Type9] = 24,
                    [ScaleKeys.Type10] = 24,
                },
                NormalityLow = new Dictionary<string, int>()
                {
                    [ScaleKeys.Type1] = _normalityLow,
                    [ScaleKeys.Type2] = _normalityLow,
                    [ScaleKeys.Type3] = _normalityLow,
                    [ScaleKeys.Type4] = _normalityLow,
                    [ScaleKeys.Type5] = _normalityLow,
                    [ScaleKeys.Type6] = _normalityLow,
                    [ScaleKeys.Type7] = _normalityLow,
                    [ScaleKeys.Type8] = _normalityLow,
                    [ScaleKeys.Type9] = _normalityLow,
                    [ScaleKeys.Type10] = _normalityLow,
                },
                NormalityHigh = new Dictionary<string, int>()
                {
                    [ScaleKeys.Type1] = _normalityHigh,
                    [ScaleKeys.Type2] = _normalityHigh,
                    [ScaleKeys.Type3] = _normalityHigh,
                    [ScaleKeys.Type4] = _normalityHigh,
                    [ScaleKeys.Type5] = _normalityHigh,
                    [ScaleKeys.Type6] = _normalityHigh,
                    [ScaleKeys.Type7] = _normalityHigh,
                    [ScaleKeys.Type8] = _normalityHigh,
                    [ScaleKeys.Type9] = _normalityHigh,
                    [ScaleKeys.Type10] = _normalityHigh,
                },
                NormalityAliases = new Dictionary<string, string>()
                {
                    [ReportStrings.NormalityLow] = "",
                    [ReportStrings.NoralityNormal] = "",
                    [ReportStrings.NormalityHigh] = ""
                },
                NormalityBrushes = new Dictionary<string, Brush>()
                {
                    [ReportStrings.NormalityLow] = Brushes.Gray,
                    [ReportStrings.NoralityNormal] = Brushes.DeepSkyBlue,
                    [ReportStrings.NormalityHigh] = Brushes.Red
                }
            };
        }

        protected override IEnumerable<Block> CreateInterpretations(TestResults results)
        {
            var actualResults = results.Values.Where(v => v.Key != _skipType);
            var blocks = new List<Block>();

            var interpretations = actualResults
                .Where(v => v.Int > _normalityHigh)
                .Select(v => Resources.Value[v.Key] as Section);
            blocks.AddRange(interpretations);

            if (blocks.Count == 0)
            {
                var tendentions = actualResults
                    .Where(v => v.Int >= _normalityLow && v.Int <= _normalityHigh)
                    .Select(v => Resources.Value[$"Tendention - {v.Key}"] as Section);
                blocks.AddRange(tendentions);
            }

            var finalBlock = blocks.Count != 0
                ? Resources.Value["Remark"] as Section
                : Resources.Value["NoResults"] as Section;
            blocks.Add(finalBlock);

            return blocks;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\ReportBlocks\Footer.cs


using System;
using System.Windows;
using System.Windows.Documents;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.ReportBlocks
{
    public static class Footer
    {
        public static Block Create(string userName)
        {
            Func<string, TextAlignment, TableCell> createCell = (text, alignment) =>
            {
                var paragraph = new Paragraph()
                {
                    Margin = new Thickness(5, 1, 1, 1),
                    TextAlignment = alignment
                };
                paragraph.Inlines.Add(new Run(text));
                var cell = new TableCell();
                cell.Blocks.Add(paragraph);

                return cell;
            };

            var table = new Table();
            table.Margin = new Thickness(0, 30, 0, 0);

            table.Columns.Add(new TableColumn());
            table.Columns.Add(new TableColumn() { Width = new GridLength(1.0, GridUnitType.Auto) });
            table.Columns.Add(new TableColumn());

            var tableRowGroup = new TableRowGroup();
            table.RowGroups.Add(tableRowGroup);

            var row1 = new TableRow();
            row1.Cells.Add(createCell("Тестирование проводил:", TextAlignment.Right));
            row1.Cells.Add(createCell("___________________", TextAlignment.Center));
            row1.Cells.Add(createCell(userName, TextAlignment.Left));
            tableRowGroup.Rows.Add(row1);

            var row2 = new TableRow();
            row2.Cells.Add(new TableCell());
            row2.Cells.Add(createCell("М.П.", TextAlignment.Center));
            row2.Cells.Add(new TableCell());
            tableRowGroup.Rows.Add(row2);

            var row3 = new TableRow();
            row3.Cells.Add(createCell("Ознакомлен:", TextAlignment.Right));
            row3.Cells.Add(createCell("___________________", TextAlignment.Center));
            row3.Cells.Add(createCell("(подпись испытуемого)", TextAlignment.Left));
            tableRowGroup.Rows.Add(row3);

            var section = new Section();
            section.Blocks.Add(table);

            return section;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\ReportBlocks\GedPressTable.cs


using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.ReportBlocks
{
    public class GedPressTable
    {
        private Func<bool> _hasResistanceColumn;

        public GedPressTable()
        {
            _hasResistanceColumn = () => Resistance != null;
        }

        public int[] Actions { get; set; }

        public int[] TimeSamples { get; set; }

        public float[] Reactions { get; set; }

        public int[] Resistance { get; set; }

        public Block CreateDocumentBlock()
        {
            var table = createTable();
            fillTable(table);

            return table;
        }

        private TableCell createTextCell(string text, FontWeight fontWeight)
        {
            var cell = new TableCell()
            {
                BorderThickness = new Thickness(0.5),
                BorderBrush = Brushes.Black,
                TextAlignment = TextAlignment.Center,
                Padding = new Thickness(0.0, 3.0, 0.0, 1.5)
            };
            cell.Blocks.Add(new Paragraph(new Run(text)) { FontWeight = fontWeight });
            return cell;
        }

        private static string mapSignalType(int value)
        {
            switch (value)
            {
                case 0: return "Без предупреждения";
                case 1: return "С предупреждением";
                case 2: return "Предупреждение";
                case 3: return "Удлиненный";
                case 4: return "Пустое нажатие";
                default: return "Неизвестное действие";
            }
        }

        private static string mapTime(int time)
        {
            var hours = Math.DivRem(time, 60 * 60, out time);
            var minutes = Math.DivRem(time, 60, out time);
            var seconds = time;

            return string.Format("{0}:{1:d2}:{2:d2}", hours, minutes, seconds);
        }

        private static string mapReactionTime(float time)
        {
            if (float.IsNaN(time) || time <= 0.0f)
                return "-/-";

            return time.ToString("f3");
        }

        private static string mapResistance(int value)
        {
            return value > 0
                ? value.ToString()
                : "-/-";
        }

        private void fillTable(Table table)
        {
            Debug.Assert(Actions.Length == TimeSamples.Length && Actions.Length == Reactions.Length);

            var rowGroup = new TableRowGroup();
            table.RowGroups.Add(rowGroup);

            for (var i = 0; i < Actions.Length; i++)
            {
                var row = new TableRow();
                row.Cells.Add(createTextCell((i + 1).ToString(), FontWeights.Normal));
                row.Cells.Add(createTextCell(mapSignalType(Actions[i]), FontWeights.Normal));
                row.Cells.Add(createTextCell(mapTime(TimeSamples[i]), FontWeights.Bold));
                row.Cells.Add(createTextCell(mapReactionTime(Reactions[i]), FontWeights.Normal));
                if (_hasResistanceColumn())
                    row.Cells.Add(createTextCell(mapResistance(Resistance[i]), FontWeights.Normal));

                rowGroup.Rows.Add(row);
            }
        }

        private Table createTable()
        {
            var table = new Table()
            {
                FontSize = 12.0,
                BorderThickness = new Thickness(1.0),
                BorderBrush = Brushes.Black,
                CellSpacing = 0.0
            };
            table.Columns.Add(new TableColumn() { Width = new GridLength(40.0) });
            table.Columns.Add(new TableColumn());
            table.Columns.Add(new TableColumn());
            table.Columns.Add(new TableColumn());

            var rowHeader = new TableRow();
            rowHeader.Cells.Add(createTextCell("#", FontWeights.Bold));
            rowHeader.Cells.Add(createTextCell("Тип сигнала", FontWeights.Bold));
            rowHeader.Cells.Add(createTextCell("Время предъявления сигнала", FontWeights.Bold));
            rowHeader.Cells.Add(createTextCell("Время реагирования, [c]", FontWeights.Bold));

            if (_hasResistanceColumn())
            {
                table.Columns.Add(new TableColumn());
                rowHeader.Cells.Add(createTextCell("Сопротивдение, кОм", FontWeights.Bold));
            }

            var rowGrop = new TableRowGroup();
            rowGrop.Rows.Add(rowHeader);
            table.RowGroups.Add(rowGrop);

            return table;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\ReportBlocks\PvPuPressTable.cs


using System.Linq;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.ReportBlocks
{
    public class PvPuPressTable
    {
        public enum Color
        {
            Black,
            Red
        }

        public class ColumnCellValue
        {
            public int Number { get; set; }
            public Color Color { get; set; }
        }

        public class Column
        {
            public List<ColumnCellValue> Values { get; } = new List<ColumnCellValue>();
        }

        private List<Column> _columns = new List<Column>();

        public PvPuPressTable()
        {
            _columns.Add(new Column());
            _columns.Add(new Column());
            _columns.Add(new Column());
            _columns.Add(new Column());
        }

        public Column this[int columnIndex] => _columns[columnIndex];

        public Block CreateDocumentBlock()
        {
            var table = createTable();
            var dataRowGroup = createTableCells(table);
            fillTabeColumn12(dataRowGroup, _columns[0], 1, Color.Black);
            fillTabeColumn12(dataRowGroup, _columns[1], 2, Color.Red);
            fillTableColumn34(dataRowGroup, _columns[2], 3, 4);
            fillTableColumn34(dataRowGroup, _columns[3], 5, 6);

            return table;
        }

        private void fillTabeColumn12(TableRowGroup dataRowGroup, Column data, int cellIndex, Color targetColor)
        {
            for (int i = 0; i < data.Values.Count; i++)
            {
                var cellValue = data.Values[i];
                if (cellValue.Color == targetColor)
                {
                    var paragraph = new Paragraph(new Run(cellValue.Number.ToString()));
                    if (targetColor == Color.Black)
                        paragraph.FontWeight = FontWeights.Bold;

                    dataRowGroup.Rows[i].Cells[cellIndex].Blocks.Add(paragraph);
                }
            }
        }

        private void fillTableColumn34(TableRowGroup dataRowGroup, Column data, int redCellIndex, 
            int blackCellIndex)
        {
            for (int i = 0; i < data.Values.Count; i++)
            {
                var cellValue = data.Values[i];
                var paragraph = new Paragraph(new Run(cellValue.Number.ToString()));

                if (cellValue.Color == Color.Red)
                    dataRowGroup.Rows[i].Cells[redCellIndex].Blocks.Add(paragraph);
                else
                {
                    paragraph.FontWeight = FontWeights.Bold;
                    dataRowGroup.Rows[i].Cells[blackCellIndex].Blocks.Add(paragraph);
                }
            }
        }

        private TableCell createEmptyCell(int columnSpan = 1)
        {
            return new TableCell()
            {
                BorderThickness = new Thickness(0.5),
                BorderBrush = Brushes.Black,
                TextAlignment = TextAlignment.Center,
                Padding = new Thickness(0.0, 3.0, 0.0, 1.5),
                ColumnSpan = columnSpan
            };
        }

        private TableCell createTextCell(string text, FontWeight fontWeight, int columnSpan = 1)
        {
            var cell = createEmptyCell(columnSpan);
            cell.Blocks.Add(new Paragraph(new Run(text)) { FontWeight = fontWeight });
            return cell;
        }

        private TableRowGroup createTableCells(Table table)
        {
            var valuesCount = Enumerable.Max(_columns.Select(c => c.Values.Count));
            var rowGroup = new TableRowGroup();
            table.RowGroups.Add(rowGroup);

            for (int i = 0; i < valuesCount; i++)
            {
                var row = new TableRow();
                row.Cells.Add(createTextCell((i + 1).ToString(), FontWeights.Normal));
                for (int j = 0; j < table.Columns.Count - 1; j++)
                    row.Cells.Add(createEmptyCell());

                rowGroup.Rows.Add(row);
            }

            return rowGroup;
        }

        private Table createTable()
        {
            var table = new Table()
            {
                FontSize = 12.0,
                BorderThickness = new Thickness(1.0),
                BorderBrush = Brushes.Black,
                CellSpacing = 0.0
            };
            table.Columns.Add(new TableColumn() { Width = new GridLength(100.0) });
            table.Columns.Add(new TableColumn());
            table.Columns.Add(new TableColumn());
            table.Columns.Add(new TableColumn());
            table.Columns.Add(new TableColumn());
            table.Columns.Add(new TableColumn());
            table.Columns.Add(new TableColumn());

            var cell11 = createEmptyCell();
            cell11.RowSpan = 2;
            cell11.Blocks.Add(
                new Paragraph(new Run("N реагирования")) 
                { 
                    Margin = new Thickness(0.0, 11.0, 0.0, 0.0) 
                });

            var row1 = new TableRow();
            row1.Cells.Add(cell11);
            row1.Cells.Add(createTextCell("Задание 1", FontWeights.Bold));
            row1.Cells.Add(createTextCell("Задание 2", FontWeights.Bold));
            row1.Cells.Add(createTextCell("Задание 3", FontWeights.Bold, 2));
            row1.Cells.Add(createTextCell("Задание 4", FontWeights.Bold, 2));

            var row2 = new TableRow();
            row2.Cells.Add(createTextCell("Черное", FontWeights.Bold));
            row2.Cells.Add(createTextCell("Красное", FontWeights.Bold));
            row2.Cells.Add(createTextCell("Красное", FontWeights.Normal));
            row2.Cells.Add(createTextCell("Черное", FontWeights.Bold));
            row2.Cells.Add(createTextCell("Красное", FontWeights.Normal));
            row2.Cells.Add(createTextCell("Черное", FontWeights.Bold));

            var rowGrop = new TableRowGroup();
            rowGrop.Rows.Add(row1);
            rowGrop.Rows.Add(row2);
            table.RowGroups.Add(rowGrop);

            return table;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\ReportBlocks\ResultsArrayTable.cs


using System.Collections.Generic;
using System.Windows.Documents;
using System.Windows;
using System.Windows.Media;
using System.Diagnostics;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.ReportBlocks
{
    public class ResultsArrayTableRow
    {
        public string Header { get; set; }

        public IEnumerable<string> Values { get; set; }
    }

    public class ResultsArrayTable
    {
        private static readonly Thickness TableCellBorderThickness = new Thickness(1);

        public ResultsArrayTable()
        {
        }

        public GridLength HeaderColumnWidth { get; set; } = new GridLength(100);

        public List<ResultsArrayTableRow> Rows { get; set; } = new List<ResultsArrayTableRow>();

        public Block CreateDocumentBlock()
        {
            Debug.Assert(Rows != null);
            Debug.Assert(HeaderColumnWidth != null);

            var table = new Table();
            table.CellSpacing = 0;
            table.FontSize = 12;
            table.Columns.Add(new TableColumn() { Width = HeaderColumnWidth });

            var tableRowGroup = new TableRowGroup();
            table.RowGroups.Add(tableRowGroup);

            foreach (var row in Rows)
            {
                var tableRow = new TableRow();
                tableRowGroup.Rows.Add(tableRow);
                tableRow.Cells.Add(createTableCell(row.Header, FontWeights.Bold));

                foreach (var value in row.Values)
                    tableRow.Cells.Add(createTableCell(value, FontWeights.Normal));
            }

            return table;
        }

        private TableCell createTableCell(string content, FontWeight fontWeight)
        {
            var tableCell = new TableCell();
            tableCell.FontWeight = fontWeight;
            tableCell.BorderBrush = Brushes.Black;
            tableCell.BorderThickness = TableCellBorderThickness;
            tableCell.Blocks.Add(new Paragraph(new Run(content)) { TextAlignment = TextAlignment.Center });

            return tableCell;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ReportGenerators\ReportBlocks\ResultsTable.cs


using System;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;

namespace Updk7.Tests.Reports.Wpf.ReportGenerators.ReportBlocks
{
    public class ResultsTable
    {
        private static readonly Thickness TableCellMargin = new Thickness(3);
        private static readonly Thickness ZeroThickness = new Thickness(0);

        public ResultsTable()
        {
        }

        public TestResults Results { get; set; }

        public Block CreateDocumentBlock()
        {
            Debug.Assert(Results != null);

            var table = new Table();
            var rowGroup = new TableRowGroup();
            var background = Brushes.LightGray;

            table.FontSize = 12;
            table.CellSpacing = 0;
            table.Columns.Add(new TableColumn() { Width = new GridLength(5, GridUnitType.Star) });
            table.Columns.Add(new TableColumn() { Width = new GridLength(1, GridUnitType.Star) });
            table.RowGroups.Add(rowGroup);
            addTableColumnsHeaders(rowGroup);

            foreach (var value in Results.Values)
            {
                var metadata = ResultsMetadata.Get(Results.Test, value.Key);
                if (metadata != null && !metadata.ShowInReport || value.IsArray)
                    continue;

                var resultValue = formatValue(value);
                var resultTitle = value.Key;
                if (metadata != null && !string.IsNullOrEmpty(metadata.Dimension))
                    resultTitle += $", ({metadata.Dimension})";

                addTableRow(rowGroup, resultTitle, resultValue, background);
                background = background == Brushes.White
                    ? Brushes.LightGray
                    : Brushes.White;
            }

            return table;
        }

        private static void addTableColumnsHeaders(TableRowGroup rowGroup)
        {
            var thickness = new Thickness(0, 1, 0, 1);
            var row = new TableRow();
            row.FontWeight = FontWeights.Bold;
            row.Cells.Add(createTableCell("Тестовые показатели", TextAlignment.Left, thickness));
            row.Cells.Add(createTableCell("Значения", TextAlignment.Center, thickness));

            rowGroup.Rows.Add(row);
        }

        private static void addTableRow(TableRowGroup rowGroup, string title, string value, 
            Brush background)
        {
            var row = new TableRow();
            row.FontWeight = FontWeights.Normal;
            row.Background = background;
            row.Cells.Add(createTableCell(title, TextAlignment.Left, ZeroThickness));
            row.Cells.Add(createTableCell(value, TextAlignment.Center, ZeroThickness));

            rowGroup.Rows.Add(row);
        }

        private static TableCell createTableCell(string title, TextAlignment alignment, 
            Thickness borderThickness)
        {
            var header = new TableCell();
            header.BorderBrush = Brushes.Black;
            header.BorderThickness = borderThickness;
            var paragraph = new Paragraph(new Run(title))
            {
                Margin = TableCellMargin,
                TextAlignment = alignment,
            };

            header.Blocks.Add(paragraph);
            return header;
        }

        private static string formatValue(TestResultValue value)
        {
            if (value.IsTypeOf<int>())
                return value.Int.ToString();
            else if (value.IsTypeOf<float>())
                return formatFloat(value.Float);
            else if (value.IsTypeOf<string>())
                return value.String;
            else
                throw new ArgumentException();
        }

        private static string formatFloat(float value)
        {
            var formatStr = "f1";
            if (Math.Abs(value) <= 10.0)
                formatStr = "f3";

            return value.ToString(formatStr, CultureInfo.InvariantCulture);
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ViewModels\GenericReportTemplateViewModel.cs


using System;

namespace Updk7.Tests.Reports.Wpf.ViewModels
{
    public class GenericReportTemplateViewModel : Prism.Mvvm.BindableBase
    {
        public GenericReportTemplateViewModel()
        {
        }

        private string title;

        public string Title
        {
            get { return title; }
            set { SetProperty(ref title, value); }
        }

        private DateTime _testDate;

        public DateTime TestDate
        {
            get { return _testDate; }
            set { SetProperty(ref _testDate, value); }
        } 

        private string _userName;

        public string UserName
        {
            get { return _userName; }
            set { SetProperty(ref _userName, value); }
        }

        private string _organizationTitle;

        public string OrganizationTitle
        {
            get { return _organizationTitle; }
            set { SetProperty(ref _organizationTitle, value); }
        }

        private TesteeInfoViewModel _testeeInfo = new TesteeInfoViewModel();

        public TesteeInfoViewModel TesteeInfo
        {
            get { return _testeeInfo; }
            set { SetProperty(ref _testeeInfo, value); }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ViewModels\QuestionnaireReportTemplateViewModel.cs


using Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid;

namespace Updk7.Tests.Reports.Wpf.ViewModels
{
    public class QuestionnaireReportTemplateViewModel : GenericReportTemplateViewModel
    {
        public QuestionnaireReportTemplateViewModel()
        {
        }

        private bool _isBarChartVisible = true;

        public bool IsBarChartVisible
        {
            get { return _isBarChartVisible; }
            set { SetProperty(ref _isBarChartVisible, value); }
        } 

        private BarChartDataGridDataCollection _barChartData = new BarChartDataGridDataCollection();

        public BarChartDataGridDataCollection BarChartData
        {
            get { return _barChartData; }
            set { SetProperty(ref _barChartData, value); }
        }

        private bool _isInterpretationTitleVisible = true;

        public bool IsInterpretetaionTitleVisible
        {
            get { return _isInterpretationTitleVisible; }
            set { SetProperty(ref _isInterpretationTitleVisible, value); }
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\ViewModels\TesteeInfoViewModel.cs


using System;
using System.Windows.Media.Imaging;
using Prism.Mvvm;

namespace Updk7.Tests.Reports.Wpf.ViewModels
{
    public class TesteeInfoViewModel : BindableBase
    {
        public TesteeInfoViewModel()
        {
        }

        private string _fullName;

        public string FullName
        {
            get { return _fullName; }
            set { SetProperty(ref _fullName, value); }
        }

        private DateTime _birthDate;

        public DateTime BirthDate
        {
            get { return _birthDate; }
            set { SetProperty(ref _birthDate, value); }
        } 

        private BitmapImage _photo;

        public BitmapImage Photo
        {
            get { return _photo; }
            set { SetProperty(ref _photo, value); }
        } 

        private string _profession;

        public string Profession
        {
            get { return _profession; }
            set { SetProperty(ref _profession, value); }
        }

        private string _jobAddress;

        public string JobAddress
        {
            get { return _jobAddress; }
            set { SetProperty(ref _jobAddress, value); }
        } 

        public void SetInfo(ReportInfo info)
        {
            FullName = info.Testee.Name;
            Photo = info.Testee.Photo;
            BirthDate = info.Testee.BirthDate;
            Profession = info.Testee.Comment;
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\GenericReportFooterView.xaml

<UserControl x:Class="Updk7.Tests.Reports.Wpf.Views.GenericReportFooterView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Tests.Reports.Wpf.Views"
             mc:Ignorable="d" 
             Background="White"
             Width="630">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock
            Grid.Row="0"
            FontWeight="Bold"
            FontSize="16"
            TextAlignment="Left"
            TextWrapping="Wrap"
            Margin="0,0,0,10">
            <Run Text="Ответственный за тестирование " />
            <Run Text="{Binding OrganizationTitle}" />
        </TextBlock>
        <Grid
            Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <TextBlock 
                Grid.Column="0"
                Grid.Row="0"
                Text="________________________________________________"
                TextAlignment="Right"
                />
            <TextBlock 
                Grid.Column="1"
                Grid.Row="0"
                Text="/"
                Margin="10,0,10,0"
                FontWeight="Bold"
                />
            <TextBlock 
                Grid.Column="2"
                Grid.Row="0"
                Text="________________________________________________"
                TextAlignment="Left"
                />
            <TextBlock 
                Grid.Column="2"
                Text="{Binding UserName}"
                TextAlignment="Left"
                FontSize="14"
                FontWeight="Bold"
                />
            <TextBlock 
                Grid.Column="0"
                Grid.Row="1"
                Text="Подпись, М.П."
                TextAlignment="Center"
                FontSize="12"
                />
            <TextBlock 
                Grid.Column="2"
                Grid.Row="1"
                Text="Ф.И.О."
                TextAlignment="Left"
                FontSize="12"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\GenericReportFooterView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Tests.Reports.Wpf.Views
{
    /// <summary>
    /// Interaction logic for GenericReportFooterView.xaml
    /// </summary>
    public partial class GenericReportFooterView : UserControl
    {
        public GenericReportFooterView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\GenericReportHeaderView.xaml

<UserControl x:Class="Updk7.Tests.Reports.Wpf.Views.GenericReportHeaderView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Tests.Reports.Wpf.Views"
             mc:Ignorable="d">
    <StackPanel>
        <StackPanel.Resources>
            <BooleanToVisibilityConverter x:Key="boolConverter" />
        </StackPanel.Resources>
        <TextBlock 
            Text="Протокол теста"
            HorizontalAlignment="Center"
            FontSize="20"
            FontWeight="ExtraBold"
            Margin="0,5,0,5"
            />
        <TextBlock 
            HorizontalAlignment="Center"
            Text="{Binding Title}"
            TextAlignment="Center"
            FontSize="20"
            FontWeight="ExtraBold"
            TextWrapping="Wrap"
            Margin="0,5,0,2"
            />
        <TextBlock
            HorizontalAlignment="Center"
            FontSize="12"
            FontWeight="Medium"
            Margin="0,2,0,5">
            <Run Text="Дата и время тестирования: " />
            <Run Text="{Binding TestDate, StringFormat=\{0:dd.MM.yyyy HH.mm\}}" />
        </TextBlock>
        <local:TesteeInfoView 
            DataContext="{Binding TesteeInfo}"
            />
    </StackPanel>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\GenericReportHeaderView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Tests.Reports.Wpf.Views
{
    /// <summary>
    /// Interaction logic for GenericReportHeaderView.xaml
    /// </summary>
    public partial class GenericReportHeaderView : UserControl
    {
        public GenericReportHeaderView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\QuestionnaireReportTemplate.xaml

<UserControl x:Class="Updk7.Tests.Reports.Wpf.Views.QuestionnaireReportTemplate"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Tests.Reports.Wpf.Views"
             xmlns:controls="clr-namespace:Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid"
             mc:Ignorable="d"
             DataContext="{Binding}">
    <StackPanel>
        <StackPanel.Resources>
            <BooleanToVisibilityConverter x:Key="boolConverter" />
        </StackPanel.Resources>
        <local:GenericReportHeaderView 
            DataContext="{Binding}"
            />
        <TextBlock 
            Text="Результаты тестирования"
            FontSize="14"
            FontWeight="Bold"
            Margin="0,5,0,5"
            Visibility="{Binding DataContext.IsBarChartVisible, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}},
                Converter={StaticResource boolConverter}}"
            />
        <controls:BarChartDataGridControl 
            DataContext="{Binding BarChartData}"
            HorizontalAlignment="Stretch"
            Visibility="{Binding DataContext.IsBarChartVisible, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}},
                Converter={StaticResource boolConverter}}"
            />
        <TextBlock 
            Text="Интерпретация результатов"
            FontSize="14"
            FontWeight="Bold"
             Visibility="{Binding DataContext.IsInterpretetaionTitleVisible, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}},
                Converter={StaticResource boolConverter}}"
            />
    </StackPanel>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\QuestionnaireReportTemplate.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Tests.Reports.Wpf.Views
{
    /// <summary>
    /// Interaction logic for QuestionnaireReportTemplate.xaml
    /// </summary>
    public partial class QuestionnaireReportTemplate : UserControl
    {
        public QuestionnaireReportTemplate()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\ResultsTable.xaml

<UserControl x:Class="Updk7.Tests.Reports.Wpf.Views.ResultsTable"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Tests.Reports.Wpf.Views"
             mc:Ignorable="d" 
             d:DesignHeight="600" 
             d:DesignWidth="800"
             Background="White">
    <!--Тестовая таблица, не используется-->
    <Grid>
        <FlowDocumentScrollViewer>
            <FlowDocument>
                <Table CellSpacing="0">
                    <Table.Columns>
                        <TableColumn Width="4*" />
                        <TableColumn Width="1*" />
                    </Table.Columns>
                    <TableRowGroup>
                        <!--Колонки талицы-->
                        <TableRow FontWeight="Bold" >
                            <TableCell BorderBrush="Black" BorderThickness="0,1,0,1">
                                <Paragraph Margin="3">Тестовые показатели</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="0,1,0,1">
                                <Paragraph Margin="3" TextAlignment="Center">Значения</Paragraph>
                            </TableCell>
                        </TableRow>
                        <!--Значения-->
                        <TableRow Background="LightGray">
                            <TableCell>
                                <Paragraph Margin="3">Среднее время реагирования, с</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph Margin="3" TextAlignment="Center">0.56</Paragraph>
                            </TableCell>
                        </TableRow>
                        <TableRow>
                            <TableCell>
                                <Paragraph Margin="3">Количество правильных ответов на зрительные стимулы в задании №1</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph Margin="3" TextAlignment="Center">19</Paragraph>
                            </TableCell>
                        </TableRow>
                        <TableRow Background="LightGray">
                            <TableCell>
                                <Paragraph Margin="3">Разница средних времен реагирования между заданием №2 и заданием №1, с</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph Margin="3" TextAlignment="Center">-0.006</Paragraph>
                            </TableCell>
                        </TableRow>
                        <TableRow>
                            <TableCell>
                                <Paragraph Margin="3">Количество правильных ответов на слуховые стимулы в задании №2</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph Margin="3" TextAlignment="Center">11</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                </Table>
                <Table
                    CellSpacing="0"
                    FontSize="12">
                    <Table.Columns>
                        <TableColumn Width="100" />
                    </Table.Columns>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell FontWeight="Bold" BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">Блоки</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">1</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">2</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">3</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">4</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">5</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">6</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">7</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">8</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">9</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">10</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">11</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">12</Paragraph>
                            </TableCell>
                        </TableRow>
                        <TableRow>
                            <TableCell FontWeight="Bold" BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">Ошибки</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">1</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                        </TableRow>
                        <TableRow>
                            <TableCell FontWeight="Bold" BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">Пропусков</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0</Paragraph>
                            </TableCell>
                        </TableRow>
                        <TableRow>
                            <TableCell FontWeight="Bold" BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">Ср. время реакции, с</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0.57</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0.58</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0.64</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0.71</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0.63</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0.61</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0.62</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0.63</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0.60</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0.61</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0.60</Paragraph>
                            </TableCell>
                            <TableCell BorderBrush="Black" BorderThickness="1">
                                <Paragraph TextAlignment="Center">0.55</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                </Table>
            </FlowDocument>
        </FlowDocumentScrollViewer>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\ResultsTable.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Tests.Reports.Wpf.Views
{
    /// <summary>
    /// Interaction logic for ResultsTable.xaml
    /// </summary>
    public partial class ResultsTable : UserControl
    {
        public ResultsTable()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\TesteeInfoView.xaml

<UserControl x:Class="Updk7.Tests.Reports.Wpf.Views.TesteeInfoView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Tests.Reports.Wpf.Views"
             xmlns:converters="clr-namespace:Updk7.Tests.Reports.Wpf.Views.Converters"
             mc:Ignorable="d">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <TextBlock
            Grid.Column="0"
            VerticalAlignment="Center"
            HorizontalAlignment="Center"
            Text="Фото"
            FontSize="16"
            FontWeight="Bold"
            Visibility="{Binding Photo, Converter={converters:ObjectToVisibilityConverter NotFlag=True}}"
            />
        <Border
            Grid.Column="0"
            BorderBrush="DarkGray"
            BorderThickness="1">
            <Image 
                Margin="4"
                Width="80"
                Height="80"
                Stretch="Uniform"
                Source="{Binding Photo}"
                />
        </Border>
        <Grid
            Grid.Column="1"
            Margin="15,5,5,5">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <TextBlock 
                Grid.Row="0"
                HorizontalAlignment="Left"
                VerticalAlignment="Center"
                Text="{Binding FullName}"
                FontSize="18"
                FontWeight="Bold"
                />
            <TextBlock 
                Grid.Row="1"
                HorizontalAlignment="Left"
                VerticalAlignment="Center">
                <Run Text="Дата рождения: " />
                <Run Text="{Binding BirthDate, StringFormat=\{0:dd.MM.yyyy\}}" />
            </TextBlock>
            <TextBlock 
                Grid.Row="2"
                HorizontalAlignment="Left"
                VerticalAlignment="Center"
                Text="{Binding Profession}"
                FontSize="14"
                FontWeight="Light"
                />
            <TextBlock 
                Grid.Row="3"
                HorizontalAlignment="Left"
                VerticalAlignment="Center"
                Text="{Binding JobAddress}"
                FontSize="14"
                FontWeight="Light"
                />
        </Grid>
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\TesteeInfoView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Tests.Reports.Wpf.Views
{
    /// <summary>
    /// Interaction logic for TesteeInfoView.xaml
    /// </summary>
    public partial class TesteeInfoView : UserControl
    {
        public TesteeInfoView()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\UnknownReportHeader.xaml

<UserControl x:Class="Updk7.Tests.Reports.Wpf.Views.UnknownReportHeader"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Tests.Reports.Wpf.Views"
             mc:Ignorable="d">
    <Grid>
        <TextBlock 
            Text="Отчета для выбранного теста не существует"
            FontSize="24"
            FontWeight="Bold"
            />
    </Grid>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\UnknownReportHeader.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Tests.Reports.Wpf.Views
{
    /// <summary>
    /// Interaction logic for UnknownReportHeader.xaml
    /// </summary>
    public partial class UnknownReportHeader : UserControl
    {
        public UnknownReportHeader()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\Аддикция_и_аддиктивное_поведение_ReportTemplate.xaml

<UserControl x:Class="Updk7.Tests.Reports.Wpf.Views.Аддикция_и_аддиктивное_поведение_ReportTemplate"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Updk7.Tests.Reports.Wpf.Views"
             xmlns:controls="clr-namespace:Updk7.Tests.Reports.Wpf.Controls.BarChartDataGrid"
             mc:Ignorable="d"
             DataContext="{Binding}">
    <StackPanel>
        <StackPanel.Resources>
            <BooleanToVisibilityConverter x:Key="boolConverter" />
        </StackPanel.Resources>
        <local:GenericReportHeaderView 
         DataContext="{Binding}"
         />
        <TextBlock 
         Text="Результаты тестирования"
         FontSize="14"
         FontWeight="Bold"
         Margin="0,5,0,5"
         Visibility="{Binding DataContext.IsBarChartVisible, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}},
             Converter={StaticResource boolConverter}}"
         />
        <ContentPresenter Content="{Binding SummaryResults}" Margin="5,0,5,0">
            <ContentPresenter.ContentTemplate>
                <DataTemplate>
                    <Grid TextBlock.FontSize="14">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition Width="30"/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <TextBlock Grid.Row="0" Grid.Column="0" Text="Уровень проявления аддикции (УП)"/>
                        <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Score}"/>
                        <TextBlock Grid.Row="0" Grid.Column="2" Text="{Binding AddictionText}" HorizontalAlignment="Right" TextWrapping="WrapWithOverflow"/>
                        <TextBlock Grid.Row="1" Grid.Column="0" Text="Степень выраженности аддикции (СВ)"/>
                        <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding AddictionLevel}"/>
                        <TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding AddictionLevelText}" HorizontalAlignment="Right" TextWrapping="WrapWithOverflow"/>
                    </Grid>
                </DataTemplate>
            </ContentPresenter.ContentTemplate>
        </ContentPresenter>
        <controls:BarChartDataGridControl 
         DataContext="{Binding BarChartData}"
         HorizontalAlignment="Stretch"
         Visibility="{Binding DataContext.IsBarChartVisible, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}},
             Converter={StaticResource boolConverter}}"
         />
        <TextBlock 
         Text="Интерпретация результатов"
         FontSize="14"
         FontWeight="Bold"
          Visibility="{Binding DataContext.IsInterpretetaionTitleVisible, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}},
             Converter={StaticResource boolConverter}}"
         />
    </StackPanel>
</UserControl>

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\Аддикция_и_аддиктивное_поведение_ReportTemplate.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Updk7.Tests.Reports.Wpf.Views
{
    /// <summary>
    /// Interaction logic for Аддикция_и_аддиктивное_поведение_ReportTemplate.xaml
    /// </summary>
    public partial class Аддикция_и_аддиктивное_поведение_ReportTemplate : UserControl
    {
        public Аддикция_и_аддиктивное_поведение_ReportTemplate()
        {
            InitializeComponent();
        }
    }
}

*** File: D:\Projects\Tmp\Updk7\Updk7.Tests\Updk7.Tests.Reports.Wpf\Source\Views\Converters\ObjectToVisibilityConverter.cs


using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;

namespace Updk7.Tests.Reports.Wpf.Views.Converters
{
    public class ObjectToVisibilityConverter : MarkupExtension, IValueConverter
    {
        public ObjectToVisibilityConverter()
        {
        }

        public bool NotFlag { get; set; }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (!NotFlag)
            {
                return value == null
                    ? Visibility.Collapsed
                    : Visibility.Visible;
            }
            else
            {
                return value == null
                    ? Visibility.Visible
                    : Visibility.Collapsed;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }
}
