С Новым годом! Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.78/130: Рейтинг темы: голосов - 130, средняя оценка - 4.78
Модератор
Эксперт .NET
15855 / 11001 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
1

Пример создания приложения для тестирования [WPF, Элд Хасп]

13.06.2019, 22:43. Показов 26547. Ответов 24
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Тема из цикла Готовые решения, примеры и рекомендации начинающим на WPF [Элд Хасп]

Пример практической реализации приложения для тестирования по мотивам темы Где можно глянуть примеры создания теста? от ahtik95.

1) Создание XML структуры

Сначала создал XML для хранения теста Test.xml
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0"?>
<Test>
  <NameTest>Самый лучший тест!</NameTest>
  <Questions>
    <Question IsOnlyOne="true">
      <Text>Три минус два</Text>
      <Answers>
        <Answer IsRight="true">Один</Answer>
        <Answer IsRight="false">Два</Answer>
      </Answers>
    </Question>
    <Question IsOnlyOne="true">
      <Text>Дважды два</Text>
      <Answers>
        <Answer IsRight="false">Три</Answer>
        <Answer IsRight="true">Четыре</Answer>
        <Answer IsRight="false">Пять</Answer>
      </Answers>
    </Question>
    <Question IsOnlyOne="false">
      <Text>Трижды два</Text>
      <Answers>
        <Answer IsRight="true">Больше трёх</Answer>
        <Answer IsRight="false">Больше шести</Answer>
        <Answer IsRight="true">Меньше девяти</Answer>
      </Answers>
    </Question>
  </Questions>
</Test>
Корневой тег теста <Test/>
Тест имеет тег для названия <NameTest/> и тег для вопросов теста <Questions/>.

В теге <Questions/> содержится список тегов <Question/> для информации об одном вопросе.
Атрибут IsOnlyOne обозначает тип вопроса: true - только с одним правильным ответом, false - с разным количеством правильных ответов.

Тег <Answers/> содержит список вариантов ответов.
Вариант ответа содержится в теге <Answer/>. Атрибут ответа IsRight обозначает правильный или ложный ответ. Значение тега - текст ответа.

2) Классы для десериализации XML

По этой XML структуре через конструктор VS (Меню -> Правка -> Специальная вставка -> Вставить XML как классы) были созданы классы для десериализации этого XML. Полученные классы после "косметической" доводки
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
namespace TestWPF
{
    // Примечание. Для запуска созданного кода может потребоваться NET Framework версии 4.5 или более поздней версии и .NET Core или Standard версии 2.0 или более поздней.
    /// <summary>Класс теста Модели</summary>
    [Serializable()]
    [System.ComponentModel.DesignerCategory("code")]
    [XmlType(AnonymousType = true)]
    [XmlRoot(Namespace = "", IsNullable = false, ElementName ="Test")]
    public partial class TestClass
    {
 
        /// <summary>Название теста</summary>
        public string NameTest { get; set; }
 
        /// <summary>Вопросы теста</summary>
        [XmlArrayItem("Question", IsNullable = false)]
        public TestClassQuestion[] Questions { get; set; }
    }
 
    /// <summary>Класс вопроса Модели</summary>
    [Serializable()]
    [System.ComponentModel.DesignerCategory("code")]
    [XmlType(AnonymousType = true)]
    public partial class TestClassQuestion
    {
 
        /// <summary>Текст вопроса</summary>
        public string Text { get; set; }
 
        /// <summary>Тип вопроса: <see langword="true"/> - только один ответ правильный,
        /// <see langword="false"/> - несколько вопросов могут быть правильными</summary>
        [XmlAttribute()]
        public bool IsOnlyOne { get; set; }
 
        /// <summary>Варианты ответов на вопрос</summary>
        [XmlArrayItem("Answer", IsNullable = false)]
        public TestClassAnswer[] Answers { get; set; }
    }
 
    /// <summary>Класс ответа на вопрос Модели</summary>
    [Serializable()]
    [System.ComponentModel.DesignerCategory("code")]
    [XmlType(AnonymousType = true)]
    public partial class TestClassAnswer
    {
 
        /// <summary>Флаг верного ответа</summary>
        [XmlAttribute()]
        public bool IsRight { get; set; }
 
        /// <summary>Текст ответа</summary>
        [XmlText()]
        public string Text { get; set; }
    }
}
3) Модель
Модель очень простая. В конструкторе происходит десериализация XML и проверка на ошибки. Два свойства: одно - для хранения теста, второе - для названия файла из которого был загружен тест.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    public class Model
    {
        /// <summary>Загруженный тест</summary>
        public TestClass Test { get; }
 
        /// <summary>Имя файла из которого загружен тест</summary>
        public string NameFileTest { get; }
 
        /// <summary>Конструтор с заданием имени файла из которого нужно загрузить тест</summary>
        /// <param name="nameFileTest">Имя файла теста</param>
        public Model(string nameFileTest)
        {
            XmlSerializer ser = new XmlSerializer(typeof(TestClass));
            using (FileStream file = new FileStream(nameFileTest, FileMode.Open))
            {
                Test = (TestClass)ser.Deserialize(file);
            }
            NameFileTest = nameFileTest;
 
            foreach (TestClassQuestion question in Test.Questions)
            {
                int countRight = question.Answers.Count(ans => ans.IsRight);
                if (countRight == 0)
                    throw new TestException("Хоть один ответ должен быть правильным!", question, TestExceptionEnum.NoRight);
                if (countRight != 1 && question.IsOnlyOne)
                    throw new TestException("Для этого вопроса только один ответ должен быть правильным!", question, TestExceptionEnum.NotOnlyOne);
            }
        }
        /// <summary>Конструтор без параметров. Загружает тест из файла "Test.xml"</summary>
        public Model() : this("Test.xml") { }
    }
