мне нужно чтобы ты ппересмотрела создание файлов
во всех файлах некоторые строки съезжают, также съезжают все заголовки
нужно настроить это все как-то чтобы ничего не съезжало
например заголовки брать не из столбцов а просто задавать их как первая строка

<Window x:Class="TxtProcessorApp.MagneteWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TxtProcessorApp"
        Title=".magnete Файлы" Height="700" Width="1000">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <!-- Центр для TabControl -->
            <RowDefinition Height="Auto"/>
            <!-- Нижняя панель с кнопками -->
        </Grid.RowDefinitions>

        <!-- TabControl для отображения .magnete файлов -->
        <TabControl x:Name="MagneteTabControl" Grid.Row="0" Margin="0,0,0,10">
            <!-- Вкладки будут добавляться динамически -->
        </TabControl>

        <!-- Кнопки объединения файлов и редактирования -->
        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Left">
            <Button Content="Объединить .magnete файлы" Width="200" Height="30" Click="CombineMagneteFiles_Click"/>
            <Button Content="Редактировать и Экспортировать" Width="200" Height="30" Margin="10,0,0,0" Click="EditAndExportMagnete_Click" IsEnabled="False" x:Name="EditExportMagneteButton"/>
        </StackPanel>
    </Grid>
</Window>

using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using ProjNet.CoordinateSystems;
using ProjNet.CoordinateSystems.Transformations;
using NetTopologySuite.Geometries;
using System.Windows.Controls.Primitives;

namespace TxtProcessorApp
{
    public partial class MagneteWindow : Window
    {
        private Dictionary<string, List<string[]>> magneteFileData;
        private DataTable combinedMagneteDataTable = null;

        public MagneteWindow(Dictionary<string, List<string[]>> loadedMagneteData)
        {
            InitializeComponent();
            magneteFileData = loadedMagneteData;
            PopulateMagneteTabControl();
        }

        private void PopulateMagneteTabControl()
        {
            MagneteTabControl.Items.Clear();

            foreach (var kvp in magneteFileData)
            {
                string fileName = kvp.Key;
                List<string[]> data = kvp.Value;

                DataGrid dataGrid = CreateDataGrid(data);

                TabItem tabItem = new TabItem
                {
                    Header = fileName,
                    Content = dataGrid
                };

                MagneteTabControl.Items.Add(tabItem);
            }
        }

        private DataGrid CreateDataGrid(List<string[]> data)
        {
            DataGrid dataGrid = new DataGrid
            {
                AutoGenerateColumns = true,
                IsReadOnly = true,
                CanUserAddRows = false,
                CanUserDeleteRows = false,
                Margin = new Thickness(0, 0, 0, 0)
            };

            // Добавление стилей для ячеек и заголовков столбцов
            Style cellStyle = new Style(typeof(DataGridCell));
            cellStyle.Setters.Add(new Setter(DataGridCell.PaddingProperty, new Thickness(5)));
            cellStyle.Setters.Add(new Setter(DataGridCell.HorizontalContentAlignmentProperty, HorizontalAlignment.Center));

            Style headerStyle = new Style(typeof(DataGridColumnHeader));
            headerStyle.Setters.Add(new Setter(DataGridColumnHeader.HorizontalContentAlignmentProperty, HorizontalAlignment.Center));
            headerStyle.Setters.Add(new Setter(DataGridColumnHeader.FontWeightProperty, FontWeights.Bold));

            dataGrid.CellStyle = cellStyle;
            dataGrid.ColumnHeaderStyle = headerStyle;

            DataTable dataTable = ConvertToDataTable(data);
            dataGrid.ItemsSource = dataTable.DefaultView;

            return dataGrid;
        }

        private DataTable ConvertToDataTable(List<string[]> data)
        {
            DataTable dt = new DataTable();

            if (data == null || data.Count == 0)
                return dt;

            // Определение столбцов только для исходных данных .magnete файлов
            dt.Columns.Add("Stan", typeof(string));
            dt.Columns.Add("Zeit", typeof(string));
            dt.Columns.Add("Noise", typeof(string));
            dt.Columns.Add("Time", typeof(string));
            dt.Columns.Add("TimeUTC", typeof(string));

            foreach (var row in data)
            {
                DataRow dr = dt.NewRow();
                dr["Stan"] = row.Length > 0 ? row[0].Trim() : string.Empty;
                dr["Zeit"] = row.Length > 1 ? row[1].Trim() : string.Empty;
                dr["Noise"] = row.Length > 2 ? row[2].Trim() : string.Empty;
                dr["Time"] = row.Length > 3 ? row[3].Trim() : string.Empty;
                dr["TimeUTC"] = row.Length > 4 ? row[4].Trim() : string.Empty;
                dt.Rows.Add(dr);
            }

            return dt;
        }

