refactor mvvm, add datacontext

This commit is contained in:
2025-12-10 21:33:06 +05:00
parent b6ddadc6d3
commit 5479da163e
5 changed files with 566 additions and 621 deletions

View File

@@ -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>

View File

@@ -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(); UpdateParentSelection();
}*/
// Если это директория и установлено конкретное значение (не null), применяем ко всем детям private void UpdateChildrenSelection(bool value)
if (IsDirectory && value.HasValue)
{ {
if (!IsDirectory) return;
foreach (var child in Children) foreach (var child in Children)
{ {
child.IsSelected = value.Value; child.IsSelected = value;
} }
} }
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
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();
}
}
} }
} }

View File

@@ -1,162 +1,89 @@
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();
private FileItem _rootDirectory; [ObservableProperty]
private string _selectedProjectFilePath; private string _selectedProjectFilePath = string.Empty;
private bool _isProcessing; [ObservableProperty]
private bool _isProcessing = false;
[ObservableProperty]
private int _progressValue; private int _progressValue;
private string _progressText; [ObservableProperty]
private CancellationTokenSource _cancellationTokenSource; private string _progressText = string.Empty;
private string _lastProjectPath; [ObservableProperty]
private string? _lastProjectPath = string.Empty;
[ObservableProperty]
private bool _isProjectLoaded; private bool _isProjectLoaded;
public FileItem RootDirectory private CancellationTokenSource _cancellationTokenSource = new();
{
get => _rootDirectory;
set
{
_rootDirectory = value;
OnPropertyChanged();
}
}
public string SelectedProjectFilePath
{
get => _selectedProjectFilePath;
set
{
_selectedProjectFilePath = value;
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 CanSelectFolder => !IsProcessing;
public bool CanGenerate => !IsProcessing && IsProjectLoaded && HasSelectedFiles(RootDirectory); public bool CanGenerate => !IsProcessing && IsProjectLoaded && HasSelectedFiles(RootDirectory);
public ICommand SelectProjectFileCommand { get; } //public ICommand SelectProjectFileCommand { get; }
public ICommand GenerateCommand { get; } //public ICommand GenerateCommand { get; }
public ICommand CancelCommand { get; } //public ICommand CancelCommand { get; }
public ICommand ExitCommand { get; } //public ICommand ExitCommand { get; }
public MainViewModel() public MainViewModel()
{ {
SelectProjectFileCommand = new RelayCommand(SelectProjectFileAsync); //ExitCommand = new RelayCommand(_ => Application.Current.Shutdown());
GenerateCommand = new RelayCommand(GenerateFileAsync, _ => CanGenerate);
CancelCommand = new RelayCommand(CancelProcessing, _ => IsProcessing);
ExitCommand = new RelayCommand(_ => Application.Current.Shutdown());
LoadSettings(); LoadSettings();
} }
private bool HasSelectedFiles(FileItem item) private bool HasSelectedFiles(FileItem item)
{ {
if (item == null) return false; if (item == null) return false;
if (item.IsSelected == true && !item.IsDirectory) return true;
if (item.IsSelected == true && !item.IsDirectory) return item.Children.Any(HasSelectedFiles);
return true;
foreach (var child in item.Children)
{
if (HasSelectedFiles(child))
return true;
} }
return false; //[RelayCommand(CanExecute = nameof(CanSelectFolder))]
[RelayCommand(CanExecute = nameof(CanSelectFolder))]
private void Exit()
{
Application.Current.Shutdown();
} }
private async void SelectProjectFileAsync(object parameter) //[RelayCommand(CanExecute = nameof(CanSelectFolder))]
[RelayCommand(CanExecute = nameof(CanGenerate))]
private void Cancel()
{ {
if (!CanSelectFolder) return; CancelProcessing();
}
[RelayCommand(CanExecute = nameof(CanSelectFolder))]
private async Task SelectProjectFileAsync()
{
var dialog = new OpenFileDialog var dialog = new OpenFileDialog
{ {
Title = "Выберите файл решения или проекта", Title = "Выберите файл решения или проекта",
CheckFileExists = true, CheckFileExists = true,
CheckPathExists = true, CheckPathExists = true,
Filter = "Файлы решений Visual Studio (*.sln)|*.sln|Файлы проектов C# (*.csproj)|*.csproj|Все поддерживаемые файлы (*.sln;*.csproj)|*.sln;*.csproj|Все файлы (*.*)|*.*", Filter = "Файлы решений (*.sln)|*.sln|Файлы проектов (*.csproj)|*.csproj|Все поддерживаемые (*.sln;*.csproj)|*.sln;*.csproj|Все файлы (*.*)|*.*",
Multiselect = false InitialDirectory = !string.IsNullOrEmpty(LastProjectPath) && Directory.Exists(LastProjectPath) ? LastProjectPath : null
}; };
if (!string.IsNullOrEmpty(_lastProjectPath) && Directory.Exists(_lastProjectPath)) if (dialog.ShowDialog() != true) return;
{
dialog.InitialDirectory = _lastProjectPath;
}
bool? result = dialog.ShowDialog(); SelectedProjectFilePath = dialog.FileName;
LastProjectPath = Path.GetDirectoryName(SelectedProjectFilePath);
if (result == true)
{
string selectedFilePath = dialog.FileName;
string projectDirectory = Path.GetDirectoryName(selectedFilePath);
if (!string.IsNullOrEmpty(projectDirectory))
{
_lastProjectPath = projectDirectory;
SaveSettings(); SaveSettings();
SelectedProjectFilePath = selectedFilePath;
IsProjectLoaded = false; IsProjectLoaded = false;
await LoadProjectAsync(SelectedProjectFilePath);
await LoadProjectAsync(selectedFilePath);
}
}
} }
private async Task LoadProjectAsync(string projectFilePath) private async Task LoadProjectAsync(string projectFilePath)
@@ -167,9 +94,7 @@ namespace CodeContextGenerator.ViewModels
try try
{ {
string projectDirectory = Path.GetDirectoryName(projectFilePath); string? projectDirectory = Path.GetDirectoryName(projectFilePath);
SelectedProjectFilePath = projectFilePath;
var progress = new Progress<int>(value => var progress = new Progress<int>(value =>
{ {
ProgressValue = value; ProgressValue = value;
@@ -178,7 +103,6 @@ namespace CodeContextGenerator.ViewModels
_cancellationTokenSource = new CancellationTokenSource(); _cancellationTokenSource = new CancellationTokenSource();
// Определяем тип файла
if (projectFilePath.EndsWith(".sln", StringComparison.OrdinalIgnoreCase)) if (projectFilePath.EndsWith(".sln", StringComparison.OrdinalIgnoreCase))
{ {
await LoadSolutionAsync(projectFilePath, progress, _cancellationTokenSource.Token); await LoadSolutionAsync(projectFilePath, progress, _cancellationTokenSource.Token);
@@ -213,7 +137,7 @@ namespace CodeContextGenerator.ViewModels
{ {
try try
{ {
string solutionDirectory = Path.GetDirectoryName(solutionPath); string? solutionDirectory = Path.GetDirectoryName(solutionPath);
var solutionProjects = ParseSolutionProjects(solutionPath); var solutionProjects = ParseSolutionProjects(solutionPath);
var solutionItem = new FileItem var solutionItem = new FileItem
@@ -233,8 +157,8 @@ namespace CodeContextGenerator.ViewModels
if (File.Exists(projectPath)) if (File.Exists(projectPath))
{ {
string projectDir = Path.GetDirectoryName(projectPath); string? projectDir = Path.GetDirectoryName(projectPath);
string projectName = Path.GetFileName(projectDir); string? projectName = Path.GetFileName(projectDir);
var projectItem = new FileItem var projectItem = new FileItem
{ {
@@ -268,18 +192,18 @@ namespace CodeContextGenerator.ViewModels
} }
} }
private List<string> ParseSolutionProjects(string solutionPath) private static List<string> ParseSolutionProjects(string solutionPath)
{ {
var projects = new List<string>(); var projects = new List<string>();
try try
{ {
string solutionDirectory = Path.GetDirectoryName(solutionPath); string? solutionDirectory = Path.GetDirectoryName(solutionPath);
foreach (string line in File.ReadAllLines(solutionPath)) foreach (string line in File.ReadAllLines(solutionPath))
{ {
if (line.Trim().StartsWith("Project(", StringComparison.OrdinalIgnoreCase)) if (line.Trim().StartsWith("Project(", StringComparison.OrdinalIgnoreCase))
{ {
var parts = line.Split(new[] { '"' }, StringSplitOptions.RemoveEmptyEntries); var parts = line.Split(['"'], StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 3) if (parts.Length >= 3)
{ {
string relativePath = parts[2].Trim(); string relativePath = parts[2].Trim();
@@ -326,7 +250,7 @@ namespace CodeContextGenerator.ViewModels
} }
} }
private void ClearAllSelections(FileItem item) private static void ClearAllSelections(FileItem item)
{ {
item.IsSelected = false; item.IsSelected = false;
foreach (var child in item.Children) foreach (var child in item.Children)
@@ -335,12 +259,10 @@ namespace CodeContextGenerator.ViewModels
} }
} }
private async void GenerateFileAsync(object parameter) [RelayCommand(CanExecute = nameof(CanGenerate))]
private async Task GenerateFileAsync()
{ {
if (!CanGenerate || RootDirectory == null) return;
var selectedFiles = GetSelectedFiles(RootDirectory); var selectedFiles = GetSelectedFiles(RootDirectory);
if (selectedFiles.Count == 0) if (selectedFiles.Count == 0)
{ {
MessageBox.Show("Пожалуйста, выберите хотя бы один файл для обработки.", "Предупреждение", MessageBoxButton.OK, MessageBoxImage.Warning); MessageBox.Show("Пожалуйста, выберите хотя бы один файл для обработки.", "Предупреждение", MessageBoxButton.OK, MessageBoxImage.Warning);
@@ -352,22 +274,13 @@ namespace CodeContextGenerator.ViewModels
Title = "Сохранить контекстный файл", Title = "Сохранить контекстный файл",
Filter = "Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*", Filter = "Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*",
FileName = $"{Path.GetFileNameWithoutExtension(SelectedProjectFilePath)}_context.txt", FileName = $"{Path.GetFileNameWithoutExtension(SelectedProjectFilePath)}_context.txt",
DefaultExt = ".txt" DefaultExt = ".txt",
InitialDirectory = LastProjectPath
}; };
if (!string.IsNullOrEmpty(_lastProjectPath) && Directory.Exists(_lastProjectPath)) if (saveDialog.ShowDialog() != true) return;
{
saveDialog.InitialDirectory = _lastProjectPath;
}
bool? result = saveDialog.ShowDialog();
if (result == true)
{
IsProcessing = true; IsProcessing = true;
ProgressText = "Генерация контекстного файла...";
ProgressValue = 0;
try try
{ {
_cancellationTokenSource = new CancellationTokenSource(); _cancellationTokenSource = new CancellationTokenSource();
@@ -378,7 +291,6 @@ namespace CodeContextGenerator.ViewModels
}); });
await GenerateContextFileAsync(selectedFiles, saveDialog.FileName, progress, _cancellationTokenSource.Token); await GenerateContextFileAsync(selectedFiles, saveDialog.FileName, progress, _cancellationTokenSource.Token);
MessageBox.Show($"Файл успешно создан:\n{saveDialog.FileName}", "Успех", MessageBoxButton.OK, MessageBoxImage.Information); MessageBox.Show($"Файл успешно создан:\n{saveDialog.FileName}", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
@@ -387,7 +299,7 @@ namespace CodeContextGenerator.ViewModels
} }
catch (IOException ex) catch (IOException ex)
{ {
MessageBox.Show($"Ошибка доступа к файлу: {ex.Message}\nПожалуйста, закройте файлы или предоставьте необходимые права доступа.", "Ошибка доступа", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show($"Ошибка доступа к файлу: {ex.Message}", "Ошибка доступа", MessageBoxButton.OK, MessageBoxImage.Error);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -398,7 +310,6 @@ namespace CodeContextGenerator.ViewModels
IsProcessing = false; IsProcessing = false;
} }
} }
}
private List<string> GetSelectedFiles(FileItem rootItem) private List<string> GetSelectedFiles(FileItem rootItem)
{ {
@@ -407,7 +318,7 @@ namespace CodeContextGenerator.ViewModels
return selectedFiles; return selectedFiles;
} }
private void CollectSelectedFiles(FileItem item, List<string> selectedFiles) private static void CollectSelectedFiles(FileItem item, List<string> selectedFiles)
{ {
if (item.IsDirectory) if (item.IsDirectory)
{ {
@@ -434,9 +345,9 @@ namespace CodeContextGenerator.ViewModels
try try
{ {
string projectDir = Path.GetDirectoryName(SelectedProjectFilePath); string? projectDir = Path.GetDirectoryName(SelectedProjectFilePath);
string relativePath = Path.GetRelativePath(projectDir, filePath); string? relativePath = Path.GetRelativePath(projectDir, filePath);
string fileContent = await File.ReadAllTextAsync(filePath, Encoding.UTF8); string? fileContent = await File.ReadAllTextAsync(filePath, Encoding.UTF8);
// Обработка комментариев // Обработка комментариев
fileContent = FileProcessorService.ProcessFileContent(fileContent, Path.GetFileName(filePath)); fileContent = FileProcessorService.ProcessFileContent(fileContent, Path.GetFileName(filePath));
@@ -461,7 +372,8 @@ namespace CodeContextGenerator.ViewModels
await File.WriteAllTextAsync(outputPath, outputContent.ToString(), encoding); await File.WriteAllTextAsync(outputPath, outputContent.ToString(), encoding);
} }
private void CancelProcessing(object parameter) [RelayCommand]
private void CancelProcessing()
{ {
_cancellationTokenSource?.Cancel(); _cancellationTokenSource?.Cancel();
} }
@@ -470,11 +382,11 @@ namespace CodeContextGenerator.ViewModels
{ {
try try
{ {
_lastProjectPath = Properties.Settings.Default.LastProjectPath; LastProjectPath = Properties.Settings.Default.LastProjectPath;
} }
catch catch
{ {
_lastProjectPath = null; LastProjectPath = string.Empty;
} }
} }
@@ -482,23 +394,21 @@ namespace CodeContextGenerator.ViewModels
{ {
try try
{ {
Properties.Settings.Default.LastProjectPath = _lastProjectPath; Properties.Settings.Default.LastProjectPath = LastProjectPath;
Properties.Settings.Default.Save(); Properties.Settings.Default.Save();
} }
catch catch { }
{
// Игнорируем ошибки сохранения настроек
}
} }
public event PropertyChangedEventHandler? PropertyChanged; /*public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }*/
} }
/*
public class RelayCommand : ICommand public class RelayCommand : ICommand
{ {
private readonly Action<object> _execute; private readonly Action<object> _execute;
@@ -518,5 +428,4 @@ namespace CodeContextGenerator.ViewModels
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter); public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
public void Execute(object parameter) => _execute(parameter); public void Execute(object parameter) => _execute(parameter);
} }*/
}

View File

@@ -1,12 +1,17 @@
<Window x:Class="CodeContextGenerator.Views.MainWindow" <Window
x:Class="CodeContextGenerator.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CodeContextGenerator.Views" xmlns:local="clr-namespace:CodeContextGenerator.Views"
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">
@@ -33,35 +38,57 @@
<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"
Margin="0,0,0,10"
Orientation="Horizontal">
<Button
Width="120"
Command="{Binding SelectProjectFileCommand}" Command="{Binding SelectProjectFileCommand}"
IsEnabled="{Binding CanSelectFolder}" Content="Выбрать файл..."
Width="120"/> IsEnabled="{Binding CanSelectFolder}" />
<TextBlock Text="{Binding SelectedProjectFilePath}" <TextBlock
VerticalAlignment="Center"
TextWrapping="Wrap"
MaxWidth="600" MaxWidth="600"
Margin="10,0,0,0"/> 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
Grid.Row="2"
Padding="5"
BorderBrush="Gray"
BorderThickness="1"
CornerRadius="4"
Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}}"> Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}}">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" <ScrollViewer
PreviewMouseWheel="ScrollViewer_PreviewMouseWheel"> HorizontalScrollBarVisibility="Auto"
<TreeView x:Name="ProjectTree" ItemsSource="{Binding RootDirectory.Children}" PreviewMouseWheel="ScrollViewer_PreviewMouseWheel"
VerticalScrollBarVisibility="Auto">
<TreeView
x:Name="ProjectTree"
ItemsSource="{Binding RootDirectory.Children}"
VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"> 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}"
IsThreeState="True" />
<TextBlock
VerticalAlignment="Center"
Text="{Binding Name}"
ToolTip="{Binding FullName}" /> ToolTip="{Binding FullName}" />
</StackPanel> </StackPanel>
</HierarchicalDataTemplate> </HierarchicalDataTemplate>
@@ -70,26 +97,48 @@
</ScrollViewer> </ScrollViewer>
</Border> </Border>
<TextBlock Grid.Row="2" Text="Проект еще не загружен. Выберите файл решения или проекта." <TextBlock
HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="2"
Foreground="Gray" FontSize="14" HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="14"
Foreground="Gray"
Text="Проект еще не загружен. Выберите файл решения или проекта."
Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Collapsed}" /> 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"
<TextBlock Grid.Row="3" Text="{Binding ProgressText}" HorizontalAlignment="Center" Margin="0,10,0,10"
VerticalAlignment="Center" FontWeight="Bold"
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<StackPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
<Button Content="Отмена" Command="{Binding CancelCommand}"
Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}" Visibility="{Binding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}"
Background="#FFDC3545" Foreground="White"/> Value="{Binding ProgressValue}" />
<Button Content="Закрыть" Command="{Binding ExitCommand}"/>
<Button Content="Сформировать" Command="{Binding GenerateCommand}" <TextBlock
Grid.Row="3"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding ProgressText}"
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}" IsEnabled="{Binding CanGenerate}"
Background="#FF28A745" Foreground="White"
Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}}" /> Visibility="{Binding IsProjectLoaded, Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel> </StackPanel>
</Grid> </Grid>

View File

@@ -4,13 +4,14 @@ 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(); InitializeComponent();
DataContext = new MainViewModel();
} }
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e) private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
@@ -26,15 +27,8 @@ namespace CodeContextGenerator.Views
{ {
if (sender is CheckBox checkBox && checkBox.DataContext is FileItem fileItem) if (sender is CheckBox checkBox && checkBox.DataContext is FileItem fileItem)
{ {
// Принудительно обновляем состояние детей при клике // Синхронизация состояния чекбокса с ViewModel
if (fileItem.IsDirectory && checkBox.IsChecked.HasValue) fileItem.IsSelected = checkBox.IsChecked;
{
foreach (var child in fileItem.Children)
{
child.IsSelected = checkBox.IsChecked;
}
}
}
} }
} }
} }