убери нумерацию столбцов в предпросмотре

<!-- MainWindow.xaml -->
<Window x:Class="magnet.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:magnet"
        Title="Magnet File Previewer" Height="800" Width="1000">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <!-- Левая колонка для кнопок -->
            <ColumnDefinition Width="*"/>
            <!-- Правая колонка для списков и предпросмотра -->
        </Grid.ColumnDefinitions>
        <!-- Левая панель с кнопками -->
        <StackPanel Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="10">
            <Button Name="LoadTxtFilesButton" Width="180" Height="40" Click="LoadTxtFilesButton_Click" Margin="0,10,0,15">Загрузить .txt файлы</Button>
            <Button Name="LoadMagnetFilesButton" Width="180" Height="40" Click="LoadMagnetFilesButton_Click" Margin="0,0,0,15">Загрузить .magnet файлы</Button>
            <Button Name="CreateCombinedFileButton" Width="180" Height="40" Click="CreateCombinedFileButton_Click" Margin="0,0,0,15">Создать объединённый файл</Button>
            <Button Name="SaveProjectButton" Width="180" Height="40" Click="SaveProjectButton_Click" Margin="0,0,0,15">Сохранить проект</Button>
            <Button Name="LoadProjectButton" Width="180" Height="40" Click="LoadProjectButton_Click">Загрузить проект</Button>
        </StackPanel>
        <!-- Правая панель с списками файлов и предпросмотром -->
        <Grid Grid.Column="1" Margin="10">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <!-- Группировка ListView -->
                <RowDefinition Height="Auto"/>
                <!-- Ввод пропуска строк -->
                <RowDefinition Height="*"/>
                <!-- Предпросмотр файла -->
                <RowDefinition Height="2*"/>
                <!-- Таблица с обработанными файлами -->
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <!-- Списки загруженных файлов -->
            <Grid Grid.Row="0" Margin="0,0,0,10">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <!-- Группа .txt файлов -->
                <GroupBox Header=".txt файлы" Grid.Column="0" Margin="0,0,5,0">
                    <ListView Name="TxtFilesListView" SelectionChanged="TxtFilesListView_SelectionChanged">
                        <ListView.View>
                            <GridView>
                                <GridViewColumn Header="Название файла" DisplayMemberBinding="{Binding FilePath}" Width="200"/>
                                <GridViewColumn Header="Строки для пропуска" Width="150">
                                    <GridViewColumn.CellTemplate>
                                        <DataTemplate>
                                            <TextBox Text="{Binding LinesToSkip, UpdateSourceTrigger=PropertyChanged}" Width="140"/>
                                        </DataTemplate>
                                    </GridViewColumn.CellTemplate>
                                </GridViewColumn>
                            </GridView>
                        </ListView.View>
                    </ListView>
                </GroupBox>
                <!-- Группа .magnet файлов -->
                <GroupBox Header=".magnet файлы" Grid.Column="1" Margin="5,0,0,0">
                    <ListView Name="MagnetFilesListView" SelectionChanged="MagnetFilesListView_SelectionChanged">
                        <ListView.View>
                            <GridView>
                                <GridViewColumn Header="Название файла" DisplayMemberBinding="{Binding FilePath}" Width="200"/>
                                <GridViewColumn Header="Строки для пропуска" Width="150">
                                    <GridViewColumn.CellTemplate>
                                        <DataTemplate>
                                            <TextBox Text="{Binding LinesToSkip, UpdateSourceTrigger=PropertyChanged}" Width="140"/>
                                        </DataTemplate>
                                    </GridViewColumn.CellTemplate>
                                </GridViewColumn>
                            </GridView>
                        </ListView.View>
                    </ListView>
                </GroupBox>
            </Grid>
            <!-- Поле ввода LinesToSkip и кнопка Обновить -->
            <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,10">
                <Label Content="Пропустить строк:" VerticalAlignment="Center"/>
                <TextBox Name="LinesToSkipTextBox" Width="60" Margin="5,0,0,0" Text="0" />
                <Button Content="Обновить" Click="UpdateLinesToSkip_Click" Margin="10,0,0,0" Width="80" />
            </StackPanel>
            <!-- Область предпросмотра файла -->
            <ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto">
                <DataGrid Name="PreviewDataGrid" IsReadOnly="True" 
                          AutoGenerateColumns="False" 
                          HeadersVisibility="Column" 
                          CanUserAddRows="False"
                          CanUserDeleteRows="False"
                          CanUserResizeColumns="True"
                          CanUserSortColumns="False"
                          FontFamily="Consolas"
                          FontSize="12"
                          EnableRowVirtualization="True"
                          EnableColumnVirtualization="True"
                          VirtualizingPanel.IsVirtualizing="True"
                          VirtualizingPanel.VirtualizationMode="Recycling"
                          ScrollViewer.IsDeferredScrollingEnabled="True">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="#" Binding="{Binding LineNumber}" Width="50"/>
                        <DataGridTextColumn Header="Столбец 1" Binding="{Binding Columns[0]}" Width="100"/>
                        <DataGridTextColumn Header="Столбец 2" Binding="{Binding Columns[1]}" Width="100"/>
                        <DataGridTextColumn Header="Столбец 3" Binding="{Binding Columns[2]}" Width="100"/>
                        <DataGridTextColumn Header="Столбец 4" Binding="{Binding Columns[3]}" Width="100"/>
                        <DataGridTextColumn Header="Столбец 5" Binding="{Binding Columns[4]}" Width="100"/>
                    </DataGrid.Columns>
                </DataGrid>
            </ScrollViewer>
            <!-- Таблица с обработанными файлами -->
            <GroupBox Grid.Row="3" Header="Обработанные файлы" Margin="0,10,0,0">
                <TabControl Name="ProcessedFilesTabControl" />
            </GroupBox>
        </Grid>
    </Grid>
