iteration 1

This commit is contained in:
2025-12-10 18:59:27 +05:00
parent 2785e1749a
commit b6ddadc6d3
5 changed files with 413 additions and 235 deletions

View File

@@ -23,7 +23,15 @@ namespace CodeContextGenerator.Models
_isSelected = value; _isSelected = value;
OnPropertyChanged(); OnPropertyChanged();
UpdateParentSelection(); UpdateParentSelection();
UpdateChildrenSelection(value);
// Если это директория и установлено конкретное значение (не null), применяем ко всем детям
if (IsDirectory && value.HasValue)
{
foreach (var child in Children)
{
child.IsSelected = value.Value;
}
}
} }
} }
} }
@@ -43,18 +51,27 @@ namespace CodeContextGenerator.Models
var allSelected = children.All(c => c.IsSelected == true); var allSelected = children.All(c => c.IsSelected == true);
var noneSelected = children.All(c => c.IsSelected == false); var noneSelected = children.All(c => c.IsSelected == false);
Parent.IsSelected = allSelected ? true : (noneSelected ? false : null); // Если есть дети с null - устанавливаем null
Parent.UpdateParentSelection(); bool hasIndeterminate = children.Any(c => c.IsSelected == null);
}
private void UpdateChildrenSelection(bool? value) if (hasIndeterminate)
{
if (!IsDirectory || !value.HasValue) return;
foreach (var child in Children)
{ {
child.IsSelected = value; Parent.IsSelected = null;
} }
else if (allSelected)
{
Parent.IsSelected = true;
}
else if (noneSelected)
{
Parent.IsSelected = false;
}
else
{
Parent.IsSelected = null;
}
Parent.UpdateParentSelection();
} }
} }
} }

View File

