Создание горизонтального DataGrid в WPF для графика работы

Я пытаюсь создать горизонтальный датагрид чтобы слева был сотрудник и сверху 30 дней месяца. На пересечении должен быть статус работы (либо "Р", либо "Н"). Если запись для дня месяца найдена в БД, то ставить "Р", если не найдена, то "Н". Также есть проблема со списком сотрудников - я вывожу таблицу из БД в List и поскольку в БД при заполнении графика создаётся множество записей для каждого дня, то слева получается много строк с одним и тем же сотрудником (но при этом мне нужны данные из таблицы для проверки даты).

Я запутался и вот что сделано:

namespace PlanningScheduleApp
{
    public partial class DataGridTestWindow : Window
    {
        public string connectionString = "";
        List<StaffModel> StaffList = new List<StaffModel>();
        DepModel SelectedDep = new DepModel();
        StaffModel SelectedStaff { get; set; }

        public DataGridTestWindow(DepModel selectedDep)
        {
            InitializeComponent();
            SelectedDep = selectedDep;

            StaffList = Odb.db.Database.SqlQuery<StaffModel>("SELECT DISTINCT a.ID_Schedule, a.STAFF_ID, LTRIM(e.TABEL_ID) as TABEL_ID, e.SHORT_FIO, a.WorkBegin, a.WorkEnd, a.DTA, a.LunchTimeBegin, a.LunchTimeEnd, a.WorkingHours, b.ID_Absence, c.Cause as CauseAbsence, b.DateBegin, b.DateEnd, b.TimeBegin, b.TimeEnd FROM [Zarplats].[dbo].[Staff_Schedule] as a left join Zarplats.dbo.Schedule_Absence as b on a.STAFF_ID = b.id_Staff and a.DTA between b.DateBegin and b.DateEnd left join Zarplats.dbo.AbsenceRef as c on b.AbsenceRef_ID = c.ID_AbsenceRef left join perco...staff as e on a.STAFF_ID = e.ID_STAFF left join Zarplats.dbo.StaffView as f on a.STAFF_ID = f.STAFF_ID where f.Position = @podrazd order by a.DTA", new SqlParameter("podrazd", SelectedDep.Position)).ToList();
            GenerateColumns();
            FillDTAStatus();
            StaffDGTest.ItemsSource = StaffList;
        }

        DateTime selectedMonth = new DateTime(2023, 11, 1);

        private void GenerateColumns()
        {
            List<DateTime> datesInMonth = GetDatesForMonth(selectedMonth);

            for (int i = 0; i < datesInMonth.Count; i++)
            {
                DataGridTemplateColumn column = new DataGridTemplateColumn
                {
                    Header = datesInMonth[i].Day.ToString(),
                    Width = DataGridLength.Auto,
                    CellTemplate = FindResource("DayColumnTemplate") as DataTemplate,
                };

                StaffDGTest.Columns.Add(column);
            }
        }

        private List<DateTime> GetDatesForMonth(DateTime month)
        {
            List<DateTime> datesInMonth = new List<DateTime>();

            DateTime firstDayOfMonth = new DateTime(month.Year, month.Month, 1);
            DateTime lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);

            for (DateTime date = firstDayOfMonth; date <= lastDayOfMonth; date = date.AddDays(1))
            {
                datesInMonth.Add(date);
            }

            return datesInMonth;
        }

        public void FillDTAStatus()
        {
            var datesInMonth = GetDatesForMonth(selectedMonth);

            foreach (var staff in StaffList)
            {
                staff.DTAStatusList = new List<StatusInfo>();

                for (int i = 0; i < datesInMonth.Count; i++)
                {
                    bool hasRecord = CheckRecordInDatabase(staff.STAFF_ID, datesInMonth[i]);
                    Console.WriteLine($"---\nhasRecord for {staff.STAFF_ID} {datesInMonth[i].Date}: {hasRecord}");

                    StatusInfo status = new StatusInfo { Date = datesInMonth[i], Status = hasRecord ? "Р" : "Н" };

                    staff.DTAStatusList.Add(status);
                }
            }
        }

        private bool CheckRecordInDatabase(int staffId, DateTime date)
        {
            string query = "SELECT COUNT(*) FROM Zarplats.dbo.Staff_Schedule WHERE STAFF_ID = @staffId AND DTA = @date";

            int recordCount = Odb.db.Database.SqlQuery<int>(query,
                new SqlParameter("staffId", staffId),
                new SqlParameter("date", date)).FirstOrDefault();

            return recordCount > 0;
        }

        private void StaffDGTest_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            SelectedStaff = (StaffModel)StaffDGTest.SelectedItem;
            MessageBox.Show($"SelectedStaff Info: \nFIO: {SelectedStaff.SHORT_FIO}\nDTAWork: {SelectedStaff.DTA}\nWorkStatus: {SelectedStaff.WorkStatus}");
        }
    }
}
<Window x:Class="PlanningScheduleApp.DataGridTestWindow"
        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:PlanningScheduleApp"
        xmlns:models="clr-namespace:PlanningScheduleApp.Models"
        xmlns:converters="clr-namespace:PlanningScheduleApp.Converters"
        mc:Ignorable="d"
        Title="DataGridTestWindow" Height="450" Width="800">
    <Window.Resources>
        <DataTemplate x:Key="DayColumnTemplate">
            <TextBlock>
                <TextBlock.Text>
                    <MultiBinding Converter="{StaticResource MultiDayStatusConverter}">
                        <Binding Path="DTA" />
                        <Binding Path="DTAStatusList" />
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <DataGrid x:Name="StaffDGTest" AutoGenerateColumns="False" MouseDoubleClick="StaffDGTest_MouseDoubleClick">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Сотрудники" Binding="{Binding SHORT_FIO}"/>
                <DataGridTemplateColumn Header="Статус работы" Width="*">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding WorkStatus}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <!-- Добавление столбцов для каждого дня в месяце -->
                <DataGridTemplateColumn Header="Дни" Width="*" CellTemplate="{StaticResource DayColumnTemplate}" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

MultiDayStatusConverter.cs

namespace PlanningScheduleApp.Converters
{
    public class MultiDayStatusConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values.Length < 2 || !(values[0] is DateTime) || !(values[1] is List<StatusInfo>))
                return string.Empty;

            DateTime date = (DateTime)values[0];
            List<StatusInfo> statusList = (List<StatusInfo>)values[1];

            var statusForDate = statusList.FirstOrDefault(s => s.Date == date);

            return statusForDate != null ? statusForDate.Status : "Н";
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

StaffModel

public class StaffModel
{
    public int ID_Schedule { get; set; }
    public int STAFF_ID { get; set; }
    public string TABEL_ID { get; set; }
    public string SHORT_FIO { get; set; }
    public string WorkBegin { get; set; }
    public string WorkEnd { get; set; }
    public string LunchTimeBegin { get; set; }
    public string LunchTimeEnd { get; set; }
    public DateTime DTA { get; set; }
    public double? WorkingHours { get; set; }
    
    public string Position { get; set; }

    public List<StatusInfo> DTAStatusList { get; set; } = new List<StatusInfo>();

    public string WorkStatus
    {
        get
        {
            return DTAStatusList.Count > 0 ? DTAStatusList[3].Status : "-";
        }
    }
}

public class StatusInfo
{
    public DateTime Date { get; set; }
    public string Status { get; set; } = "Н"; // По умолчанию статус "Н"
}

Как хранится график в БД Как хранится график в БД


Ответы (0 шт):