diff --git a/KPO_Lab.sln b/KPO_Lab.sln
index 3fb4f3d..d856148 100644
--- a/KPO_Lab.sln
+++ b/KPO_Lab.sln
@@ -7,10 +7,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tool", "Tool\Tool.csproj",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lab2", "Lab2\Lab2.csproj", "{BC81743D-B4D3-4D25-A966-AD697757E98C}"
ProjectSection(ProjectDependencies) = postProject
- {5E9876F7-1F15-4759-8D96-D950649BBDD7} = {5E9876F7-1F15-4759-8D96-D950649BBDD7}
{73C85CBC-5C3A-4F43-8B10-1462B52F709E} = {73C85CBC-5C3A-4F43-8B10-1462B52F709E}
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProgressBar", "ProgressBar\ProgressBar.csproj", "{E00EA033-284E-44D9-88E1-8677B0E361AA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -45,18 +46,18 @@ Global
{BC81743D-B4D3-4D25-A966-AD697757E98C}.Release|x64.Build.0 = Release|Any CPU
{BC81743D-B4D3-4D25-A966-AD697757E98C}.Release|x86.ActiveCfg = Release|Any CPU
{BC81743D-B4D3-4D25-A966-AD697757E98C}.Release|x86.Build.0 = Release|Any CPU
- {5E9876F7-1F15-4759-8D96-D950649BBDD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5E9876F7-1F15-4759-8D96-D950649BBDD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5E9876F7-1F15-4759-8D96-D950649BBDD7}.Debug|x64.ActiveCfg = Debug|Any CPU
- {5E9876F7-1F15-4759-8D96-D950649BBDD7}.Debug|x64.Build.0 = Debug|Any CPU
- {5E9876F7-1F15-4759-8D96-D950649BBDD7}.Debug|x86.ActiveCfg = Debug|Any CPU
- {5E9876F7-1F15-4759-8D96-D950649BBDD7}.Debug|x86.Build.0 = Debug|Any CPU
- {5E9876F7-1F15-4759-8D96-D950649BBDD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5E9876F7-1F15-4759-8D96-D950649BBDD7}.Release|Any CPU.Build.0 = Release|Any CPU
- {5E9876F7-1F15-4759-8D96-D950649BBDD7}.Release|x64.ActiveCfg = Release|Any CPU
- {5E9876F7-1F15-4759-8D96-D950649BBDD7}.Release|x64.Build.0 = Release|Any CPU
- {5E9876F7-1F15-4759-8D96-D950649BBDD7}.Release|x86.ActiveCfg = Release|Any CPU
- {5E9876F7-1F15-4759-8D96-D950649BBDD7}.Release|x86.Build.0 = Release|Any CPU
+ {E00EA033-284E-44D9-88E1-8677B0E361AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E00EA033-284E-44D9-88E1-8677B0E361AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E00EA033-284E-44D9-88E1-8677B0E361AA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E00EA033-284E-44D9-88E1-8677B0E361AA}.Debug|x64.Build.0 = Debug|Any CPU
+ {E00EA033-284E-44D9-88E1-8677B0E361AA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E00EA033-284E-44D9-88E1-8677B0E361AA}.Debug|x86.Build.0 = Debug|Any CPU
+ {E00EA033-284E-44D9-88E1-8677B0E361AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E00EA033-284E-44D9-88E1-8677B0E361AA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E00EA033-284E-44D9-88E1-8677B0E361AA}.Release|x64.ActiveCfg = Release|Any CPU
+ {E00EA033-284E-44D9-88E1-8677B0E361AA}.Release|x64.Build.0 = Release|Any CPU
+ {E00EA033-284E-44D9-88E1-8677B0E361AA}.Release|x86.ActiveCfg = Release|Any CPU
+ {E00EA033-284E-44D9-88E1-8677B0E361AA}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Lab2/Lab2.csproj b/Lab2/Lab2.csproj
index d40a68d..1f13045 100644
--- a/Lab2/Lab2.csproj
+++ b/Lab2/Lab2.csproj
@@ -8,6 +8,7 @@
+
diff --git a/Lab2/src/Form/MainWindow.xaml b/Lab2/src/Form/MainWindow.xaml
index 1a47af6..5ba52d8 100644
--- a/Lab2/src/Form/MainWindow.xaml
+++ b/Lab2/src/Form/MainWindow.xaml
@@ -9,28 +9,60 @@
-
-
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/Lab2/src/Form/MainWindow.xaml.cs b/Lab2/src/Form/MainWindow.xaml.cs
index a512cd0..8e11e2c 100644
--- a/Lab2/src/Form/MainWindow.xaml.cs
+++ b/Lab2/src/Form/MainWindow.xaml.cs
@@ -4,6 +4,9 @@ using System.Collections.ObjectModel;
using Patterns;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Data;
namespace Lab2
{
@@ -17,6 +20,11 @@ namespace Lab2
///
private ObservableCollection _tools = new();
+ ///
+ /// Источник представлений для данных
+ ///
+ private CollectionViewSource _viewSource = new();
+
///
/// Случайный генератор
///
@@ -27,9 +35,7 @@ namespace Lab2
///
private List _factoryMethods = new()
{
- new ScissorsAndTrimmerFactoryMethod(),
- new ScytheAndLawnmowerFactoryMethod(),
- new PrototypeFactoryMethod(),
+ new ScissorsAndTrimmerFactoryMethod(), new ScytheAndLawnmowerFactoryMethod(), new PrototypeFactoryMethod(),
};
///
@@ -60,39 +66,42 @@ namespace Lab2
}
}
+ ///
+ /// Фильтр по имени инструмента.
+ ///
+ public string NameFilter { get; set; } = string.Empty;
+
+ ///
+ /// Минимальный вес для фильтрации инструментов.
+ ///
+ public int WeightMinFilter { get; set; } = 0;
+
+ ///
+ /// Максимальный вес для фильтрации инструментов.
+ ///
+ public int WeightMaxFilter { get; set; } = 1_000;
+
///
/// Конструктор
///
public MainWindow()
{
+ DataContext = this;
InitializeComponent();
SelectedMethod = _factoryMethods.First();
+ _viewSource.Source = _tools;
- DataContext = this;
- _dataGrid.ItemsSource = _tools;
+ _dataGrid.ItemsSource = _viewSource.View;
}
///
- /// Проверяет, является ли индекс валидным
+ /// Возвращает выбранный в таблице инструмент.
///
- /// Индекс
- /// True, если индекс валиден, иначе - false
- private bool IsValidIndex(int parIndex)
- {
- return _tools.Count > 0 && parIndex >= 0 && parIndex < _tools.Count;
- }
-
- ///
- /// Возвращает выбранный инструмент
- ///
- /// Выбранный инструмент
+ /// Выбранный инструмент или null, если ничего не выбрано.
private Tool? GetSelectedItem()
{
- if (!IsValidIndex(_dataGrid.SelectedIndex))
- return null;
-
- return _tools[_dataGrid.SelectedIndex];
+ return _dataGrid.SelectedItem as Tool;
}
///
@@ -101,7 +110,8 @@ namespace Lab2
/// Случайный триммер
private Trimmer GenerateTrimmer()
{
- return new Trimmer($"Tr{_random.Next(0, 100)}", (Material)_random.Next(0, 3), _random.Next(1, 100), (uint)_random.Next(1, 100));
+ return new Trimmer($"Tr{_random.Next(0, 100)}", (Material)_random.Next(0, 3), _random.Next(1, 100),
+ (uint)_random.Next(1, 100));
}
///
@@ -110,7 +120,8 @@ namespace Lab2
/// Случайная газонокосилка
private Lawnmower GenerateLawnmower()
{
- return new Lawnmower($"La{_random.Next(0, 100)}", (Material)_random.Next(0, 3), _random.Next(1, 100), (uint)_random.Next(1, 100), _random.Next(1, 100));
+ return new Lawnmower($"La{_random.Next(0, 100)}", (Material)_random.Next(0, 3), _random.Next(1, 100),
+ (uint)_random.Next(1, 100), _random.Next(1, 100));
}
///
@@ -119,7 +130,8 @@ namespace Lab2
/// Случайные ножницы
private Scissors GenerateScissors()
{
- return new Scissors($"Si{_random.Next(0, 100)}", (Material)_random.Next(0, 3), _random.Next(1, 100), (uint)_random.Next(1, 100), _random.Next(1, 100));
+ return new Scissors($"Si{_random.Next(0, 100)}", (Material)_random.Next(0, 3), _random.Next(1, 100),
+ (uint)_random.Next(1, 100), _random.Next(1, 100));
}
///
@@ -128,7 +140,41 @@ namespace Lab2
/// Случайная коса
private Scythe GenerateScythe()
{
- return new Scythe($"Sc{_random.Next(0, 100)}", (Material)_random.Next(0, 3), _random.Next(1, 100), (uint)_random.Next(1, 100), (BladeType)_random.Next(0, 3));
+ return new Scythe($"Sc{_random.Next(0, 100)}", (Material)_random.Next(0, 3), _random.Next(1, 100),
+ (uint)_random.Next(1, 100), (BladeType)_random.Next(0, 3));
+ }
+
+ ///
+ /// Генерирует список инструментов.
+ ///
+ /// Количество инструментов для генерации.
+ /// Прогресс выполнения.
+ /// Токен отмены задачи.
+ /// Сгенерированный список инструментов.
+ private List GenerateTools(int parAmount, IProgress parProgress,
+ CancellationToken parCancellationToken)
+ {
+ var tools = new List();
+ for (int i = 0; i < parAmount; i++)
+ {
+ if (parCancellationToken.IsCancellationRequested)
+ break;
+
+ Dispatcher.Invoke(() => parProgress.Report(i));
+
+ Tool tool = _random.Next(0, 4) switch
+ {
+ 0 => GenerateScissors(),
+ 1 => GenerateScythe(),
+ 2 => GenerateTrimmer(),
+ 3 => GenerateLawnmower(),
+ _ => GenerateScissors(),
+ };
+
+ tools.Add(tool);
+ }
+
+ return tools;
}
///
@@ -185,16 +231,73 @@ namespace Lab2
///
private void GenerateButton_Click(object sender, RoutedEventArgs e)
{
- Tool tool = _random.Next(0, 4) switch
+ int generateAmount = 1_000_000;
+
+ var progressWindow =
+ new ProgressWindow.ProgressBarWindow("Tool generation", "Please wait...", generateAmount, false);
+
+ IProgress progress = new Progress(i =>
{
- 0 => GenerateScissors(),
- 1 => GenerateScythe(),
- 2 => GenerateTrimmer(),
- 3 => GenerateLawnmower(),
- _ => GenerateScissors(),
+ if (i % 60 == 0)
+ progressWindow.AdvanceProgress(60);
+
+ if (i == generateAmount - 1)
+ progressWindow.AdvanceProgress(0, "Generation completed");
+ });
+
+ var cancellationTokenSource = new CancellationTokenSource();
+ progressWindow.Cancelled += (s, e) => cancellationTokenSource.Cancel();
+ var task = new Task>(() => GenerateTools(generateAmount, progress, cancellationTokenSource.Token));
+ task.Start();
+
+ progressWindow.ShowDialog();
+
+ if (cancellationTokenSource.IsCancellationRequested)
+ return;
+
+ var tools = task.Result;
+
+ tools.EnsureCapacity(tools.Count + generateAmount);
+
+ var filter = _viewSource.View.Filter;
+ _viewSource.View.Filter = null;
+
+ foreach (var tool in tools)
+ _tools.Add(tool);
+
+ _viewSource.View.Filter = filter;
+ _viewSource.View.Refresh();
+ }
+
+ ///
+ /// Обработчик нажатия кнопки "Filter". Применяет фильтры к инструментам.
+ ///
+ private void FilterButton_OnClick(object parSender, RoutedEventArgs parE)
+ {
+ _viewSource.View.Filter = o =>
+ {
+ var tool = o as Tool;
+
+ if (!string.IsNullOrEmpty(NameFilter) && !tool.Name.Contains(NameFilter))
+ return false;
+
+ if (WeightMinFilter > WeightMaxFilter)
+ return false;
+
+ return tool.Weight >= WeightMinFilter && tool.Weight <= WeightMaxFilter;
};
- _tools.Add(tool);
+ _dataGrid.ScrollIntoView(_dataGrid.SelectedItem);
+ }
+
+ ///
+ /// Обработчик нажатия кнопки "Clear Filter". Сбрасывает фильтры.
+ ///
+ private void ClearFilterButton_OnClick(object parSender, RoutedEventArgs parE)
+ {
+ _viewSource.View.Filter = null;
+
+ _dataGrid.ScrollIntoView(_dataGrid.SelectedItem);
}
}
-}
+}
\ No newline at end of file
diff --git a/Lab2/src/Form/Util.cs b/Lab2/src/Form/Util.cs
index d479f5e..67dfc13 100644
--- a/Lab2/src/Form/Util.cs
+++ b/Lab2/src/Form/Util.cs
@@ -11,6 +11,8 @@ namespace Lab2
/// Открывает форму редактирования
///
/// Инструмент
+ /// Доступность полей формы
+ /// Признак невозможности отмены
/// True, если редактирование прошло успешно, иначе - false
public static bool ShowEditForm(Tool parTool, bool parReadOnly = false, bool parIsForced = false)
{
diff --git a/ProgressBar/ProgressBar.csproj b/ProgressBar/ProgressBar.csproj
new file mode 100644
index 0000000..eb6bdfa
--- /dev/null
+++ b/ProgressBar/ProgressBar.csproj
@@ -0,0 +1,10 @@
+
+
+
+ net7.0-windows
+ enable
+ true
+ ProgressWindow
+
+
+
diff --git a/ProgressBar/ProgressBarWindow.xaml b/ProgressBar/ProgressBarWindow.xaml
new file mode 100644
index 0000000..d0b6bbc
--- /dev/null
+++ b/ProgressBar/ProgressBarWindow.xaml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ProgressBar/ProgressBarWindow.xaml.cs b/ProgressBar/ProgressBarWindow.xaml.cs
new file mode 100644
index 0000000..22a99c3
--- /dev/null
+++ b/ProgressBar/ProgressBarWindow.xaml.cs
@@ -0,0 +1,135 @@
+using System;
+using System.ComponentModel;
+using System.Windows;
+
+namespace ProgressWindow;
+
+///
+/// Interaction logic for ProgressBar.xaml
+///
+public partial class ProgressBarWindow : Window
+{
+ ///
+ /// Событие, вызываемое при отмене задачи.
+ ///
+ public event EventHandler? Cancelled;
+
+ ///
+ /// Объект для синхронизации доступа к переменным.
+ ///
+ private readonly object _lock = new();
+
+ ///
+ /// Флаг, указывающий, нужно ли автоматически закрывать окно по завершении задачи.
+ ///
+ private readonly bool _autoClose;
+
+ ///
+ /// Максимальное значение прогресса.
+ ///
+ private readonly int _maxProgress;
+
+ ///
+ /// Текущее значение прогресса.
+ ///
+ private int _progress = 0;
+
+ ///
+ /// Флаг, указывающий, была ли задача отменена.
+ ///
+ private bool _isCancelled = false;
+
+ ///
+ /// Флаг, указывающий, завершена ли задача.
+ ///
+ private bool _isCompleted = false;
+
+ ///
+ /// Конструктор для инициализации окна прогресса.
+ ///
+ /// Заголовок окна.
+ /// Начальное сообщение для отображения.
+ /// Максимальное значение прогресса.
+ /// Флаг, указывающий, должно ли окно закрываться автоматически по завершении.
+ public ProgressBarWindow(string parTitle, string parInitialMessage, int parMaxProgress, bool parAutoClose = true)
+ {
+ InitializeComponent();
+
+ Title = parTitle;
+ _messageTextBlock.Text = parInitialMessage;
+
+ _maxProgress = parMaxProgress;
+ _autoClose = parAutoClose;
+ }
+
+ ///
+ /// Увеличивает текущий прогресс на указанную величину и обновляет сообщение, если предоставлено.
+ ///
+ /// Количество единиц, на которое нужно увеличить прогресс.
+ /// Сообщение для отображения в окне (опционально).
+ /// Возвращает true, если задача была отменена.
+ public bool AdvanceProgress(int parIncrement = 1, string? parMessage = null)
+ {
+ lock (_lock)
+ {
+ _progress += parIncrement;
+ if (_progress >= _maxProgress)
+ {
+ _progress = _maxProgress;
+
+ _isCompleted = true;
+ _cancelButton.Content = "Close";
+ }
+
+ if (parMessage != null)
+ _messageTextBlock.Text = parMessage;
+
+ _progressBar.Value = (double)_progress / (double)_maxProgress * 100;
+ _progressBar.ToolTip = $"{_progress}/{_maxProgress} {parMessage}";
+
+ if (_isCompleted && _autoClose)
+ Close();
+ }
+
+ return _isCancelled;
+ }
+
+ ///
+ /// Обработчик события закрытия окна.
+ ///
+ /// Источник события.
+ /// Аргументы события закрытия окна.
+ private void Window_Closing(object parSender, CancelEventArgs parE)
+ {
+ if (_isCompleted)
+ return;
+
+ if (!_isCancelled)
+ Cancel();
+ }
+
+ ///
+ /// Обработчик события нажатия на кнопку "Cancel/Close".
+ ///
+ /// Источник события.
+ /// Аргументы события.
+ private void CancelButton_Click(object parSender, RoutedEventArgs parE)
+ {
+ if (!_isCompleted)
+ Cancel();
+ Close();
+ }
+
+ ///
+ /// Выполняет отмену задачи и отключает кнопку отмены.
+ ///
+ private void Cancel()
+ {
+ if (_isCancelled)
+ return;
+
+ _cancelButton.IsEnabled = false;
+ _isCancelled = true;
+ Cancelled?.Invoke(this, EventArgs.Empty);
+ }
+}
\ No newline at end of file