</Window>

// MainWindow.xaml.cs
using Microsoft.Win32;
using Newtonsoft.Json;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;

namespace magnet
{
    public partial class MainWindow : Window
    {
        private ProjectModel currentProject = new ProjectModel();
        private FileSetting selectedFile = null;

        public MainWindow()
        {
            InitializeComponent();
            TxtFilesListView.ItemsSource = currentProject.TxtFiles;
            MagnetFilesListView.ItemsSource = currentProject.MagnetFiles;
        }

        /// <summary>
        /// Обработчик нажатия кнопки "Загрузить .txt файлы".
        /// </summary>
        private async void LoadTxtFilesButton_Click(object sender, RoutedEventArgs e)
        {
            await LoadFilesAsync(".txt", currentProject.TxtFiles, TxtFilesListView);
        }

        /// <summary>
        /// Обработчик нажатия кнопки "Загрузить .magnet файлы".
        /// </summary>
        private async void LoadMagnetFilesButton_Click(object sender, RoutedEventArgs e)
        {
            await LoadFilesAsync(".magnet", currentProject.MagnetFiles, MagnetFilesListView);
        }

        /// <summary>
        /// Асинхронный метод для загрузки файлов определенного типа.
        /// </summary>
        private async Task LoadFilesAsync(string extension, ObservableCollection<FileSetting> fileCollection, ListView listView)
        {
            OpenFileDialog dlg = new OpenFileDialog
            {
                Multiselect = true,
                Filter = $"Файлы {extension}|*{extension}"
            };

            bool? result = dlg.ShowDialog();

            if (result == true)
            {
                var newFiles = dlg.FileNames.Where(filename => !fileCollection.Any(f => f.FilePath.Equals(filename, StringComparison.OrdinalIgnoreCase))).ToList();

                if (newFiles.Any())
                {
                    foreach (string filename in newFiles)
                    {
                        fileCollection.Add(new FileSetting { FilePath = filename, LinesToSkip = 0 });
                    }

                    // Автоматически выбрать последний добавленный файл
                    var lastFile = fileCollection.Last();
                    listView.SelectedItem = lastFile;
                }
                else
                {
                    MessageBox.Show("Все выбранные файлы уже добавлены.", "Информация", MessageBoxButton.OK, MessageBoxImage.Information);
                }
            }
        }