Для исключений создал тип исключения TestException
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    public class TestException : Exception
    {
        /// <summary>Вопрос теста в котором ошибка</summary>
        public TestClassQuestion Question { get; }
 
        /// <summary>Тип ошибки</summary>
        public TestExceptionEnum Error { get; }
 
        #region Конструторы с различным набором параметров
        public TestException(TestClassQuestion question, TestExceptionEnum error)
        {
            Question = question;
            Error = error;
        }
        public TestException(string message, TestClassQuestion question, TestExceptionEnum error)
            : base(message)
        {
            Question = question;
            Error = error;
        }
        public TestException(string message, Exception innerException, TestClassQuestion question, TestExceptionEnum error)
            : base(message, innerException)
        {
            Question = question;
            Error = error;
        }
        #endregion
    }
И так же перечисление для указания типа ошибки
C#
1
2
3
4
5
6
7
8
9
    public enum TestExceptionEnum
    {
        /// <summary>Нет правильных ответов</summary>
        NoRight,
        /// <summary>Правильный должен быть только один ответ</summary>
        NotOnlyOne,
        /// <summary>Неверное значение свойства IsOnlyOne</summary>
        InvalidIsOnlyOne
    }
4) Классы для команд и реализации INPC

Использую в самых простых реализациях
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
namespace TestWPF
{
    #region Делегаты для методов WPF команд
    public delegate void ExecuteHandler(object parameter);
    public delegate bool CanExecuteHandler(object parameter);
    #endregion
 
    /// <summary>Класс реализующий интерфейс ICommand для создания WPF команд</summary>
    public class RelayCommand : ICommand
    {
        private readonly CanExecuteHandler _canExecute;
        private readonly ExecuteHandler _onExecute;
 
        /// <summary>Событие извещающее об изменении состояния команды</summary>
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
 
        /// <summary>Конструктор команды</summary>
        /// <param name="execute">Выполняемый метод команды</param>
        /// <param name="canExecute">Метод разрешающий выполнение команды</param>
        public RelayCommand(ExecuteHandler execute, CanExecuteHandler canExecute = null)
        {
            this._onExecute = execute;
            this._canExecute = canExecute;
        }
 
 
        /// <summary>Вызов разрешающего метода команды</summary>
        /// <param name="parameter">Параметр команды</param>
        /// <returns>True - если выполнение команды разрешено</returns>
        public bool CanExecute(object parameter) => _canExecute == null ? true : _canExecute.Invoke(parameter);
 
