iteration 2
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:CodeContextGenerator.Converters"
|
||||
StartupUri="Views/MainWindow.xaml">
|
||||
ShutdownMode="OnMainWindowClose">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
|
||||
@@ -1,8 +1,47 @@
|
||||
using System.Windows;
|
||||
using CodeContextGenerator.Services;
|
||||
using CodeContextGenerator.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace CodeContextGenerator
|
||||
namespace CodeContextGenerator;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
public partial class App : Application
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public App()
|
||||
{
|
||||
// Инициализируем сервисы
|
||||
var serviceCollection = new ServiceCollection();
|
||||
|
||||
// Регистрация сервисов
|
||||
serviceCollection.AddSingleton<ISettingsService, SettingsService>();
|
||||
serviceCollection.AddSingleton<IFileProcessorService, FileProcessorService>();
|
||||
serviceCollection.AddSingleton<IFileScannerService, FileScannerService>();
|
||||
serviceCollection.AddSingleton<IProjectLoaderService, ProjectLoaderService>();
|
||||
serviceCollection.AddSingleton<IContextFileGenerator, ContextFileGenerator>();
|
||||
serviceCollection.AddSingleton<IUIService, UIService>();
|
||||
|
||||
// Регистрация ViewModel
|
||||
serviceCollection.AddTransient<MainViewModel>();
|
||||
|
||||
_serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
}
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
// Создаем главное окно только один раз
|
||||
var mainWindow = new Views.MainWindow();
|
||||
|
||||
// Устанавливаем DataContext
|
||||
mainWindow.DataContext = _serviceProvider.GetRequiredService<MainViewModel>();
|
||||
|
||||
// Устанавливаем главное окно приложения
|
||||
MainWindow = mainWindow;
|
||||
|
||||
// Показываем окно
|
||||
mainWindow.Show();
|
||||
|
||||
base.OnStartup(e);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,66 +1,56 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace CodeContextGenerator.Models
|
||||
namespace CodeContextGenerator.Models;
|
||||
|
||||
public partial class FileItem : ObservableObject
|
||||
{
|
||||
public partial class FileItem : ObservableObject
|
||||
[ObservableProperty]
|
||||
private string name;
|
||||
|
||||
[ObservableProperty]
|
||||
private string fullName;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isDirectory;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool? isSelected;
|
||||
|
||||
[ObservableProperty]
|
||||
private FileItem parent;
|
||||
|
||||
public ObservableCollection<FileItem> Children { get; } = new ObservableCollection<FileItem>();
|
||||
|
||||
partial void OnIsSelectedChanged(bool? oldValue, bool? newValue)
|
||||
{
|
||||
[ObservableProperty]
|
||||
private string? name;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? fullName;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isDirectory;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool? isSelected;
|
||||
|
||||
[ObservableProperty]
|
||||
private FileItem? parent;
|
||||
|
||||
public ObservableCollection<FileItem> Children { get; } = new ObservableCollection<FileItem>();
|
||||
|
||||
/*partial void OnIsSelectedChanged(bool? oldValue, bool? newValue)
|
||||
if (newValue.HasValue)
|
||||
{
|
||||
if (newValue.HasValue)
|
||||
{
|
||||
UpdateChildrenSelection(newValue.Value);
|
||||
}
|
||||
UpdateParentSelection();
|
||||
}*/
|
||||
|
||||
private void UpdateChildrenSelection(bool value)
|
||||
{
|
||||
if (!IsDirectory) return;
|
||||
|
||||
foreach (var child in Children)
|
||||
{
|
||||
child.IsSelected = value;
|
||||
}
|
||||
UpdateChildrenSelection(newValue.Value);
|
||||
}
|
||||
UpdateParentSelection();
|
||||
}
|
||||
|
||||
private void UpdateParentSelection()
|
||||
private void UpdateChildrenSelection(bool value)
|
||||
{
|
||||
if (!IsDirectory) return;
|
||||
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (Parent == null) return;
|
||||
|
||||
var children = Parent.Children;
|
||||
var allSelected = children.All(c => c.IsSelected == true);
|
||||
var noneSelected = children.All(c => c.IsSelected == false);
|
||||
var hasIndeterminate = children.Any(c => c.IsSelected == null);
|
||||
|
||||
Parent.IsSelected = hasIndeterminate ? null : (allSelected ? true : (noneSelected ? false : null));
|
||||
Parent.UpdateParentSelection();
|
||||
}
|
||||
|
||||
public void ClearSelection()
|
||||
{
|
||||
IsSelected = false;
|
||||
foreach (var child in Children)
|
||||
{
|
||||
child.ClearSelection();
|
||||
}
|
||||
child.IsSelected = value;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateParentSelection()
|
||||
{
|
||||
if (Parent == null) return;
|
||||
|
||||
var children = Parent.Children;
|
||||
var allSelected = children.All(c => c.IsSelected == true);
|
||||
var noneSelected = children.All(c => c.IsSelected == false);
|
||||
var hasIndeterminate = children.Any(c => c.IsSelected == null);
|
||||
|
||||
Parent.IsSelected = hasIndeterminate ? null : (allSelected ? true : (noneSelected ? false : null));
|
||||
Parent.UpdateParentSelection();
|
||||
}
|
||||
}
|
||||
58
CodeContextGenerator/Services/ContextFileGenerator.cs
Normal file
58
CodeContextGenerator/Services/ContextFileGenerator.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace CodeContextGenerator.Services;
|
||||
|
||||
public interface IContextFileGenerator
|
||||
{
|
||||
Task GenerateContextFileAsync(List<string> selectedFiles, string outputPath, string projectRootPath, IProgress<int> progress, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public class ContextFileGenerator : IContextFileGenerator
|
||||
{
|
||||
private readonly IFileProcessorService _fileProcessorService;
|
||||
|
||||
public ContextFileGenerator(IFileProcessorService fileProcessorService)
|
||||
{
|
||||
_fileProcessorService = fileProcessorService;
|
||||
}
|
||||
|
||||
public async Task GenerateContextFileAsync(List<string> selectedFiles, string outputPath, string projectRootPath, IProgress<int> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
var outputContent = new StringBuilder();
|
||||
int totalFiles = selectedFiles.Count;
|
||||
int processedFiles = 0;
|
||||
|
||||
foreach (var filePath in selectedFiles)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
string relativePath = Path.GetRelativePath(projectRootPath, filePath);
|
||||
string fileContent = await File.ReadAllTextAsync(filePath, Encoding.UTF8);
|
||||
|
||||
// Обработка комментариев
|
||||
fileContent = _fileProcessorService.ProcessFileContent(fileContent, Path.GetFileName(filePath));
|
||||
|
||||
outputContent.AppendLine($"=== Файл: {relativePath} ===");
|
||||
outputContent.AppendLine(fileContent);
|
||||
outputContent.AppendLine(); // Пустая строка между файлами
|
||||
|
||||
processedFiles++;
|
||||
if (totalFiles > 0 && progress != null)
|
||||
{
|
||||
progress.Report((int)((processedFiles * 100.0) / totalFiles));
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new IOException($"Файл '{Path.GetFileName(filePath)}' заблокирован или недоступен: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Сохраняем результат с кодировкой UTF-8 с BOM
|
||||
var encoding = new UTF8Encoding(true);
|
||||
await File.WriteAllTextAsync(outputPath, outputContent.ToString(), encoding);
|
||||
}
|
||||
}
|
||||
@@ -1,191 +1,193 @@
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace CodeContextGenerator.Services
|
||||
namespace CodeContextGenerator.Services;
|
||||
|
||||
public interface IFileProcessorService
|
||||
{
|
||||
public static class FileProcessorService
|
||||
string ProcessFileContent(string content, string fileName);
|
||||
}
|
||||
|
||||
public class FileProcessorService : IFileProcessorService
|
||||
{
|
||||
public string ProcessFileContent(string content, string fileName)
|
||||
{
|
||||
public static string ProcessFileContent(string content, string fileName)
|
||||
if (fileName.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (fileName.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return RemoveXamlComments(content);
|
||||
}
|
||||
else if (fileName.EndsWith(".cs", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return RemoveCSharpComments(content);
|
||||
}
|
||||
return content;
|
||||
return RemoveXamlComments(content);
|
||||
}
|
||||
else if (fileName.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) ||
|
||||
fileName.EndsWith(".razor", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return RemoveCSharpComments(content);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
private string RemoveXamlComments(string content)
|
||||
{
|
||||
// Удаляем XAML комментарии <!-- ... -->
|
||||
return Regex.Replace(content, @"<!--.*?-->", "", RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
}
|
||||
|
||||
private string RemoveCSharpComments(string content)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var lines = content.Split('\n');
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
string processedLine = ProcessCSharpLine(line);
|
||||
sb.AppendLine(processedLine);
|
||||
}
|
||||
|
||||
private static string RemoveXamlComments(string content)
|
||||
return RemoveMultiLineComments(sb.ToString());
|
||||
}
|
||||
|
||||
private string ProcessCSharpLine(string line)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
return line;
|
||||
|
||||
// Удаляем XML-комментарии ///
|
||||
if (line.TrimStart().StartsWith("///", StringComparison.Ordinal))
|
||||
return string.Empty;
|
||||
|
||||
// Проверяем на однострочные комментарии
|
||||
int commentIndex = line.IndexOf("//", StringComparison.Ordinal);
|
||||
if (commentIndex >= 0)
|
||||
{
|
||||
// Удаляем XAML комментарии <!-- ... -->
|
||||
return Regex.Replace(content, @"<!--.*?-->", "", RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
}
|
||||
|
||||
private static string RemoveCSharpComments(string content)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var lines = content.Split('\n');
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
string processedLine = ProcessCSharpLine(line);
|
||||
sb.AppendLine(processedLine);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string ProcessCSharpLine(string line)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
return line;
|
||||
|
||||
// Удаляем XML-комментарии ///
|
||||
if (line.TrimStart().StartsWith("///", StringComparison.Ordinal))
|
||||
return string.Empty;
|
||||
|
||||
// Проверяем на однострочные комментарии
|
||||
int commentIndex = line.IndexOf("//", StringComparison.Ordinal);
|
||||
if (commentIndex >= 0)
|
||||
{
|
||||
// Проверяем, не находится ли // внутри строки
|
||||
bool inString = false;
|
||||
bool inChar = false;
|
||||
bool escapeNext = false;
|
||||
|
||||
for (int i = 0; i < commentIndex; i++)
|
||||
{
|
||||
char c = line[i];
|
||||
|
||||
if (escapeNext)
|
||||
{
|
||||
escapeNext = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\\')
|
||||
{
|
||||
escapeNext = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inChar && c == '"')
|
||||
{
|
||||
inString = !inString;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inString && c == '\'')
|
||||
{
|
||||
inChar = !inChar;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inString && !inChar)
|
||||
{
|
||||
return line.Substring(0, commentIndex).TrimEnd();
|
||||
}
|
||||
}
|
||||
|
||||
return line.TrimEnd();
|
||||
}
|
||||
|
||||
public static string RemoveMultiLineComments(string content)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
var stack = new Stack<int>();
|
||||
bool inComment = false;
|
||||
// Проверяем, не находится ли // внутри строки
|
||||
bool inString = false;
|
||||
bool inChar = false;
|
||||
bool escapeNext = false;
|
||||
|
||||
for (int i = 0; i < content.Length; i++)
|
||||
for (int i = 0; i < commentIndex; i++)
|
||||
{
|
||||
char c = content[i];
|
||||
char c = line[i];
|
||||
|
||||
if (escapeNext)
|
||||
{
|
||||
escapeNext = false;
|
||||
if (!inComment) result.Append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\\' && (inString || inChar))
|
||||
{
|
||||
escapeNext = true;
|
||||
if (!inComment) result.Append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Обработка строковых литералов
|
||||
if (!inComment)
|
||||
if (!inChar && c == '"')
|
||||
{
|
||||
if (!inString && !inChar && c == '"')
|
||||
{
|
||||
inString = true;
|
||||
result.Append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inString && !inChar && c == '\'')
|
||||
{
|
||||
inChar = true;
|
||||
result.Append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inString && c == '"')
|
||||
{
|
||||
inString = false;
|
||||
result.Append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inChar && c == '\'')
|
||||
{
|
||||
inChar = false;
|
||||
result.Append(c);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (inString || inChar)
|
||||
{
|
||||
result.Append(c);
|
||||
inString = !inString;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Обработка многострочных комментариев
|
||||
if (i < content.Length - 1)
|
||||
if (!inString && c == '\'')
|
||||
{
|
||||
if (!inComment && c == '/' && content[i + 1] == '*')
|
||||
{
|
||||
inComment = true;
|
||||
stack.Push(i);
|
||||
i++; // Пропускаем следующий символ
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inComment && c == '*' && content[i + 1] == '/')
|
||||
{
|
||||
inComment = false;
|
||||
stack.Pop();
|
||||
i++; // Пропускаем следующий символ
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inComment)
|
||||
{
|
||||
result.Append(c);
|
||||
inChar = !inChar;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
if (!inString && !inChar)
|
||||
{
|
||||
return line.Substring(0, commentIndex).TrimEnd();
|
||||
}
|
||||
}
|
||||
|
||||
return line.TrimEnd();
|
||||
}
|
||||
|
||||
private string RemoveMultiLineComments(string content)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
bool inComment = false;
|
||||
bool inString = false;
|
||||
bool inChar = false;
|
||||
bool escapeNext = false;
|
||||
|
||||
for (int i = 0; i < content.Length; i++)
|
||||
{
|
||||
char c = content[i];
|
||||
|
||||
if (escapeNext)
|
||||
{
|
||||
escapeNext = false;
|
||||
if (!inComment) result.Append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\\' && (inString || inChar))
|
||||
{
|
||||
escapeNext = true;
|
||||
if (!inComment) result.Append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Обработка строковых литералов
|
||||
if (!inComment)
|
||||
{
|
||||
if (!inString && !inChar && c == '"')
|
||||
{
|
||||
inString = true;
|
||||
result.Append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inString && !inChar && c == '\'')
|
||||
{
|
||||
inChar = true;
|
||||
result.Append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inString && c == '"')
|
||||
{
|
||||
inString = false;
|
||||
result.Append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inChar && c == '\'')
|
||||
{
|
||||
inChar = false;
|
||||
result.Append(c);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (inString || inChar)
|
||||
{
|
||||
result.Append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Обработка многострочных комментариев
|
||||
if (i < content.Length - 1)
|
||||
{
|
||||
if (!inComment && c == '/' && content[i + 1] == '*')
|
||||
{
|
||||
inComment = true;
|
||||
i++; // Пропускаем следующий символ
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inComment && c == '*' && content[i + 1] == '/')
|
||||
{
|
||||
inComment = false;
|
||||
i++; // Пропускаем следующий символ
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inComment)
|
||||
{
|
||||
result.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
128
CodeContextGenerator/Services/FileScannerService.cs
Normal file
128
CodeContextGenerator/Services/FileScannerService.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using CodeContextGenerator.Models;
|
||||
using System.IO;
|
||||
|
||||
namespace CodeContextGenerator.Services;
|
||||
|
||||
public interface IFileScannerService
|
||||
{
|
||||
Task BuildDirectoryTreeAsync(string path, FileItem parentItem, IProgress<int> progress = null, CancellationToken cancellationToken = default);
|
||||
List<string> GetSelectedFiles(FileItem rootItem);
|
||||
}
|
||||
|
||||
public class FileScannerService : IFileScannerService
|
||||
{
|
||||
private static readonly string[] ExcludedDirectories = {
|
||||
"bin", "obj", ".git", "packages", ".vs", "Properties",
|
||||
"node_modules", ".vscode", ".idea", "Debug", "Release",
|
||||
"wwwroot", "dist", "build", "node_modules"
|
||||
};
|
||||
|
||||
private static readonly string[] IncludedExtensions = { ".cs", ".xaml" };
|
||||
|
||||
public async Task BuildDirectoryTreeAsync(string path, FileItem parentItem, IProgress<int> progress = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
return;
|
||||
|
||||
var directories = Directory.GetDirectories(path)
|
||||
.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)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
processedItems++;
|
||||
if (totalItems > 0 && progress != null)
|
||||
{
|
||||
progress.Report((int)((processedItems * 100.0) / totalItems));
|
||||
}
|
||||
}
|
||||
|
||||
// Обрабатываем файлы
|
||||
foreach (var file in files)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var fileItem = new FileItem
|
||||
{
|
||||
Name = Path.GetFileName(file),
|
||||
FullName = file,
|
||||
IsDirectory = false,
|
||||
Parent = parentItem,
|
||||
IsSelected = false
|
||||
};
|
||||
|
||||
parentItem.Children.Add(fileItem);
|
||||
processedItems++;
|
||||
if (totalItems > 0 && progress != null)
|
||||
{
|
||||
progress.Report((int)((processedItems * 100.0) / totalItems));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// Игнорируем ошибки доступа к директориям
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
// Игнорируем ошибки доступа
|
||||
}
|
||||
}
|
||||
|
||||
public 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
177
CodeContextGenerator/Services/ProjectLoaderService.cs
Normal file
177
CodeContextGenerator/Services/ProjectLoaderService.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
using CodeContextGenerator.Models;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
|
||||
namespace CodeContextGenerator.Services;
|
||||
|
||||
public interface IProjectLoaderService
|
||||
{
|
||||
Task<FileItem> LoadProjectFromPathAsync(string projectPath, IProgress<int> progress, CancellationToken cancellationToken);
|
||||
string GetDefaultOutputFileName(string projectPath);
|
||||
}
|
||||
|
||||
public class ProjectLoaderService : IProjectLoaderService
|
||||
{
|
||||
private readonly IFileScannerService _fileScannerService;
|
||||
|
||||
public ProjectLoaderService(IFileScannerService fileScannerService)
|
||||
{
|
||||
_fileScannerService = fileScannerService;
|
||||
}
|
||||
|
||||
public async Task<FileItem> LoadProjectFromPathAsync(string projectPath, IProgress<int> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Directory.Exists(projectPath))
|
||||
{
|
||||
return await LoadDirectoryAsProjectAsync(projectPath, progress, cancellationToken);
|
||||
}
|
||||
else if (File.Exists(projectPath))
|
||||
{
|
||||
if (projectPath.EndsWith(".sln", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return await LoadSolutionAsync(projectPath, progress, cancellationToken);
|
||||
}
|
||||
else if (projectPath.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return await LoadCsprojAsync(projectPath, progress, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Указанный путь не является папкой проекта, решением (.sln) или проектом (.csproj)");
|
||||
}
|
||||
|
||||
private async Task<FileItem> LoadDirectoryAsProjectAsync(string directoryPath, IProgress<int> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
var projectName = Path.GetFileName(directoryPath);
|
||||
var rootItem = new FileItem
|
||||
{
|
||||
Name = projectName,
|
||||
FullName = directoryPath,
|
||||
IsDirectory = true,
|
||||
IsSelected = false
|
||||
};
|
||||
|
||||
await _fileScannerService.BuildDirectoryTreeAsync(directoryPath, rootItem, progress, cancellationToken);
|
||||
return rootItem;
|
||||
}
|
||||
|
||||
private async Task<FileItem> LoadSolutionAsync(string solutionPath, IProgress<int> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
string solutionDir = Path.GetDirectoryName(solutionPath);
|
||||
var solutionItem = new FileItem
|
||||
{
|
||||
Name = Path.GetFileName(solutionPath),
|
||||
FullName = solutionPath,
|
||||
IsDirectory = true,
|
||||
IsSelected = false
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var projectPaths = ParseSolutionProjects(solutionPath);
|
||||
int totalProjects = projectPaths.Count;
|
||||
int processedProjects = 0;
|
||||
|
||||
foreach (var projectPath in projectPaths)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (File.Exists(projectPath))
|
||||
{
|
||||
string projectDir = Path.GetDirectoryName(projectPath);
|
||||
string projectName = Path.GetFileNameWithoutExtension(projectPath);
|
||||
|
||||
var projectItem = new FileItem
|
||||
{
|
||||
Name = projectName,
|
||||
FullName = projectDir,
|
||||
IsDirectory = true,
|
||||
Parent = solutionItem,
|
||||
IsSelected = false
|
||||
};
|
||||
|
||||
await _fileScannerService.BuildDirectoryTreeAsync(projectDir, projectItem, progress, cancellationToken);
|
||||
|
||||
if (projectItem.Children.Any())
|
||||
{
|
||||
solutionItem.Children.Add(projectItem);
|
||||
}
|
||||
}
|
||||
|
||||
processedProjects++;
|
||||
progress?.Report((int)((processedProjects * 100.0) / totalProjects));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Ошибка при парсинге решения: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
return solutionItem;
|
||||
}
|
||||
|
||||
private async Task<FileItem> LoadCsprojAsync(string csprojPath, IProgress<int> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
string projectDir = Path.GetDirectoryName(csprojPath);
|
||||
string projectName = Path.GetFileNameWithoutExtension(csprojPath);
|
||||
|
||||
var projectItem = new FileItem
|
||||
{
|
||||
Name = projectName,
|
||||
FullName = projectDir,
|
||||
IsDirectory = true,
|
||||
IsSelected = false
|
||||
};
|
||||
|
||||
await _fileScannerService.BuildDirectoryTreeAsync(projectDir, projectItem, progress, cancellationToken);
|
||||
return projectItem;
|
||||
}
|
||||
|
||||
private List<string> ParseSolutionProjects(string solutionPath)
|
||||
{
|
||||
var projects = new List<string>();
|
||||
string solutionDir = Path.GetDirectoryName(solutionPath);
|
||||
|
||||
try
|
||||
{
|
||||
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();
|
||||
if (relativePath.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string absolutePath = Path.GetFullPath(Path.Combine(solutionDir, relativePath));
|
||||
if (File.Exists(absolutePath))
|
||||
{
|
||||
projects.Add(absolutePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Ошибка парсинга файла решения: {ex.Message}", ex);
|
||||
}
|
||||
|
||||
return projects;
|
||||
}
|
||||
|
||||
public string GetDefaultOutputFileName(string projectPath)
|
||||
{
|
||||
if (Directory.Exists(projectPath))
|
||||
{
|
||||
return $"{Path.GetFileName(projectPath)}_context.txt";
|
||||
}
|
||||
else if (File.Exists(projectPath))
|
||||
{
|
||||
return $"{Path.GetFileNameWithoutExtension(projectPath)}_context.txt";
|
||||
}
|
||||
return "project_context.txt";
|
||||
}
|
||||
}
|
||||
62
CodeContextGenerator/Services/SettingsService.cs
Normal file
62
CodeContextGenerator/Services/SettingsService.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace CodeContextGenerator.Services;
|
||||
|
||||
public interface ISettingsService
|
||||
{
|
||||
string GetLastProjectPath();
|
||||
void SaveLastProjectPath(string path);
|
||||
}
|
||||
|
||||
public class SettingsService : ISettingsService
|
||||
{
|
||||
private const string SettingsFileName = "app_settings.json";
|
||||
private readonly string _settingsFilePath;
|
||||
|
||||
public SettingsService()
|
||||
{
|
||||
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
string appFolder = Path.Combine(appDataPath, "CodeContextGenerator");
|
||||
Directory.CreateDirectory(appFolder);
|
||||
_settingsFilePath = Path.Combine(appFolder, SettingsFileName);
|
||||
}
|
||||
|
||||
public string GetLastProjectPath()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_settingsFilePath))
|
||||
{
|
||||
var json = File.ReadAllText(_settingsFilePath);
|
||||
var settings = JsonSerializer.Deserialize<Settings>(json);
|
||||
return settings?.LastProjectPath;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Игнорируем ошибки чтения настроек
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SaveLastProjectPath(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = new Settings { LastProjectPath = path };
|
||||
var options = new JsonSerializerOptions { WriteIndented = true };
|
||||
string json = JsonSerializer.Serialize(settings, options);
|
||||
File.WriteAllText(_settingsFilePath, json);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Игнорируем ошибки сохранения настроек
|
||||
}
|
||||
}
|
||||
|
||||
private class Settings
|
||||
{
|
||||
public string LastProjectPath { get; set; }
|
||||
}
|
||||
}
|
||||
98
CodeContextGenerator/Services/UIService.cs
Normal file
98
CodeContextGenerator/Services/UIService.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using Microsoft.Win32;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
|
||||
namespace CodeContextGenerator.Services;
|
||||
|
||||
public interface IUIService
|
||||
{
|
||||
string ShowFolderBrowserDialog(string initialDirectory = null);
|
||||
bool ShowOpenProjectFileDialog(out string selectedPath);
|
||||
bool ShowSaveFileDialog(string defaultFileName, string initialDirectory, out string savePath);
|
||||
void ShowMessage(string message, string title, MessageBoxImage icon = MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
public class UIService : IUIService
|
||||
{
|
||||
public string ShowFolderBrowserDialog(string initialDirectory = null)
|
||||
{
|
||||
// Используем OpenFileDialog для выбора папки - это стандартный способ в WPF
|
||||
var dialog = new OpenFileDialog
|
||||
{
|
||||
Title = "Выберите папку с проектом",
|
||||
Filter = "Папки|*.folder", // Фильтр для отображения только папок
|
||||
FileName = "select_folder", // Имя файла для обхода проверки существования файла
|
||||
CheckFileExists = false,
|
||||
CheckPathExists = true,
|
||||
ValidateNames = false, // Отключаем валидацию имен для выбора папок
|
||||
DereferenceLinks = true
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(initialDirectory) && Directory.Exists(initialDirectory))
|
||||
{
|
||||
dialog.InitialDirectory = initialDirectory;
|
||||
}
|
||||
|
||||
var result = dialog.ShowDialog();
|
||||
if (result == true)
|
||||
{
|
||||
// Возвращаем папку, а не файл
|
||||
return Path.GetDirectoryName(dialog.FileName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool ShowOpenProjectFileDialog(out string selectedPath)
|
||||
{
|
||||
selectedPath = null;
|
||||
|
||||
var dialog = new OpenFileDialog
|
||||
{
|
||||
Title = "Выберите файл решения, проекта или папку",
|
||||
Filter = "Все поддерживаемые файлы (*.sln;*.csproj;*.razor)|*.sln;*.csproj;*.razor|Файлы решений (*.sln)|*.sln|Файлы проектов (*.csproj)|*.csproj|Файлы Razor (*.razor)|*.razor|Все файлы (*.*)|*.*",
|
||||
Multiselect = false
|
||||
};
|
||||
|
||||
bool? result = dialog.ShowDialog();
|
||||
if (result == true)
|
||||
{
|
||||
selectedPath = dialog.FileName;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool ShowSaveFileDialog(string defaultFileName, string initialDirectory, out string savePath)
|
||||
{
|
||||
savePath = null;
|
||||
|
||||
var dialog = new SaveFileDialog
|
||||
{
|
||||
Title = "Сохранить контекстный файл",
|
||||
Filter = "Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*",
|
||||
FileName = defaultFileName,
|
||||
DefaultExt = ".txt"
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(initialDirectory) && Directory.Exists(initialDirectory))
|
||||
{
|
||||
dialog.InitialDirectory = initialDirectory;
|
||||
}
|
||||
|
||||
bool? result = dialog.ShowDialog();
|
||||
if (result == true)
|
||||
{
|
||||
savePath = dialog.FileName;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ShowMessage(string message, string title, MessageBoxImage icon = MessageBoxImage.Information)
|
||||
{
|
||||
MessageBox.Show(message, title, MessageBoxButton.OK, icon);
|
||||
}
|
||||
}
|
||||
@@ -2,285 +2,96 @@
|
||||
using CodeContextGenerator.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Microsoft.Win32;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
|
||||
namespace CodeContextGenerator.ViewModels;
|
||||
|
||||
public partial class MainViewModel : ObservableObject, INotifyPropertyChanged
|
||||
public partial class MainViewModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
private FileItem? _rootDirectory = new();
|
||||
[ObservableProperty]
|
||||
private string _selectedProjectFilePath = string.Empty;
|
||||
[ObservableProperty]
|
||||
private bool _isProcessing = false;
|
||||
[ObservableProperty]
|
||||
private int _progressValue;
|
||||
[ObservableProperty]
|
||||
private string _progressText = string.Empty;
|
||||
[ObservableProperty]
|
||||
private string? _lastProjectPath = string.Empty;
|
||||
[ObservableProperty]
|
||||
private bool _isProjectLoaded;
|
||||
private readonly IProjectLoaderService _projectLoaderService;
|
||||
private readonly IFileScannerService _fileScannerService;
|
||||
private readonly IContextFileGenerator _contextFileGenerator;
|
||||
private readonly IUIService _uiService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
|
||||
private CancellationTokenSource _cancellationTokenSource = new();
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public bool CanSelectFolder => !IsProcessing;
|
||||
[ObservableProperty]
|
||||
private FileItem rootDirectory;
|
||||
|
||||
[ObservableProperty]
|
||||
private string selectedProjectPath;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isProcessing;
|
||||
|
||||
[ObservableProperty]
|
||||
private int progressValue;
|
||||
|
||||
[ObservableProperty]
|
||||
private string progressText;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isProjectLoaded;
|
||||
|
||||
public MainViewModel(
|
||||
IProjectLoaderService projectLoaderService,
|
||||
IFileScannerService fileScannerService,
|
||||
IContextFileGenerator contextFileGenerator,
|
||||
IUIService uiService,
|
||||
ISettingsService settingsService)
|
||||
{
|
||||
_projectLoaderService = projectLoaderService;
|
||||
_fileScannerService = fileScannerService;
|
||||
_contextFileGenerator = contextFileGenerator;
|
||||
_uiService = uiService;
|
||||
_settingsService = settingsService;
|
||||
}
|
||||
|
||||
public bool CanSelectProject => !IsProcessing;
|
||||
public bool CanGenerate => !IsProcessing && IsProjectLoaded && HasSelectedFiles(RootDirectory);
|
||||
|
||||
//public ICommand SelectProjectFileCommand { get; }
|
||||
//public ICommand GenerateCommand { get; }
|
||||
//public ICommand CancelCommand { get; }
|
||||
//public ICommand ExitCommand { get; }
|
||||
|
||||
public MainViewModel()
|
||||
[RelayCommand(CanExecute = nameof(CanSelectProject))]
|
||||
private void SelectProject()
|
||||
{
|
||||
//ExitCommand = new RelayCommand(_ => Application.Current.Shutdown());
|
||||
LoadSettings();
|
||||
}
|
||||
var initialDir = _settingsService.GetLastProjectPath();
|
||||
|
||||
private bool HasSelectedFiles(FileItem item)
|
||||
{
|
||||
if (item == null) return false;
|
||||
if (item.IsSelected == true && !item.IsDirectory) return true;
|
||||
return item.Children.Any(HasSelectedFiles);
|
||||
}
|
||||
|
||||
//[RelayCommand(CanExecute = nameof(CanSelectFolder))]
|
||||
[RelayCommand(CanExecute = nameof(CanSelectFolder))]
|
||||
private void Exit()
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
//[RelayCommand(CanExecute = nameof(CanSelectFolder))]
|
||||
[RelayCommand(CanExecute = nameof(CanGenerate))]
|
||||
private void Cancel()
|
||||
{
|
||||
CancelProcessing();
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanSelectFolder))]
|
||||
private async Task SelectProjectFileAsync()
|
||||
{
|
||||
var dialog = new OpenFileDialog
|
||||
// Сначала пробуем выбрать файл проекта/решения
|
||||
if (_uiService.ShowOpenProjectFileDialog(out var filePath))
|
||||
{
|
||||
Title = "Выберите файл решения или проекта",
|
||||
CheckFileExists = true,
|
||||
CheckPathExists = true,
|
||||
Filter = "Файлы решений (*.sln)|*.sln|Файлы проектов (*.csproj)|*.csproj|Все поддерживаемые (*.sln;*.csproj)|*.sln;*.csproj|Все файлы (*.*)|*.*",
|
||||
InitialDirectory = !string.IsNullOrEmpty(LastProjectPath) && Directory.Exists(LastProjectPath) ? LastProjectPath : null
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() != true) return;
|
||||
|
||||
SelectedProjectFilePath = dialog.FileName;
|
||||
LastProjectPath = Path.GetDirectoryName(SelectedProjectFilePath);
|
||||
SaveSettings();
|
||||
|
||||
IsProjectLoaded = false;
|
||||
await LoadProjectAsync(SelectedProjectFilePath);
|
||||
}
|
||||
|
||||
private async Task LoadProjectAsync(string projectFilePath)
|
||||
{
|
||||
IsProcessing = true;
|
||||
ProgressText = "Загрузка проекта...";
|
||||
ProgressValue = 0;
|
||||
|
||||
try
|
||||
{
|
||||
string? projectDirectory = Path.GetDirectoryName(projectFilePath);
|
||||
var progress = new Progress<int>(value =>
|
||||
{
|
||||
ProgressValue = value;
|
||||
ProgressText = $"Загрузка: {value}%";
|
||||
});
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
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)
|
||||
{
|
||||
ProgressText = "Загрузка отменена";
|
||||
RootDirectory = null;
|
||||
IsProjectLoaded = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Ошибка при загрузке проекта: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
RootDirectory = null;
|
||||
IsProjectLoaded = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsProcessing = false;
|
||||
}
|
||||
}
|
||||
|
||||
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 static 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(['"'], 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 static void ClearAllSelections(FileItem item)
|
||||
{
|
||||
item.IsSelected = false;
|
||||
foreach (var child in item.Children)
|
||||
{
|
||||
ClearAllSelections(child);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanGenerate))]
|
||||
private async Task GenerateFileAsync()
|
||||
{
|
||||
var selectedFiles = GetSelectedFiles(RootDirectory);
|
||||
if (selectedFiles.Count == 0)
|
||||
{
|
||||
MessageBox.Show("Пожалуйста, выберите хотя бы один файл для обработки.", "Предупреждение", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
LoadProject(filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
var saveDialog = new SaveFileDialog
|
||||
// Если отменили выбор файла - предлагаем выбрать папку
|
||||
var folderPath = _uiService.ShowFolderBrowserDialog(initialDir);
|
||||
if (!string.IsNullOrEmpty(folderPath))
|
||||
{
|
||||
Title = "Сохранить контекстный файл",
|
||||
Filter = "Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*",
|
||||
FileName = $"{Path.GetFileNameWithoutExtension(SelectedProjectFilePath)}_context.txt",
|
||||
DefaultExt = ".txt",
|
||||
InitialDirectory = LastProjectPath
|
||||
};
|
||||
LoadProject(folderPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (saveDialog.ShowDialog() != true) return;
|
||||
[RelayCommand(CanExecute = nameof(CanGenerate))]
|
||||
private async Task GenerateContextFileAsync()
|
||||
{
|
||||
var selectedFiles = _fileScannerService.GetSelectedFiles(RootDirectory);
|
||||
if (selectedFiles.Count == 0)
|
||||
{
|
||||
_uiService.ShowMessage("Пожалуйста, выберите хотя бы один файл для обработки.", "Предупреждение", MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var defaultFileName = _projectLoaderService.GetDefaultOutputFileName(SelectedProjectPath);
|
||||
var initialDir = _settingsService.GetLastProjectPath();
|
||||
|
||||
if (!_uiService.ShowSaveFileDialog(defaultFileName, initialDir, out var savePath))
|
||||
return;
|
||||
|
||||
IsProcessing = true;
|
||||
ProgressText = "Генерация контекстного файла...";
|
||||
ProgressValue = 0;
|
||||
|
||||
try
|
||||
{
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
@@ -290,8 +101,14 @@ public partial class MainViewModel : ObservableObject, INotifyPropertyChanged
|
||||
ProgressText = $"Генерация: {value}%";
|
||||
});
|
||||
|
||||
await GenerateContextFileAsync(selectedFiles, saveDialog.FileName, progress, _cancellationTokenSource.Token);
|
||||
MessageBox.Show($"Файл успешно создан:\n{saveDialog.FileName}", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
await _contextFileGenerator.GenerateContextFileAsync(
|
||||
selectedFiles,
|
||||
savePath,
|
||||
Path.GetDirectoryName(SelectedProjectPath),
|
||||
progress,
|
||||
_cancellationTokenSource.Token);
|
||||
|
||||
_uiService.ShowMessage($"Файл успешно создан:\n{savePath}", "Успех");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -299,11 +116,11 @@ public partial class MainViewModel : ObservableObject, INotifyPropertyChanged
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
MessageBox.Show($"Ошибка доступа к файлу: {ex.Message}", "Ошибка доступа", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
_uiService.ShowMessage($"Ошибка доступа к файлу: {ex.Message}\nПожалуйста, закройте файлы или предоставьте необходимые права доступа.", "Ошибка доступа", MessageBoxImage.Error);
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Ошибка при генерации файла: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
_uiService.ShowMessage($"Ошибка при генерации файла: {ex.Message}", "Ошибка", MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -311,121 +128,81 @@ public partial class MainViewModel : ObservableObject, INotifyPropertyChanged
|
||||
}
|
||||
}
|
||||
|
||||
private 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);
|
||||
}
|
||||
}
|
||||
else if (item.IsSelected == true)
|
||||
{
|
||||
selectedFiles.Add(item.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GenerateContextFileAsync(List<string> selectedFiles, string outputPath, IProgress<int> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
var outputContent = new StringBuilder();
|
||||
int totalFiles = selectedFiles.Count;
|
||||
int processedFiles = 0;
|
||||
|
||||
foreach (var filePath in selectedFiles)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
string? projectDir = Path.GetDirectoryName(SelectedProjectFilePath);
|
||||
string? relativePath = Path.GetRelativePath(projectDir, filePath);
|
||||
string? fileContent = await File.ReadAllTextAsync(filePath, Encoding.UTF8);
|
||||
|
||||
// Обработка комментариев
|
||||
fileContent = FileProcessorService.ProcessFileContent(fileContent, Path.GetFileName(filePath));
|
||||
fileContent = FileProcessorService.RemoveMultiLineComments(fileContent);
|
||||
|
||||
outputContent.AppendLine($"=== Файл: {relativePath} ===");
|
||||
outputContent.AppendLine(fileContent);
|
||||
outputContent.AppendLine(); // Пустая строка между файлами
|
||||
|
||||
processedFiles++;
|
||||
int progressValue = (int)((processedFiles * 100.0) / totalFiles);
|
||||
progress?.Report(progressValue);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new IOException($"Файл '{Path.GetFileName(filePath)}' заблокирован или недоступен: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Сохраняем результат с кодировкой UTF-8 с BOM
|
||||
var encoding = new UTF8Encoding(true);
|
||||
await File.WriteAllTextAsync(outputPath, outputContent.ToString(), encoding);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CancelProcessing()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
private void LoadSettings()
|
||||
[RelayCommand]
|
||||
private void ExitApplication()
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
private void LoadProject(string projectPath)
|
||||
{
|
||||
SelectedProjectPath = projectPath;
|
||||
_settingsService.SaveLastProjectPath(Path.GetDirectoryName(projectPath));
|
||||
|
||||
IsProjectLoaded = false;
|
||||
LoadProjectAsync(projectPath);
|
||||
}
|
||||
|
||||
private async void LoadProjectAsync(string projectPath)
|
||||
{
|
||||
IsProcessing = true;
|
||||
ProgressText = "Загрузка проекта...";
|
||||
ProgressValue = 0;
|
||||
|
||||
try
|
||||
{
|
||||
LastProjectPath = Properties.Settings.Default.LastProjectPath;
|
||||
var progress = new Progress<int>(value =>
|
||||
{
|
||||
ProgressValue = value;
|
||||
ProgressText = $"Загрузка: {value}%";
|
||||
});
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
RootDirectory = await _projectLoaderService.LoadProjectFromPathAsync(projectPath, progress, _cancellationTokenSource.Token);
|
||||
|
||||
IsProjectLoaded = true;
|
||||
ProgressText = "Проект загружен успешно";
|
||||
|
||||
// Сбрасываем выделение после загрузки
|
||||
ClearSelections(RootDirectory);
|
||||
}
|
||||
catch
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
LastProjectPath = string.Empty;
|
||||
ProgressText = "Загрузка отменена";
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveSettings()
|
||||
{
|
||||
try
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Properties.Settings.Default.LastProjectPath = LastProjectPath;
|
||||
Properties.Settings.Default.Save();
|
||||
_uiService.ShowMessage($"Ошибка при загрузке проекта: {ex.Message}", "Ошибка", MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsProcessing = false;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/*public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
private void ClearSelections(FileItem item)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}*/
|
||||
}
|
||||
|
||||
/*
|
||||
public class RelayCommand : ICommand
|
||||
{
|
||||
private readonly Action<object> _execute;
|
||||
private readonly Func<object, bool> _canExecute;
|
||||
|
||||
public event EventHandler? CanExecuteChanged
|
||||
{
|
||||
add => CommandManager.RequerySuggested += value;
|
||||
remove => CommandManager.RequerySuggested -= value;
|
||||
item.IsSelected = false;
|
||||
foreach (var child in item.Children)
|
||||
{
|
||||
ClearSelections(child);
|
||||
}
|
||||
}
|
||||
|
||||
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
|
||||
private bool HasSelectedFiles(FileItem item)
|
||||
{
|
||||
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
|
||||
_canExecute = canExecute;
|
||||
if (item == null) return false;
|
||||
if (item.IsSelected == true && !item.IsDirectory) return true;
|
||||
foreach (var child in item.Children)
|
||||
{
|
||||
if (HasSelectedFiles(child)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
|
||||
public void Execute(object parameter) => _execute(parameter);
|
||||
}*/
|
||||
}
|
||||
@@ -1,95 +1,68 @@
|
||||
<Window
|
||||
x:Class="CodeContextGenerator.Views.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:CodeContextGenerator.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewmodels="clr-namespace:CodeContextGenerator.ViewModels"
|
||||
Title="Code Context Generator"
|
||||
Width="800"
|
||||
Height="600"
|
||||
d:DataContext="{d:DesignInstance Type=viewmodels:MainViewModel}"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
mc:Ignorable="d">
|
||||
<Window x:Class="CodeContextGenerator.Views.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:CodeContextGenerator.Views"
|
||||
mc:Ignorable="d"
|
||||
Title="Code Context Generator" Height="600" Width="800"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<Window.Resources>
|
||||
<Style TargetType="TreeViewItem">
|
||||
<Setter Property="IsExpanded" Value="True" />
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<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" />
|
||||
<Setter Property="Padding" Value="10,5"/>
|
||||
<Setter Property="Margin" Value="5,0"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Margin="0,0,0,5"
|
||||
FontWeight="Bold"
|
||||
Text="Выберите файл решения (.sln) или проекта (.csproj):" />
|
||||
<TextBlock Grid.Row="0" Text="Выберите файл решения, проекта или папку:" FontWeight="Bold" Margin="0,0,0,5"/>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="0,0,0,10"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Width="120"
|
||||
Command="{Binding SelectProjectFileCommand}"
|
||||
Content="Выбрать файл..."
|
||||
IsEnabled="{Binding CanSelectFolder}" />
|
||||
<TextBlock
|
||||
MaxWidth="600"
|
||||
Margin="10,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding SelectedProjectFilePath}"
|
||||
TextWrapping="Wrap" />
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<Button Content="Выбрать..."
|
||||
Command="{Binding SelectProjectCommand}"
|
||||
IsEnabled="{Binding CanSelectProject}"
|
||||
Width="100"/>
|
||||
<TextBlock Text="{Binding SelectedProjectPath}"
|
||||
VerticalAlignment="Center"
|
||||
TextWrapping="Wrap"
|
||||
MaxWidth="600"
|
||||
Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Padding="5"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4"
|
||||
Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<ScrollViewer
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
PreviewMouseWheel="ScrollViewer_PreviewMouseWheel"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<TreeView
|
||||
x:Name="ProjectTree"
|
||||
ItemsSource="{Binding RootDirectory.Children}"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling">
|
||||
<Border Grid.Row="2" BorderBrush="Gray" BorderThickness="1" CornerRadius="4" Padding="5"
|
||||
Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
|
||||
PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
|
||||
<TreeView x:Name="ProjectTree" ItemsSource="{Binding RootDirectory.Children}"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling">
|
||||
<TreeView.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||
<StackPanel MinHeight="24" Orientation="Horizontal">
|
||||
<CheckBox
|
||||
Margin="2,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
Click="CheckBox_Click"
|
||||
IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsThreeState="True" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Name}"
|
||||
ToolTip="{Binding FullName}" />
|
||||
<StackPanel Orientation="Horizontal" MinHeight="24">
|
||||
<CheckBox IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsThreeState="True"
|
||||
VerticalAlignment="Center" Margin="2,0,5,0"
|
||||
Click="CheckBox_Click"/>
|
||||
<TextBlock Text="{Binding Name}" VerticalAlignment="Center"
|
||||
ToolTip="{Binding FullName}"/>
|
||||
</StackPanel>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
@@ -97,49 +70,27 @@
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Foreground="Gray"
|
||||
Text="Проект еще не загружен. Выберите файл решения или проекта."
|
||||
Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Collapsed}" />
|
||||
<TextBlock Grid.Row="2" Text="Проект еще не загружен. Выберите файл решения или проекта."
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Foreground="Gray" FontSize="14"
|
||||
Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Collapsed}"/>
|
||||
|
||||
<ProgressBar
|
||||
Grid.Row="3"
|
||||
Height="20"
|
||||
Margin="0,10,0,10"
|
||||
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Value="{Binding ProgressValue}" />
|
||||
<ProgressBar Grid.Row="3" Value="{Binding ProgressValue}" Height="20" Margin="0,10,0,10"
|
||||
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="Bold"
|
||||
Text="{Binding ProgressText}"
|
||||
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<TextBlock Grid.Row="3" Text="{Binding ProgressText}" HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" FontWeight="Bold"
|
||||
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="4"
|
||||
Margin="0,10,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Background="#FFDC3545"
|
||||
Command="{Binding CancelCommand}"
|
||||
Content="Отмена"
|
||||
Foreground="White"
|
||||
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<Button Command="{Binding ExitCommand}" Content="Закрыть" />
|
||||
<Button
|
||||
Background="#FF28A745"
|
||||
Command="{Binding GenerateFileCommand}"
|
||||
Content="Сформировать"
|
||||
Foreground="White"
|
||||
IsEnabled="{Binding CanGenerate}"
|
||||
Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<StackPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
|
||||
<Button Content="Отмена" Command="{Binding CancelProcessingCommand}"
|
||||
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Background="#FFDC3545" Foreground="White"/>
|
||||
<Button Content="Закрыть" Command="{Binding ExitApplicationCommand}"/>
|
||||
<Button Content="Сформировать" Command="{Binding GenerateContextFileCommand}"
|
||||
IsEnabled="{Binding CanGenerate}"
|
||||
Background="#FF28A745" Foreground="White"
|
||||
Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -1,5 +1,4 @@
|
||||
using CodeContextGenerator.Models;
|
||||
using CodeContextGenerator.ViewModels;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
@@ -11,7 +10,6 @@ public partial class MainWindow : Window
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new MainViewModel();
|
||||
}
|
||||
|
||||
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
|
||||
@@ -27,8 +25,9 @@ public partial class MainWindow : Window
|
||||
{
|
||||
if (sender is CheckBox checkBox && checkBox.DataContext is FileItem fileItem)
|
||||
{
|
||||
// Синхронизация состояния чекбокса с ViewModel
|
||||
// Синхронизируем состояние чекбокса с ViewModel
|
||||
fileItem.IsSelected = checkBox.IsChecked;
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user