refactor mvvm, add datacontext
This commit is contained in:
@@ -8,6 +8,10 @@
|
|||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Properties\Settings.Designer.cs">
|
<Compile Update="Properties\Settings.Designer.cs">
|
||||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
|||||||
@@ -1,77 +1,66 @@
|
|||||||
using System.Collections.ObjectModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using System.ComponentModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace CodeContextGenerator.Models
|
namespace CodeContextGenerator.Models
|
||||||
{
|
{
|
||||||
public class FileItem : INotifyPropertyChanged
|
public partial class FileItem : ObservableObject
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
[ObservableProperty]
|
||||||
public string FullName { get; set; }
|
private string? name;
|
||||||
public bool IsDirectory { get; set; }
|
|
||||||
public ObservableCollection<FileItem> Children { get; set; } = new ObservableCollection<FileItem>();
|
|
||||||
public FileItem Parent { get; set; }
|
|
||||||
|
|
||||||
private bool? _isSelected;
|
[ObservableProperty]
|
||||||
public bool? IsSelected
|
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)
|
||||||
{
|
{
|
||||||
get => _isSelected;
|
if (newValue.HasValue)
|
||||||
set
|
|
||||||
{
|
{
|
||||||
if (_isSelected != value)
|
UpdateChildrenSelection(newValue.Value);
|
||||||
{
|
|
||||||
_isSelected = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
UpdateParentSelection();
|
|
||||||
|
|
||||||
// Если это директория и установлено конкретное значение (не null), применяем ко всем детям
|
|
||||||
if (IsDirectory && value.HasValue)
|
|
||||||
{
|
|
||||||
foreach (var child in Children)
|
|
||||||
{
|
|
||||||
child.IsSelected = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
UpdateParentSelection();
|
||||||
|
}*/
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
private void UpdateChildrenSelection(bool value)
|
||||||
|
|
||||||
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
|
||||||
{
|
{
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
if (!IsDirectory) return;
|
||||||
|
|
||||||
|
foreach (var child in Children)
|
||||||
|
{
|
||||||
|
child.IsSelected = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateParentSelection()
|
private void UpdateParentSelection()
|
||||||
{
|
{
|
||||||
if (Parent == null) return;
|
if (Parent == null) return;
|
||||||
|
|
||||||
var children = Parent.Children.ToList();
|
var children = Parent.Children;
|
||||||
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);
|
||||||
|
var hasIndeterminate = children.Any(c => c.IsSelected == null);
|
||||||
|
|
||||||
// Если есть дети с null - устанавливаем null
|
Parent.IsSelected = hasIndeterminate ? null : (allSelected ? true : (noneSelected ? false : null));
|
||||||
bool hasIndeterminate = children.Any(c => c.IsSelected == null);
|
|
||||||
|
|
||||||
if (hasIndeterminate)
|
|
||||||
{
|
|
||||||
Parent.IsSelected = null;
|
|
||||||
}
|
|
||||||
else if (allSelected)
|
|
||||||
{
|
|
||||||
Parent.IsSelected = true;
|
|
||||||
}
|
|
||||||
else if (noneSelected)
|
|
||||||
{
|
|
||||||
Parent.IsSelected = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Parent.IsSelected = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Parent.UpdateParentSelection();
|
Parent.UpdateParentSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ClearSelection()
|
||||||
|
{
|
||||||
|
IsSelected = false;
|
||||||
|
foreach (var child in Children)
|
||||||
|
{
|
||||||
|
child.ClearSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,522 +1,431 @@
|
|||||||
using System.ComponentModel;
|
using CodeContextGenerator.Models;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using Microsoft.Win32;
|
|
||||||
using CodeContextGenerator.Models;
|
|
||||||
using CodeContextGenerator.Services;
|
using CodeContextGenerator.Services;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Windows;
|
||||||
using System.Threading;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace CodeContextGenerator.ViewModels
|
namespace CodeContextGenerator.ViewModels;
|
||||||
|
|
||||||
|
public partial class MainViewModel : ObservableObject, INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
public class MainViewModel : INotifyPropertyChanged
|
[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 CancellationTokenSource _cancellationTokenSource = new();
|
||||||
|
|
||||||
|
public bool CanSelectFolder => !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()
|
||||||
{
|
{
|
||||||
private FileItem _rootDirectory;
|
//ExitCommand = new RelayCommand(_ => Application.Current.Shutdown());
|
||||||
private string _selectedProjectFilePath;
|
LoadSettings();
|
||||||
private bool _isProcessing;
|
}
|
||||||
private int _progressValue;
|
|
||||||
private string _progressText;
|
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
|
||||||
private string _lastProjectPath;
|
|
||||||
private bool _isProjectLoaded;
|
|
||||||
|
|
||||||
public FileItem RootDirectory
|
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
|
||||||
{
|
{
|
||||||
get => _rootDirectory;
|
Title = "Выберите файл решения или проекта",
|
||||||
set
|
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 =>
|
||||||
{
|
{
|
||||||
_rootDirectory = value;
|
ProgressValue = value;
|
||||||
OnPropertyChanged();
|
ProgressText = $"Загрузка: {value}%";
|
||||||
|
});
|
||||||
|
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
if (projectFilePath.EndsWith(".sln", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
await LoadSolutionAsync(projectFilePath, progress, _cancellationTokenSource.Token);
|
||||||
}
|
}
|
||||||
}
|
else if (projectFilePath.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase))
|
||||||
|
|
||||||
public string SelectedProjectFilePath
|
|
||||||
{
|
|
||||||
get => _selectedProjectFilePath;
|
|
||||||
set
|
|
||||||
{
|
{
|
||||||
_selectedProjectFilePath = value;
|
await LoadProjectDirectoryAsync(projectDirectory, progress, _cancellationTokenSource.Token);
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsProcessing
|
|
||||||
{
|
|
||||||
get => _isProcessing;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_isProcessing = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
OnPropertyChanged(nameof(CanSelectFolder));
|
|
||||||
OnPropertyChanged(nameof(CanGenerate));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ProgressValue
|
|
||||||
{
|
|
||||||
get => _progressValue;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_progressValue = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ProgressText
|
|
||||||
{
|
|
||||||
get => _progressText;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_progressText = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsProjectLoaded
|
|
||||||
{
|
|
||||||
get => _isProjectLoaded;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_isProjectLoaded = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanSelectFolder => !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()
|
|
||||||
{
|
|
||||||
SelectProjectFileCommand = new RelayCommand(SelectProjectFileAsync);
|
|
||||||
GenerateCommand = new RelayCommand(GenerateFileAsync, _ => CanGenerate);
|
|
||||||
CancelCommand = new RelayCommand(CancelProcessing, _ => IsProcessing);
|
|
||||||
ExitCommand = new RelayCommand(_ => Application.Current.Shutdown());
|
|
||||||
|
|
||||||
LoadSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool HasSelectedFiles(FileItem item)
|
|
||||||
{
|
|
||||||
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;
|
IsProjectLoaded = true;
|
||||||
|
ProgressText = "Проект загружен успешно";
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
private async void SelectProjectFileAsync(object parameter)
|
|
||||||
{
|
{
|
||||||
if (!CanSelectFolder) return;
|
ProgressText = "Загрузка отменена";
|
||||||
|
RootDirectory = null;
|
||||||
|
IsProjectLoaded = false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Ошибка при загрузке проекта: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
RootDirectory = null;
|
||||||
|
IsProjectLoaded = false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsProcessing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var dialog = new OpenFileDialog
|
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
|
||||||
{
|
{
|
||||||
Title = "Выберите файл решения или проекта",
|
Name = Path.GetFileName(solutionPath),
|
||||||
CheckFileExists = true,
|
FullName = solutionPath,
|
||||||
CheckPathExists = true,
|
IsDirectory = true,
|
||||||
Filter = "Файлы решений Visual Studio (*.sln)|*.sln|Файлы проектов C# (*.csproj)|*.csproj|Все поддерживаемые файлы (*.sln;*.csproj)|*.sln;*.csproj|Все файлы (*.*)|*.*",
|
IsSelected = false
|
||||||
Multiselect = false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_lastProjectPath) && Directory.Exists(_lastProjectPath))
|
int totalProjects = solutionProjects.Count;
|
||||||
{
|
int processedProjects = 0;
|
||||||
dialog.InitialDirectory = _lastProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool? result = dialog.ShowDialog();
|
foreach (var projectPath in solutionProjects)
|
||||||
|
|
||||||
if (result == true)
|
|
||||||
{
|
|
||||||
string selectedFilePath = dialog.FileName;
|
|
||||||
string projectDirectory = Path.GetDirectoryName(selectedFilePath);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(projectDirectory))
|
|
||||||
{
|
|
||||||
_lastProjectPath = projectDirectory;
|
|
||||||
SaveSettings();
|
|
||||||
|
|
||||||
SelectedProjectFilePath = selectedFilePath;
|
|
||||||
IsProjectLoaded = false;
|
|
||||||
|
|
||||||
await LoadProjectAsync(selectedFilePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LoadProjectAsync(string projectFilePath)
|
|
||||||
{
|
|
||||||
IsProcessing = true;
|
|
||||||
ProgressText = "Загрузка проекта...";
|
|
||||||
ProgressValue = 0;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string projectDirectory = Path.GetDirectoryName(projectFilePath);
|
|
||||||
SelectedProjectFilePath = 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 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)
|
|
||||||
{
|
|
||||||
if (!CanGenerate || RootDirectory == null) return;
|
|
||||||
|
|
||||||
var selectedFiles = GetSelectedFiles(RootDirectory);
|
|
||||||
|
|
||||||
if (selectedFiles.Count == 0)
|
|
||||||
{
|
|
||||||
MessageBox.Show("Пожалуйста, выберите хотя бы один файл для обработки.", "Предупреждение", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var saveDialog = new SaveFileDialog
|
|
||||||
{
|
|
||||||
Title = "Сохранить контекстный файл",
|
|
||||||
Filter = "Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*",
|
|
||||||
FileName = $"{Path.GetFileNameWithoutExtension(SelectedProjectFilePath)}_context.txt",
|
|
||||||
DefaultExt = ".txt"
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_lastProjectPath) && Directory.Exists(_lastProjectPath))
|
|
||||||
{
|
|
||||||
saveDialog.InitialDirectory = _lastProjectPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool? result = saveDialog.ShowDialog();
|
|
||||||
|
|
||||||
if (result == true)
|
|
||||||
{
|
|
||||||
IsProcessing = true;
|
|
||||||
ProgressText = "Генерация контекстного файла...";
|
|
||||||
ProgressValue = 0;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
var progress = new Progress<int>(value =>
|
|
||||||
{
|
|
||||||
ProgressValue = value;
|
|
||||||
ProgressText = $"Генерация: {value}%";
|
|
||||||
});
|
|
||||||
|
|
||||||
await GenerateContextFileAsync(selectedFiles, saveDialog.FileName, progress, _cancellationTokenSource.Token);
|
|
||||||
|
|
||||||
MessageBox.Show($"Файл успешно создан:\n{saveDialog.FileName}", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
ProgressText = "Генерация отменена";
|
|
||||||
}
|
|
||||||
catch (IOException ex)
|
|
||||||
{
|
|
||||||
MessageBox.Show($"Ошибка доступа к файлу: {ex.Message}\nПожалуйста, закройте файлы или предоставьте необходимые права доступа.", "Ошибка доступа", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
MessageBox.Show($"Ошибка при генерации файла: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
IsProcessing = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
var outputContent = new StringBuilder();
|
|
||||||
int totalFiles = selectedFiles.Count;
|
|
||||||
int processedFiles = 0;
|
|
||||||
|
|
||||||
foreach (var filePath in selectedFiles)
|
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
try
|
if (File.Exists(projectPath))
|
||||||
{
|
{
|
||||||
string projectDir = Path.GetDirectoryName(SelectedProjectFilePath);
|
string? projectDir = Path.GetDirectoryName(projectPath);
|
||||||
string relativePath = Path.GetRelativePath(projectDir, filePath);
|
string? projectName = Path.GetFileName(projectDir);
|
||||||
string fileContent = await File.ReadAllTextAsync(filePath, Encoding.UTF8);
|
|
||||||
|
|
||||||
// Обработка комментариев
|
var projectItem = new FileItem
|
||||||
fileContent = FileProcessorService.ProcessFileContent(fileContent, Path.GetFileName(filePath));
|
{
|
||||||
fileContent = FileProcessorService.RemoveMultiLineComments(fileContent);
|
Name = projectName,
|
||||||
|
FullName = projectDir,
|
||||||
|
IsDirectory = true,
|
||||||
|
Parent = solutionItem,
|
||||||
|
IsSelected = false
|
||||||
|
};
|
||||||
|
|
||||||
outputContent.AppendLine($"=== Файл: {relativePath} ===");
|
await ProjectScannerService.BuildDirectoryTreeAsync(projectDir, projectItem, progress, cancellationToken);
|
||||||
outputContent.AppendLine(fileContent);
|
|
||||||
outputContent.AppendLine(); // Пустая строка между файлами
|
|
||||||
|
|
||||||
processedFiles++;
|
if (projectItem.Children.Any())
|
||||||
int progressValue = (int)((processedFiles * 100.0) / totalFiles);
|
{
|
||||||
progress?.Report(progressValue);
|
solutionItem.Children.Add(projectItem);
|
||||||
}
|
}
|
||||||
catch (IOException ex)
|
|
||||||
{
|
|
||||||
throw new IOException($"Файл '{Path.GetFileName(filePath)}' заблокирован или недоступен: {ex.Message}", ex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processedProjects++;
|
||||||
|
progress?.Report((int)((processedProjects * 100.0) / totalProjects));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сохраняем результат с кодировкой UTF-8 с BOM
|
RootDirectory = solutionItem;
|
||||||
var encoding = new UTF8Encoding(true);
|
|
||||||
await File.WriteAllTextAsync(outputPath, outputContent.ToString(), encoding);
|
// После загрузки решения - не выбираем ничего по умолчанию
|
||||||
|
ClearAllSelections(solutionItem);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
private void CancelProcessing(object parameter)
|
|
||||||
{
|
{
|
||||||
_cancellationTokenSource?.Cancel();
|
throw new Exception($"Ошибка при обработке решения: {ex.Message}", ex);
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadSettings()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_lastProjectPath = Properties.Settings.Default.LastProjectPath;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
_lastProjectPath = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SaveSettings()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Properties.Settings.Default.LastProjectPath = _lastProjectPath;
|
|
||||||
Properties.Settings.Default.Save();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Игнорируем ошибки сохранения настроек
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
|
||||||
|
|
||||||
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
|
||||||
{
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RelayCommand : ICommand
|
private static List<string> ParseSolutionProjects(string solutionPath)
|
||||||
{
|
{
|
||||||
private readonly Action<object> _execute;
|
var projects = new List<string>();
|
||||||
private readonly Func<object, bool> _canExecute;
|
try
|
||||||
|
|
||||||
public event EventHandler? CanExecuteChanged
|
|
||||||
{
|
{
|
||||||
add => CommandManager.RequerySuggested += value;
|
string? solutionDirectory = Path.GetDirectoryName(solutionPath);
|
||||||
remove => CommandManager.RequerySuggested -= value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
|
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)
|
||||||
{
|
{
|
||||||
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
|
throw new Exception($"Ошибка парсинга файла решения: {ex.Message}", ex);
|
||||||
_canExecute = canExecute;
|
|
||||||
}
|
}
|
||||||
|
return projects;
|
||||||
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
|
|
||||||
public void Execute(object parameter) => _execute(parameter);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var saveDialog = new SaveFileDialog
|
||||||
|
{
|
||||||
|
Title = "Сохранить контекстный файл",
|
||||||
|
Filter = "Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*",
|
||||||
|
FileName = $"{Path.GetFileNameWithoutExtension(SelectedProjectFilePath)}_context.txt",
|
||||||
|
DefaultExt = ".txt",
|
||||||
|
InitialDirectory = LastProjectPath
|
||||||
|
};
|
||||||
|
|
||||||
|
if (saveDialog.ShowDialog() != true) return;
|
||||||
|
|
||||||
|
IsProcessing = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
var progress = new Progress<int>(value =>
|
||||||
|
{
|
||||||
|
ProgressValue = value;
|
||||||
|
ProgressText = $"Генерация: {value}%";
|
||||||
|
});
|
||||||
|
|
||||||
|
await GenerateContextFileAsync(selectedFiles, saveDialog.FileName, progress, _cancellationTokenSource.Token);
|
||||||
|
MessageBox.Show($"Файл успешно создан:\n{saveDialog.FileName}", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
ProgressText = "Генерация отменена";
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Ошибка доступа к файлу: {ex.Message}", "Ошибка доступа", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Ошибка при генерации файла: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsProcessing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LastProjectPath = Properties.Settings.Default.LastProjectPath;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
LastProjectPath = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Properties.Settings.Default.LastProjectPath = LastProjectPath;
|
||||||
|
Properties.Settings.Default.Save();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
|
||||||
|
{
|
||||||
|
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
|
||||||
|
_canExecute = canExecute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
|
||||||
|
public void Execute(object parameter) => _execute(parameter);
|
||||||
|
}*/
|
||||||
@@ -1,68 +1,95 @@
|
|||||||
<Window x:Class="CodeContextGenerator.Views.MainWindow"
|
<Window
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
x:Class="CodeContextGenerator.Views.MainWindow"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:CodeContextGenerator.Views"
|
xmlns:local="clr-namespace:CodeContextGenerator.Views"
|
||||||
mc:Ignorable="d"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
Title="Code Context Generator" Height="600" Width="800"
|
xmlns:viewmodels="clr-namespace:CodeContextGenerator.ViewModels"
|
||||||
WindowStartupLocation="CenterScreen">
|
Title="Code Context Generator"
|
||||||
|
Width="800"
|
||||||
|
Height="600"
|
||||||
|
d:DataContext="{d:DesignInstance Type=viewmodels:MainViewModel}"
|
||||||
|
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"/>
|
<Setter Property="Focusable" Value="False" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style TargetType="Button">
|
<Style TargetType="Button">
|
||||||
<Setter Property="Padding" Value="10,5"/>
|
<Setter Property="Padding" Value="10,5" />
|
||||||
<Setter Property="Margin" Value="5,0"/>
|
<Setter Property="Margin" Value="5,0" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style TargetType="TextBlock">
|
<Style TargetType="TextBlock">
|
||||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
<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 Grid.Row="0" Text="Выберите файл решения (.sln) или проекта (.csproj):" FontWeight="Bold" Margin="0,0,0,5"/>
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Margin="0,0,0,5"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Text="Выберите файл решения (.sln) или проекта (.csproj):" />
|
||||||
|
|
||||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,10">
|
<StackPanel
|
||||||
<Button Content="Выбрать файл..."
|
Grid.Row="1"
|
||||||
Command="{Binding SelectProjectFileCommand}"
|
Margin="0,0,0,10"
|
||||||
IsEnabled="{Binding CanSelectFolder}"
|
Orientation="Horizontal">
|
||||||
Width="120"/>
|
<Button
|
||||||
<TextBlock Text="{Binding SelectedProjectFilePath}"
|
Width="120"
|
||||||
VerticalAlignment="Center"
|
Command="{Binding SelectProjectFileCommand}"
|
||||||
TextWrapping="Wrap"
|
Content="Выбрать файл..."
|
||||||
MaxWidth="600"
|
IsEnabled="{Binding CanSelectFolder}" />
|
||||||
Margin="10,0,0,0"/>
|
<TextBlock
|
||||||
|
MaxWidth="600"
|
||||||
|
Margin="10,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding SelectedProjectFilePath}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<Border Grid.Row="2" BorderBrush="Gray" BorderThickness="1" CornerRadius="4" Padding="5"
|
<Border
|
||||||
Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}}">
|
Grid.Row="2"
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
|
Padding="5"
|
||||||
PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
|
BorderBrush="Gray"
|
||||||
<TreeView x:Name="ProjectTree" ItemsSource="{Binding RootDirectory.Children}"
|
BorderThickness="1"
|
||||||
VirtualizingPanel.IsVirtualizing="True"
|
CornerRadius="4"
|
||||||
VirtualizingPanel.VirtualizationMode="Recycling">
|
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">
|
||||||
<TreeView.ItemTemplate>
|
<TreeView.ItemTemplate>
|
||||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||||
<StackPanel Orientation="Horizontal" MinHeight="24">
|
<StackPanel MinHeight="24" Orientation="Horizontal">
|
||||||
<CheckBox IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}"
|
<CheckBox
|
||||||
IsThreeState="True"
|
Margin="2,0,5,0"
|
||||||
VerticalAlignment="Center" Margin="2,0,5,0"
|
VerticalAlignment="Center"
|
||||||
Click="CheckBox_Click"/>
|
Click="CheckBox_Click"
|
||||||
<TextBlock Text="{Binding Name}" VerticalAlignment="Center"
|
IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}"
|
||||||
ToolTip="{Binding FullName}"/>
|
IsThreeState="True" />
|
||||||
|
<TextBlock
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding Name}"
|
||||||
|
ToolTip="{Binding FullName}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</HierarchicalDataTemplate>
|
</HierarchicalDataTemplate>
|
||||||
</TreeView.ItemTemplate>
|
</TreeView.ItemTemplate>
|
||||||
@@ -70,27 +97,49 @@
|
|||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<TextBlock Grid.Row="2" Text="Проект еще не загружен. Выберите файл решения или проекта."
|
<TextBlock
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
Grid.Row="2"
|
||||||
Foreground="Gray" FontSize="14"
|
HorizontalAlignment="Center"
|
||||||
Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Collapsed}"/>
|
VerticalAlignment="Center"
|
||||||
|
FontSize="14"
|
||||||
|
Foreground="Gray"
|
||||||
|
Text="Проект еще не загружен. Выберите файл решения или проекта."
|
||||||
|
Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Collapsed}" />
|
||||||
|
|
||||||
<ProgressBar Grid.Row="3" Value="{Binding ProgressValue}" Height="20" Margin="0,10,0,10"
|
<ProgressBar
|
||||||
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
Grid.Row="3"
|
||||||
|
Height="20"
|
||||||
|
Margin="0,10,0,10"
|
||||||
|
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||||
|
Value="{Binding ProgressValue}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="3" Text="{Binding ProgressText}" HorizontalAlignment="Center"
|
<TextBlock
|
||||||
VerticalAlignment="Center" FontWeight="Bold"
|
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 Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
|
<StackPanel
|
||||||
<Button Content="Отмена" Command="{Binding CancelCommand}"
|
Grid.Row="4"
|
||||||
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}"
|
Margin="0,10,0,0"
|
||||||
Background="#FFDC3545" Foreground="White"/>
|
HorizontalAlignment="Right"
|
||||||
<Button Content="Закрыть" Command="{Binding ExitCommand}"/>
|
Orientation="Horizontal">
|
||||||
<Button Content="Сформировать" Command="{Binding GenerateCommand}"
|
<Button
|
||||||
IsEnabled="{Binding CanGenerate}"
|
Background="#FFDC3545"
|
||||||
Background="#FF28A745" Foreground="White"
|
Command="{Binding CancelCommand}"
|
||||||
Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
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>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
@@ -4,37 +4,31 @@ using System.Windows;
|
|||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
|
||||||
namespace CodeContextGenerator.Views
|
namespace CodeContextGenerator.Views;
|
||||||
|
|
||||||
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
public partial class MainWindow : Window
|
public MainWindow()
|
||||||
{
|
{
|
||||||
public MainWindow()
|
InitializeComponent();
|
||||||
{
|
DataContext = new MainViewModel();
|
||||||
InitializeComponent();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
|
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is ScrollViewer scrollViewer)
|
||||||
{
|
{
|
||||||
if (sender is ScrollViewer scrollViewer)
|
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta / 3);
|
||||||
{
|
e.Handled = true;
|
||||||
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta / 3);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void CheckBox_Click(object sender, RoutedEventArgs e)
|
private void CheckBox_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is CheckBox checkBox && checkBox.DataContext is FileItem fileItem)
|
||||||
{
|
{
|
||||||
if (sender is CheckBox checkBox && checkBox.DataContext is FileItem fileItem)
|
// Синхронизация состояния чекбокса с ViewModel
|
||||||
{
|
fileItem.IsSelected = checkBox.IsChecked;
|
||||||
// Принудительно обновляем состояние детей при клике
|
|
||||||
if (fileItem.IsDirectory && checkBox.IsChecked.HasValue)
|
|
||||||
{
|
|
||||||
foreach (var child in fileItem.Children)
|
|
||||||
{
|
|
||||||
child.IsSelected = checkBox.IsChecked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user