        /// <summary>Вызов выполняющего метода команды</summary>
        /// <param name="parameter">Параметр команды</param>
        public void Execute(object parameter) => _onExecute?.Invoke(parameter);
    }
}
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace TestWPF
{
    /// <summary>Базовый класс с реализацией INPC </summary>
    public abstract class OnPropertyChangedClass : INotifyPropertyChanged
    {
        /// <summary>Событие для извещения об изменения свойства</summary>
        public event PropertyChangedEventHandler PropertyChanged;
 
        /// <summary>Метод для вызова события извещения об изменении свойства</summary>
        /// <param name="propertyName">Изменившееся свойство</param>
        public void OnPropertyChanged([CallerMemberName]string propertyName = "") =>
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
5
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
13.06.2019, 22:43
Ответы с готовыми решениями:

Пример создания приложения "Работа с комнатами в студенческих общежитиях" [WPF, SQLite, Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html Ссоздана как...

WPF vs WinForms (для начинающих) [Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html Эту тему решил...

WPF команды и MVVM. Часть 2. Всплытие команд. Реализация команды для списка элементов [WPF, Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html На практике часто...

WPF команды и MVVM. Часть 1. [WPF, Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html Для использования и...

24
Модератор
Эксперт .NET
15855 / 11001 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
13.06.2019, 23:26  [ТС] 2
5) Классы теста для ViewModel

Тест полученный после десериализации неудобен и неполон для View.
Поэтому создал классы в которые будут конвертироваться классы Модели. Эти классы созданы как контейнеры для классов Модели - в конструкторе они получают класс Модели и сохраняют ссылку на него. Значения своих свойств получают от значений свойств класса Модели.

Класс ответа на вопрос имеет свойства:
  • Text - текст ответа. Получает значение от аналогичного свойства класса ответа Модели
  • IsRightView - свойство для View. Служит для отметки выбора пользователем варианта ответа.
  • Value - значение правильности ответа. True - пользователь правильно отметил или не отметил этот вариант ответа.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    /// <summary>Класс ответа на вопрос для ViewModel</summary>
    public class AnswerVM : OnPropertyChangedClass
    {
        /// <summary>Базовый ответ типа Модели</summary>
        private readonly TestClassAnswer TestAnswer;
 
        /// <summary>Поле для хранения значения свойства</summary>
        private bool _isRightView;
 
        /// <summary>Конструктор из ответа типа Модели</summary>
        /// <param name="testAnswer">Базовый ответ типа Модели</param>
        public AnswerVM(TestClassAnswer testAnswer) => TestAnswer = testAnswer;
 
        /// <summary>Текст ответа</summary>
        public string Text => TestAnswer.Text;
 
        /// <summary>Свойство для выбора ответа</summary>
        public bool IsRightView { get => _isRightView; set { _isRightView = value; OnPropertyChanged(); } }
 
        /// <summary>Возвращает <see langword="true"/> при правильном выборе</summary>
        public bool Value => IsRightView == TestAnswer.IsRight;
    }

Для вопроса в ViewModel создал базовый класс QuestionVM. В конструкторе рандомно перемешиваются варианты ответов. Так же класс (по сравнению с классом для Модели) дополнен свойством Value возвращающим True если все варианты ответов отмечены или не отмечены верно.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    /// <summary>Базовый класс вопроса для ViewModel</summary>
    public class QuestionVM : OnPropertyChangedClass
    {
        /// <summary>Статический ГСЧ</summary>
        protected static readonly Random rand = new Random();
 
        /// <summary>Базовый вопрос типа Модели</summary>
        protected readonly TestClassQuestion TestQuestion;
 
        /// <summary>Конструктор из базового вопроса типа Модели</summary>
        /// <param name="testQuestion">Базовый вопрос типа Модели</param>
        public QuestionVM(TestClassQuestion testQuestion)
        {
            TestQuestion = testQuestion;
            Answers = TestQuestion.Answers.Select(ans => new AnswerVM(ans))
                .OrderBy(x => rand.Next()).ToArray();
        }
 
        /// <summary>Тип вопроса: <see langword="true"/> - только один правильный ответ, 
        /// <see langword="false"/> - может быть несколько правильных ответов</summary>
        public bool IsOnleOne => TestQuestion.IsOnlyOne;
 
        /// <summary>Текст вопроса</summary>
        public string Text => TestQuestion.Text;
 
        /// <summary>Варианты ответов</summary>
        public AnswerVM[] Answers { get; }
 
        /// <summary><see langword="true"/> - если выбраны правильные ответы</summary>
        public bool Value => Answers.All(ans => ans.Value);
 
        /// <summary>Статический конструктор из вопроса типа Модели</summary>
        /// <param name="testQuestion">Базовый вопрос типа Модели</param>
        /// <returns>Тип QuestionRadioButtonVM для вопросов с одним правильным ответом,
        /// тип QuestionCheckBoxVM для вопросов с несколькими правильными ответами</returns>
        public static QuestionVM Create(TestClassQuestion testQuestion)
        {
            if (testQuestion.IsOnlyOne)
                return new QuestionRadioButtonVM(testQuestion);
            return new QuestionCheckBoxVM(testQuestion);
        }
    }
Так как вопросы существуют двух типов и у них разная визуализация, то созданы два производных типов для вопросов.
Их отличие от базового только в том, что в базовом свойство IsOnleOne может иметь любое значение, в QuestionRadioButtonVM - True, в QuestionCheckBoxVM -False.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    /// <summary>Класс вопроса для ViewModel с только одним правильным ответом</summary>
    public class QuestionRadioButtonVM : QuestionVM
    {
        /// <summary>Конструктор из базового вопроса типа Модели</summary>
        /// <param name="testQuestion">Базовый вопрос типа Модели</param>
        public QuestionRadioButtonVM(TestClassQuestion testQuestion)
            : base(testQuestion)
        {
            if (!IsOnleOne)
                throw new TestException("Неверное значение свойства IsOnlyOne!", testQuestion, TestExceptionEnum.InvalidIsOnlyOne);
        }
    }
 
    /// <summary>Класс вопроса для ViewModel с несколькими правильными ответами</summary>
    public class QuestionCheckBoxVM : QuestionVM
    {
        /// <summary>Конструктор из базового вопроса типа Модели</summary>
        /// <param name="testQuestion">Базовый вопрос типа Модели</param>
        public QuestionCheckBoxVM(TestClassQuestion testQuestion)
            : base(testQuestion)
        {
            if (IsOnleOne)
                throw new TestException("Неверное значение свойства IsOnlyOne!", testQuestion, TestExceptionEnum.InvalidIsOnlyOne);
        }
    }
Класс для теста в ViewModel. Перемешивает все вопросы и имеет метод для подсчёта вопросов с правильными ответами.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    /// <summary>Класс теста для ViewModel</summary>
    public class TestVM : OnPropertyChangedClass
    {
        /// <summary>Статический ГСЧ</summary>
        private static readonly Random rand = new Random();
        /// <summary>Базовый тест типа Модели</summary>
        private readonly TestClass Test;
 
        /// <summary>Конструктор из базового теста типа Модели</summary>
        /// <param name="test">Базовый тест типа Модели</param>
        public TestVM(TestClass test)
        {
            Test = test;
            Questions = Test.Questions.Select(qu => QuestionVM.Create(qu))
                .OrderBy(x => rand.Next()).ToArray();
        }
 
        /// <summary>Название теста</summary>
        public string NameTest => Test.NameTest;
 
        /// <summary>Вопросы теста</summary>
        public QuestionVM[] Questions { get; }
 
        /// <summary>Количество вопросов с правильными ответами</summary>
        public int CountRight() => Questions.Count(qu => qu.Value);
    }
6) Классы для контента ViewModel

Для контента VM создал базовый класс с одной командой для перехода к следующему контенту
C#
1
2
3
4
5
6
7
8
9
10
11
    public class BaseContent : OnPropertyChangedClass
    {
        /// <summary>Команда перехода к следующему контенту</summary>
        public RelayCommand JumpCommand { get; }
 
        /// <summary>Конструктор с делегатами для команды</summary>
        /// <param name="execute">Делегат метода перехода к следующему контенту</param>
        /// <param name="canExecute">Делегат метода проверяющего переход к следующему контенту</param>
        public BaseContent(ExecuteHandler execute, CanExecuteHandler canExecute = null) 
            => JumpCommand = new RelayCommand(execute, canExecute);
    }
Производным от него создал класс для контента титульной страницы теста. В нём только одно свойство для названия теста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
    /// <summary>Класс начала теста контента ViewModel</summary>
    public class TitleContent : BaseContent
    {
        private string _nameTest;
        /// <summary>Название теста</summary>
        public string NameTest { get => _nameTest; set { _nameTest = value; OnPropertyChanged(); } }
 
        /// <summary>Конструктор через базовый конструктор</summary>
        /// <param name="execute">Метод перехода к списку вопросов</param>
        /// <param name="canExecute">Метод проверяющий переход к списку вопросов</param>
        public TitleContent(ExecuteHandler execute, CanExecuteHandler canExecute = null) : base(execute, canExecute) { }
 
    }
Класс для контента итогов теста. В нём два свойства для количества вопросов и для количества вопросов с правильными ответами
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    /// <summary>Класс окончания теста контента ViewModel</summary>
    public class TotalContent : BaseContent
    {
        private int _countRight;
        private int _countTotal;
 
        /// <summary>Количество правильных ответов</summary>
        public int CountRight { get => _countRight; set { _countRight = value; OnPropertyChanged(); } }
 
        /// <summary>Количество вопросов в тесте</summary>
        public int CountTotal { get => _countTotal; set { _countTotal = value; OnPropertyChanged(); } }
 
        /// <summary>Конструктор через базовый конструктор</summary>
        /// <param name="execute">Метод перехода к повтору теста</param>
        /// <param name="canExecute">Метод проверяющий переход к повтору теста</param>
        public TotalContent(ExecuteHandler execute, CanExecuteHandler canExecute = null) : base(execute, canExecute) { }
    }
Класс для контента с вопросами. Этот класс уже несколько сложнее.
У него есть:
  • Свойство Questions - список всех вопросов
  • Свойство CurrentQuestion - отображаемый вопрос
  • Свойство CurrQuestionIndex - индекс отображаемого вопроса
  • Команда JumpQuestionCommand - осуществляющая переход на другой вопрос
  • Методы JumpQuestionMetod и JumpQuestionCanMetod - исполняющий и проверяющий методы команды JumpQuestionCommand

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    /// <summary>Класс для контента со списком вопросов</summary>
    public class QuestionsContent : BaseContent
    {
        #region Поля для хранения значений свойств
        private QuestionVM[] _questions;
        private QuestionVM _currentQuestion;
        private int _currQuestionIndex;
        private RelayCommand _jumpQuestionCommand;
        #endregion
 
        /// <summary>Вопросы</summary>
        public QuestionVM[] Questions { get => _questions; set { _questions = value; OnPropertyChanged(); CurrQuestionIndex = -1; JumpQuestionMetod(1); } }
 
        /// <summary>Текущий вопрос</summary>
        public QuestionVM CurrentQuestion { get => _currentQuestion; set { _currentQuestion = value; OnPropertyChanged(); } }
 
        /// <summary>Индекс текущего вопроса</summary>
        private int CurrQuestionIndex { get => _currQuestionIndex; set { _currQuestionIndex = value; OnPropertyChanged(); } }
 
        /// <summary>Конструктор наследующий от базового конструктора</summary>
        /// <param name="execute">Делегат метода исполняющего выход из контента</param>
        /// <param name="canExecute">Делегат метода проверяющего выход из контента</param>
        public QuestionsContent(ExecuteHandler execute, CanExecuteHandler canExecute = null)
            : base(execute, canExecute) { }
 
        /// <summary>Команда для перехода между вопросами</summary>
        public RelayCommand JumpQuestionCommand => _jumpQuestionCommand ?? (_jumpQuestionCommand = new RelayCommand(JumpQuestionMetod, JumpQuestionCanMetod));
 
        /// <summary>Метод проверяющий переход на вопрос с указанным смещением</summary>
        /// <param name="parameter">Смешение на следующий вопрос</param>
        /// <returns><see langword="true"/> если переход возможен</returns>
        private bool JumpQuestionCanMetod(object parameter)
        => parameter != null
            && int.TryParse(parameter.ToString(), out int parInt)
            && CurrQuestionIndex + parInt >= 0 && CurrQuestionIndex + parInt < Questions.Length;
 
        /// <summary>Метод перехода на вопрос с указанным смещением</summary>
        /// <param name="parameter">Смешение на следующий вопрос</param>
        private void JumpQuestionMetod(object parameter)
        {
            int newIndex = CurrQuestionIndex + int.Parse(parameter.ToString());
            if (newIndex != CurrQuestionIndex)
            {
                CurrQuestionIndex = newIndex;
                CurrentQuestion = Questions[CurrQuestionIndex];
            }
        }
 
    }
7) Класс ViewModel

ViewModel создаёт Модель. Получает от неё тест и сохраняет его в приватном поле Test. У ViewModel два свойства: TestView - возвращает тест в типах VM с рандомно перемешанными вопросами и ответами и Content - для текущего контента VM.
Также в VM три приватных метода для изменения контента. Делегаты на них передаются при изменении контента в соответствующий тип контента.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    /// <summary>Класс ViewModel</summary>
    public class ViewModel : OnPropertyChangedClass
    {
        /// <summary>Тест полученный от Модели</summary>
        private readonly TestClass Test;
 
        /// <summary>Тест созданный ViewModel для View</summary>
        public TestVM TestView { get; private set; }
 
        /// <summary>Поле для хранения значения контента</summary>
        private BaseContent _content;
        /// <summary>Контент ViewModel</summary>
        public BaseContent Content { get => _content; set { _content = value; OnPropertyChanged(); } }
        Model model;
 
        /// <summary>Конструктор создающий Модель и загружающий из неё тест</summary>
        public ViewModel()
        {
            model = new Model("Test.xml");
            Test = model.Test;
            TotalMetod(null);
        }
 
        /// <summary>Метод для контента начала теста</summary>
        /// <param name="parameter">Не используется</param>
        private void TitleMetod(object parameter)
        {
            Content = new QuestionsContent(QuestionsMetod) { Questions = TestView.Questions };
        }
 
        /// <summary>Метод для контента вопросов теста</summary>
        /// <param name="parameter">Не используется</param>
        private void QuestionsMetod(object parameter)
        {
            Content = new TotalContent(TotalMetod)
            {
                CountRight = TestView.CountRight(),
                CountTotal = TestView.Questions.Length
            };
        }
 
        /// <summary>Метод для контента окончания теста</summary>
        /// <param name="parameter">Не используется</param>
        private void TotalMetod(object parameter)
        {
            TestView = new TestVM(Test);
            Content = new TitleContent(TitleMetod) { NameTest = TestView.NameTest };
        }
    }
4
Модератор
Эксперт .NET
15855 / 11001 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
13.06.2019, 23:45  [ТС] 3
8) UserControl'ы для отображения различного контента
Для отображения типов TitleContent и TotalContent используются очень простые UC в каждом по одной кнопке для перехода к следующему контенту и одному TextBlock заключённому в ViewBox для вывода информации.
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<UserControl x:Class="TestWPF.TitleUC"
             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:TestWPF"
             mc:Ignorable="d"  d:DataContext="{x:Type local:TitleContent}"
             d:DesignHeight="200" d:DesignWidth="200">
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Viewbox>
            <TextBlock Text="{Binding NameTest}" Width="100" Background="Coral" 
                       TextWrapping="Wrap" TextAlignment="Center"/>
        </Viewbox>
        <Button Grid.Row="1" Content="Начать тест" Command="{Binding JumpCommand}"/>
    </Grid>
</UserControl>
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<UserControl x:Class="TestWPF.TotalUC"
             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:TestWPF"
             mc:Ignorable="d"  d:DataContext="{x:Type local:TotalContent}"
             d:DesignHeight="200" d:DesignWidth="200">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Viewbox>
            <TextBlock Background="LightGreen">
                <Run Text="Правильных ответов"/>
                <Run Text="{Binding CountRight}"/>
                <Run Text="из"/>
                <Run Text="{Binding CountTotal}"/><Run Text="."/>
           </TextBlock>
        </Viewbox>
        <Button Grid.Row="1" Content="Повторить тест" Command="{Binding JumpCommand}"/>
    </Grid>
</UserControl>
Для визуализации типа QuestionsContent используется уже более сложный UC. В нём три кнопки: для перехода к контенту итогов теста, для перехода к следующему вопросу и для перехода к предыдущему вопросу.
Верху UC TextBlock выводит текст вопроса. Под ним находится ContentControl у которого свойство Content привязано к текущему вопросу. В ресурсах ContentControl прописано два шаблона данных для разного типа вопросов. Для QuestionRadioButtonVM выводится список RadioButton, для QuestionCheckBoxVM - CheckBox.
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<UserControl x:Class="TestWPF.QuestionsUC"
             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:TestWPF"
             mc:Ignorable="d"  d:DataContext="{x:Type local:QuestionsContent}"
             d:DesignHeight="200" d:DesignWidth="200" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <TextBlock Grid.ColumnSpan="2" Text="{Binding CurrentQuestion.Text}" Background="LightCyan" TextWrapping="Wrap"/>
            <ContentControl Content="{Binding CurrentQuestion}" Grid.Row="1" Grid.ColumnSpan="2">
                <ContentControl.Resources>
                    <DataTemplate DataType="{x:Type local:QuestionRadioButtonVM}">
                        <StackPanel>
                            <ItemsControl ItemsSource="{Binding Answers}">
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate DataType="{x:Type local:AnswerVM}">
                                        <RadioButton Content="{Binding Text}" IsChecked="{Binding IsRightView}"/>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
                        </StackPanel>
                    </DataTemplate>
                    <DataTemplate DataType="{x:Type local:QuestionCheckBoxVM}">
                        <ItemsControl ItemsSource="{Binding Answers}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate DataType="{x:Type local:AnswerVM}">
                                    <CheckBox Content="{Binding Text}" IsChecked="{Binding IsRightView}"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </DataTemplate>
                </ContentControl.Resources>
 
            </ContentControl>
            <Button x:Name="btPrev" Grid.Row="2" Content="Предыдущий" Command="{Binding JumpQuestionCommand}" CommandParameter="-1"/>
            <Button x:Name="btNext" Grid.Row="2" Grid.Column="1" Content="Следующий" Command="{Binding JumpQuestionCommand}" CommandParameter="1"/>
        </Grid>
        <Button Grid.Row="1" Content="Закончить тест" Command="{Binding JumpCommand}"/>
    </Grid>
</UserControl>
9) View

View очень простая. Создаёт в Window.DataContext экземпляр ViewModel. В Grid единственный элемент ContentPresenter с тремя шаблонами данных для типов TitleContent, QuestionsContent и TotalContent.
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<Window x:Class="TestWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestWPF"
        mc:Ignorable="d" d:DataContext="{x:Type local:ViewModel}"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <Grid>
        <ContentPresenter Content="{Binding Content}">
            <ContentPresenter.Resources>
                <DataTemplate DataType="{x:Type local:TitleContent}">
                    <local:TitleUC/>
                </DataTemplate>
                <DataTemplate DataType="{x:Type local:QuestionsContent}">
                    <local:QuestionsUC/>
                </DataTemplate>
                <DataTemplate DataType="{x:Type local:TotalContent}">
                    <local:TotalUC/>
                </DataTemplate>
            </ContentPresenter.Resources>
        </ContentPresenter>
    </Grid>
</Window>
Архив проекта приложен
Вложения
Тип файла: 7z TestWPF.7z (43.6 Кб, 462 просмотров)
5
Модератор
Эксперт .NET
15855 / 11001 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
14.06.2019, 09:26  [ТС] 4
10) Как создавалось приложение