@@ -1,116 +1,96 @@
using System.IO; using System.IO;
using CodeContextGenerator.Models; using CodeContextGenerator.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
namespace CodeContextGenerator.Services; namespace CodeContextGenerator.Services
public static class ProjectScannerService
{ {
private static readonly string[] ExcludedDirectories = { "bin", "obj", ".git", "packages", ".vs", "Properties", "node_modules", ".vscode" }; public static class ProjectScannerService
private static readonly string[] IncludedExtensions = { ".cs", ".xaml" };
public static async Task<FileItem> ScanProjectDirectoryAsync(string rootPath, IProgress<int> progress = null, CancellationToken cancellationToken = default)
{ {
var rootItem = new FileItem private static readonly string[] ExcludedDirectories = {
{ "bin", "obj", ".git", "packages", ".vs", "Properties",
Name = Path.GetFileName(rootPath), "node_modules", ".vscode", ".idea", ".vs", "Debug", "Release"
FullName = rootPath,
IsDirectory = true,
IsSelected = false
}; };
await BuildDirectoryTreeAsync(rootPath, rootItem, progress, cancellationToken); private static readonly string[] IncludedExtensions = { ".cs", ".xaml" };
return rootItem;
}
private static async Task BuildDirectoryTreeAsync(string path, FileItem parentItem, IProgress<int> progress = null, CancellationToken cancellationToken = default) public static async Task BuildDirectoryTreeAsync(string path, FileItem parentItem, IProgress<int> progress = null, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
try
{ {
var directories = Directory.GetDirectories(path) cancellationToken.ThrowIfCancellationRequested();
.Where(d => !ExcludedDirectories.Any(ex => d.EndsWith(ex, StringComparison.OrdinalIgnoreCase)))
.ToList();
var files = Directory.GetFiles(path) try
.Where(f => IncludedExtensions.Any(ext => f.EndsWith(ext, StringComparison.OrdinalIgnoreCase)))
.ToList();
int totalItems = directories.Count + files.Count;
int processedItems = 0;
// Обрабатываем поддиректории
foreach (var dir in directories)
{ {
cancellationToken.ThrowIfCancellationRequested(); if (!Directory.Exists(path))
return;
var dirName = Path.GetFileName(dir); var directories = Directory.GetDirectories(path)
var dirItem = new FileItem .Where(d => !ExcludedDirectories.Any(ex => d.EndsWith(ex, System.StringComparison.OrdinalIgnoreCase) ||
Path.GetFileName(d).Equals(ex, System.StringComparison.OrdinalIgnoreCase)))
.ToList();
var files = Directory.GetFiles(path)
.Where(f => IncludedExtensions.Any(ext => f.EndsWith(ext, System.StringComparison.OrdinalIgnoreCase)))
.ToList();
int totalItems = directories.Count + files.Count;
int processedItems = 0;
// Обрабатываем поддиректории
foreach (var dir in directories)
{ {
Name = dirName, cancellationToken.ThrowIfCancellationRequested();
FullName = dir,
IsDirectory = true,
Parent = parentItem,
IsSelected = false
};
await BuildDirectoryTreeAsync(dir, dirItem, progress, cancellationToken); var dirName = Path.GetFileName(dir);
var dirItem = new FileItem
{
Name = dirName,
FullName = dir,
IsDirectory = true,
Parent = parentItem,
IsSelected = false
};
// Добавляем директорию только если в ней есть файлы или поддиректории с файлами await BuildDirectoryTreeAsync(dir, dirItem, progress, cancellationToken);
if (dirItem.Children.Any())
{ // Добавляем директорию только если в ней есть файлы или поддиректории с файлами
parentItem.Children.Add(dirItem); if (dirItem.Children.Any())
{
parentItem.Children.Add(dirItem);
}
processedItems++;
progress?.Report((int)((processedItems * 100.0) / totalItems));
} }
processedItems++; // Обрабатываем файлы
progress?.Report((int)((processedItems * 100.0) / totalItems)); foreach (var file in files)
}
// Обрабатываем файлы
foreach (var file in files)
{
cancellationToken.ThrowIfCancellationRequested();
var fileItem = new FileItem
{ {
Name = Path.GetFileName(file), cancellationToken.ThrowIfCancellationRequested();
FullName = file,
IsDirectory = false,
Parent = parentItem,
IsSelected = false
};
parentItem.Children.Add(fileItem); var fileItem = new FileItem
processedItems++; {
progress?.Report((int)((processedItems * 100.0) / totalItems)); Name = Path.GetFileName(file),
FullName = file,
IsDirectory = false,
Parent = parentItem,
IsSelected = false
};
parentItem.Children.Add(fileItem);
processedItems++;
progress?.Report((int)((processedItems * 100.0) / totalItems));
}
} }
} catch (IOException)
catch (Exception ex)
{
// Логируем ошибку, но продолжаем работу
Console.WriteLine($"Error scanning directory {path}: {ex.Message}");
}
}
public static List<string> GetSelectedFiles(FileItem rootItem)
{
var selectedFiles = new List<string>();
CollectSelectedFiles(rootItem, selectedFiles);
return selectedFiles;
}
private static void CollectSelectedFiles(FileItem item, List<string> selectedFiles)
{
if (item.IsDirectory)
{
foreach (var child in item.Children)
{ {
CollectSelectedFiles(child, selectedFiles); // Игнорируем ошибки доступа к директориям
}
catch (UnauthorizedAccessException)
{
// Игнорируем ошибки доступа
} }
}
else if (item.IsSelected==true)
{
selectedFiles.Add(item.FullName);
} }
} }
} }

View File

@@ -1,24 +1,29 @@
using CodeContextGenerator.Models; using System.ComponentModel;
using CodeContextGenerator.Services;
using Microsoft.Win32;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using Microsoft.Win32;
using CodeContextGenerator.Models;
using CodeContextGenerator.Services;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
namespace CodeContextGenerator.ViewModels namespace CodeContextGenerator.ViewModels
{ {
public class MainViewModel : INotifyPropertyChanged public class MainViewModel : INotifyPropertyChanged
{ {
private FileItem _rootDirectory; private FileItem _rootDirectory;
private string _selectedFolderPath; private string _selectedProjectFilePath;
private bool _isProcessing; private bool _isProcessing;
private int _progressValue; private int _progressValue;
private string _progressText; private string _progressText;
private CancellationTokenSource _cancellationTokenSource; private CancellationTokenSource _cancellationTokenSource;
private string _lastProjectPath; private string _lastProjectPath;
private bool _isProjectLoaded;
public FileItem RootDirectory public FileItem RootDirectory
{ {
@@ -30,12 +35,12 @@ namespace CodeContextGenerator.ViewModels
} }
} }
public string SelectedFolderPath public string SelectedProjectFilePath
{ {
get => _selectedFolderPath; get => _selectedProjectFilePath;
set set
{ {
_selectedFolderPath = value; _selectedProjectFilePath = value;
OnPropertyChanged(); OnPropertyChanged();
} }
} }
@@ -72,17 +77,27 @@ namespace CodeContextGenerator.ViewModels
} }
} }
public bool CanSelectFolder => !IsProcessing; public bool IsProjectLoaded
public bool CanGenerate => !IsProcessing && RootDirectory != null && HasSelectedFiles(RootDirectory); {
get => _isProjectLoaded;
set
{
_isProjectLoaded = value;
OnPropertyChanged();
}
}
public ICommand SelectFolderCommand { get; } public bool CanSelectFolder => !IsProcessing;
public bool CanGenerate => !IsProcessing && IsProjectLoaded && HasSelectedFiles(RootDirectory);
public ICommand SelectProjectFileCommand { get; }
public ICommand GenerateCommand { get; } public ICommand GenerateCommand { get; }
public ICommand CancelCommand { get; } public ICommand CancelCommand { get; }
public ICommand ExitCommand { get; } public ICommand ExitCommand { get; }
public MainViewModel() public MainViewModel()
{ {
SelectFolderCommand = new RelayCommand(SelectFolderAsync); SelectProjectFileCommand = new RelayCommand(SelectProjectFileAsync);
GenerateCommand = new RelayCommand(GenerateFileAsync, _ => CanGenerate); GenerateCommand = new RelayCommand(GenerateFileAsync, _ => CanGenerate);
CancelCommand = new RelayCommand(CancelProcessing, _ => IsProcessing); CancelCommand = new RelayCommand(CancelProcessing, _ => IsProcessing);
ExitCommand = new RelayCommand(_ => Application.Current.Shutdown()); ExitCommand = new RelayCommand(_ => Application.Current.Shutdown());
@@ -92,6 +107,8 @@ namespace CodeContextGenerator.ViewModels
private bool HasSelectedFiles(FileItem item) private bool HasSelectedFiles(FileItem item)
{ {
if (item == null) return false;
if (item.IsSelected == true && !item.IsDirectory) if (item.IsSelected == true && !item.IsDirectory)
return true; return true;
@@ -104,17 +121,17 @@ namespace CodeContextGenerator.ViewModels
return false; return false;
} }
private async void SelectFolderAsync(object parameter) private async void SelectProjectFileAsync(object parameter)
{ {
if (!CanSelectFolder) return; if (!CanSelectFolder) return;
var dialog = new OpenFileDialog var dialog = new OpenFileDialog
{ {
Title = "Выберите файл проекта или папку", Title = "Выберите файл решения или проекта",
CheckFileExists = false, CheckFileExists = true,
CheckPathExists = true, CheckPathExists = true,
FileName = "dummy", Filter = "Файлы решений Visual Studio (*.sln)|*.sln|Файлы проектов C# (*.csproj)|*.csproj|Все поддерживаемые файлы (*.sln;*.csproj)|*.sln;*.csproj|Все файлы (*.*)|*.*",
Filter = "Проекты C# (*.csproj)|*.csproj|Все файлы (*.*)|*.*" Multiselect = false
}; };
if (!string.IsNullOrEmpty(_lastProjectPath) && Directory.Exists(_lastProjectPath)) if (!string.IsNullOrEmpty(_lastProjectPath) && Directory.Exists(_lastProjectPath))
@@ -126,52 +143,65 @@ namespace CodeContextGenerator.ViewModels
if (result == true) if (result == true)
{ {
string selectedPath = dialog.FileName; string selectedFilePath = dialog.FileName;
string projectDirectory = Path.GetDirectoryName(selectedPath); string projectDirectory = Path.GetDirectoryName(selectedFilePath);
if (!string.IsNullOrEmpty(projectDirectory)) if (!string.IsNullOrEmpty(projectDirectory))
{ {
_lastProjectPath = projectDirectory; _lastProjectPath = projectDirectory;
SaveSettings(); SaveSettings();
await LoadProjectDirectoryAsync(projectDirectory); SelectedProjectFilePath = selectedFilePath;
IsProjectLoaded = false;
await LoadProjectAsync(selectedFilePath);
} }
} }
} }
private async Task LoadProjectDirectoryAsync(string projectDirectory) private async Task LoadProjectAsync(string projectFilePath)
{ {
IsProcessing = true; IsProcessing = true;
ProgressText = "Сканирование проекта..."; ProgressText = "Загрузка проекта...";
ProgressValue = 0; ProgressValue = 0;
try try
{ {
SelectedFolderPath = projectDirectory; string projectDirectory = Path.GetDirectoryName(projectFilePath);
SelectedProjectFilePath = projectFilePath;
var progress = new Progress<int>(value => var progress = new Progress<int>(value =>
{ {
ProgressValue = value; ProgressValue = value;
ProgressText = $"Сканирование: {value}%"; ProgressText = $"Загрузка: {value}%";
}); });
_cancellationTokenSource = new CancellationTokenSource(); _cancellationTokenSource = new CancellationTokenSource();
RootDirectory = await ProjectScannerService.ScanProjectDirectoryAsync(
projectDirectory,
progress,
_cancellationTokenSource.Token
);
ProgressText = "Сканирование завершено"; // Определяем тип файла
if (projectFilePath.EndsWith(".sln", StringComparison.OrdinalIgnoreCase))
{
await LoadSolutionAsync(projectFilePath, progress, _cancellationTokenSource.Token);
}
else if (projectFilePath.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase))
{
await LoadProjectDirectoryAsync(projectDirectory, progress, _cancellationTokenSource.Token);
}
IsProjectLoaded = true;
ProgressText = "Проект загружен успешно";
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
ProgressText = "Сканирование отменено"; ProgressText = "Загрузка отменена";
RootDirectory = null; RootDirectory = null;
IsProjectLoaded = false;
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.Show($"Ошибка при сканировании проекта: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show($"Ошибка при загрузке проекта: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
RootDirectory = null; RootDirectory = null;
IsProjectLoaded = false;
} }
finally finally
{ {
@@ -179,11 +209,137 @@ namespace CodeContextGenerator.ViewModels
} }
} }
private async Task LoadSolutionAsync(string solutionPath, IProgress<int> progress, CancellationToken cancellationToken)
{
try
{
string solutionDirectory = Path.GetDirectoryName(solutionPath);
var solutionProjects = ParseSolutionProjects(solutionPath);
var solutionItem = new FileItem
{
Name = Path.GetFileName(solutionPath),
FullName = solutionPath,
IsDirectory = true,
IsSelected = false
};
int totalProjects = solutionProjects.Count;
int processedProjects = 0;
foreach (var projectPath in solutionProjects)
{
cancellationToken.ThrowIfCancellationRequested();
if (File.Exists(projectPath))
{
string projectDir = Path.GetDirectoryName(projectPath);
string projectName = Path.GetFileName(projectDir);
var projectItem = new FileItem
{
Name = projectName,
FullName = projectDir,
IsDirectory = true,
Parent = solutionItem,
IsSelected = false
};
await ProjectScannerService.BuildDirectoryTreeAsync(projectDir, projectItem, progress, cancellationToken);
if (projectItem.Children.Any())
{
solutionItem.Children.Add(projectItem);
}
}
processedProjects++;
progress?.Report((int)((processedProjects * 100.0) / totalProjects));
}
RootDirectory = solutionItem;
// После загрузки решения - не выбираем ничего по умолчанию
ClearAllSelections(solutionItem);
}
catch (Exception ex)
{
throw new Exception($"Ошибка при обработке решения: {ex.Message}", ex);
}
}
private List<string> ParseSolutionProjects(string solutionPath)
{
var projects = new List<string>();
try
{
string solutionDirectory = Path.GetDirectoryName(solutionPath);
foreach (string line in File.ReadAllLines(solutionPath))
{
if (line.Trim().StartsWith("Project(", StringComparison.OrdinalIgnoreCase))
{
var parts = line.Split(new[] { '"' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 3)
{
string relativePath = parts[2].Trim();
string absolutePath = Path.GetFullPath(Path.Combine(solutionDirectory, relativePath));
if (absolutePath.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase) && File.Exists(absolutePath))
{
projects.Add(absolutePath);
}
}
}
}
}
catch (Exception ex)
{
throw new Exception($"Ошибка парсинга файла решения: {ex.Message}", ex);
}
return projects;
}
private async Task LoadProjectDirectoryAsync(string projectDirectory, IProgress<int> progress, CancellationToken cancellationToken)
{
try
{
var projectName = Path.GetFileName(projectDirectory);
var rootItem = new FileItem
{
Name = projectName,
FullName = projectDirectory,
IsDirectory = true,
IsSelected = false
};
await ProjectScannerService.BuildDirectoryTreeAsync(projectDirectory, rootItem, progress, cancellationToken);
RootDirectory = rootItem;
// После загрузки проекта - не выбираем ничего по умолчанию
ClearAllSelections(rootItem);
}
catch (Exception ex)
{
throw new Exception($"Ошибка при загрузке проекта: {ex.Message}", ex);
}
}
private void ClearAllSelections(FileItem item)
{
item.IsSelected = false;
foreach (var child in item.Children)
{
ClearAllSelections(child);
}
}
private async void GenerateFileAsync(object parameter) private async void GenerateFileAsync(object parameter)
{ {
if (!CanGenerate || RootDirectory == null) return; if (!CanGenerate || RootDirectory == null) return;
var selectedFiles = ProjectScannerService.GetSelectedFiles(RootDirectory); var selectedFiles = GetSelectedFiles(RootDirectory);
if (selectedFiles.Count == 0) if (selectedFiles.Count == 0)
{ {
@@ -195,7 +351,7 @@ namespace CodeContextGenerator.ViewModels
{ {
Title = "Сохранить контекстный файл", Title = "Сохранить контекстный файл",
Filter = "Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*", Filter = "Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*",
FileName = $"{Path.GetFileName(SelectedFolderPath)}_context.txt", FileName = $"{Path.GetFileNameWithoutExtension(SelectedProjectFilePath)}_context.txt",
DefaultExt = ".txt" DefaultExt = ".txt"
}; };
@@ -244,6 +400,28 @@ namespace CodeContextGenerator.ViewModels
} }
} }
private List<string> GetSelectedFiles(FileItem rootItem)
{
var selectedFiles = new List<string>();
CollectSelectedFiles(rootItem, selectedFiles);
return selectedFiles;
}
private void CollectSelectedFiles(FileItem item, List<string> selectedFiles)
{
if (item.IsDirectory)
{
foreach (var child in item.Children)
{
CollectSelectedFiles(child, selectedFiles);
}
}
else if (item.IsSelected == true)
{
selectedFiles.Add(item.FullName);
}
}
private async Task GenerateContextFileAsync(List<string> selectedFiles, string outputPath, IProgress<int> progress, CancellationToken cancellationToken) private async Task GenerateContextFileAsync(List<string> selectedFiles, string outputPath, IProgress<int> progress, CancellationToken cancellationToken)
{ {
var outputContent = new StringBuilder(); var outputContent = new StringBuilder();
@@ -256,7 +434,8 @@ namespace CodeContextGenerator.ViewModels
try try
{ {
string relativePath = Path.GetRelativePath(SelectedFolderPath, filePath); string projectDir = Path.GetDirectoryName(SelectedProjectFilePath);
string relativePath = Path.GetRelativePath(projectDir, filePath);
string fileContent = await File.ReadAllTextAsync(filePath, Encoding.UTF8); string fileContent = await File.ReadAllTextAsync(filePath, Encoding.UTF8);
// Обработка комментариев // Обработка комментариев
@@ -273,13 +452,12 @@ namespace CodeContextGenerator.ViewModels
} }
catch (IOException ex) catch (IOException ex)
{ {
// Если файл заблокирован или недоступен - останавливаем процесс
throw new IOException($"Файл '{Path.GetFileName(filePath)}' заблокирован или недоступен: {ex.Message}", ex); throw new IOException($"Файл '{Path.GetFileName(filePath)}' заблокирован или недоступен: {ex.Message}", ex);
} }
} }
// Сохраняем результат с кодировкой UTF-8 с BOM для максимальной совместимости // Сохраняем результат с кодировкой UTF-8 с BOM
var encoding = new UTF8Encoding(true); // true для BOM var encoding = new UTF8Encoding(true);
await File.WriteAllTextAsync(outputPath, outputContent.ToString(), encoding); await File.WriteAllTextAsync(outputPath, outputContent.ToString(), encoding);
} }

View File

@@ -1,72 +1,68 @@
<Window <Window x:Class="CodeContextGenerator.Views.MainWindow"
x:Class="CodeContextGenerator.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CodeContextGenerator.Views" xmlns:local="clr-namespace:CodeContextGenerator.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
xmlns:models="clr-namespace:CodeContextGenerator.Models" Title="Code Context Generator" Height="600" Width="800"
Title="Code Context Generator" WindowStartupLocation="CenterScreen">
Width="800"
Height="600"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Window.Resources> <Window.Resources>
<Style TargetType="TreeViewItem"> <Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" /> <Setter Property="IsExpanded" Value="True"/>
<Setter Property="Focusable" Value="False"/>
</Style>
<Style TargetType="Button">
<Setter Property="Padding" Value="10,5"/>
<Setter Property="Margin" Value="5,0"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style> </Style>
</Window.Resources> </Window.Resources>
<Grid Margin="10"> <Grid Margin="10">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto"/>
<RowDefinition Height="*" /> <RowDefinition Height="*"/>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock <TextBlock Grid.Row="0" Text="Выберите файл решения (.sln) или проекта (.csproj):" FontWeight="Bold" Margin="0,0,0,5"/>
Grid.Row="0"
Margin="0,0,0,5"
FontWeight="Bold"
Text="Выберите папку проекта:" />
<StackPanel <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,10">
Grid.Row="1" <Button Content="Выбрать файл..."
Margin="0,0,0,10" Command="{Binding SelectProjectFileCommand}"
Orientation="Horizontal"> IsEnabled="{Binding CanSelectFolder}"
<Button Width="120"/>
Margin="0,0,10,0" <TextBlock Text="{Binding SelectedProjectFilePath}"
Padding="10,5" VerticalAlignment="Center"
Command="{Binding SelectFolderCommand}" TextWrapping="Wrap"
Content="Выбрать папку..." MaxWidth="600"
IsEnabled="{Binding CanSelectFolder}" /> Margin="10,0,0,0"/>
<TextBlock
MaxWidth="600"
VerticalAlignment="Center"
Text="{Binding SelectedFolderPath}"
TextWrapping="Wrap" />
</StackPanel> </StackPanel>
<Border <Border Grid.Row="2" BorderBrush="Gray" BorderThickness="1" CornerRadius="4" Padding="5"
Grid.Row="2" Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}}">
Padding="5" <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
BorderBrush="Gray" PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
BorderThickness="1" <TreeView x:Name="ProjectTree" ItemsSource="{Binding RootDirectory.Children}"
CornerRadius="4"> VirtualizingPanel.IsVirtualizing="True"
<ScrollViewer> VirtualizingPanel.VirtualizationMode="Recycling">
<TreeView ItemsSource="{Binding RootDirectory.Children}" Visibility="{Binding RootDirectory, Converter={StaticResource NullToVisibilityConverter}}">
<TreeView.ItemTemplate> <TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}"> <HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal" MinHeight="24">
<CheckBox <CheckBox IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,5,0" IsThreeState="True"
VerticalAlignment="Center" VerticalAlignment="Center" Margin="2,0,5,0"
IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}" Click="CheckBox_Click"/>
IsThreeState="True" /> <TextBlock Text="{Binding Name}" VerticalAlignment="Center"
<TextBlock VerticalAlignment="Center" Text="{Binding Name}" /> ToolTip="{Binding FullName}"/>
</StackPanel> </StackPanel>
</HierarchicalDataTemplate> </HierarchicalDataTemplate>
</TreeView.ItemTemplate> </TreeView.ItemTemplate>
@@ -74,46 +70,27 @@
</ScrollViewer> </ScrollViewer>
</Border> </Border>
<ProgressBar <TextBlock Grid.Row="2" Text="Проект еще не загружен. Выберите файл решения или проекта."
Grid.Row="3" HorizontalAlignment="Center" VerticalAlignment="Center"
Height="20" Foreground="Gray" FontSize="14"
Margin="0,10,0,10" Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Collapsed}"/>
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}"
Value="{Binding ProgressValue}" />
<TextBlock <ProgressBar Grid.Row="3" Value="{Binding ProgressValue}" Height="20" Margin="0,10,0,10"
Grid.Row="3" Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}"/>
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding ProgressText}"
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}" />
<StackPanel <TextBlock Grid.Row="3" Text="{Binding ProgressText}" HorizontalAlignment="Center"
Grid.Row="4" VerticalAlignment="Center" FontWeight="Bold"
Margin="0,10,0,0" Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}"/>
HorizontalAlignment="Right"
Orientation="Horizontal"> <StackPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
<Button <Button Content="Отмена" Command="{Binding CancelCommand}"
Margin="0,0,10,0" Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}"
Padding="15,5" Background="#FFDC3545" Foreground="White"/>
Background="#FFDC3545" <Button Content="Закрыть" Command="{Binding ExitCommand}"/>
Command="{Binding CancelCommand}" <Button Content="Сформировать" Command="{Binding GenerateCommand}"
Content="Отмена" IsEnabled="{Binding CanGenerate}"
Foreground="White" Background="#FF28A745" Foreground="White"
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}" /> Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Button
Margin="0,0,10,0"
Padding="15,5"
Command="{Binding ExitCommand}"
Content="Закрыть" />
<Button
Padding="15,5"
Background="#FF28A745"
Command="{Binding GenerateCommand}"
Content="Сформировать"
Foreground="White"
IsEnabled="{Binding CanGenerate}" />
</StackPanel> </StackPanel>
</Grid> </Grid>
</Window> </Window>

View File

@@ -1,5 +1,8 @@
using System.Windows; using CodeContextGenerator.Models;
using CodeContextGenerator.ViewModels; using CodeContextGenerator.ViewModels;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace CodeContextGenerator.Views namespace CodeContextGenerator.Views
{ {
@@ -8,7 +11,30 @@ namespace CodeContextGenerator.Views
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
DataContext = new MainViewModel(); }
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if (sender is ScrollViewer scrollViewer)
{
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta / 3);
e.Handled = true;
}
}
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
if (sender is CheckBox checkBox && checkBox.DataContext is FileItem fileItem)
{
// Принудительно обновляем состояние детей при клике
if (fileItem.IsDirectory && checkBox.IsChecked.HasValue)
{
foreach (var child in fileItem.Children)
{
child.IsSelected = checkBox.IsChecked;
}
}
}
} }
} }
} }