        /// <summary>
        /// Обработчик изменения выбора в ListView .txt файлов.
        /// </summary>
        private async void TxtFilesListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (TxtFilesListView.SelectedItem is FileSetting selected)
            {
                selectedFile = selected;
                LinesToSkipTextBox.Text = selected.LinesToSkip.ToString();
                MagnetFilesListView.SelectedItem = null; // Снять выделение из другого списка
                await PreviewFileAsync(selected);
            }
            else
            {
                selectedFile = null;
                PreviewDataGrid.ItemsSource = null;
                LinesToSkipTextBox.Text = "0";
            }
        }

        /// <summary>
        /// Обработчик изменения выбора в ListView .magnet файлов.
        /// </summary>
        private async void MagnetFilesListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (MagnetFilesListView.SelectedItem is FileSetting selected)
            {
                selectedFile = selected;
                LinesToSkipTextBox.Text = selected.LinesToSkip.ToString();
                TxtFilesListView.SelectedItem = null; // Снять выделение из другого списка
                await PreviewFileAsync(selected);
            }
            else
            {
                selectedFile = null;
                PreviewDataGrid.ItemsSource = null;
                LinesToSkipTextBox.Text = "0";
            }
        }

        /// <summary>
        /// Асинхронный метод для загрузки и отображения содержимого файла с нумерацией строк и столбцов.
        /// Ограничивает количество строк для предпросмотра.
        /// </summary>
        private async Task PreviewFileAsync(FileSetting fileSetting)
        {
            try
            {
                if (!File.Exists(fileSetting.FilePath))
                {
                    PreviewDataGrid.ItemsSource = null;
                    MessageBox.Show($"Файл не найден: {fileSetting.FilePath}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }

                var data = new ObservableCollection<PreviewRow>();
                int currentLine = 0;
                int maxColumns = 0;
                int maxPreviewLines = 1000; // Максимальное количество строк для предпросмотра

                await Task.Run(() =>
                {
                    using (StreamReader reader = new StreamReader(fileSetting.FilePath))
                    {
                        // Пропускаем указанные строки
                        for (int i = 0; i < fileSetting.LinesToSkip; i++)
                        {
                            if (reader.ReadLine() == null)
                                break;
                            currentLine++;
                        }

                        string line;
                        while ((line = reader.ReadLine()) != null && data.Count < maxPreviewLines)
                        {
                            currentLine++;
                            var columns = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                            if (columns.Length > maxColumns)
                                maxColumns = columns.Length;

                            var previewRow = new PreviewRow
                            {
                                LineNumber = currentLine,
                                Columns = columns.ToList()
                            };

                            // Добавление строки в коллекцию
                            Dispatcher.Invoke(() => data.Add(previewRow));
                        }
                    }
                });

                // Создать колонки для DataGrid
                Dispatcher.Invoke(() =>
                {
                    PreviewDataGrid.Columns.Clear();
                    PreviewDataGrid.Columns.Add(new DataGridTextColumn
                    {
                        Header = "#",
                        Binding = new System.Windows.Data.Binding("LineNumber"),
                        Width = 50
                    });

                    for (int i = 0; i < maxColumns; i++)
                    {
                        PreviewDataGrid.Columns.Add(new DataGridTextColumn
                        {
                            Header = $"C{i + 1}",
                            Binding = new System.Windows.Data.Binding($"Columns[{i}]"),
                            Width = 100
                        });
                    }

                    PreviewDataGrid.ItemsSource = data;
                    this.Title = $"Magnet File Previewer - {System.IO.Path.GetFileName(fileSetting.FilePath)} (Предпросмотр {data.Count} строк)";
                });
            }
            catch (Exception ex)
            {
                PreviewDataGrid.ItemsSource = null;
                MessageBox.Show($"Ошибка при чтении файла: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        /// <summary>
        /// Обработчик нажатия кнопки "Обновить".
        /// Обновляет количество строк для пропуска и предпросмотр.
        /// </summary>
        private async void UpdateLinesToSkip_Click(object sender, RoutedEventArgs e)
        {
            if (selectedFile != null)
            {
                if (int.TryParse(LinesToSkipTextBox.Text, out int skip) && skip >= 0)
                {
                    selectedFile.LinesToSkip = skip;
                    await PreviewFileAsync(selectedFile);
                    // Обновить соответствующий ListView
                    if (selectedFile.FilePath.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
                    {
                        TxtFilesListView.Items.Refresh();
                    }
                    else if (selectedFile.FilePath.EndsWith(".magnet", StringComparison.OrdinalIgnoreCase))
                    {
                        MagnetFilesListView.Items.Refresh();
                    }
                }
                else
                {
                    MessageBox.Show("Введите корректное неотрицательное число строк для пропуска.", "Ошибка ввода", MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }
            else
            {
                MessageBox.Show("Выберите файл для обновления.", "Предупреждение", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        /// <summary>
        /// Обработчик нажатия кнопки "Сохранить проект".
        /// Сохраняет текущий проект в файл с расширением .magn.
        /// </summary>
        private void SaveProjectButton_Click(object sender, RoutedEventArgs e)
        {
            if (currentProject.TxtFiles.Count == 0 && currentProject.MagnetFiles.Count == 0)
            {
                MessageBox.Show("Нет файлов для сохранения проекта.", "Предупреждение", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            SaveFileDialog dlg = new SaveFileDialog
            {
                Filter = "Magnet Project Files (*.magn)|*.magn",
                DefaultExt = "magn",
                FileName = "project.magn"
            };

            bool? result = dlg.ShowDialog();

            if (result == true)
            {
                try
                {
                    string json = JsonConvert.SerializeObject(currentProject, Formatting.Indented);
                    File.WriteAllText(dlg.FileName, json);
                    MessageBox.Show("Проект успешно сохранён!", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"Ошибка при сохранении проекта: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        /// <summary>
        /// Обработчик нажатия кнопки "Загрузить проект".
        /// Загружает проект из файла с расширением .magn.
        /// </summary>
        private async void LoadProjectButton_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog
            {
                Filter = "Magnet Project Files (*.magn)|*.magn",
                DefaultExt = "magn"
            };

            bool? result = dlg.ShowDialog();

            if (result == true)
            {
                try
                {
                    string json = await File.ReadAllTextAsync(dlg.FileName);
                    ProjectModel loadedProject = JsonConvert.DeserializeObject<ProjectModel>(json);

                    if (loadedProject != null)
                    {
                        // Очистка текущих коллекций
                        currentProject.TxtFiles.Clear();
                        currentProject.MagnetFiles.Clear();

                        // Добавление загруженных файлов
                        foreach (var file in loadedProject.TxtFiles)
                        {
                            currentProject.TxtFiles.Add(file);
                        }
                        foreach (var file in loadedProject.MagnetFiles)
                        {
                            currentProject.MagnetFiles.Add(file);
                        }

                        // Обновить списки
                        TxtFilesListView.Items.Refresh();
                        MagnetFilesListView.Items.Refresh();

                        // Очистить предпросмотр и LinesToSkipTextBox
                        PreviewDataGrid.ItemsSource = null;
                        LinesToSkipTextBox.Text = "0";
                        selectedFile = null;

                        MessageBox.Show("Проект успешно загружен!", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);
                    }
                    else
                    {
                        MessageBox.Show("Не удалось загрузить проект.", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"Ошибка при загрузке проекта: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        /// <summary>
        /// Обработчик нажатия кнопки "Создать объединённый файл".
        /// Объединяет два выбранных .txt файла и отображает результат в новой вкладке.
        /// </summary>
        private async void CreateCombinedFileButton_Click(object sender, RoutedEventArgs e)
        {
            // Проверка наличия как минимум двух .txt файлов
            if (currentProject.TxtFiles.Count < 2)
            {
                MessageBox.Show("Необходимо загрузить минимум два .txt файла для объединения.", "Предупреждение", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            // Открытие окна выбора двух файлов
            SelectFilesWindow selectFilesWindow = new SelectFilesWindow(currentProject.TxtFiles.ToList(), 2);
            if (selectFilesWindow.ShowDialog() == true)
            {
                List<FileSetting> selectedTxtFiles = selectFilesWindow.SelectedFilesSettings;

                if (selectedTxtFiles.Count != 2)
                {
                    MessageBox.Show("Выберите ровно два файла для объединения.", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }

                try
                {
                    // Чтение и объединение файлов асинхронно
                    List<ProcessedRow> combinedData = new List<ProcessedRow>();
                    int numbering = 1;

                    foreach (var file in selectedTxtFiles)
                    {
                        using (StreamReader reader = new StreamReader(file.FilePath))
                        {
                            string headerLine = await reader.ReadLineAsync(); // Предполагается, что есть заголовок
                            // Можно игнорировать заголовок или добавить проверку его структуры

                            string line;
                            while ((line = await reader.ReadLineAsync()) != null)
                            {
                                if (string.IsNullOrWhiteSpace(line))
                                    continue;
                                // Разделитель - пробел
                                string[] columns = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                                // Проверка наличия минимального количества столбцов
                                if (columns.Length < 5)
                                {
                                    MessageBox.Show($"Файл {file.FilePath} содержит строку с недостаточным количеством столбцов: {line}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                                    return;
                                }

                                // Обработка столбца 5: деление на 24 и форматирование как время
                                string formattedTime = "00:00:00";
                                if (double.TryParse(columns[4], out double originalValue))
                                {
                                    double divided = originalValue / 24.0;
                                    TimeSpan time = TimeSpan.FromHours(divided);
                                    formattedTime = time.ToString(@"hh\:mm\:ss");
                                }

                                // Создание нового ProcessedRow
                                combinedData.Add(new ProcessedRow
                                {
                                    Number = numbering++,
                                    Column2 = columns[2],
                                    Column3 = columns[3],
                                    Column4 = columns[4],
                                    Column5 = formattedTime
                                });
                            }
                        }
                    }

                    if (combinedData.Count == 0)
                    {
                        MessageBox.Show("Объединённые файлы не содержат данных для объединения.", "Информация", MessageBoxButton.OK, MessageBoxImage.Information);
                        return;
                    }

                    // Создание объекта обработанного файла
                    ProcessedFile processedFile = new ProcessedFile
                    {
                        FileName = $"Combined_{DateTime.Now:yyyyMMdd_HHmmss}.magn",
                        Data = combinedData
                    };

                    // Добавление новой вкладки с результатом
                    AddProcessedFileTab(processedFile);
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"Ошибка при объединении файлов: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        /// <summary>
        /// Метод для добавления новой вкладки с обработанным файлом.
        /// </summary>
        private void AddProcessedFileTab(ProcessedFile processedFile)
        {
            // Создание DataGrid
            DataGrid dataGrid = new DataGrid
            {
                AutoGenerateColumns = false,
                IsReadOnly = true,
                CanUserAddRows = false,
                ItemsSource = processedFile.Data,
                Margin = new Thickness(5),
                EnableRowVirtualization = true,
                EnableColumnVirtualization = true
            };

            // Добавление столбцов
            dataGrid.Columns.Add(new DataGridTextColumn { Header = "№", Binding = new System.Windows.Data.Binding("Number"), Width = 50 });
            dataGrid.Columns.Add(new DataGridTextColumn { Header = "Столбец 2", Binding = new System.Windows.Data.Binding("Column2"), Width = 150 });
            dataGrid.Columns.Add(new DataGridTextColumn { Header = "Столбец 3", Binding = new System.Windows.Data.Binding("Column3"), Width = 150 });
            dataGrid.Columns.Add(new DataGridTextColumn { Header = "Столбец 4", Binding = new System.Windows.Data.Binding("Column4"), Width = 150 });
            dataGrid.Columns.Add(new DataGridTextColumn { Header = "Столбец 5", Binding = new System.Windows.Data.Binding("Column5"), Width = 100 });

            // Создание кнопки для сохранения
            Button saveButton = new Button
            {
                Content = "Сохранить файл",
                Width = 120,
                Height = 30,
                Margin = new Thickness(5)
            };
            saveButton.Click += (s, e) => SaveProcessedFile(processedFile);

            // Создание StackPanel для DataGrid и кнопки
            StackPanel stackPanel = new StackPanel();
            stackPanel.Children.Add(dataGrid);
            stackPanel.Children.Add(saveButton);

            // Создание новой вкладки
            TabItem tabItem = new TabItem
            {
                Header = processedFile.FileName,
                Content = stackPanel
            };

            ProcessedFilesTabControl.Items.Add(tabItem);
            ProcessedFilesTabControl.SelectedItem = tabItem;
        }

        /// <summary>
        /// Метод для сохранения обработанного файла.
        /// </summary>
        private async void SaveProcessedFile(ProcessedFile processedFile)
        {
            SaveFileDialog dlg = new SaveFileDialog
            {
                Filter = "Magnet Files (*.magn)|*.magn",
                DefaultExt = "magn",
                FileName = processedFile.FileName
            };

            bool? result = dlg.ShowDialog();

            if (result == true)
            {
                try
                {
                    using (StreamWriter writer = new StreamWriter(dlg.FileName))
                    {
                        // Запись заголовка
                        await writer.WriteLineAsync("№\tСтолбец 2\tСтолбец 3\tСтолбец 4\tСтолбец 5");

                        foreach (var row in processedFile.Data)
                        {
                            string line = $"{row.Number}\t{row.Column2}\t{row.Column3}\t{row.Column4}\t{row.Column5}";
                            await writer.WriteLineAsync(line);
                        }
                    }

                    MessageBox.Show("Файл успешно сохранён!", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"Ошибка при сохранении файла: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }
    }

    /// <summary>
    /// Класс для строки предпросмотра файла с нумерацией строк и столбцов.
    /// </summary>
    public class PreviewRow : INotifyPropertyChanged
    {
        private int lineNumber;
        private List<string> columns;

        public int LineNumber
        {
            get => lineNumber;
            set
            {
                if (lineNumber != value)
                {
                    lineNumber = value;
                    OnPropertyChanged(nameof(LineNumber));
                }
            }
        }

        public List<string> Columns
        {
            get => columns;
            set
            {
                if (columns != value)
                {
                    columns = value;
                    OnPropertyChanged(nameof(Columns));
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName) =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

<!-- SelectFilesWindow.xaml -->
<Window x:Class="magnet.SelectFilesWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Выберите файлы для объединения" Height="400" Width="300">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ListBox Name="FilesListBox" SelectionMode="Extended" DisplayMemberPath="FilePath">
            <!-- Элементы будут добавлены из кода -->
        </ListBox>
        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
            <Button Content="OK" Width="75" Click="OKButton_Click" Margin="0,0,10,0"/>
            <Button Content="Отмена" Width="75" Click="CancelButton_Click"/>
        </StackPanel>
    </Grid>
</Window>

// SelectFilesWindow.xaml.cs
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace magnet
{
    /// <summary>
    /// Логика взаимодействия для SelectFilesWindow.xaml
    /// </summary>
    public partial class SelectFilesWindow : Window
    {
        /// <summary>
        /// Выбранные файлы после закрытия окна.
        /// </summary>
        public List<FileSetting> SelectedFilesSettings { get; private set; } = new List<FileSetting>();

        private int maxSelection;

        /// <summary>
        /// Конструктор окна выбора файлов.
        /// </summary>
        /// <param name="availableFiles">Список доступных файлов для выбора.</param>
        /// <param name="maxSelection">Максимальное количество выбираемых файлов.</param>
        public SelectFilesWindow(List<FileSetting> availableFiles, int maxSelection)
        {
            InitializeComponent();
            FilesListBox.ItemsSource = availableFiles;
            this.maxSelection = maxSelection;
        }

        /// <summary>
        /// Обработчик нажатия кнопки "OK".
        /// Проверяет, что выбрано ровно необходимое количество файлов.
        /// </summary>
        private void OKButton_Click(object sender, RoutedEventArgs e)
        {
            if (FilesListBox.SelectedItems.Count != maxSelection)
            {
                MessageBox.Show($"Выберите ровно {maxSelection} файла(ов) для объединения.", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            foreach (var item in FilesListBox.SelectedItems)
            {
                if (item is FileSetting fileSetting)
                {
                    SelectedFilesSettings.Add(fileSetting);
                }
            }

            this.DialogResult = true;
            this.Close();
        }

        /// <summary>
        /// Обработчик нажатия кнопки "Отмена".
        /// Закрывает окно без выбора файлов.
        /// </summary>
        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = false;
            this.Close();
        }
    }
}

// ProjectModel.cs
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.ComponentModel;

namespace magnet
{
    public class ProjectModel : INotifyPropertyChanged
    {
        private ObservableCollection<FileSetting> txtFiles = new ObservableCollection<FileSetting>();
        private ObservableCollection<FileSetting> magnetFiles = new ObservableCollection<FileSetting>();

        public ObservableCollection<FileSetting> TxtFiles
        {
            get => txtFiles;
            set
            {
                if (txtFiles != value)
                {
                    txtFiles = value;
                    OnPropertyChanged(nameof(TxtFiles));
                }
            }
        }

        public ObservableCollection<FileSetting> MagnetFiles
        {
            get => magnetFiles;
            set
            {
                if (magnetFiles != value)
                {
                    magnetFiles = value;
                    OnPropertyChanged(nameof(MagnetFiles));
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName) =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public class FileSetting : INotifyPropertyChanged
    {
        private string filePath;
        private int linesToSkip;

        public string FilePath
        {
            get => filePath;
            set
            {
                if (filePath != value)
                {
                    filePath = value;
                    OnPropertyChanged(nameof(FilePath));
                }
            }
        }

        public int LinesToSkip
        {
            get => linesToSkip;
            set
            {
                if (linesToSkip != value)
                {
                    linesToSkip = value;
                    OnPropertyChanged(nameof(LinesToSkip));
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName) =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public class ProcessedFile
    {
        public string FileName { get; set; }
        public List<ProcessedRow> Data { get; set; } = new List<ProcessedRow>();
    }

    public class ProcessedRow : INotifyPropertyChanged
    {
        private int number;
        private string column2;
        private string column3;
        private string column4;
        private string column5;

        public int Number
        {
            get => number;
            set
            {
                if (number != value)
                {
                    number = value;
                    OnPropertyChanged(nameof(Number));
                }
            }
        }

        public string Column2
        {
            get => column2;
            set
            {
                if (column2 != value)
                {
                    column2 = value;
                    OnPropertyChanged(nameof(Column2));
                }
            }
        }

        public string Column3
        {
            get => column3;
            set
            {
                if (column3 != value)
                {
                    column3 = value;
                    OnPropertyChanged(nameof(Column3));
                }
            }
        }

        public string Column4
        {
            get => column4;
            set
            {
                if (column4 != value)
                {
                    column4 = value;
                    OnPropertyChanged(nameof(Column4));
                }
            }
        }

        public string Column5
        {
            get => column5;
            set
            {
                if (column5 != value)
                {
                    column5 = value;
                    OnPropertyChanged(nameof(Column5));
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName) =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}