Рекомендуемый порядок создания MVVM приложения Model, View, ViewModel.
В данном случае тема изложена в несколько ином порядке Model, ViewModel, View.
Но это только изложение, так как на мой взгляд, так понятнее описываются связи в приложении.
Для разъяснения решил написать как же это приложение создавалось.

Создание приложения началось с его проектирования и формирования ТЗ самого приложения и каждой из частей приложения.

ТЗ приложения:
  1. Тест отображается в одном окне.
  2. Перед началом теста отображаются его исходные данные. В данном случае это только его название
  3. После начала тестирования выводятся вопросы теста в случайном порядке
  4. Каждый вопрос теста имеет текст вопроса и несколько вариантов ответа на него
  5. Варианты ответа расположены в случайном порядке
  6. Случайный порядок вопросов и ответов задаётся однократно для каждого нового прохождения теста
  7. Вопросы могут быть двух типов: с только одним правильным ответом и с разным количеством правильных ответов
  8. Ответы на вопросы с одним правильным вопросом отображаются в виде RadioButton
  9. Ответы на вопросы с несколькими правильными вопросами отображаются в виде CheckBox
  10. У каждого вопроса должен быть хотя бы одни правильный ответ
  11. Правильным ответом на вопрос считается правильная отметка пользователем всех верных ответов и отсутствие отметок у всех неверных ответов
  12. По окончании тестирования выводится количество вопросов теста и количество вопросов с правильными ответами
  13. После окончания тестирования можно пройти тест ещё раз, но порядок вопросов и ответов будет изменён.

