Загрузка данных


Точно, не доделал. Ниже даю полное продолжение: MainWindow.xaml.cs, что проверить, и как это всё работает. Сделаю в том же стиле — компактно и по делу.

⸻

11. MainWindow.xaml.cs

Замени весь файл:

using System;
using System.Data;
using System.Windows;
using MySqlConnector;

namespace LabApp
{
    public partial class MainWindow : Window
    {
        string cs = "Server=localhost;Port=3306;Database=lab_accounting_db;Uid=root;Pwd=12345;";
        int currentUserId;
        string currentFullName;
        string currentRoleName;

        public MainWindow(int userId, string fullName, string roleName)
        {
            InitializeComponent();

            currentUserId = userId;
            currentFullName = fullName;
            currentRoleName = roleName;

            tbUser.Text = currentFullName;
            tbRole.Text = currentRoleName;

            LoadData();
        }

        DataTable Q(string sql, params MySqlParameter[] p)
        {
            using var c = new MySqlConnection(cs);
            using var m = new MySqlCommand(sql, c);
            if (p.Length > 0) m.Parameters.AddRange(p);
            using var a = new MySqlDataAdapter(m);
            var t = new DataTable();
            a.Fill(t);
            return t;
        }

        void X(string sql, params MySqlParameter[] p)
        {
            using var c = new MySqlConnection(cs);
            c.Open();
            using var m = new MySqlCommand(sql, c);
            if (p.Length > 0) m.Parameters.AddRange(p);
            m.ExecuteNonQuery();
        }

        void LoadData()
        {
            try
            {
                dgPatients.ItemsSource = Q(@"
select 
    PatientID 'ID',
    LastName 'Фамилия',
    FirstName 'Имя',
    MiddleName 'Отчество',
    date_format(BirthDate, '%d.%m.%Y') 'Дата рождения',
    Gender 'Пол',
    Phone 'Телефон'
from Patients
order by LastName, FirstName, MiddleName").DefaultView;

                dgTests.ItemsSource = Q(@"
select
    lt.LabTestID 'ID',
    concat(p.LastName, ' ', p.FirstName, ' ', ifnull(p.MiddleName,'')) 'Пациент',
    at.AnalysisName 'Анализ',
    lt.ResultValue 'Результат',
    at.Unit 'Ед.изм.',
    at.ReferenceRange 'Норма',
    date_format(lt.TestDate, '%d.%m.%Y %H:%i') 'Дата',
    u.FullName 'Лаборант',
    lt.CommentText 'Комментарий'
from LabTests lt
join Patients p on p.PatientID = lt.PatientID
join AnalysisTypes at on at.AnalysisTypeID = lt.AnalysisTypeID
join Users u on u.UserID = lt.UserID
order by lt.TestDate desc").DefaultView;

                cbPatients.ItemsSource = Q(@"
select
    PatientID,
    concat(LastName, ' ', FirstName, ' ', ifnull(MiddleName,''), ' [', date_format(BirthDate, '%d.%m.%Y'), ']') as FullInfo
from Patients
order by LastName, FirstName, MiddleName").DefaultView;

                cbAnalysis.ItemsSource = Q(@"
select
    AnalysisTypeID,
    concat(AnalysisName, ' (', Unit, ', норма: ', ReferenceRange, ')') as FullInfo
from AnalysisTypes
order by AnalysisName").DefaultView;

                cbPatients.DisplayMemberPath = "FullInfo";
                cbPatients.SelectedValuePath = "PatientID";

                cbAnalysis.DisplayMemberPath = "FullInfo";
                cbAnalysis.SelectedValuePath = "AnalysisTypeID";

                if (cbPatients.Items.Count > 0) cbPatients.SelectedIndex = 0;
                if (cbAnalysis.Items.Count > 0) cbAnalysis.SelectedIndex = 0;

                tbResult.Clear();
                tbComment.Clear();
                tbMsg.Text = "";
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        void AddTest(object s, RoutedEventArgs e)
        {
            if (cbPatients.SelectedValue == null)
            {
                tbMsg.Text = "Выберите пациента";
                return;
            }

            if (cbAnalysis.SelectedValue == null)
            {
                tbMsg.Text = "Выберите анализ";
                return;
            }

            if (string.IsNullOrWhiteSpace(tbResult.Text))
            {
                tbMsg.Text = "Введите результат";
                return;
            }

            if (!decimal.TryParse(tbResult.Text.Trim().Replace('.', ','), out decimal result))
            {
                tbMsg.Text = "Результат должен быть числом";
                return;
            }

            try
            {
                X(@"
insert into LabTests
(PatientID, AnalysisTypeID, UserID, TestDate, ResultValue, CommentText)
values
(@p, @a, @u, now(), @r, @c)",
                    new("@p", cbPatients.SelectedValue),
                    new("@a", cbAnalysis.SelectedValue),
                    new("@u", currentUserId),
                    new("@r", result),
                    new("@c", string.IsNullOrWhiteSpace(tbComment.Text) ? DBNull.Value : tbComment.Text.Trim()));

                tbResult.Clear();
                tbComment.Clear();
                tbMsg.Text = "Данные успешно добавлены";

                dgTests.ItemsSource = Q(@"
select
    lt.LabTestID 'ID',
    concat(p.LastName, ' ', p.FirstName, ' ', ifnull(p.MiddleName,'')) 'Пациент',
    at.AnalysisName 'Анализ',
    lt.ResultValue 'Результат',
    at.Unit 'Ед.изм.',
    at.ReferenceRange 'Норма',
    date_format(lt.TestDate, '%d.%m.%Y %H:%i') 'Дата',
    u.FullName 'Лаборант',
    lt.CommentText 'Комментарий'
from LabTests lt
join Patients p on p.PatientID = lt.PatientID
join AnalysisTypes at on at.AnalysisTypeID = lt.AnalysisTypeID
join Users u on u.UserID = lt.UserID
order by lt.TestDate desc").DefaultView;
            }
            catch (Exception ex)
            {
                tbMsg.Text = ex.Message;
            }
        }

        void ExitClick(object s, RoutedEventArgs e)
        {
            new LoginWindow().Show();
            Close();
        }
    }
}


⸻

12. MainWindow.xaml

Я тоже дам его полностью, чтобы всё было целостно.

Замени весь файл:

<Window x:Class="LabApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Учет лабораторных анализов" Height="700" Width="1200" WindowStartupLocation="CenterScreen">
    <Grid Margin="15">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Border BorderBrush="Gray" BorderThickness="1" Padding="10" Margin="0,0,0,10">
            <DockPanel>
                <StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
                    <TextBlock Text="Пользователь: " FontWeight="Bold"/>
                    <TextBlock x:Name="tbUser" Margin="0,0,20,0"/>
                    <TextBlock Text="Роль: " FontWeight="Bold"/>
                    <TextBlock x:Name="tbRole"/>
                </StackPanel>

                <Button Content="Выйти" Width="100" Height="30" DockPanel.Dock="Right" Click="ExitClick"/>
            </DockPanel>
        </Border>

        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="3*"/>
                <ColumnDefinition Width="2*"/>
            </Grid.ColumnDefinitions>

            <TabControl Margin="0,0,10,0">
                <TabItem Header="Пациенты">
                    <DataGrid x:Name="dgPatients" IsReadOnly="True" CanUserAddRows="False"/>
                </TabItem>

                <TabItem Header="Исследования">
                    <DataGrid x:Name="dgTests" IsReadOnly="True" CanUserAddRows="False"/>
                </TabItem>
            </TabControl>

            <StackPanel Grid.Column="1">
                <TextBlock Text="Пациент"/>
                <ComboBox x:Name="cbPatients" Margin="0,5,0,10"/>

                <TextBlock Text="Тип анализа"/>
                <ComboBox x:Name="cbAnalysis" Margin="0,5,0,10"/>

                <TextBlock Text="Результат"/>
                <TextBox x:Name="tbResult" Height="30" Margin="0,5,0,10"/>

                <TextBlock Text="Комментарий"/>
                <TextBox x:Name="tbComment" Height="100" Margin="0,5,0,10" AcceptsReturn="True" TextWrapping="Wrap"/>

                <Button Content="Добавить исследование" Height="35" Margin="0,0,0,10" Click="AddTest"/>

                <TextBlock x:Name="tbMsg" TextWrapping="Wrap"/>
            </StackPanel>
        </Grid>
    </Grid>
</Window>


⸻

13. Что поменять в строке подключения

Во всех .cs файлах укажи свой пароль от MySQL:

string cs = "Server=localhost;Port=3306;Database=lab_accounting_db;Uid=root;Pwd=12345;";

Например, если пароль root, то:

string cs = "Server=localhost;Port=3306;Database=lab_accounting_db;Uid=root;Pwd=root;";


⸻

14. Что должно совпадать

Проверь:
	•	namespace в .cs файлах:

namespace LabApp

	•	в LoginWindow.xaml:

x:Class="LabApp.LoginWindow"

	•	в MainWindow.xaml:

x:Class="LabApp.MainWindow"


⸻

15. Что делает программа

Авторизация

Вход по логину и паролю из таблицы Users.

Тестовые пользователи:
	•	admin / 1234
	•	lab1 / 1234
	•	doctor1 / 1234

⸻

После входа

В верхней части окна показывается:
	•	ФИО пользователя
	•	его роль

⸻

Вкладка “Пациенты”

Показывает список пациентов.

⸻

Вкладка “Исследования”

Показывает:
	•	пациента
	•	тип анализа
	•	результат
	•	единицу измерения
	•	норму
	•	дату
	•	лаборанта
	•	комментарий

⸻

Добавление нового исследования

Справа выбирается:
	•	пациент
	•	тип анализа
	•	результат
	•	комментарий

После нажатия кнопки запись сохраняется в таблицу LabTests.

⸻

16. Почему это 3НФ

Коротко для защиты:

Roles

Хранит только роли.

Users

Хранит пользователей и ссылку на роль через RoleID.

Patients

Хранит только данные пациента.

AnalysisTypes

Хранит только справочник анализов:
	•	название анализа
	•	единицы измерения
	•	референсный диапазон

LabTests

Хранит факт проведённого исследования:
	•	какой пациент
	•	какой анализ
	•	кто внёс
	•	дата
	•	результат
	•	комментарий

Здесь нет лишнего дублирования текстовых данных, потому что названия ролей и анализов вынесены в отдельные таблицы.

⸻

17. Мини-описание ERD для отчёта

Можно написать так:

ERD содержит 5 сущностей:
	•	Roles
	•	Users
	•	Patients
	•	AnalysisTypes
	•	LabTests

Связи:
	•	одна роль может принадлежать многим пользователям
	•	один пользователь может внести много исследований
	•	один пациент может иметь много исследований
	•	один тип анализа может встречаться во многих исследованиях

⸻

18. Мини-описание тестовых данных

Тоже можно вставить в отчёт:

В базу добавлены тестовые данные:
	•	3 роли
	•	3 пользователя
	•	3 пациента
	•	3 типа анализов
	•	3 записи исследований

Это удовлетворяет требованию задания: не менее 3 связанных таблиц, корректный формат данных, тестовые данные, ERD.

⸻

19. Если преподаватель спросит про функциональные требования

Можно ответить так:

Функциональные возможности приложения:
	•	авторизация пользователя
	•	отображение роли текущего пользователя
	•	просмотр списка пациентов
	•	просмотр данных о проведенных исследованиях
	•	внесение новых данных о лабораторных исследованиях

⸻

20. Если хочешь чуть лучше, чем в задании

Можно быстро улучшить:
	•	скрывать кнопку добавления исследования для роли Врач
	•	сделать хэширование пароля
	•	добавить поиск пациента
	•	добавить фильтр по типу анализа
	•	добавить регистрацию новых пациентов

Но для базового задания текущего варианта уже достаточно.

⸻

21. Итоговая структура проекта

В проекте должно быть:
	•	App.xaml
	•	App.xaml.cs
	•	LoginWindow.xaml
	•	LoginWindow.xaml.cs
	•	MainWindow.xaml
	•	MainWindow.xaml.cs

⸻

22. Порядок запуска
	1.	Выполни SQL-скрипт создания БД
	2.	Убедись, что MySQL запущен
	3.	Установи пакет MySqlConnector
	4.	Впиши свой пароль в строку подключения
	5.	Build -> Rebuild Solution
	6.	F5

⸻

23. Очень коротко по смыслу
	•	SQL создает БД в 3НФ
	•	LoginWindow делает авторизацию
	•	MainWindow показывает пользователя, роль, пациентов и исследования
	•	кнопка добавляет новое исследование
	•	все данные берутся из MySQL

Если хочешь, следующим сообщением я могу сразу собрать для тебя ещё и готовый текст для отчёта:
“Предметная область”, “Функциональные требования”, “Обоснование 3НФ”, “Описание таблиц”, “Описание ERD”.