исправь ошибки

<Window x:Class="ContourMapVisualizer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ContourMapVisualizer"
        Title="Contour Map Visualizer" Height="700" Width="1200" 
        Icon="/Screenshot 2024-11-06 152658.png" 
        ResizeMode="CanResizeWithGrip" 
        WindowStyle="SingleBorderWindow"
        AllowDrop="True"
        Drop="Window_Drop"
        DragOver="Window_DragOver">

    <Window.Resources>
        <DataTemplate x:Key="ContourMapTemplate">
            <Border BorderBrush="Gray" BorderThickness="1" Margin="5" Padding="5" CornerRadius="5">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>

                    <!-- Заголовок и кнопка удаления -->
                    <StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center">
                        <TextBlock Text="{Binding FileName}" FontWeight="Bold" FontSize="14" VerticalAlignment="Center"/>
                    </StackPanel>
                    <Button Content="×" 
                            Width="25" 
                            Height="25" 
                            Grid.Row="0" Grid.Column="1"
                            HorizontalAlignment="Right" 
                            VerticalAlignment="Top" 
                            Background="Transparent" 
                            BorderBrush="Transparent" 
                            Foreground="Red" 
                            FontWeight="Bold"
                            ToolTip="Удалить карту"
                            Click="RemoveMapButton_Click" 
                            Margin="0,0,0,0"/>

                    <!-- Изображение с возможностью масштабирования и перемещения -->
                    <ScrollViewer Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,5,0,5">
                        <Grid>
                            <Image Source="{Binding ContourBitmap}" Stretch="Uniform" />
                            <Canvas x:Name="ZoomCanvas" Background="Transparent">
                                <!-- Дополнительные элементы управления для Zoom/Pan можно добавить здесь -->
                            </Canvas>
                        </Grid>
                    </ScrollViewer>

                    <!-- Панель управления диапазоном -->
                    <StackPanel Grid.Row="2" Grid.Column="0" Orientation="Horizontal" Margin="0,5,0,0" HorizontalAlignment="Center">
                        <TextBlock Text="Min:" VerticalAlignment="Center" Margin="0,0,5,0"/>
                        <TextBox Text="{Binding MinValue, UpdateSourceTrigger=PropertyChanged}" Width="60" Margin="0,0,10,0" />
                        <TextBlock Text="Max:" VerticalAlignment="Center" Margin="0,0,5,0"/>
                        <TextBox Text="{Binding MaxValue, UpdateSourceTrigger=PropertyChanged}" Width="60" Margin="0,0,10,0"/>
                        <Button Content="Auto Range" Click="AutoRangeButton_Click" Tag="{Binding}" Margin="0,0,10,0" ToolTip="Автоматически определить диапазон"/>
                        <Button Content="Save Image" Click="SaveImageButton_Click" Tag="{Binding}" ToolTip="Сохранить изображение"/>
                    </StackPanel>
                </Grid>
            </Border>
        </DataTemplate>
    </Window.Resources>

    <DockPanel>
        <!-- Меню -->
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_Файл">
                <MenuItem Header="_Открыть DAT Файлы" Click="OpenFile_Click" InputGestureText="Ctrl+O"/>
                <MenuItem Header="_Сохранить все изображения" Click="SaveAllImages_Click" />
                <Separator/>
                <MenuItem Header="_Выход" Click="ExitMenuItem_Click"/>
            </MenuItem>
            <MenuItem Header="_Настройки">
                <MenuItem Header="_Палитры">
                    <ComboBox x:Name="PaletteSelector" Width="150" SelectionChanged="PaletteSelector_SelectionChanged" ToolTip="Выбрать палитру">
                        <ComboBoxItem Content="Rainbow"/>
                        <ComboBoxItem Content="Hot"/>
                        <ComboBoxItem Content="Cool"/>
                        <ComboBoxItem Content="Gray Scale"/>
                    </ComboBox>
                </MenuItem>
                <MenuItem Header="_Обратный диапазон">
                    <CheckBox x:Name="ReverseScale" Content="Reverse Scale" Checked="ReverseScale_CheckedChanged" 
                             Unchecked="ReverseScale_CheckedChanged" VerticalAlignment="Center" ToolTip="Обратить цветовую шкалу"/>
                </MenuItem>
            </MenuItem>
            <MenuItem Header="_Помощь">
                <MenuItem Header="_О программе" Click="AboutMenuItem_Click"/>
            </MenuItem>
        </Menu>

        <!-- Панель инструментов -->
        <ToolBarTray DockPanel.Dock="Top">
            <ToolBar>
                <Button Content="Open DAT Files" Click="OpenFile_Click" Width="120" Margin="5" ToolTip="Открыть DAT файлы (Ctrl+O)"/>
                <Button Content="Auto Range" Click="AutoRange_Click" Width="100" Margin="5" ToolTip="Автоматически определить диапазон (Ctrl+R)"/>
                <Separator/>
                <Button Content="Save All Images" Click="SaveAllImages_Click" Width="120" Margin="5" ToolTip="Сохранить все изображения"/>
            </ToolBar>
        </ToolBarTray>

        <!-- Строка состояния -->
        <StatusBar DockPanel.Dock="Bottom">
            <StatusBarItem>
                <TextBlock x:Name="StatusText" Text="Готово."/>
            </StatusBarItem>
            <StatusBarItem>
                <ProgressBar x:Name="LoadingProgressBar" Width="200" Height="16" IsIndeterminate="False" Visibility="Collapsed"/>
            </StatusBarItem>
        </StatusBar>

        <!-- Основное содержимое -->
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="30"/>
            </Grid.RowDefinitions>

            <ScrollViewer Grid.Row="0" Margin="10">
                <ItemsControl x:Name="ContourImagesControl" 
                              ItemTemplate="{StaticResource ContourMapTemplate}"
                              ItemsSource="{Binding LoadedFiles}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapPanel/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
            </ScrollViewer>

            <!-- Панель шкалы цвета -->
            <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" 
                        Visibility="Collapsed" x:Name="ColorScalePanel">
                <TextBlock x:Name="MinScaleLabel" Text="Min" Margin="5,0" VerticalAlignment="Center"/>
                <Image x:Name="ColorScale" Height="20" Width="256"/>
                <TextBlock x:Name="MaxScaleLabel" Text="Max" Margin="5,0" VerticalAlignment="Center"/>
            </StackPanel>
        </Grid>
    </DockPanel>