Исходя из общего ТЗ были сформированы ТЗ к частям приложения.

ТЗ Модели:
  1. Так как функции редактирования теста в приложении не предусматривается, то Модель должна обеспечивать только загрузку теста из сохранённого файла
  2. Так как тест имеет древовидную структуру, то удобным форматом его хранения является XML
  3. Для восстановления теста из XML удобнее всего воспользоваться десериализацией
  4. У теста в Модели есть только название и список вопросов с ответами в том порядке в котором они находятся в файле. Типы и свойства необходимые для View будут создаваться в ViewModel
ТЗ View:
  1. Основная View отображает только один UC из трёх следующих типов: для титульной страницы, для списка вопросов, для итогов теста
  2. Из каждого UC можно переходить в следующий в порядке Титульная страница -> Список вопросов -> Итог теста -> Титульная страница
  3. Вопросы и ответы выводятся в порядке заданном ViewModel
  4. Список вопросов должен быть двух типов с RadioButton и с CheckBox
  5. В списке вопросов можно переходить к следующему и предыдущему вопросу
ТЗ ViewModel:
  1. Получение теста от Модели
  2. Перемешивание вопросов и ответов в случайном порядке
  3. Добавление для ответа свойств для выбора пользователя и оценки правильности выбора ответа
  4. Добавление для вопроса свойства для оценки правильности выбранной комбинации ответов
  5. Добавление для теста метода подсчёта количества вопросов с правильной комбинацией ответов
  6. В VM должно быть свойство для контента данных, которое может принимать три типа данных по одному для каждого типа UC
  7. Вопросы VM должны быть двух разных типов для отображения с RadioButton и с CheckBox. В остальном эти типы идентичны