        // Метод логирования расчётов и ошибок
        private void LogCalculation(string message, string logFileName = "calculation_log.txt")
        {
            string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, logFileName);
            try
            {
                File.AppendAllText(logFilePath, $"{DateTime.Now}: {message}{Environment.NewLine}");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Не удалось записать лог: {ex.Message}", "Ошибка логирования", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        // Метод для определения UTM-зоны по долготе
        private int GetUTMZone(double longitude)
        {
            // Убедитесь, что долгота находится в диапазоне от -180 до 180 градусов
            if (longitude < -180 || longitude > 180)
                throw new ArgumentOutOfRangeException("Долгота должна быть в диапазоне от -180 до 180 градусов.");

            // Формула для расчёта зоны
            int zone = (int)Math.Floor((longitude + 180) / 6) + 1;
            return zone;
        }

        // Метод для преобразования WGS84 координат в UTM
        private (double Easting, double Northing) ConvertWGS84ToUTM(double longitude, double latitude, int utmZone)
        {
            // Определяем систему координат WGS84
            var wgs84 = GeographicCoordinateSystem.WGS84;

            // Определяем систему координат UTM для данного пояса и полушария
            var utmCS = ProjectedCoordinateSystem.WGS84_UTM(utmZone, latitude >= 0);

            // Создаём трансформацию
            var transformFactory = new CoordinateTransformationFactory();
            var transformation = transformFactory.CreateFromCoordinateSystems(wgs84, utmCS);

            // Преобразование координат
            double[] fromPoint = new double[] { longitude, latitude };
            double[] toPoint = transformation.MathTransform.Transform(fromPoint);

            return (toPoint[0], toPoint[1]); // Easting, Northing
        }

        // Обработчик кнопки Объединить .magnete файлы
        private void CombineMagneteFiles_Click(object sender, RoutedEventArgs e)
        {
            if (!magneteFileData.Any())
            {
                MessageBox.Show("Нет загруженных .magnete файлов для объединения.", "Информация", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            // Фиксированная дата (06.01.1980) в UTC
            DateTime fixedDate = new DateTime(1980, 1, 6, 0, 0, 0, DateTimeKind.Utc);

            // Объединяем все данные из .magnete файлов
            combinedMagneteDataTable = new DataTable();

            // Определение столбцов с уникальными внутренними именами
            combinedMagneteDataTable.Columns.Add("DATA", typeof(string));
            combinedMagneteDataTable.Columns.Add("DATA UTC", typeof(string));
            combinedMagneteDataTable.Columns.Add("TIME", typeof(string));
            combinedMagneteDataTable.Columns.Add("TIME UTC", typeof(string));
            combinedMagneteDataTable.Columns.Add("Долгота WGS", typeof(string)); // раньше "Noise"
            combinedMagneteDataTable.Columns.Add("Широта WGS", typeof(string));  // раньше "Zeit"
            combinedMagneteDataTable.Columns.Add("Долгота UTM", typeof(double)); // раньше "Zero1"
            combinedMagneteDataTable.Columns.Add("Широта UTM", typeof(double));  // раньше "Zero2"
            combinedMagneteDataTable.Columns.Add("№1", typeof(int));             // раньше "SequentialNumber1"
            combinedMagneteDataTable.Columns.Add("Альтитуда", typeof(string));    // раньше "Time"
            combinedMagneteDataTable.Columns.Add("№2", typeof(int));             // раньше "SequentialNumber2"
            combinedMagneteDataTable.Columns.Add("Shark", typeof(string));        // раньше "TimeUTC"

            int sequentialNumber = 1;
            List<string> errorMessages = new List<string>();

            foreach (var file in magneteFileData.Values)
            {
                foreach (var row in file)
                {
                    // Создание новой строки для объединённой таблицы
                    DataRow newRow = combinedMagneteDataTable.NewRow();

                    // Столбцы "DATA UTC"
                    newRow["DATA UTC"] = fixedDate.ToString("dd.MM.yyyy");

                    // Столбцы "TIME" и "TIME UTC"
                    string stanValue = row.Length > 0 ? row[0].Trim() : string.Empty;
                    newRow["TIME"] = stanValue;

                    // Столбцы "Долгота WGS" и "Широта WGS"
                    string zeitValue = row.Length > 1 ? row[1].Trim() : string.Empty;
                    newRow["Широта WGS"] = zeitValue;

                    string noiseValue = row.Length > 2 ? row[2].Trim() : string.Empty;
                    newRow["Долгота WGS"] = noiseValue;

                    // Столбцы "No1" и "No2"
                    newRow["№1"] = sequentialNumber; // SequentialNumber1
                    newRow["№2"] = sequentialNumber; // SequentialNumber2

                    // Столбцы "Альтитуда" и "Shark"
                    string timeValueStr = row.Length > 3 ? row[3].Trim() : string.Empty;
                    newRow["Альтитуда"] = timeValueStr;

                    string timeUtcValue = row.Length > 4 ? row[4].Trim() : string.Empty;
                    newRow["Shark"] = timeUtcValue;

                    // Столбцы "Долгота UTM" и "Широта UTM" будут заполнены позже
                    newRow["Долгота UTM"] = 0.0; // Изначально 0.0, потом заменим
                    newRow["Широта UTM"] = 0.0;

                    // Для логирования
                    string computedDateStr = "N/A";
                    string computedTimeStr = "N/A"; // Объявлено

                    // Преобразование координат из "Долгота WGS" и "Широта WGS" в UTM
                    double longitude, latitude;
                    bool validCoordinates = true;

                    if (!double.TryParse(noiseValue, NumberStyles.Any, CultureInfo.InvariantCulture, out longitude))
                    {
                        string error = $"Не удалось распарсить Долгота WGS='{noiseValue}' как долготу в строке: {string.Join("\t", row)}.";
                        errorMessages.Add(error);
                        LogCalculation(error);
                        validCoordinates = false;
                    }

                    if (!double.TryParse(zeitValue, NumberStyles.Any, CultureInfo.InvariantCulture, out latitude))
                    {
                        string error = $"Не удалось распарсить Широта WGS='{zeitValue}' как широту в строке: {string.Join("\t", row)}.";
                        errorMessages.Add(error);
                        LogCalculation(error);
                        validCoordinates = false;
                    }

                    if (validCoordinates)
                    {
                        try
                        {
                            // Определяем UTM-зону
                            int utmZone = GetUTMZone(longitude);

                            // Преобразуем координаты в UTM
                            var utmCoordinates = ConvertWGS84ToUTM(longitude, latitude, utmZone);

                            // Записываем UTM координаты в "Долгота UTM" и "Широта UTM"
                            newRow["Долгота UTM"] = utmCoordinates.Easting;
                            newRow["Широта UTM"] = utmCoordinates.Northing;
                        }
                        catch (Exception ex)
                        {
                            string error = $"Ошибка при преобразовании координат (Долгота WGS='{noiseValue}', Широта WGS='{zeitValue}') в строке: {string.Join("\t", row)}. Текст ошибки: {ex.Message}.";
                            errorMessages.Add(error);
                            LogCalculation(error);
                        }
                    }

                    // Обработка столбца "Stan" как секунд с фиксированной даты
                    if (double.TryParse(stanValue, NumberStyles.Any, CultureInfo.InvariantCulture, out double stanSeconds))
                    {
                        if (stanSeconds < 0)
                        {
                            string warning = $"Отрицательное значение TIME='{stanSeconds}' в строке: {string.Join("\t", row)}. Установка даты как фиксированной.";
                            errorMessages.Add(warning);
                            LogCalculation(warning);

                            newRow["DATA"] = fixedDate.ToString("dd.MM.yyyy");
                            // ComputedTime - фиксированное время минус 18 секунд (оставляет статус 00:00:00.0)
                            computedTimeStr = fixedDate.ToString("HH:mm:ss.f");
                            newRow["TIME UTC"] = computedTimeStr;
                        }
                        else
                        {
                            try
                            {
                                // Добавляем секунды к фиксированной дате
                                DateTime computedDate = fixedDate.AddSeconds(stanSeconds);

                                // Проверка, что дата находится в допустимом диапазоне
                                if (computedDate > DateTime.MaxValue || computedDate < DateTime.MinValue)
                                {
                                    string warning = $"TIME слишком велико='{stanSeconds}' в строке: {string.Join("\t", row)}. Установка даты как фиксированной.";
                                    errorMessages.Add(warning);
                                    LogCalculation(warning);

                                    newRow["DATA"] = fixedDate.ToString("dd.MM.yyyy");
                                    computedTimeStr = fixedDate.ToString("HH:mm:ss.f");
                                    newRow["TIME UTC"] = computedTimeStr;
                                }
                                else
                                {
                                    newRow["DATA"] = computedDate.ToString("dd.MM.yyyy");

                                    // Формат даты
                                    computedDateStr = computedDate.ToString("dd.MM.yyyy");

                                    // Вычисляем ComputedTime как остаток времени после вычитания 18 секунд
                                    TimeSpan timeOfDay = computedDate.TimeOfDay;

                                    // Вычитаем 18 секунд
                                    TimeSpan offsetTime = TimeSpan.FromSeconds(18);
                                    TimeSpan computedTime;

                                    if (timeOfDay >= offsetTime)
                                    {
                                        computedTime = timeOfDay - offsetTime;
                                    }
                                    else
                                    {
                                        // Если время дня меньше 18 секунд, устанавливаем время как 00:00:00.0
                                        computedTime = TimeSpan.Zero;
                                        string info = $"Время дня меньше 18 секунд в строке: {string.Join("\t", row)}. ComputedTime установлено как 00:00:00.0.";
                                        errorMessages.Add(info);
                                        LogCalculation(info);
                                    }

                                    // Округляем каждый TimeSpan.TotalSeconds до одного знака после запятой
                                    double totalSeconds = computedTime.TotalSeconds;
                                    double roundedSeconds = Math.Round(totalSeconds, 1, MidpointRounding.AwayFromZero);

                                    // Создаём TimeSpan из округлённого количества секунд
                                    TimeSpan roundedTime = TimeSpan.FromSeconds(roundedSeconds);

                                    // Форматируем ComputedTime с учетом одной десятичной части секунд
                                    computedTimeStr = roundedTime.ToString(@"hh\:mm\:ss\.f");
                                    newRow["TIME UTC"] = computedTimeStr;
                                }
                            }
                            catch (Exception ex)
                            {
                                string error = $"Ошибка при расчёте даты для TIME='{stanSeconds}' в строке: {string.Join("\t", row)}. Текст ошибки: {ex.Message}. Установка даты как фиксированной.";
                                errorMessages.Add(error);
                                LogCalculation(error);

                                newRow["DATA"] = fixedDate.ToString("dd.MM.yyyy");
                                computedDateStr = fixedDate.ToString("dd.MM.yyyy");
                                computedTimeStr = fixedDate.ToString("HH:mm:ss.f");
                                newRow["TIME UTC"] = computedTimeStr;
                            }
                        }
                    }
                    else
                    {
                        // Если парсинг не удался, устанавливаем фиксированную дату и время, и логируем
                        string error = $"Не удалось распарсить TIME='{stanValue}' в строке: {string.Join("\t", row)}. Установка даты как фиксированной.";
                        errorMessages.Add(error);
                        LogCalculation(error);

                        newRow["DATA"] = fixedDate.ToString("dd.MM.yyyy");
                        computedDateStr = fixedDate.ToString("dd.MM.yyyy");
                        computedTimeStr = fixedDate.ToString("HH:mm:ss.f");
                        newRow["TIME UTC"] = computedTimeStr;
                    }

                    combinedMagneteDataTable.Rows.Add(newRow);
                    sequentialNumber++;

                    // Логируем расчёты для каждой строки
                    string logMessage = $"TIME: '{stanValue}', ComputedDate: {computedDateStr}, ComputedTime: {computedTimeStr}";
                    LogCalculation(logMessage);
                }
            }

            // Создаём DataGrid с явно определёнными столбцами
            DataGrid dataGridCombined = new DataGrid
            {
                AutoGenerateColumns = false,
                IsReadOnly = true,
                CanUserAddRows = false,
                CanUserDeleteRows = false,
            };

            // Определяем столбцы вручную с необходимым форматированием
            // 1. DATA
            dataGridCombined.Columns.Add(new DataGridTextColumn
            {
                Header = "DATA",
                Binding = new Binding("DATA"),
                Width = new DataGridLength(150, DataGridLengthUnitType.Auto)
            });

            // 2. DATA UTC
            dataGridCombined.Columns.Add(new DataGridTextColumn
            {
                Header = "DATA UTC",
                Binding = new Binding("DATA UTC"),
                Width = new DataGridLength(150, DataGridLengthUnitType.Auto)
            });

            // 3. TIME
            dataGridCombined.Columns.Add(new DataGridTextColumn
            {
                Header = "TIME",
                Binding = new Binding("TIME"),
                Width = new DataGridLength(100, DataGridLengthUnitType.Auto)
            });

            // 4. TIME UTC
            dataGridCombined.Columns.Add(new DataGridTextColumn
            {
                Header = "TIME UTC",
                Binding = new Binding("TIME UTC"),
                Width = new DataGridLength(150, DataGridLengthUnitType.Auto)
            });

            // 5. Долгота WGS
            dataGridCombined.Columns.Add(new DataGridTextColumn
            {
                Header = "Долгота WGS",
                Binding = new Binding("Долгота WGS"),
                Width = new DataGridLength(120, DataGridLengthUnitType.Auto)
            });

            // 6. Широта WGS
            dataGridCombined.Columns.Add(new DataGridTextColumn
            {
                Header = "Широта WGS",
                Binding = new Binding("Широта WGS"),
                Width = new DataGridLength(120, DataGridLengthUnitType.Auto)
            });

            // 7. Долгота UTM
            dataGridCombined.Columns.Add(new DataGridTextColumn
            {
                Header = "Долгота UTM",
                Binding = new Binding("Долгота UTM") { StringFormat = "F2" }, // Форматирование до 2 десятичных знаков
                Width = new DataGridLength(140, DataGridLengthUnitType.Auto)
            });

            // 8. Широта UTM
            dataGridCombined.Columns.Add(new DataGridTextColumn
            {
                Header = "Широта UTM",
                Binding = new Binding("Широта UTM") { StringFormat = "F2" },
                Width = new DataGridLength(140, DataGridLengthUnitType.Auto)
            });

            // 9. №1 (ранее No1)
            dataGridCombined.Columns.Add(new DataGridTextColumn
            {
                Header = "№1", // Изменено с "No1" на "№1"
                Binding = new Binding("№1"),
                Width = new DataGridLength(60, DataGridLengthUnitType.Auto)
            });

            // 10. Альтитуда
            dataGridCombined.Columns.Add(new DataGridTextColumn
            {
                Header = "Альтитуда",
                Binding = new Binding("Альтитуда"),
                Width = new DataGridLength(100, DataGridLengthUnitType.Auto)
            });

            // 11. №2 (ранее No2)
            dataGridCombined.Columns.Add(new DataGridTextColumn
            {
                Header = "№2", // Изменено с "No2" на "№2"
                Binding = new Binding("№2"),
                Width = new DataGridLength(60, DataGridLengthUnitType.Auto)
            });

            // 12. Shark
            dataGridCombined.Columns.Add(new DataGridTextColumn
            {
                Header = "Shark",
                Binding = new Binding("Shark"),
                Width = new DataGridLength(100, DataGridLengthUnitType.Auto)
            });

            // Присваиваем DataTable как источник данных
            dataGridCombined.ItemsSource = combinedMagneteDataTable.DefaultView;

            // Добавляем вкладку с объединёнными данными
            TabItem tabItemCombined = new TabItem
            {
                Header = "Объединённые данные",
                Content = dataGridCombined
            };

            MagneteTabControl.Items.Add(tabItemCombined);

            // Активируем кнопку редактирования и экспорта
            EditExportMagneteButton.IsEnabled = true;

            // Логируем основные события
            LogCalculation($"Файлы .magnete успешно объединены. Объединено строк: {combinedMagneteDataTable.Rows.Count}.");

            if (errorMessages.Any())
            {
                string allErrors = string.Join("\n", errorMessages);
                LogCalculation($"Обнаружены следующие проблемы:\n{allErrors}");
                MessageBox.Show($"Файлы .magnete объединены с ошибками. Подробности в файле логов.", "Внимание", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
            else
            {
                MessageBox.Show($"Файлы .magnete успешно объединены. Объединено строк: {combinedMagneteDataTable.Rows.Count}", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        private void EditAndExportMagnete_Click(object sender, RoutedEventArgs e)
        {
            if (combinedMagneteDataTable == null)
            {
                MessageBox.Show("Нет объединённых данных для редактирования.", "Информация", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            // Создаём и открываем окно редактирования
            EditWindow editWindow = new EditWindow(combinedMagneteDataTable);
            editWindow.Owner = this;
            editWindow.ShowDialog();
        }
    }
}

<Window x:Class="TxtProcessorApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TxtProcessorApp"
        Title="Txt Processor" Height="600" Width="900">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!-- Панель кнопок загрузки файлов -->
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
            <Button Content="Загрузить .txt файлы" Width="150" Height="30" Click="LoadTxtFiles_Click"/>
            <Button Content="Загрузить .magnete файлы" Width="180" Height="30" Click="LoadMagneteFiles_Click" Margin="10,0,0,0"/>
            <Button Content="Просмотр .magnete файлов" Width="180" Height="30" Click="OpenMagneteView_Click" Margin="10,0,0,0"/>
        </StackPanel>

        <!-- TabControl для отображения .txt файлов -->
        <TabControl x:Name="FilesTabControl" Grid.Row="1" Margin="0,10,0,10"/>

        <!-- Кнопка объединения файлов и редактирования -->
        <StackPanel Grid.Row="2" Orientation="Horizontal">
            <Button Content="Объединить файлы" Width="150" Height="30" Click="CombineFiles_Click"/>
            <Button Content="Редактировать и Экспортировать" Width="200" Height="30" Margin="10,0,0,0" Click="EditAndExport_Click" IsEnabled="False" x:Name="EditExportButton"/>
        </StackPanel>
    </Grid>
</Window>

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

namespace TxtProcessorApp
{
    public partial class MainWindow : Window
    {
        // Словарь для хранения данных из .txt файлов. Ключ - имя файла, значение - список строк данных.
        private Dictionary<string, List<string[]>> txtFileData = new Dictionary<string, List<string[]>>();

        // Словарь для хранения данных из .magnete файлов. Ключ - имя файла, значение - список строк данных.
        private Dictionary<string, List<string[]>> magneteFileData = new Dictionary<string, List<string[]>>();

        // Хранение объединённых данных для дальнейшего редактирования
        private DataTable combinedDataTable = null;

        public MainWindow()
        {
            InitializeComponent();
        }

        #region Обработчики загрузки файлов .txt

        // Обработчик кнопки загрузки .txt файлов
        private void LoadTxtFiles_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog
            {
                Multiselect = true,
                Filter = "Text files (*.txt)|*.txt"
            };

            if (ofd.ShowDialog() == true)
            {
                foreach (var file in ofd.FileNames)
                {
                    ProcessTxtFile(file);
                }

                // Обновляем TabControl с вкладками для каждого .txt файла
                PopulateTxtTabControl();

                // Деактивируем кнопку редактирования до объединения
                EditExportButton.IsEnabled = false;
                combinedDataTable = null;
            }
        }

        // Метод для обработки отдельного .txt файла
        private void ProcessTxtFile(string filePath)
        {
            List<string[]> processedLines = new List<string[]>();

            try
            {
                var lines = File.ReadAllLines(filePath).Skip(11); // Пропуск первых 11 строк

                foreach (var line in lines)
                {
                    if (string.IsNullOrWhiteSpace(line))
                        continue;

                    var columns = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                    if (columns.Length == 0)
                        continue;

                    // Проверка первого столбца
                    if (columns[0].ToLower() == "nan" || string.IsNullOrWhiteSpace(columns[0]))
                        continue;

                    // Создаем список для новых столбцов
                    List<string> newColumns = new List<string>();

                    // Добавляем столбцы, кроме первого
                    for (int i = 1; i < columns.Length; i++)
                    {
                        newColumns.Add(columns[i]);
                    }

                    processedLines.Add(newColumns.ToArray());
                }

                // Добавляем обработанные данные в словарь
                string fileName = System.IO.Path.GetFileName(filePath);

                // Проверяем, если файл уже загружен, добавляем суффикс
                string originalFileName = fileName;
                int duplicateCount = 1;
                while (txtFileData.ContainsKey(fileName))
                {
                    fileName = $"{System.IO.Path.GetFileNameWithoutExtension(originalFileName)}_{duplicateCount}{System.IO.Path.GetExtension(originalFileName)}";
                    duplicateCount++;
                }

                txtFileData[fileName] = processedLines;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Ошибка при обработке файла {filePath}:\n{ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        // Метод для заполнения TabControl вкладками для каждого .txt файла
        private void PopulateTxtTabControl()
        {
            FilesTabControl.Items.Clear();

            foreach (var kvp in txtFileData)
            {
                string fileName = kvp.Key;
                List<string[]> data = kvp.Value;

                // Создаем DataGrid для текущего файла
                DataGrid dataGrid = new DataGrid
                {
                    AutoGenerateColumns = true,
                    IsReadOnly = true
                };

                // Преобразуем данные в DataTable
                DataTable dataTable = ConvertToDataTable(data);
                dataGrid.ItemsSource = dataTable.DefaultView;

                // Создаем вкладку
                TabItem tabItem = new TabItem
                {
                    Header = fileName,
                    Content = dataGrid
                };

                FilesTabControl.Items.Add(tabItem);
            }
        }

        #endregion

        #region Обработчики загрузки файлов .magnete

        // Обработчик кнопки загрузки .magnete файлов
        private void LoadMagneteFiles_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog
            {
                Multiselect = true,
                Filter = "Magnete files (*.magnete)|*.magnete"
            };

            if (ofd.ShowDialog() == true)
            {
                foreach (var file in ofd.FileNames)
                {
                    ProcessMagneteFile(file);
                }

                MessageBox.Show("Файлы .magnete успешно загружены.", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        // Метод для обработки отдельного .magnete файла
        private void ProcessMagneteFile(string filePath)
        {
            List<string[]> processedLines = new List<string[]>();

            try
            {
                var lines = File.ReadAllLines(filePath); // Удалено .Skip(11) для обработки всех строк

                foreach (var line in lines)
                {
                    if (string.IsNullOrWhiteSpace(line))
                        continue;

                    var columns = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                    if (columns.Length == 0)
                        continue;

                    // Проверка первого столбца
                    if (columns[0].ToLower() == "nan" || string.IsNullOrWhiteSpace(columns[0]))
                        continue;

                    // Создаем список для новых столбцов
                    List<string> newColumns = new List<string>();

                    // Добавляем все столбцы (можно изменить диапазон, если нужно пропускать определённые)
                    for (int i = 0; i < columns.Length; i++)
                    {
                        newColumns.Add(columns[i]);
                    }

                    processedLines.Add(newColumns.ToArray());
                }

                // Добавляем обработанные данные в словарь
                string fileName = System.IO.Path.GetFileName(filePath);

                // Проверяем, если файл уже загружен, добавляем суффикс
                string originalFileName = fileName;
                int duplicateCount = 1;
                while (magneteFileData.ContainsKey(fileName))
                {
                    fileName = $"{System.IO.Path.GetFileNameWithoutExtension(originalFileName)}_{duplicateCount}{System.IO.Path.GetExtension(originalFileName)}";
                    duplicateCount++;
                }

                magneteFileData[fileName] = processedLines;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Ошибка при обработке файла {filePath}:\n{ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        #endregion

        #region Общие методы

        // Метод для преобразования списка строк в DataTable с нужными названиями столбцов
        private DataTable ConvertToDataTable(List<string[]> data)
        {
            DataTable dt = new DataTable();

            if (data == null || data.Count == 0)
                return dt;

            // Создание столбцов с заданными названиями
            dt.Columns.Add("№");
            dt.Columns.Add("BASE-1");
            dt.Columns.Add("Noise");
            dt.Columns.Add("Time");
            dt.Columns.Add("Time UTC");

            // Добавление строк
            foreach (var row in data)
            {
                DataRow dr = dt.NewRow();
                dr["№"] = row.Length > 0 ? row[0] : string.Empty;
                dr["BASE-1"] = row.Length > 1 ? row[1] : string.Empty;
                dr["Noise"] = row.Length > 2 ? row[2] : string.Empty;
                dr["Time"] = row.Length > 3 ? row[3] : string.Empty;
                dr["Time UTC"] = row.Length > 4 ? row[4] : string.Empty;
                dt.Rows.Add(dr);
            }

            return dt;
        }

        // Обработчик кнопки Объединить файлы (предполагается только для .txt файлов)
        private void CombineFiles_Click(object sender, RoutedEventArgs e)
        {
            if (!txtFileData.Any())
            {
                MessageBox.Show("Нет загруженных .txt файлов для объединения.", "Информация", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            List<string[]> combinedData = new List<string[]>();
            int lineNumber = 1;

            foreach (var file in txtFileData.Values)
            {
                foreach (var row in file)
                {
                    if (row.Length < 3)
                        continue; // Убедитесь, что есть минимум 3 столбца

                    // Извлечение необходимых столбцов
                    string secondColumn = row[1];
                    string thirdColumn = row.Length > 1 ? row[2] : string.Empty;
                    string fourthColumn = row.Length > 2 ? row[3] : "0"; // Если нет 4-го столбца, используем "0"

                    // Обработка пятого столбца
                    if (double.TryParse(fourthColumn, NumberStyles.Any, CultureInfo.InvariantCulture, out double number))
                    {
                        double divided = number / 24;
                        TimeSpan time = TimeSpan.FromDays(divided);
                        string timeString = time.ToString(@"hh\:mm\:ss");
                        combinedData.Add(new string[]
                        {
                            lineNumber.ToString(),
                            secondColumn,
                            thirdColumn,
                            fourthColumn,
                            timeString
                        });
                    }
                    else
                    {
                        combinedData.Add(new string[]
                        {
                            lineNumber.ToString(),
                            secondColumn,
                            thirdColumn,
                            fourthColumn,
                            "InvalidNumber"
                        });
                    }

                    lineNumber++;
                }
            }

            // Преобразуем объединённые данные в DataTable с нужными названиями столбцов
            combinedDataTable = ConvertToCombinedDataTable(combinedData);

            // Активируем кнопку редактирования
            EditExportButton.IsEnabled = true;

            MessageBox.Show("Файлы успешно объединены. Теперь вы можете отредактировать данные и экспортировать.", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);
        }

        // Метод для преобразования объединённых данных в DataTable с нужными названиями столбцов
        private DataTable ConvertToCombinedDataTable(List<string[]> data)
        {
            DataTable dt = new DataTable();

            if (data == null || data.Count == 0)
                return dt;

            // Создание столбцов согласно требованиям
            dt.Columns.Add("№");
            dt.Columns.Add("BASE-1");
            dt.Columns.Add("Noise");
            dt.Columns.Add("Time");
            dt.Columns.Add("Time UTC");

            // Добавление строк
            foreach (var row in data)
            {
                DataRow dr = dt.NewRow();
                dr["№"] = row.Length > 0 ? row[0] : string.Empty;
                dr["BASE-1"] = row.Length > 1 ? row[1] : string.Empty;
                dr["Noise"] = row.Length > 2 ? row[2] : string.Empty;
                dr["Time"] = row.Length > 3 ? row[3] : string.Empty;
                dr["Time UTC"] = row.Length > 4 ? row[4] : string.Empty;
                dt.Rows.Add(dr);
            }

            return dt;
        }

        // Обработчик кнопки Редактировать и Экспортировать
        private void EditAndExport_Click(object sender, RoutedEventArgs e)
        {
            if (combinedDataTable == null)
            {
                MessageBox.Show("Нет объединённых данных для редактирования.", "Информация", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            // Создаём и открываем окно редактирования
            EditWindow editWindow = new EditWindow(combinedDataTable);
            editWindow.Owner = this;
            editWindow.ShowDialog();

            // После закрытия EditWindow обновляем данные в TabControl (опционально)
            // Можно перезагрузить TabControl, чтобы отобразить отредактированные данные
            // В данном примере это не делается, но можно реализовать по желанию
        }

        #endregion

        #region Обработчик открытия окна просмотра .magnete файлов

        private void OpenMagneteView_Click(object sender, RoutedEventArgs e)
        {
            if (!magneteFileData.Any())
            {
                MessageBox.Show("Нет загруженных .magnete файлов для просмотра.", "Информация", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            // Создаём и открываем окно просмотра .magnete файлов
            MagneteWindow magneteWindow = new MagneteWindow(magneteFileData);
            magneteWindow.Owner = this;
            magneteWindow.Show();
        }

        #endregion
    }
}

<Window x:Class="TxtProcessorApp.EditWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TxtProcessorApp"
        Title="Редактирование данных" Height="600" Width="900">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!-- DataGrid для отображения и редактирования данных -->
        <DataGrid x:Name="EditDataGrid" Grid.Row="0" AutoGenerateColumns="True" 
                  CanUserAddRows="True" CanUserDeleteRows="True" 
                  IsReadOnly="False" 
                  SelectionUnit="CellOrRowHeader" 
                  SelectedCellsChanged="EditDataGrid_SelectedCellsChanged">
            <!-- Контекстное меню для вставки строк и столбцов -->
            <DataGrid.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Вставить строку выше" Click="InsertRowAbove_Click"/>
                    <MenuItem Header="Вставить строку ниже" Click="InsertRowBelow_Click"/>
                    <Separator/>
                    <MenuItem Header="Вставить столбец слева" Click="InsertColumnLeft_Click"/>
                    <MenuItem Header="Вставить столбец справа" Click="InsertColumnRight_Click"/>
                </ContextMenu>
            </DataGrid.ContextMenu>
        </DataGrid>

        <!-- Панель кнопок ниже DataGrid -->
        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,10,0,0">
            <!-- Элемент управления для выбора разделителя -->
            <ComboBox x:Name="DelimiterComboBox" Width="100" Height="30" SelectedIndex="0">
                <ComboBoxItem Content="Пробел"/>
                <ComboBoxItem Content="Табуляция"/>
                <ComboBoxItem Content="Запятая"/>
            </ComboBox>
            <Button Content="Экспортировать файл" Width="150" Height="30" Margin="10,0,0,0" Click="ExportButton_Click"/>
        </StackPanel>
    </Grid>
</Window>

using Microsoft.Win32;
using System;
using System.Data;
using System.Linq;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace TxtProcessorApp
{
    /// <summary>
    /// Логика взаимодействия для EditWindow.xaml
    /// </summary>
    public partial class EditWindow : Window
    {
        private DataTable _dataTable;

        // Словарь для сопоставления исходных заголовков с заголовками для экспорта
        private readonly Dictionary<string, string> exportHeaderMapping = new Dictionary<string, string>
        {
            { "No1", "№" },
            { "No2", "№" }
            // Добавьте другие сопоставления, если необходимо
        };

        public EditWindow(DataTable dataTable)
        {
            InitializeComponent();
            _dataTable = dataTable;
            EditDataGrid.ItemsSource = _dataTable.DefaultView;

            // Разрешаем добавление и удаление строк через интерфейс DataGrid
            EditDataGrid.CanUserAddRows = true;
            EditDataGrid.CanUserDeleteRows = true;
        }

        #region Вставка строк и столбцов

        // Вставка строки выше выбранной
        private void InsertRowAbove_Click(object sender, RoutedEventArgs e)
        {
            int rowIndex = GetSelectedRowIndex();
            if (rowIndex != -1)
            {
                _dataTable.Rows.InsertAt(_dataTable.NewRow(), rowIndex);
            }
            else
            {
                // Если ничего не выбрано, вставляем в конец
                _dataTable.Rows.Add(_dataTable.NewRow());
            }

            RefreshDataGrid();
        }

        // Вставка строки ниже выбранной
        private void InsertRowBelow_Click(object sender, RoutedEventArgs e)
        {
            int rowIndex = GetSelectedRowIndex();
            if (rowIndex != -1)
            {
                _dataTable.Rows.InsertAt(_dataTable.NewRow(), rowIndex + 1);
            }
            else
            {
                // Если ничего не выбрано, вставляем в конец
                _dataTable.Rows.Add(_dataTable.NewRow());
            }

            RefreshDataGrid();
        }

        // Вставка столбца слева от выбранного
        private void InsertColumnLeft_Click(object sender, RoutedEventArgs e)
        {
            int columnIndex = GetSelectedColumnIndex();
            InsertNewColumn(columnIndex, "NewColumn");
            RefreshDataGrid();
        }

        // Вставка столбца справа от выбранного
        private void InsertColumnRight_Click(object sender, RoutedEventArgs e)
        {
            int columnIndex = GetSelectedColumnIndex();
            InsertNewColumn(columnIndex + 1, "NewColumn");
            RefreshDataGrid();
        }

        // Добавление нового столбца в указанное место
        private void InsertNewColumn(int index, string defaultName)
        {
            // Предлагаем пользователю ввести имя нового столбца
            InputDialog inputDialog = new InputDialog("Введите имя нового столбца:", defaultName);
            if (inputDialog.ShowDialog() == true)
            {
                string columnName = inputDialog.ResponseText.Trim();
                if (string.IsNullOrEmpty(columnName))
                {
                    MessageBox.Show("Имя столбца не может быть пустым.", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }

                // Проверяем, нет ли уже столбца с таким именем
                if (_dataTable.Columns.Contains(columnName))
                {
                    MessageBox.Show("Столбец с таким именем уже существует.", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }

                // Создаём новый столбец
                DataColumn newColumn = new DataColumn(columnName, typeof(string));

                // Вставляем столбец в нужное место
                _dataTable.Columns.Add(newColumn);
                newColumn.SetOrdinal(Math.Min(index, _dataTable.Columns.Count - 1));

                // Устанавливаем значение по умолчанию для существующих строк
                foreach (DataRow row in _dataTable.Rows)
                {
                    row[newColumn] = string.Empty;
                }
            }
            else
            {
                // Если пользователь отменил ввод, автоматически генерируем уникальное имя
                string autoColumnName = GenerateUniqueColumnName("NewColumn");
                DataColumn newColumn = new DataColumn(autoColumnName, typeof(string));
                _dataTable.Columns.Add(newColumn);
                newColumn.SetOrdinal(Math.Min(index, _dataTable.Columns.Count - 1));

                foreach (DataRow row in _dataTable.Rows)
                {
                    row[newColumn] = string.Empty;
                }
            }
        }

        // Генерация уникального имени столбца
        private string GenerateUniqueColumnName(string baseName)
        {
            int suffix = 1;
            string newName = baseName;
            while (_dataTable.Columns.Contains(newName))
            {
                newName = $"{baseName}_{suffix}";
                suffix++;
            }
            return newName;
        }

        // Получение индекса выбранной строки
        private int GetSelectedRowIndex()
        {
            if (EditDataGrid.SelectedItem is DataRowView selectedRow)
            {
                return _dataTable.Rows.IndexOf(selectedRow.Row);
            }
            return -1;
        }

        // Получение индекса выбранного столбца
        private int GetSelectedColumnIndex()
        {
            if (EditDataGrid.SelectedCells.Count > 0)
            {
                var selectedCell = EditDataGrid.SelectedCells[0];
                var column = selectedCell.Column;
                if (column != null)
                {
                    return column.DisplayIndex;
                }
            }
            return -1;
        }

        #endregion

        // Метод для обновления привязки данных к DataGrid
        private void RefreshDataGrid()
        {
            // Получаем текущий view
            var view = CollectionViewSource.GetDefaultView(EditDataGrid.ItemsSource);

            if (view != null)
            {
                view.Refresh();
            }
        }

        // Обработчик изменения выбранных ячеек (опционально)
        private void EditDataGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
        {
            // Дополнительная логика при изменении выбора (если требуется)
        }

        // Обработчик кнопки Экспорта файла
        private void ExportButton_Click(object sender, RoutedEventArgs e)
        {
            // Выбор разделителя
            string delimiter = "\t";

            SaveFileDialog sfd = new SaveFileDialog
            {
                Filter = "Text files (*.txt)|*.txt",
                FileName = "EditedCombinedMagneteFile.txt"
            };

            if (sfd.ShowDialog() == true)
            {
                try
                {
                    using (StreamWriter sw = new StreamWriter(sfd.FileName, false, Encoding.UTF8))
                    {
                        // Запись заголовков столбцов с заменой
                        string[] headers = _dataTable.Columns
                            .Cast<DataColumn>()
                            .Select(column =>
                            {
                                if (exportHeaderMapping.ContainsKey(column.ColumnName))
                                    return exportHeaderMapping[column.ColumnName];
                                else
                                    return column.ColumnName;
                            })
                            .ToArray();
                        sw.WriteLine(string.Join(delimiter, headers));

                        // Запись данных строк
                        foreach (DataRow row in _dataTable.Rows)
                        {
                            string[] fields = new string[_dataTable.Columns.Count];
                            for (int i = 0; i < _dataTable.Columns.Count; i++)
                            {
                                string field = row[i].ToString();
                                // Если поле содержит разделитель, обрамляем его в кавычки
                                if (field.Contains(delimiter))
                                {
                                    field = $"\"{field}\"";
                                }
                                fields[i] = field;
                            }
                            sw.WriteLine(string.Join(delimiter, fields));
                        }
                    }

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

<Window x:Class="TxtProcessorApp.InputDialog"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Ввод" Height="150" Width="400" WindowStartupLocation="CenterOwner" ResizeMode="NoResize">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <TextBlock x:Name="PromptTextBlock" Grid.Row="0" Text="Prompt:" Margin="0,0,0,10" TextWrapping="Wrap"/>

        <TextBox x:Name="ResponseTextBox" Grid.Row="1" Height="25"/>

        <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
            <Button Content="OK" Width="75" Margin="0,0,10,0" IsDefault="True" Click="OK_Click"/>
            <Button Content="Отмена" Width="75" IsCancel="True"/>
        </StackPanel>
    </Grid>
</Window>

using System.Windows;

namespace TxtProcessorApp
{
    /// <summary>
    /// Логика взаимодействия для InputDialog.xaml
    /// </summary>
    public partial class InputDialog : Window
    {
        public string ResponseText { get; private set; }

        public InputDialog(string question, string defaultAnswer = "")
        {
            InitializeComponent();
            PromptTextBlock.Text = question;
            ResponseTextBox.Text = defaultAnswer;
            ResponseTextBox.SelectAll();
            ResponseTextBox.Focus();
        }

        private void OK_Click(object sender, RoutedEventArgs e)
        {
            ResponseText = ResponseTextBox.Text;
            DialogResult = true;
        }
    }
}