у меня все плохо работает проанализируй код и исправь все неисправности как и технические так и логические

<!-- GraphWindow.xaml -->
<Window x:Class="TxtProcessorApp.GraphWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:oxy="http://oxyplot.org/wpf"
        Title="GraphWindow" Height="800" Width="1000">
    <DockPanel>
        <!-- Верхнее меню -->
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="Действия">
                <MenuItem Header="Сбросить масштаб" Click="ResetZoomMenuItem_Click"/>
                <MenuItem Header="Сбросить масштаб по Y" Click="ResetYZoomMenuItem_Click"/>
                <MenuItem Header="Сбросить масштаб по X" Click="ResetXZoomMenuItem_Click"/>
            </MenuItem>
        </Menu>

        <!-- Панель управления -->
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" Margin="10">
            <Label Content="Выберите графики:" VerticalAlignment="Center" Margin="0,0,5,0"/>
            <!-- Используем ListBox с возможностью множественного выбора -->
            <ListBox x:Name="ChartSelector" Width="300" SelectionMode="Multiple" SelectionChanged="ChartSelector_SelectionChanged">
                <ListBoxItem Content=".txt Time UTC vs BASE-1"/>
                <ListBoxItem Content=".txt Time UTC vs Noise"/>
                <ListBoxItem Content=".magnete Долгота UTM vs Широта UTM (Альтитуда)"/>
                <ListBoxItem Content=".magnete Долгота UTM vs Широта UTM (Shark)"/>
                <ListBoxItem Content=".magnete Time UTC vs Shark"/>
            </ListBox>
            <Button x:Name="UndoButton" Content="Отменить" Width="100" Click="UndoButton_Click" IsEnabled="False" Margin="10,0,0,0"/>
            <Button x:Name="RedoButton" Content="Повторить" Width="100" Click="RedoButton_Click" IsEnabled="False" Margin="5,0,0,0"/>
            <Button Content="Сохранить" Width="100" Click="SaveButton_Click" Margin="10,0,0,0"/>
            <Button Content="Отмена" Width="100" Click="CancelButton_Click" Margin="5,0,0,0"/>
            <!-- Добавляем ToggleButton для переключения режима -->
            <ToggleButton x:Name="EditModeToggleButton" Content="Режим редактирования" Width="150" Margin="10,0,0,0" Checked="EditModeToggleButton_Checked" Unchecked="EditModeToggleButton_Unchecked"/>
        </StackPanel>

        <!-- Область для отображения графиков -->
        <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
            <!-- Используем UniformGrid для размещения графиков -->
            <UniformGrid x:Name="PlotGrid" Margin="10"/>
        </ScrollViewer>
    </DockPanel>
</Window>


// GraphWindow.xaml.cs
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Series;
using OxyPlot.Annotations;
using OxyPlot.Wpf;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.IO;

namespace TxtProcessorApp
{
    /// <summary>
    /// Окно для отображения и взаимодействия с графиками.
    /// </summary>
    public partial class GraphWindow : Window
    {
        // Словарь для хранения фабрик PlotInfo
        private readonly Dictionary<string, Func<PlotInfo>> plotModelFactories = new();

        // Хранит текущие отображаемые графики
        private List<PlotInfo> currentPlotInfos = new();

        private readonly Stack<IUndoRedoAction> undoStack = new();
        private readonly Stack<IUndoRedoAction> redoStack = new();

        private ScreenPoint? selectionStartPoint;
        private RectangleAnnotation selectionRectangle;

        private readonly List<(ScatterSeries Series, ScatterPoint Point, int Index, DataRow DataRow, List<DataRow> DataRows)> selectedPoints = new();

        private readonly Dictionary<PlotModel, List<PointAnnotation>> selectedPointAnnotations = new();

        private readonly DataTable txtDataCopy;
        private readonly DataTable magneteDataCopy;


        public DataTable TxtDataCopy { get; set; }
        public DataTable MagneteDataCopy { get; set; }