Создание Модели
  1. Сначала была создана структура XML для теста
  2. По ней созданы классы для её десериализации
  3. Весь код Модели, фактически, состоит из одного конструктора десериализующего XML и проверяющего его на возможные ошибки
  4. Добавлены в Модель класс и перечисление для указания типа возможной ошибки

Создание View
При правильном подходе надо было создать интерфейсы для всех типов ViewModel, потом контекст данных времени разработки (Я обычно это делаю в виде Модели Представления Времени Разработки - VMDD). Заполнить в XAML этот контекст данных и по нему создавать View.
Но в данном случае, так как функционал очень простой, я использовал другой подход.
Создал все нужные типы ViewModel со свойствами-"заглушками", то есть свойства выводили заранее заданные значения.
Допустим, для типа предназначенного для отображения итогов теста был создан такой код
C#
1
2
3
4
5
6
7
8
9
    /// <summary>Клас окончания теста контента ViewModel</summary>
    public class TotalContent : BaseContent
    {
        /// <summary>Количество правильных ответов</summary>
        public int CountRight => 5;
 
        /// <summary>Количество вопросов в тесте</summary>
        public int CountTotal => 10;
   }
Для варианта ответа такой
C#
1
2
3
4
5
6
7
8
9
    /// <summary>Класс ответа на вопрос для ViewModel</summary>
    public class AnswerVM
    {
        /// <summary>Текст ответа</summary>
        public string Text => "Ответ";
 
        /// <summary>Свойство для выбора ответа</summary>
        public bool IsRightView => true;
    }
И аналогичным образом были созданы все другие необходимые типы.
Эти типы облегчили создание типов View в конструкторе VS. По мере создания и отладки типов View вносились необходимые изменения в типы VM. Допустим все свойства предназначенные для отображения были дополнены поддержкой INPC, добавлены сеттеры для нужных свойств, методы команд.

Создание ViewModel
К концу создания View у всех типов VM сторона обращённая к View тоже уже была полностью создана.
То есть фактически это была создана VMDD, просто, вместо наследования от неё VM, я продолжил добавлять функционал для связи с Моделью в эти же типы.
Для связи типов VM с типами Модели я выбрал принцип контейнера. Это когда экземпляр типа Модели сохраняется внутри экземпляра типа VM и свойства VM не сами хранят свои значения, а обмениваются этими значениями с сохранённым экземпляром типа Модели.
В данном случае, так как типы Модели неизменяемы, то свойства VM просто ссылаются на свойства Модели через оператор => там где их надо предоставить для отображения.
Или просто получают значение, если это нужно для какого-то внутреннего функционала. Пример строка public bool Value => IsRightView == TestAnswer.IsRight; из типа AnswerVM.

На этом вроде всё. Объяснил подробно насколько смог.
5
78 / 67 / 13
Регистрация: 04.10.2018
Сообщений: 327
11.11.2019, 18:05 5
Элд Хасп, Добрый вечер. Подскажите как вы реализовали, где три ответа получается три RadioButton, где два то два?
Начал изучать ваш пример. Для практики очень хорош! Я делаю свою реализацию. И столкнулся с проблемами.
1) У меня RadioButton, при переходе на новый вопрос не сбрасывается "нажатый".
2) Если было четыре вопроса, а на следующем вопросе три то четвертый остается видимым но без текста.
3) Я ещё не придумал как правильно сохранять выбранный вопрос.
4) Переход на DownEnd
Вопросов много. Но ваш пример очень помогает.
В моем понимании все кроется здесь?

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<ContentControl Content="{Binding CurrentQuestion}" Grid.Row="1" Grid.ColumnSpan="2">
                <ContentControl.Resources>
                    <DataTemplate DataType="{x:Type local:QuestionRadioButtonVM}">
                        <StackPanel>
                            <ItemsControl ItemsSource="{Binding Answers}">
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate DataType="{x:Type local:AnswerVM}">
                                        <RadioButton Content="{Binding Text}" IsChecked="{Binding IsRightView}"/>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
                        </StackPanel>
                    </DataTemplate>
                    <DataTemplate DataType="{x:Type local:QuestionCheckBoxVM}">
                        <ItemsControl ItemsSource="{Binding Answers}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate DataType="{x:Type local:AnswerVM}">
                                    <CheckBox Content="{Binding Text}" IsChecked="{Binding IsRightView}"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </DataTemplate>
                </ContentControl.Resources>
