Загрузка данных
Точно, не доделал. Ниже даю полное продолжение: 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”.