        public GraphWindow(DataTable txtData, DataTable magneteData)
        {
            InitializeComponent();

            // Создание копий для работы
            txtDataCopy = txtData.Copy();
            magneteDataCopy = magneteData.Copy();

            InitializePlotModelFactories();

            // Настройка селектора графиков
            ChartSelector.SelectionChanged -= ChartSelector_SelectionChanged;
            ChartSelector.SelectedItems.Add(ChartSelector.Items[0]);
            ChartSelector.SelectedItems.Add(ChartSelector.Items[1]);
            ChartSelector.SelectionChanged += ChartSelector_SelectionChanged;

            UpdatePlots();
            UpdateUndoRedoButtons();

            Loaded += (s, e) => Keyboard.Focus(this);
        }



        /// <summary>
        /// Инициализирует фабрики для создания графиков.
        /// </summary>
        private void InitializePlotModelFactories()
        {
            plotModelFactories[".txt Time UTC vs BASE-1"] = () => CreateTxtPlotModel("BASE-1");
            plotModelFactories[".txt Time UTC vs Noise"] = () => CreateTxtPlotModel("Noise");
            plotModelFactories[".magnete Долгота UTM vs Широта UTM (Альтитуда)"] = () => CreateMagnetePlotModel("Альтитуда");
            plotModelFactories[".magnete Долгота UTM vs Широта UTM (Shark)"] = () => CreateMagnetePlotModel("Shark");
            plotModelFactories[".magnete Time UTC vs Shark"] = CreateMagneteTimeSharkPlotModel;
        }

        /// <summary>
        /// Класс для хранения информации о графике.
        /// </summary>
        private class PlotInfo
        {
            public PlotModel Model { get; set; }
            public Dictionary<ScatterSeries, List<DataRow>> SeriesDataRows { get; set; } = new();
        }

        private PlotInfo CreateTxtPlotModel(string valueColumn)
        {
            string title = $".txt Time UTC vs {valueColumn}";
            var model = new PlotModel { Title = title };
            var xAxis = new DateTimeAxis
            {
                Position = AxisPosition.Bottom,
                StringFormat = "HH:mm:ss",
                Title = "Time UTC",
                IntervalType = DateTimeIntervalType.Hours,
                MinorIntervalType = DateTimeIntervalType.Minutes
            };
            var yAxis = new LinearAxis
            {
                Position = AxisPosition.Left,
                Title = valueColumn
            };
            model.Axes.Add(xAxis);
            model.Axes.Add(yAxis);

            var series = new ScatterSeries
            {
                Title = valueColumn,
                MarkerType = MarkerType.Circle,
                MarkerSize = 5,
                TrackerFormatString = $"Time UTC: {{2:HH:mm:ss}}\n{valueColumn}: {{4}}"
            };

            var dataRows = new List<DataRow>();

            foreach (DataRow row in txtDataCopy.Rows)
            {
                if (row.RowState == DataRowState.Deleted)
                    continue;

                if (DateTime.TryParse(row["Time UTC"].ToString(), out DateTime timeUtc) &&
                    double.TryParse(row[valueColumn].ToString(), out double value))
                {
                    double timeInDouble = DateTimeAxis.ToDouble(timeUtc);
                    series.Points.Add(new ScatterPoint(timeInDouble, value));
                    dataRows.Add(row);
                }
            }

            model.Series.Add(series);

            var plotInfo = new PlotInfo { Model = model };
            plotInfo.SeriesDataRows[series] = dataRows;
            return plotInfo;
        }
        private PlotInfo CreateMagnetePlotModel(string valueColumn)
        {
            string title = $".magnete Долгота UTM vs Широта UTM ({valueColumn})";
            var model = new PlotModel { Title = title };

            // Добавление осей
            var xAxis = new LinearAxis { Position = AxisPosition.Bottom, Title = "Долгота UTM" };
            var yAxis = new LinearAxis { Position = AxisPosition.Left, Title = "Широта UTM" };
            model.Axes.Add(xAxis);
            model.Axes.Add(yAxis);

            var series = new ScatterSeries
            {
                Title = valueColumn,
                MarkerType = MarkerType.Circle,
                MarkerSize = 5,
                TrackerFormatString = $"Долгота UTM: {{2}}\nШирота UTM: {{4}}\n{valueColumn}: {{6}}"
            };

            var dataRows = new List<DataRow>();

            foreach (DataRow row in magneteDataCopy.Rows)
            {
                if (row.RowState == DataRowState.Deleted)
                    continue;

                if (magneteDataCopy.Columns.Contains("Долгота UTM") &&
                    magneteDataCopy.Columns.Contains("Широта UTM") &&
                    double.TryParse(row["Долгота UTM"].ToString(), out double longitudeUTM) &&
                    double.TryParse(row["Широта UTM"].ToString(), out double latitudeUTM) &&
                    double.TryParse(row[valueColumn].ToString(), out double value))
                {
                    series.Points.Add(new ScatterPoint(longitudeUTM, latitudeUTM, 5, value));
                    dataRows.Add(row);
                }
            }

            model.Series.Add(series);

            var plotInfo = new PlotInfo { Model = model };
            plotInfo.SeriesDataRows[series] = dataRows;
            return plotInfo;
        }

        private PlotInfo CreateMagneteTimeSharkPlotModel()
        {
            string title = ".magnete Time UTC vs Shark";
            var model = new PlotModel { Title = title };

            // Настройка осей
            var xAxis = new DateTimeAxis
            {
                Position = AxisPosition.Bottom,
                StringFormat = "HH:mm:ss",
                Title = "Time UTC",
                IntervalType = DateTimeIntervalType.Hours,
                MinorIntervalType = DateTimeIntervalType.Minutes
            };
            var yAxis = new LinearAxis
            {
                Position = AxisPosition.Left,
                Title = "Shark"
            };
            model.Axes.Add(xAxis);
            model.Axes.Add(yAxis);

            // Настройка серии
            var series = new ScatterSeries
            {
                Title = "Shark",
                MarkerType = MarkerType.Circle,
                MarkerSize = 5,
                TrackerFormatString = $"Time UTC: {{2:HH:mm:ss}}\nShark: {{4}}"
            };

            var dataRows = new List<DataRow>();

            // Обработка строк таблицы magneteDataCopy
            foreach (DataRow row in magneteDataCopy.Rows)
            {
                if (row.RowState == DataRowState.Deleted)
                    continue;

                if (magneteDataCopy.Columns.Contains("TIME UTC") &&
                    magneteDataCopy.Columns.Contains("Shark") &&
                    DateTime.TryParse(row["TIME UTC"].ToString(), out DateTime timeUtc) &&
                    double.TryParse(row["Shark"].ToString(), out double shark))
                {
                    double timeInDouble = DateTimeAxis.ToDouble(timeUtc);
                    series.Points.Add(new ScatterPoint(timeInDouble, shark));
                    dataRows.Add(row);
                }
            }

            model.Series.Add(series);

            var plotInfo = new PlotInfo { Model = model };
            plotInfo.SeriesDataRows[series] = dataRows;

            return plotInfo;
        }



        private void SetEqualAxisScaling(LinearAxis xAxis, LinearAxis yAxis, IEnumerable<DataRow> dataRows, string xColumn, string yColumn)
        {
            try
            {
                double minX = dataRows.Min(row => Convert.ToDouble(row[xColumn]));
                double maxX = dataRows.Max(row => Convert.ToDouble(row[xColumn]));
                double minY = dataRows.Min(row => Convert.ToDouble(row[yColumn]));
                double maxY = dataRows.Max(row => Convert.ToDouble(row[yColumn]));

                double maxRange = Math.Max(maxX - minX, maxY - minY);
                double centerX = (minX + maxX) / 2;
                double centerY = (minY + maxY) / 2;

                xAxis.Minimum = centerX - maxRange / 2;
                xAxis.Maximum = centerX + maxRange / 2;
                yAxis.Minimum = centerY - maxRange / 2;
                yAxis.Maximum = centerY + maxRange / 2;

                xAxis.MajorStep = yAxis.MajorStep = maxRange / 10;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Ошибка при установке масштаба осей: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void ChartSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            UpdatePlots();
        }

        private void UpdatePlots()
        {
            PlotGrid.Children.Clear();
            currentPlotInfos.Clear();
            selectedPointAnnotations.Clear();
            selectedPoints.Clear();

            var selectedTitles = ChartSelector.SelectedItems.Cast<ListBoxItem>().Select(item => item.Content.ToString()).ToList();

            if (selectedTitles.Count == 0)
                return;

            int columns = (int)Math.Ceiling(Math.Sqrt(selectedTitles.Count));
            int rows = (int)Math.Ceiling((double)selectedTitles.Count / columns);

            PlotGrid.Rows = rows;
            PlotGrid.Columns = columns;

            foreach (var title in selectedTitles)
            {
                if (plotModelFactories.TryGetValue(title, out var factory))
                {
                    var plotInfo = factory();
                    currentPlotInfos.Add(plotInfo);

                    var plotView = new PlotView
                    {
                        Model = plotInfo.Model,
                        Margin = new Thickness(5),
                        Focusable = true
                    };

                    var controller = new PlotController();

                    if (EditModeToggleButton.IsChecked == true)
                    {
                        controller.UnbindAll();
                    }

                    plotView.Controller = controller;

                    plotView.MouseDown += PlotView_MouseDown;
                    plotView.MouseMove += PlotView_MouseMove;
                    plotView.MouseUp += PlotView_MouseUp;
                    plotView.KeyDown += PlotView_KeyDown;

                    selectedPointAnnotations[plotInfo.Model] = new List<PointAnnotation>();

                    PlotGrid.Children.Add(plotView);
                }
            }
        }

        private void PlotView_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (sender is not PlotView plotView)
                return;

            plotView.Focus();

            if (EditModeToggleButton.IsChecked == true && e.LeftButton == MouseButtonState.Pressed)
            {
                var mousePos = e.GetPosition(plotView);
                selectionStartPoint = new ScreenPoint(mousePos.X, mousePos.Y);

                selectionRectangle = new RectangleAnnotation
                {
                    Stroke = OxyColors.Blue,
                    StrokeThickness = 1,
                    Fill = OxyColor.FromAColor(40, OxyColors.SkyBlue),
                    Layer = AnnotationLayer.AboveSeries
                };
                plotView.Model.Annotations.Add(selectionRectangle);

                plotView.CaptureMouse();
                e.Handled = true;
            }
        }

        private void PlotView_MouseMove(object sender, MouseEventArgs e)
        {
            if (sender is not PlotView plotView || selectionStartPoint == null || selectionRectangle == null)
                return;

            var mousePos = e.GetPosition(plotView);
            var currentPoint = new ScreenPoint(mousePos.X, mousePos.Y);

            selectionRectangle.MinimumX = Math.Min(selectionStartPoint.Value.X, currentPoint.X);
            selectionRectangle.MaximumX = Math.Max(selectionStartPoint.Value.X, currentPoint.X);
            selectionRectangle.MinimumY = Math.Min(selectionStartPoint.Value.Y, currentPoint.Y);
            selectionRectangle.MaximumY = Math.Max(selectionStartPoint.Value.Y, currentPoint.Y);

            plotView.Model.InvalidatePlot(false);
            e.Handled = true;
        }

        private void PlotView_MouseUp(object sender, MouseButtonEventArgs e)
        {
            if (sender is not PlotView plotView || selectionStartPoint == null || selectionRectangle == null)
                return;

            if (EditModeToggleButton.IsChecked == true && e.LeftButton == MouseButtonState.Released)
            {
                var mousePos = e.GetPosition(plotView);

                var xAxis = plotView.Model.Axes.FirstOrDefault(a => a.IsHorizontal());
                var yAxis = plotView.Model.Axes.FirstOrDefault(a => a.IsVertical());

                if (xAxis != null && yAxis != null)
                {
                    double x0 = xAxis.InverseTransform(selectionRectangle.MinimumX);
                    double x1 = xAxis.InverseTransform(selectionRectangle.MaximumX);
                    double y0 = yAxis.InverseTransform(selectionRectangle.MinimumY);
                    double y1 = yAxis.InverseTransform(selectionRectangle.MaximumY);

                    double minX = Math.Min(x0, x1);
                    double maxX = Math.Max(x0, x1);
                    double minY = Math.Min(y0, y1);
                    double maxY = Math.Max(y0, y1);

                    selectedPoints.Clear();

                    var annotations = selectedPointAnnotations[plotView.Model];
                    foreach (var annotation in annotations)
                    {
                        plotView.Model.Annotations.Remove(annotation);
                    }
                    annotations.Clear();

                    var plotInfo = currentPlotInfos.FirstOrDefault(pi => pi.Model == plotView.Model);
                    if (plotInfo != null)
                    {
                        foreach (var series in plotView.Model.Series.OfType<ScatterSeries>())
                        {
                            var dataRows = plotInfo.SeriesDataRows[series];

                            for (int i = 0; i < series.Points.Count; i++)
                            {
                                var point = series.Points[i];
                                if (point.X >= minX && point.X <= maxX && point.Y >= minY && point.Y <= maxY)
                                {
                                    selectedPoints.Add((series, point, i, dataRows[i], dataRows));

                                    var pointAnnotation = new PointAnnotation
                                    {
                                        X = point.X,
                                        Y = point.Y,
                                        Shape = MarkerType.Circle,
                                        Fill = OxyColors.Red,
                                        Stroke = OxyColors.Transparent,
                                        Size = 7
                                    };
                                    plotView.Model.Annotations.Add(pointAnnotation);
                                    annotations.Add(pointAnnotation);
                                }
                            }
                        }
                    }

                    plotView.Model.Annotations.Remove(selectionRectangle);
                    selectionRectangle = null;
                    selectionStartPoint = null;

                    plotView.ReleaseMouseCapture();
                    plotView.Model.InvalidatePlot(false);
                    e.Handled = true;
                }
            }
        }

        private void PlotView_KeyDown(object sender, KeyEventArgs e)
        {
            if (EditModeToggleButton.IsChecked == true && e.Key == Key.Delete && selectedPoints.Any())
            {
                DeleteSelectedPoints();
                e.Handled = true;
            }
        }

        private void DeleteSelectedPoints()
        {
            if (selectedPoints.Any())
            {
                var result = MessageBox.Show($"Вы уверены, что хотите удалить выбранные точки ({selectedPoints.Count})?", "Подтверждение удаления", MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (result == MessageBoxResult.Yes)
                {
                    var seriesGroups = selectedPoints.GroupBy(sp => sp.Series);

                    foreach (var group in seriesGroups)
                    {
                        var scatterSeries = group.Key;
                        var pointsToRemove = group.OrderByDescending(sp => sp.Index).ToList();

                        foreach (var sp in pointsToRemove)
                        {
                            sp.DataRow.Delete(); // Удаление строки из копии таблицы
                            scatterSeries.Points.RemoveAt(sp.Index);
                            sp.DataRows.RemoveAt(sp.Index);
                        }

                        scatterSeries.PlotModel.InvalidatePlot(true);

                        undoStack.Push(new MultiPointGraphAction(pointsToRemove));
                        redoStack.Clear();
                    }

                    UpdateUndoRedoButtons();
                }

                selectedPoints.Clear();
            }
        }




        private void UndoButton_Click(object sender, RoutedEventArgs e)
        {
            if (undoStack.Any())
            {
                var action = undoStack.Pop();
                action.Undo();

                redoStack.Push(action);

                UpdatePlots(); // Перестраиваем графики
                UpdateUndoRedoButtons();
            }
        }

        private void RedoButton_Click(object sender, RoutedEventArgs e)
        {
            if (redoStack.Any())
            {
                var action = redoStack.Pop();
                action.Redo();

                undoStack.Push(action);

                UpdatePlots(); // Перестраиваем графики
                UpdateUndoRedoButtons();
            }
        }



        private void SynchronizeDataRowWithGraph(DataRow dataRow, ScatterPoint point)
        {
            try
            {
                // Здесь замените "X", "Y" на реальные названия колонок таблицы
                if (dataRow.Table.Columns.Contains("X") && dataRow.Table.Columns.Contains("Y"))
                {
                    dataRow.BeginEdit();
                    dataRow["X"] = point.X;
                    dataRow["Y"] = point.Y;
                    dataRow.EndEdit();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Ошибка синхронизации данных: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }








        private void SynchronizeDataTable(ScatterSeries series, DataRow dataRow)
        {
            try
            {
                if (dataRow.RowState == DataRowState.Deleted)
                {
                    dataRow.RejectChanges(); // Отменяем удаление, если строка была помечена как удалённая
                }

                // Получаем информацию о графике
                var plotInfo = currentPlotInfos
                    .FirstOrDefault(info => info.SeriesDataRows.ContainsKey(series));

                if (plotInfo != null && plotInfo.SeriesDataRows.TryGetValue(series, out var dataRows))
                {
                    // Находим индекс строки в списке
                    int index = dataRows.IndexOf(dataRow);
                    if (index >= 0 && index < series.Points.Count)
                    {
                        // Получаем точку графика
                        var point = series.Points[index];

                        // Обновляем все столбцы строки
                        foreach (DataColumn column in dataRow.Table.Columns)
                        {
                            string columnName = column.ColumnName;

                            if (columnName.Equals("X", StringComparison.OrdinalIgnoreCase))
                                dataRow[columnName] = point.X;
                            else if (columnName.Equals("Y", StringComparison.OrdinalIgnoreCase))
                                dataRow[columnName] = point.Y;
                            else if (columnName.Equals("Value", StringComparison.OrdinalIgnoreCase))
                                dataRow[columnName] = point.Value; // Если используется размер или цвет
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Ошибка синхронизации данных: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }











        private void SaveButton_Click(object sender, RoutedEventArgs e)
        {
            var result = MessageBox.Show("Вы уверены, что хотите сохранить изменения?", "Подтверждение сохранения", MessageBoxButton.YesNo, MessageBoxImage.Question);
            if (result == MessageBoxResult.Yes)
            {
                try
                {
                    // Проверяем, что копии таблиц не null
                    if (txtDataCopy == null || magneteDataCopy == null)
                    {
                        throw new NullReferenceException("Копии таблиц данных не инициализированы.");
                    }

                    // Сохраняем изменения из копий в оригинальные таблицы
                    if (TxtDataCopy != null)
                    {
                        TxtDataCopy.Clear();
                        foreach (DataRow row in txtDataCopy.Rows)
                        {
                            TxtDataCopy.ImportRow(row);
                        }
                    }

                    if (MagneteDataCopy != null)
                    {
                        MagneteDataCopy.Clear();
                        foreach (DataRow row in magneteDataCopy.Rows)
                        {
                            MagneteDataCopy.ImportRow(row);
                        }
                    }

                    // Сохраняем в файлы, если это требуется
                    SaveDataTableToFile(TxtDataCopy, "txtDataFile.csv");
                    SaveDataTableToFile(MagneteDataCopy, "magneteDataFile.csv");

                    MessageBox.Show("Изменения успешно сохранены.", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);

                    DialogResult = true;
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"Ошибка при сохранении данных: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        private void SaveDataTableToFile(DataTable table, string filePath)
        {
            if (table == null)
            {
                MessageBox.Show($"Ошибка: Таблица данных не инициализирована. Невозможно сохранить файл {filePath}.", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            try
            {
                using (var writer = new StreamWriter(filePath))
                {
                    // Запись заголовков
                    for (int i = 0; i < table.Columns.Count; i++)
                    {
                        writer.Write(table.Columns[i].ColumnName);
                        if (i < table.Columns.Count - 1)
                            writer.Write(",");
                    }
                    writer.WriteLine();

                    // Запись строк
                    foreach (DataRow row in table.Rows)
                    {
                        for (int i = 0; i < table.Columns.Count; i++)
                        {
                            writer.Write(row[i]?.ToString() ?? string.Empty); // Проверка на null
                            if (i < table.Columns.Count - 1)
                                writer.Write(",");
                        }
                        writer.WriteLine();
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Ошибка при сохранении файла {filePath}: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }







        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            var result = MessageBox.Show("Вы уверены, что хотите отменить все изменения?", "Подтверждение отмены", MessageBoxButton.YesNo, MessageBoxImage.Question);
            if (result == MessageBoxResult.Yes)
            {
                txtDataCopy.RejectChanges();
                magneteDataCopy.RejectChanges();
                DialogResult = false;
            }
        }

        private void ResetZoomMenuItem_Click(object sender, RoutedEventArgs e)
        {
            foreach (var plotView in PlotGrid.Children.OfType<PlotView>())
            {
                if (plotView.Model != null)
                {
                    foreach (var axis in plotView.Model.Axes)
                    {
                        axis.Reset();
                    }
                    plotView.Model.InvalidatePlot(false);
                }
            }
        }

        private void ResetYZoomMenuItem_Click(object sender, RoutedEventArgs e)
        {
            foreach (var plotView in PlotGrid.Children.OfType<PlotView>())
            {
                if (plotView.Model != null)
                {
                    foreach (var axis in plotView.Model.Axes.Where(a => a.IsVertical()))
                    {
                        axis.Reset();
                    }
                    plotView.Model.InvalidatePlot(false);
                }
            }
        }

        private void ResetXZoomMenuItem_Click(object sender, RoutedEventArgs e)
        {
            foreach (var plotView in PlotGrid.Children.OfType<PlotView>())
            {
                if (plotView.Model != null)
                {
                    foreach (var axis in plotView.Model.Axes.Where(a => a.IsHorizontal()))
                    {
                        axis.Reset();
                    }
                    plotView.Model.InvalidatePlot(false);
                }
            }
        }

        private void UpdateUndoRedoButtons()
        {
            UndoButton.IsEnabled = undoStack.Any();
            RedoButton.IsEnabled = redoStack.Any();
        }

        private void EditModeToggleButton_Checked(object sender, RoutedEventArgs e)
        {
            UpdatePlots();
        }

        private void EditModeToggleButton_Unchecked(object sender, RoutedEventArgs e)
        {
            UpdatePlots();
        }

        // Интерфейс для действий Undo/Redo
        private interface IUndoRedoAction
        {
            void Undo();
            void Redo();
        }

        // Класс для действий с множественными точками
        // Класс для действий с множественными точками
        // Класс для действий с множественными точками
        // Класс для Undo/Redo действий с несколькими точками
        private class MultiPointGraphAction : IUndoRedoAction
        {
            public List<(ScatterSeries Series, ScatterPoint Point, int Index, DataRow DataRow, List<DataRow> DataRows)> Points { get; }

            public MultiPointGraphAction(List<(ScatterSeries Series, ScatterPoint Point, int Index, DataRow DataRow, List<DataRow> DataRows)> points)
            {
                Points = points ?? throw new ArgumentNullException(nameof(points));
            }

            public void Undo()
            {
                foreach (var (series, point, index, dataRow, dataRows) in Points.OrderBy(p => p.Index))
                {
                    dataRow.RejectChanges(); // Восстанавливаем строку
                    series.Points.Insert(index, point); // Вставляем точку обратно в график
                    dataRows.Insert(index, dataRow); // Вставляем строку обратно в список данных
                    series.PlotModel.InvalidatePlot(true); // Обновляем график
                }
            }

            public void Redo()
            {
                foreach (var (series, point, index, dataRow, dataRows) in Points.OrderByDescending(p => p.Index))
                {
                    dataRow.Delete(); // Удаляем строку
                    series.Points.RemoveAt(index); // Удаляем точку из графика
                    dataRows.RemoveAt(index); // Удаляем строку из списка данных
                    series.PlotModel.InvalidatePlot(true); // Обновляем график
                }
            }

            public void InvalidatePlots()
            {
                var seriesSet = Points.Select(p => p.Series).Distinct();
                foreach (var series in seriesSet)
                {
                    series.PlotModel.InvalidatePlot(true); // Обновляем все графики, где есть изменения
                }
            }
        }




    }
}



// GraphAction.cs
using OxyPlot.Series;
using System.Data;

namespace TxtProcessorApp
{
    /// <summary>
    /// Класс, представляющий действие на графике для реализации Undo и Redo.
    /// </summary>
    public class GraphAction : IUndoRedoAction
    {
        public ScatterSeries Series { get; }
        public ScatterPoint Point { get; }
        public int PointIndex { get; }
        public DataRow DataRow { get; }

        public GraphAction(ScatterSeries series, ScatterPoint point, int pointIndex, DataRow dataRow)
        {
            Series = series;
            Point = point;
            PointIndex = pointIndex;
            DataRow = dataRow;
        }

        public void Undo()
        {
            if (DataRow.RowState == DataRowState.Deleted)
                DataRow.RejectChanges();

            Series.Points.Insert(PointIndex, Point);
        }

        public void Redo()
        {
            Series.Points.RemoveAt(PointIndex);

            if (DataRow.RowState != DataRowState.Deleted)
                DataRow.Delete();
        }
    }
}