Спасибо.
0
Элд Хасп
11.11.2019, 21:13  [ТС]
  #6
 Комментарий модератора 
Дальнейшее обсуждение решения Varyna перенесено в тему Создания приложения для тестирования с заполнением тестов в БД
0
5 / 5 / 0
Регистрация: 12.08.2015
Сообщений: 340
23.07.2020, 14:22 7
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Меню -> Правка -> Специальная вставка -> Вставить XML как классы
Почему-то не вижу этого пункта меню. Вообще нет Специальной вставки.
0
Модератор
Эксперт .NET
15855 / 11001 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
23.07.2020, 14:39  [ТС] 8
Цитата Сообщение от Semyon001 Посмотреть сообщение
Почему-то не вижу этого пункта меню. Вообще нет Специальной вставки.
2
5 / 5 / 0
Регистрация: 12.08.2015
Сообщений: 340
23.07.2020, 15:18 9
Элд Хасп, проделал все шаги, но проблема в том, что у меня такого пункта меню в принципе нет...
Миниатюры
Пример создания приложения для тестирования [WPF, Элд Хасп]  
0
Модератор
Эксперт .NET
15855 / 11001 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
23.07.2020, 18:35  [ТС] 10
Цитата Сообщение от Semyon001 Посмотреть сообщение
проделал все шаги, но проблема в том, что у меня такого пункта меню в принципе нет...
Может какого-то плагина не стоит....
Версия Студии какая?


Добавлено через 2 минуты
Прочитайте https://stackoverflow.com/ques... s-installe

Я встретил здесь те же проблемы с Visual Studio 2019 и узнал, как это исправить.

Перейдите в установщик Visual Studio => Изменить => Отдельные компоненты => Инструменты веб-разработки. И тогда тебе хорошо идти
Пример создания приложения для тестирования [WPF, Элд Хасп]
2
5 / 5 / 0
Регистрация: 12.08.2015
Сообщений: 340
24.07.2020, 13:53 11
А в чем принципиальная разница использования ContentControl и ContentPresenter? Получается это взаимозаменяемые контролы?
P.S. Элд Хасп, в вашем примере ContentControl используется в QuestionsUC, а ContentPresenter - в MainWindow.
0
Модератор
Эксперт .NET
15855 / 11001 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
24.07.2020, 14:22  [ТС] 12
Цитата Сообщение от Semyon001 Посмотреть сообщение
А в чем принципиальная разница использования ContentControl и ContentPresenter? Получается это взаимозаменяемые контролы?
Между ними есть небольшая разница как они реагируют на изменение контекста данных, и предназначении.
Из документацииContentPresenter Class Отображает содержимое элемента управления ContentControl.

Окно (Window) тоже производный от ContentControl класс.
ContentPresenter более "лёгкий", но некоторые функции требуют ContentControl.

Я часто "на автомате" вставляю ContentPresenter, но если он начинает как-то не так работать, заменяю его на ContentControl.

По какой причине использовал в QuestionsUC именно ContentControl сейчас не помню.

Посмотрите тему пост в теме Создание приложения "Штатное Расписание" в паттерне MVVM [WPF, Элд Хасп]

Мне там после начала отладки тоже пришлось заменить ContentPresenter на ContentControl.
1
0 / 0 / 0
Регистрация: 01.10.2019
Сообщений: 8
17.05.2021, 13:00 13
Цитата Сообщение от Элд Хасп Посмотреть сообщение
using (FileStream file = new FileStream(nameFileTest, FileMode.Open))
У меня тут выдает ошибку, что нет имени и т.д. Что делать?
0
Модератор
Эксперт .NET
15855 / 11001 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
17.05.2021, 13:13  [ТС] 14
Цитата Сообщение от Kiyoto Посмотреть сообщение
выдает ошибку, что нет имени и т.д. Что делать?
Имени файла?
Проверьте есть ли такой файл (Test.xml) в вашем проекте.
0
0 / 0 / 0
Регистрация: 01.06.2019
Сообщений: 13
22.05.2021, 18:53 15
Элд Хасп, А что нужно изменить для реализации несколько разных тестов в приложении? Заранее спасибо.
0
Модератор
Эксперт .NET
15855 / 11001 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
22.05.2021, 21:01  [ТС] 16
Цитата Сообщение от ocbsmokingraw Посмотреть сообщение
что нужно изменить для реализации несколько разных тестов в приложении?
Добавить в XML дерево Тестов в которое будут вложены тестов тестов из топика темы.
Или в БД добавить таблицу тестов, а в вопросы Id теста к котором он относится.

В разделе есть пара тем, для нескольких тестов.

Добавлено через 39 секунд
Вот один из аналогов Заполнение вкладок TabItem данными из БД WPF

Добавлено через 1 минуту
Вот ещё Создания приложения для тестирования с заполнением тестов в БД
0
0 / 0 / 0
Регистрация: 01.06.2019
Сообщений: 13
31.05.2021, 04:38 17
А если мне нужно сохранить количество правильных ответов в конце теста допустим в переменную как реализовать? Заранее спасибо.
0
Модератор
Эксперт .NET
15855 / 11001 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
31.05.2021, 08:09  [ТС] 18
Цитата Сообщение от ocbsmokingraw Посмотреть сообщение
А если мне нужно сохранить количество правильных ответов в конце теста допустим в переменную как реализовать?
А где находится эта переменная?
Количество правильных вопросов и правильных ответов находится в свойствах TotalContent.
Вы можете добавить туда команду и/или метод сохраняющий их куда вам надо.
Команду можно вызывать из кнопки добавленной на соответствующую страницу, команду и метод можно вызывать при загрузке страницы.