</Window>

using Microsoft.Win32;
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.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Forms; // Добавлено
using Forms = System.Windows.Forms; // Альяс

namespace ContourMapVisualizer
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private ObservableCollection<LoadedFile> loadedFiles = new ObservableCollection<LoadedFile>();
        public ObservableCollection<LoadedFile> LoadedFiles
        {
            get => loadedFiles;
            set
            {
                if (loadedFiles != value)
                {
                    loadedFiles = value;
                    OnPropertyChanged(nameof(LoadedFiles));
                }
            }
        }

        private string currentPalette = "Rainbow";
        private bool isScaleReversed = false;
        private string lastOpenedDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

        public MainWindow()
        {
            InitializeComponent();
            PaletteSelector.SelectedIndex = 0;
            this.DataContext = this; // Установка DataContext

            // Установка горячих клавиш
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Open, OpenFile_Click));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, SaveAllImages_Click));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo, AutoRange_Click));
        }

        public event PropertyChangedEventHandler PropertyChanged;

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

        // Обработчики Drag-and-Drop
        private void Window_DragOver(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effects = DragDropEffects.Copy;
            else
                e.Effects = DragDropEffects.None;

            e.Handled = true;
        }

        private void Window_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                var files = (string[])e.Data.GetData(DataFormats.FileDrop);
                HandleFiles(files);
            }
        }

        private async void OpenFile_Click(object sender, RoutedEventArgs e)
        {
            var openFileDialog = new OpenFileDialog
            {
                Filter = "DAT files (*.dat)|*.dat|All files (*.*)|*.*",
                Multiselect = true,
                InitialDirectory = lastOpenedDirectory
            };

            if (openFileDialog.ShowDialog() == true)
            {
                lastOpenedDirectory = Path.GetDirectoryName(openFileDialog.FileName);
                await LoadFilesAsync(openFileDialog.FileNames);
            }
        }

        private async Task LoadFilesAsync(string[] filePaths)
        {
            LoadingProgressBar.Visibility = Visibility.Visible;
            LoadingProgressBar.IsIndeterminate = true;
            StatusText.Text = "Загрузка файлов...";

            try
            {
                foreach (var file in filePaths)
                {
                    var loadedFile = await Task.Run(() => LoadDataFromFile(file, 0));
                    LoadedFiles.Add(loadedFile);
                    loadedFile.PropertyChanged += LoadedFile_PropertyChanged;
                }

                StatusText.Text = $"Загружено {filePaths.Length} файлов.";
                UpdateColorScales();
                ColorScalePanel.Visibility = Visibility.Visible;
            }
            catch (Exception ex)
            {
                System.Windows.MessageBox.Show($"Ошибка при загрузке файлов: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                StatusText.Text = "Ошибка при загрузке файлов.";
            }
            finally
            {
                LoadingProgressBar.IsIndeterminate = false;
                LoadingProgressBar.Visibility = Visibility.Collapsed;
            }
        }

        private void HandleFiles(string[] filePaths)
        {
            // Метод можно использовать для дополнительной обработки файлов при Drag-and-Drop
            // В текущей реализации перехватывается только открытие через диалог
            // При необходимости можно добавить аналогичную логику как в OpenFile_Click
            OpenFile_Click(this, new RoutedEventArgs());
        }

        private LoadedFile LoadDataFromFile(string filename, int skipLines)
        {
            var allLines = File.ReadAllLines(filename);
            var lines = allLines.Skip(skipLines).ToArray();

            if (lines.Length == 0)
            {
                throw new Exception($"Нет данных для отображения в файле {filename} после пропуска {skipLines} строк.");
            }

            int rows = lines.Length;
            int cols = lines[0].Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries).Length;

            double[,] data = new double[rows, cols];
            double minValue = double.MaxValue;
            double maxValue = double.MinValue;

            for (int i = 0; i < rows; i++)
            {
                var values = lines[i].Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
                if (values.Length != cols)
                {
                    throw new Exception($"Неправильный формат данных в файле {filename} на строке {i + skipLines + 1}.");
                }

                for (int j = 0; j < cols; j++)
                {
                    if (!double.TryParse(values[j], out double value))
                    {
                        throw new Exception($"Неверное число в файле {filename} на строке {i + skipLines + 1}, позиции {j + 1}.");
                    }
                    data[i, j] = value;
                    minValue = Math.Min(minValue, data[i, j]);
                    maxValue = Math.Max(maxValue, data[i, j]);
                }
            }

            var loadedFileObj = new LoadedFile
            {
                FilePath = filename,
                FileName = Path.GetFileName(filename),
                Data = data,
                MinValue = minValue,
                MaxValue = maxValue,
                Palette = currentPalette,
                IsScaleReversed = isScaleReversed
            };

            // Инициализируем ContourBitmap
            loadedFileObj.ContourBitmap = GenerateContourBitmap(loadedFileObj);

            return loadedFileObj;
        }

        private BitmapSource GenerateContourBitmap(LoadedFile file)
        {
            if (file.Data == null) return null;

            // Используем индивидуальные Min и Max для каждой карты
            double minVal = file.MinValue;
            double maxVal = file.MaxValue;

            if (minVal == maxVal)
            {
                // Избегаем деления на ноль
                minVal -= 1;
                maxVal += 1;
            }

            int width = file.Data.GetLength(1);
            int height = file.Data.GetLength(0);

            var bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgr32, null);
            int stride = bitmap.BackBufferStride;
            int[] pixels = new int[width * height];

            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    double normalizedValue = (file.Data[i, j] - minVal) / (maxVal - minVal);
                    normalizedValue = Math.Max(0, Math.Min(1, normalizedValue));

                    double effectiveValue = file.IsScaleReversed ? 1 - normalizedValue : normalizedValue;

                    Color color = GetColorFromPalette(effectiveValue, file.Palette);
                    pixels[i * width + j] = (color.A << 24) | (color.R << 16) | (color.G << 8) | color.B;
                }
            }

            bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, stride, 0);

            // Масштабируем bitmap до желаемного размера отображения
            return new TransformedBitmap(bitmap, new ScaleTransform(3, 3));
        }

        private void UpdateColorScales()
        {
            if (LoadedFiles.Count == 0)
            {
                ColorScalePanel.Visibility = Visibility.Collapsed;
                return;
            }

            // Вычисляем глобальный min и max для цветовой шкалы
            double globalMin = LoadedFiles.Min(f => f.MinValue);
            double globalMax = LoadedFiles.Max(f => f.MaxValue);

            if (globalMin == globalMax)
            {
                globalMin -= 1;
                globalMax += 1;
            }

            MinScaleLabel.Text = globalMin.ToString("F2");
            MaxScaleLabel.Text = globalMax.ToString("F2");

            int scaleWidth = 256;
            int scaleHeight = 20;

            var bitmap = new WriteableBitmap(scaleWidth, scaleHeight, 96, 96, PixelFormats.Bgr32, null);
            int stride = bitmap.BackBufferStride;
            int[] pixels = new int[scaleWidth * scaleHeight];

            for (int x = 0; x < scaleWidth; x++)
            {
                double value = (double)x / (scaleWidth - 1);
                value = isScaleReversed ? 1 - value : value;

                // Используем глобальную палитру для шкалы
                Color color = GetColorFromPalette(value, currentPalette);

                for (int y = 0; y < scaleHeight; y++)
                {
                    pixels[y * scaleWidth + x] = (color.A << 24) | (color.R << 16) | (color.G << 8) | color.B;
                }
            }

            bitmap.WritePixels(new Int32Rect(0, 0, scaleWidth, scaleHeight), pixels, stride, 0);
            ColorScale.Source = bitmap;
        }

        private void LoadedFile_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (sender is LoadedFile file)
            {
                if (e.PropertyName == nameof(LoadedFile.MinValue) || e.PropertyName == nameof(LoadedFile.MaxValue))
                {
                    // Перегенерируем ContourBitmap для этого файла
                    file.ContourBitmap = GenerateContourBitmap(file);
                }
                else if (e.PropertyName == nameof(LoadedFile.Palette))
                {
                    // Палитра изменена: перегенерируем все карты
                    foreach (var f in LoadedFiles)
                    {
                        f.ContourBitmap = GenerateContourBitmap(f);
                    }
                    UpdateColorScales();
                }
                else if (e.PropertyName == nameof(LoadedFile.IsScaleReversed))
                {
                    // Обновление при изменении направления шкалы
                    file.ContourBitmap = GenerateContourBitmap(file);
                    UpdateColorScales();
                }
            }
        }

        private void ReverseScale_CheckedChanged(object sender, RoutedEventArgs e)
        {
            isScaleReversed = ReverseScale.IsChecked ?? false;

            // Обновляем свойство для всех загруженных файлов
            foreach (var file in LoadedFiles)
            {
                file.IsScaleReversed = isScaleReversed;
                file.ContourBitmap = GenerateContourBitmap(file);
            }

            UpdateColorScales();
            StatusText.Text = "Изменен порядок цветовой шкалы.";
        }

        private Color GetColorFromPalette(double value, string palette)
        {
            return palette switch
            {
                "Rainbow" => GetRainbowColor(value),
                "Hot" => GetHotColor(value),
                "Cool" => GetCoolColor(value),
                "Gray Scale" => GetGrayScaleColor(value),
                _ => GetRainbowColor(value),
            };
        }

        private Color GetGrayScaleColor(double value)
        {
            byte intensity = (byte)(value * 255);
            return Color.FromRgb(intensity, intensity, intensity);
        }

        private Color GetRainbowColor(double value)
        {
            double hue = 240 - (240 * value); // 240 (синий) до 0 (красный)
            return ColorFromHSV(hue, 1, 1);
        }

        private Color GetHotColor(double value)
        {
            if (value < 0.33)
                return Color.FromRgb((byte)(value * 3 * 255), 0, 0);
            else if (value < 0.66)
                return Color.FromRgb(255, (byte)((value - 0.33) * 3 * 255), 0);
            else
                return Color.FromRgb(255, 255, (byte)((value - 0.66) * 3 * 255));
        }

        private Color GetCoolColor(double value)
        {
            return Color.FromRgb((byte)(value * 255), (byte)((1 - value) * 255), 255);
        }

        private Color ColorFromHSV(double hue, double saturation, double value)
        {
            int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
            double f = hue / 60 - Math.Floor(hue / 60);

            value = value * 255;
            byte v = (byte)value;
            byte p = (byte)(value * (1 - saturation));
            byte q = (byte)(value * (1 - f * saturation));
            byte t = (byte)(value * (1 - (1 - f) * saturation));

            return hi switch
            {
                0 => Color.FromRgb(v, t, p),
                1 => Color.FromRgb(q, v, p),
                2 => Color.FromRgb(p, v, t),
                3 => Color.FromRgb(p, q, v),
                4 => Color.FromRgb(t, p, v),
                _ => Color.FromRgb(v, p, q),
            };
        }

        private void PaletteSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (PaletteSelector.SelectedItem != null)
            {
                ComboBoxItem selectedItem = (ComboBoxItem)PaletteSelector.SelectedItem;
                currentPalette = selectedItem.Content.ToString();

                // Обновляем палитру для всех файлов и пересоздаём контурные карты
                foreach (var file in LoadedFiles)
                {
                    file.Palette = currentPalette;
                    file.ContourBitmap = GenerateContourBitmap(file);
                }

                UpdateColorScales();
                StatusText.Text = $"Выбрана палитра: {currentPalette}.";
            }
        }

        private void AutoRangeButton_Click(object sender, RoutedEventArgs e)
        {
            if (sender is Button button && button.DataContext is LoadedFile file)
            {
                // Рассчитываем min и max для конкретного файла
                double min = double.MaxValue;
                double max = double.MinValue;
                int rows = file.Data.GetLength(0);
                int cols = file.Data.GetLength(1);

                for (int i = 0; i < rows; i++)
                    for (int j = 0; j < cols; j++)
                    {
                        if (file.Data[i, j] < min) min = file.Data[i, j];
                        if (file.Data[i, j] > max) max = file.Data[i, j];
                    }

                // Обновляем свойства LoadedFile
                file.MinValue = min;
                file.MaxValue = max;

                StatusText.Text = $"Авто-диапазон установлен для {file.FileName}.";
            }
        }

        private void AutoRange_Click(object sender, RoutedEventArgs e)
        {
            if (LoadedFiles.Count == 0) return;

            // Вычисляем глобальный min и max для всех загруженных файлов
            double globalMin = LoadedFiles.Min(f => f.MinValue);
            double globalMax = LoadedFiles.Max(f => f.MaxValue);

            // Обновляем MinValue и MaxValue каждого файла до глобальных значений
            foreach (var file in LoadedFiles)
            {
                file.MinValue = globalMin;
                file.MaxValue = globalMax;
            }

            UpdateColorScales();
            StatusText.Text = "Авто-диапазон установлен для всех файлов.";
        }

        private void RemoveMapButton_Click(object sender, RoutedEventArgs e)
        {
            if (sender is Button button && button.DataContext is LoadedFile file)
            {
                // Подтверждение удаления
                var result = System.Windows.MessageBox.Show(
                    $"Вы уверены, что хотите удалить карту \"{file.FileName}\"?",
                    "Подтверждение удаления",
                    MessageBoxButton.YesNo,
                    MessageBoxImage.Question
                );

                if (result == MessageBoxResult.Yes)
                {
                    // Удаление файла из коллекции
                    LoadedFiles.Remove(file);

                    // Обновление цветовой шкалы
                    UpdateColorScales();

                    // Если все карты удалены, скрыть панель цветовой шкалы
                    if (LoadedFiles.Count == 0)
                    {
                        ColorScalePanel.Visibility = Visibility.Collapsed;
                        StatusText.Text = "Все карты удалены.";
                    }
                    else
                    {
                        StatusText.Text = $"Карта \"{file.FileName}\" удалена.";
                    }
                }
            }
        }

        private void AboutMenuItem_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.MessageBox.Show("Contour Map Visualizer\nВерсия 1.1\nАвтор: Ваше Имя", "О программе", MessageBoxButton.OK, MessageBoxImage.Information);
        }

        private void ExitMenuItem_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void SaveImageButton_Click(object sender, RoutedEventArgs e)
        {
            if (sender is Button button && button.DataContext is LoadedFile file)
            {
                var saveFileDialog = new SaveFileDialog
                {
                    Filter = "PNG Image (*.png)|*.png|JPEG Image (*.jpg)|*.jpg",
                    FileName = Path.GetFileNameWithoutExtension(file.FileName) + "_Contour",
                    InitialDirectory = lastOpenedDirectory
                };

                if (saveFileDialog.ShowDialog() == true)
                {
                    lastOpenedDirectory = Path.GetDirectoryName(saveFileDialog.FileName);
                    SaveBitmap(file.ContourBitmap, saveFileDialog.FileName);
                    StatusText.Text = $"Изображение сохранено: {saveFileDialog.FileName}";
                }
            }
        }

        private void SaveAllImages_Click(object sender, RoutedEventArgs e)
        {
            if (LoadedFiles.Count == 0)
            {
                System.Windows.MessageBox.Show("Нет загруженных файлов для сохранения.", "Информация", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            var folderDialog = new Forms.FolderBrowserDialog // Используем алиас здесь
            {
                Description = "Выберите папку для сохранения изображений.",
                SelectedPath = lastOpenedDirectory
            };

            // Поскольку FolderBrowserDialog может быть отклонён
            var result = folderDialog.ShowDialog();
            if (result == Forms.DialogResult.OK)
            {
                lastOpenedDirectory = folderDialog.SelectedPath;
                LoadingProgressBar.Visibility = Visibility.Visible;
                LoadingProgressBar.IsIndeterminate = true;
                StatusText.Text = "Сохранение изображений...";

                foreach (var file in LoadedFiles)
                {
                    string filePath = Path.Combine(folderDialog.SelectedPath, Path.GetFileNameWithoutExtension(file.FileName) + "_Contour.png");
                    SaveBitmap(file.ContourBitmap, filePath);
                }

                LoadingProgressBar.IsIndeterminate = false;
                LoadingProgressBar.Visibility = Visibility.Collapsed;
                StatusText.Text = "Все изображения сохранены.";
                System.Windows.MessageBox.Show("Все изображения успешно сохранены.", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        private void SaveBitmap(BitmapSource bitmap, string filename)
        {
            BitmapEncoder encoder = null;
            string extension = Path.GetExtension(filename).ToLower();
            switch (extension)
            {
                case ".png":
                    encoder = new PngBitmapEncoder();
                    break;
                case ".jpg":
                case ".jpeg":
                    encoder = new JpegBitmapEncoder();
                    break;
                default:
                    encoder = new PngBitmapEncoder();
                    break;
            }

            encoder.Frames.Add(BitmapFrame.Create(bitmap));
            using (var stream = new FileStream(filename, FileMode.Create))
            {
                encoder.Save(stream);
            }
        }

        // Обработчики горячих клавиш
        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            if (Keyboard.Modifiers == ModifierKeys.Control)
            {
                switch (e.Key)
                {
                    case Key.O:
                        OpenFile_Click(this, new RoutedEventArgs());
                        e.Handled = true;
                        break;
                    case Key.R:
                        AutoRange_Click(this, new RoutedEventArgs());
                        e.Handled = true;
                        break;
                    case Key.S:
                        SaveAllImages_Click(this, new RoutedEventArgs());
                        e.Handled = true;
                        break;
                }
            }
            base.OnPreviewKeyDown(e);
        }
    }
}

<Window x:Class="ContourMapVisualizer.FilePreviewWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Предпросмотр файлов" Height="700" Width="900" WindowStartupLocation="CenterOwner"
        AllowDrop="False">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="2*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <TextBlock Grid.Row="0" Text="Выберите файл и настройте пропуск строк:" FontWeight="Bold" />

        <DataGrid x:Name="FilesDataGrid" Grid.Row="1" AutoGenerateColumns="False" CanUserAddRows="False" 
                  SelectionMode="Single" SelectionChanged="FilesDataGrid_SelectionChanged" Margin="0,10,0,10">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Файл" Binding="{Binding FileName}" IsReadOnly="True" Width="*"/>
                <DataGridTextColumn Header="Пропустить строк" Binding="{Binding SkipLines, UpdateSourceTrigger=PropertyChanged}" Width="150"/>
            </DataGrid.Columns>
        </DataGrid>

        <ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Margin="0,10,0,10">
            <TextBox x:Name="PreviewTextBox" IsReadOnly="True" FontFamily="Consolas" FontSize="12" TextWrapping="NoWrap"/>
        </ScrollViewer>

        <StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
            <Button Content="OK" Click="ConfirmButton_Click" Width="75" Margin="0,0,10,0" ToolTip="Подтвердить выбор"/>
            <Button Content="Отмена" Click="CancelButton_Click" Width="75" ToolTip="Отменить операцию"/>
        </StackPanel>
    </Grid>
</Window>

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace ContourMapVisualizer
{
    public partial class FilePreviewWindow : Window
    {
        public List<LoadedFilePreview> SelectedFiles { get; private set; } = new List<LoadedFilePreview>();

        public FilePreviewWindow(string[] filePaths)
        {
            InitializeComponent();
            InitializeDataGrid(filePaths);
        }

        private void InitializeDataGrid(string[] filePaths)
        {
            foreach (var path in filePaths)
            {
                try
                {
                    var preview = new LoadedFilePreview
                    {
                        FilePath = path,
                        FileName = Path.GetFileName(path),
                        SkipLines = 0 // Строки по умолчанию для пропуска
                    };
                    SelectedFiles.Add(preview);
                }
                catch (Exception ex)
                {
                    System.Windows.MessageBox.Show($"Ошибка при загрузке файла {path}: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
            FilesDataGrid.ItemsSource = SelectedFiles;
            if (SelectedFiles.Count > 0)
                FilesDataGrid.SelectedIndex = 0;
        }

        private void FilesDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (FilesDataGrid.SelectedItem is LoadedFilePreview selectedFile)
            {
                DisplayPreview(selectedFile);
            }
            else
            {
                PreviewTextBox.Text = string.Empty;
            }
        }

        private void DisplayPreview(LoadedFilePreview selectedFile)
        {
            try
            {
                var allLines = File.ReadAllLines(selectedFile.FilePath);
                if (selectedFile.SkipLines >= allLines.Length)
                {
                    PreviewTextBox.Text = "Нет данных для предварительного просмотра.";
                    return;
                }

                var previewLines = allLines.Skip(selectedFile.SkipLines).Take(100).ToArray(); // Отображаем 100 строк

                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < previewLines.Length; i++)
                {
                    int lineNumber = selectedFile.SkipLines + i + 1;
                    sb.AppendLine($"{lineNumber}: {previewLines[i]}");
                }

                if (allLines.Length > selectedFile.SkipLines + 100)
                {
                    sb.AppendLine("...");
                }

                PreviewTextBox.Text = sb.ToString();
            }
            catch (Exception ex)
            {
                System.Windows.MessageBox.Show($"Ошибка при загрузке файла: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                PreviewTextBox.Text = string.Empty;
            }
        }

        private void ConfirmButton_Click(object sender, RoutedEventArgs e)
        {
            // Проверка корректности пропусков строк для каждого файла
            foreach (var file in SelectedFiles)
            {
                if (file.SkipLines < 0)
                {
                    System.Windows.MessageBox.Show($"Неверное количество пропущенных строк для файла {file.FileName}.", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }

                try
                {
                    int totalLines = File.ReadAllLines(file.FilePath).Length;
                    if (file.SkipLines >= totalLines)
                    {
                        System.Windows.MessageBox.Show($"Количество пропущенных строк для файла {file.FileName} превышает общее количество строк в файле.", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
                }
                catch (Exception ex)
                {
                    System.Windows.MessageBox.Show($"Ошибка при проверке файла {file.FileName}: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
            }

            this.DialogResult = true;
            Close();
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = false;
            Close();
        }
    }

    public class LoadedFilePreview : INotifyPropertyChanged
    {
        private int skipLines;
        public string FilePath { get; set; }
        public string FileName { get; set; }
        public int SkipLines
        {
            get => skipLines;
            set
            {
                if (skipLines != value)
                {
                    skipLines = value;
                    OnPropertyChanged(nameof(SkipLines));
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

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

using System.ComponentModel;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace ContourMapVisualizer
{
    public class LoadedFile : INotifyPropertyChanged
    {
        public string FilePath { get; set; }
        public string FileName { get; set; }
        public double[,] Data { get; set; }

        private double minValue;
        public double MinValue
        {
            get => minValue;
            set
            {
                if (minValue != value)
                {
                    minValue = value;
                    OnPropertyChanged(nameof(MinValue));
                }
            }
        }

        private double maxValue;
        public double MaxValue
        {
            get => maxValue;
            set
            {
                if (maxValue != value)
                {
                    maxValue = value;
                    OnPropertyChanged(nameof(MaxValue));
                }
            }
        }

        private string palette = "Rainbow";
        public string Palette
        {
            get => palette;
            set
            {
                if (palette != value)
                {
                    palette = value;
                    OnPropertyChanged(nameof(Palette));
                }
            }
        }

        private bool isScaleReversed = false;
        public bool IsScaleReversed
        {
            get => isScaleReversed;
            set
            {
                if (isScaleReversed != value)
                {
                    isScaleReversed = value;
                    OnPropertyChanged(nameof(IsScaleReversed));
                }
            }
        }

        private BitmapSource contourBitmap; // Изменено на BitmapSource
        public BitmapSource ContourBitmap
        {
            get => contourBitmap;
            set
            {
                if (contourBitmap != value)
                {
                    contourBitmap = value;
                    OnPropertyChanged(nameof(ContourBitmap));
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

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