Добавлено через 1 минуту
ocbsmokingraw, если у вас есть трудности с реализацией, советую создавть свою теиу, показать в ней, что у вас получилось сделать самостоятельно, объяснить что хотелось реализовать, но не получилось.
Тогда можно будет дать более детальные ответы, рекомендации.
0
0 / 0 / 0
Регистрация: 05.05.2020
Сообщений: 35
18.04.2022, 16:08 19
Здравствуйте,Элд Хасп. Делаю приложение по вашему примеру со своими дополнениями. Мне нужно реализовать список окон и переходы между ними, как на картинке ниже. Стоит ли делать одну главную View, а остальные реализовывать через user control'ы и потом также их отображать на главном View через <ContentControl/>? Или всё сделать именно окнами?
Миниатюры
Пример создания приложения для тестирования [WPF, Элд Хасп]  
0
Модератор
Эксперт .NET
15855 / 11001 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
18.04.2022, 17:02  [ТС] 20
Цитата Сообщение от Bellatrix27 Посмотреть сообщение
Или всё сделать именно окнами?
В современном дизайне превалирует однооконный многостраничный GUI - это позволяет строить унифицированные (Desktop, WEB, UWP и т.д.) GUI для приложения.

В WPF это реализуется на Page или UserControl.
Но вам потребуется хотя бы минимальное изучение способов построения навигации в WPF/UWP.

UserControl более типичен для WPF, особенно, с учётом единого основного Контекста Данных в Окне для всех страниц.
Page требует задание для каждой страницы своего Контекста Навигации или в коде самой Страницы, или при навигации на неё.

В описанной вами задаче оба подхода приемлемы.

Добавлено через 3 минуты
Bellatrix27, и насколько это "серьёзное" приложение?
Если делать "по взрослому" в расчёте на многопользовательское применение, WEB сервер и т.п. - потребуется очень много труда и знаний, в том числе в областях не связанных с WPF.

Добавлено через 1 минуту
Или это просто курсач - надо сделать минимальный функционал для однопользовательского использования на переносимой БД?
0
18.04.2022, 17:02
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
18.04.2022, 17:02
Помогаю со студенческими работами здесь

WPF конвертеры [Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html View получает данные...

INPC (INotifyPropertyChanged) и получение данных из Модели [WPF, Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html MVVM состоит из трёх...

Готовые решения, примеры и рекомендации начинающим на WPF [Элд Хасп]
В этой теме перечень полезных тем для начинающих. Обсуждение введите в самих темах. Для включения...

Создания приложения для тестирования с заполнением тестов в БД
Перенесено из темы https://www.cyberforum.ru/wpf-silverlight/thread2471200.html Элд Хасп, Добрый...

Пример реализации WPF+MVVM приложения
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html Пример решения ТЗ по...

Обсуждение языка C↑ᶜC (стрелки), начатое Etyuhibosecyu и Элд Хасп
По причинам личного характера первое сообщение в дискусси лучше не показывать массам. Элд Хасп: ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Блоги программистов
Подброка решений задач на Python
IT_Exp 06.01.2025
Целью данной подборки является предоставление возможности ознакомиться с различными задачами и их решениями на Python, что может быть полезно как для начинающих, так и для опытных программистов. . . .
С чего начать программировать микроконтроллер­­ы
raxper 06.01.2025
Введение в мир микроконтроллеров Микроконтроллеры стали неотъемлемой частью современного мира, окружая нас повсюду: от простых бытовых приборов до сложных промышленных систем. Эти маленькие. . .
Из чего собрать игровой компьютер
inter-admin 06.01.2025
Сборка игрового компьютера требует особого внимания к выбору комплектующих и их совместимости. Правильно собранный игровой ПК не только обеспечивает комфортный геймплей в современных играх, но и. . .
Обновление сайта www.historian.b­y
Reglage 05.01.2025
Обещал подвести итоги 2024 года для сайта. Однако начну с того, что изменилось за неделю. Добавил краткий урок по последовательности действий при анализе вредоносных файлов и значительно улучшил урок. . .
Как использовать GraphQL в C# с HotChocolate
Programming 05.01.2025
GraphQL — это современный подход к разработке API, который позволяет клиентам запрашивать только те данные, которые им необходимы. Это делает взаимодействие с API более гибким и эффективным по. . .
Модель полного двоичного суматора с помощью логических операций (python)
AlexSky-coder 04.01.2025
def binSum(x:list, y:list): s=^y] p=x and y for i in range(1,len(x)): s. append((x^y)^p) p=(x and y)or(p and (x or y)) return s x=list() y=list()
Это мы не проходили, это нам не задавали...(аси­­­­­­­­­­­­­­хро­н­н­ы­й счётчик с управляющим сигналом задержки).
Hrethgir 04.01.2025
Асинхронный счётчик на сумматорах (шестиразрядный по числу диодов на плате, но наверное разрядов будет больше - восемь или шестнадцать, а диоды на старшие), так как триггеры прошли тестирование и. . .
Руководство по созданию бота для Телеграм на Python
IT_Exp 04.01.2025
Боты для Телеграм представляют собой автоматизированные программы, которые выполняют различные задачи, взаимодействуя с пользователями через интерфейс мессенджера. В данной статье мы рассмотрим,. . .
Применение компонентов PrimeVue в Vue.js 3 на TypeScript
BasicMan 04.01.2025
Введение в PrimeVue и настройка окружения PrimeVue представляет собой мощную библиотеку компонентов пользовательского интерфейса для Vue. js 3, которая предоставляет разработчикам богатый набор. . .
Как стать Senior developer
cpp_developer 04.01.2025
В современной индустрии разработки программного обеспечения позиция Senior Developer представляет собой не просто следующую ступень карьерной лестницы, а качественно новый уровень профессионального. . .
Что известно о дате выхода Windows 12 и чего от нее ждать
IT_Exp 04.01.2025
В мире технологий постоянно происходят изменения, и операционные системы не являются исключением. Windows 11, выпущенная в октябре 2021 года, принесла множество инноваций и улучшений, но. . .
Что новенького в .NET Core 9
Programming 04.01.2025
Обзор ключевых изменений в . NET Core 9 Платформа . NET Core продолжает активно развиваться, и версия 9 представляет собой значительный шаг вперед в эволюции этой технологии. Новый релиз. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru