diff --git a/MathCore.WPF.sln.DotSettings b/MathCore.WPF.sln.DotSettings
index f23f904..bf06b76 100644
--- a/MathCore.WPF.sln.DotSettings
+++ b/MathCore.WPF.sln.DotSettings
@@ -20,6 +20,7 @@
True
True
True
+ True
True
True
True
diff --git a/MathCore.WPF/Commands/BindingGroupCommit.cs b/MathCore.WPF/Commands/BindingGroupCommit.cs
new file mode 100644
index 0000000..3702e35
--- /dev/null
+++ b/MathCore.WPF/Commands/BindingGroupCommit.cs
@@ -0,0 +1,11 @@
+using System.Windows.Data;
+
+namespace MathCore.WPF.Commands;
+
+/// Команда для подтверждения изменений в BindingGroup, если они были изменены
+public class BindingGroupCommit : Command
+{
+ public override bool CanExecute(object? parameter) => parameter is BindingGroup { IsDirty: true };
+
+ public override void Execute(object? parameter) => (parameter as BindingGroup).CommitEdit();
+}
diff --git a/MathCore.WPF/Commands/BindingGroupRollback.cs b/MathCore.WPF/Commands/BindingGroupRollback.cs
new file mode 100644
index 0000000..85ba582
--- /dev/null
+++ b/MathCore.WPF/Commands/BindingGroupRollback.cs
@@ -0,0 +1,11 @@
+using System.Windows.Data;
+
+namespace MathCore.WPF.Commands;
+
+/// Команда для отмены изменений в BindingGroup, если они были изменены
+public class BindingGroupRollback : Command
+{
+ public override bool CanExecute(object? parameter) => parameter is BindingGroup { IsDirty: true };
+
+ public override void Execute(object? parameter) => (parameter as BindingGroup).CancelEdit();
+}
diff --git a/MathCore.WPF/Extensions/WindowExtensions.cs b/MathCore.WPF/Extensions/WindowExtensions.cs
index c849b84..9651f45 100644
--- a/MathCore.WPF/Extensions/WindowExtensions.cs
+++ b/MathCore.WPF/Extensions/WindowExtensions.cs
@@ -11,23 +11,26 @@ public static class WindowExtensions
{
public static IntPtr GetWindowHandle(this Window window) => new WindowInteropHelper(window).Handle;
- public static void ForWindowFromChild(this object ChildDependencyObject, Action action)
+ public static void ForWindowFromChild(this T ChildDependencyObject, Action action)
+ where T : DependencyObject
{
if (action is null) throw new ArgumentNullException(nameof(action));
- var element = ChildDependencyObject as DependencyObject;
+ DependencyObject? element = ChildDependencyObject;
while (element != null)
{
element = VisualTreeHelper.GetParent(element);
- if(element is not Window window) continue;
- action(window);
+ if (element is not Window window) continue;
+ action(window);
break;
}
}
- public static void ForWindowFromTemplate(this object TemplateFrameworkElement, Action action)
+ public static void ForWindowFromTemplate(this T TemplateFrameworkElement, Action action)
+ where T : DependencyObject
{
if (action is null) throw new ArgumentNullException(nameof(action));
- if (((FrameworkElement)TemplateFrameworkElement)?.TemplatedParent is Window window) action(window);
+ if (TemplateFrameworkElement is FrameworkElement { TemplatedParent: Window window })
+ action(window);
}
public static void AddHook(this Window window, HwndSourceHook WndProc)
@@ -39,7 +42,7 @@ public static void AddHook(this Window window, HwndSourceHook WndProc)
source.AddHook(WndProc);
}
else
- window.SourceInitialized += (_,_) => window.AddHook(WndProc);
+ window.SourceInitialized += (_, _) => window.AddHook(WndProc);
}
public static void RemoveHook(this Window window, HwndSourceHook WndProc)
diff --git a/MathCore.WPF/MathCore.WPF.csproj b/MathCore.WPF/MathCore.WPF.csproj
index dbc5f06..4a22d4b 100644
--- a/MathCore.WPF/MathCore.WPF.csproj
+++ b/MathCore.WPF/MathCore.WPF.csproj
@@ -24,10 +24,12 @@
enable
preview
+ true
+ true
- 0.0.48.1
+ 0.0.48.2
Добавлены стили для кнопок и полей ввода текста в стилистике Bootstrap 5
@@ -49,24 +51,24 @@
-
+
-
+
-
+
-
+
@@ -75,7 +77,7 @@
-
+
diff --git a/MathCore.WPF/TextBoxEx.cs b/MathCore.WPF/TextBoxEx.cs
index 3cdf2c7..4ea12a9 100644
--- a/MathCore.WPF/TextBoxEx.cs
+++ b/MathCore.WPF/TextBoxEx.cs
@@ -20,9 +20,10 @@ public static class TextBoxEx
typeof(TextBoxEx),
new(default(bool), OnUpdateBindingOnEnterChanged));
+ /// Обработчик изменения свойства UpdateBindingSourceOnEnter
private static void OnUpdateBindingOnEnterChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
- if(sender is not TextBox text_box || e.NewValue is not bool value) return;
+ if (sender is not TextBox text_box || e.NewValue is not bool value) return;
if (value)
text_box.KeyDown += OnTextBoxKeyDown;
@@ -30,30 +31,31 @@ private static void OnUpdateBindingOnEnterChanged(DependencyObject sender, Depen
text_box.KeyDown -= OnTextBoxKeyDown;
}
+ /// Обработчик события нажатия клавиши в TextBox
private static void OnTextBoxKeyDown(object Sender, KeyEventArgs E)
{
- if(E.Key != Key.Enter) return;
+ if (E.Key != Key.Enter) return;
var text_box = (TextBox)Sender;
- if (text_box.AcceptsReturn && !E.KeyboardDevice.IsKeyDown(Key.LeftCtrl))
+ if (text_box.AcceptsReturn && !E.KeyboardDevice.IsKeyDown(Key.LeftCtrl))
return;
- if (BindingOperations.GetBindingExpression(text_box, TextBox.TextProperty) is not { } binding)
+ if (BindingOperations.GetBindingExpression(text_box, TextBox.TextProperty) is not { } binding)
return;
- if(binding.ParentBinding.UpdateSourceTrigger != UpdateSourceTrigger.Explicit && !E.KeyboardDevice.IsKeyDown(Key.LeftCtrl))
+ if (binding.ParentBinding.UpdateSourceTrigger != UpdateSourceTrigger.Explicit && !E.KeyboardDevice.IsKeyDown(Key.LeftCtrl))
return;
binding.UpdateSource();
E.Handled = true;
}
- /// Обновить привязку при нажатии Enter
+ /// Установить значение свойства UpdateBindingSourceOnEnter
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static void SetUpdateBindingSourceOnEnter(DependencyObject d, bool value) => d.SetValue(UpdateBindingSourceOnEnterProperty, value);
- /// Обновить привязку при нажатии Enter
+ /// Получить значение свойства UpdateBindingSourceOnEnter
public static bool GetUpdateBindingSourceOnEnter(DependencyObject d) => (bool)d.GetValue(UpdateBindingSourceOnEnterProperty);
#endregion
@@ -66,11 +68,12 @@ private static void OnTextBoxKeyDown(object Sender, KeyEventArgs E)
"ValidateInputScope",
typeof(bool),
typeof(TextBoxEx),
- new(default(bool), OnValidateInputScopeChanged));
+ new(false, OnValidateInputScopeChanged));
+ /// Обработчик изменения свойства ValidateInputScope
private static void OnValidateInputScopeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
- if(obj is not TextBox text_box || e.NewValue is not bool value) return;
+ if (obj is not TextBox text_box || e.NewValue is not bool value) return;
if (value)
text_box.PreviewTextInput += ValidateScopes_OnPreviewTextInput;
@@ -78,9 +81,10 @@ private static void OnValidateInputScopeChanged(DependencyObject obj, Dependency
text_box.PreviewTextInput -= ValidateScopes_OnPreviewTextInput;
}
+ /// Обработчик события предварительного ввода текста в TextBox
private static void ValidateScopes_OnPreviewTextInput(object Sender, TextCompositionEventArgs E)
{
- if(Sender is not TextBox { InputScope: { } input_scope, Text: var current_text } text_box) return;
+ if (Sender is not TextBox { InputScope: { } input_scope, Text: var current_text } text_box) return;
var input_text = E.Text;
var full_text = current_text + input_text;
@@ -95,20 +99,20 @@ private static void ValidateScopes_OnPreviewTextInput(object Sender, TextComposi
case InputScopeNameValue.FullFilePath when Path.GetInvalidPathChars().Any(c => input_text.Contains(c)):
case InputScopeNameValue.FileName when Path.GetInvalidFileNameChars().Any(c => input_text.Contains(c)):
case InputScopeNameValue.DateDayName when full_text.ToLower() is not (
- "sunday" or "sun" or "su" or
+ "sunday" or "sun" or "su" or
"monday" or "mon" or "mo" or
"tuesday" or "tue" or "tu" or
"wednesday" or "wed" or "wd" or
"thursday" or "thu" or "th" or
"friday" or "fri" or "fr" or
- "saturday" or "sat" or "st" or
+ "saturday" or "sat" or "st" or
"понедельник" or "пон" or "пн" or
"вторник" or "втр" or "вт" or
"среда" or "срд" or "ср" or
"четверг" or "чет" or "чт" or
"пятница" or "пят" or "пт" or
"суббота" or "суб" or "сб" or
- "воскресенье" or "вос" or "вс"
+ "воскресенье" or "вос" or "вс"
):
case InputScopeNameValue.DateDay when !int.TryParse(full_text, out var day) && day is not (>= 1 and <= 31):
case InputScopeNameValue.DateMonth when !int.TryParse(full_text, out var month) && month is not (>= 1 and <= 12):
@@ -120,15 +124,15 @@ private static void ValidateScopes_OnPreviewTextInput(object Sender, TextComposi
return;
}
- if (full_text.Length > 0 && input_scope.RegularExpression is { Length: > 0 } regex_str && !Regex.IsMatch(full_text, regex_str))
+ if (full_text.Length > 0 && input_scope.RegularExpression is { Length: > 0 } regex_str && !Regex.IsMatch(full_text, regex_str))
E.Handled = true;
}
- /// Проверять корректность правил InputScope
+ /// Установить значение свойства ValidateInputScope
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static void SetValidateInputScope(DependencyObject d, bool value) => d.SetValue(ValidateInputScopeProperty, value);
- /// Проверять корректность правил InputScope
+ /// Получить значение свойства ValidateInputScope
public static bool GetValidateInputScope(DependencyObject d) => (bool)d.GetValue(ValidateInputScopeProperty);
#endregion
@@ -143,19 +147,25 @@ private static void ValidateScopes_OnPreviewTextInput(object Sender, TextComposi
typeof(TextBoxEx),
new(decimal.Zero, OnMouseWheelIncrementChanged));
+ /// Обработчик изменения свойства MouseWheelIncrement
private static void OnMouseWheelIncrementChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
- if (e is { OldValue: decimal.Zero, NewValue: not decimal.Zero })
- ((TextBox)s).MouseWheel += OnMouseWheel;
- else if (e is { OldValue: not decimal.Zero, NewValue: decimal.Zero })
- ((TextBox)s).MouseWheel -= OnMouseWheel;
+ switch (e)
+ {
+ case { OldValue: decimal.Zero, NewValue: not decimal.Zero }:
+ ((TextBox)s).MouseWheel += OnMouseWheel;
+ break;
+ case { OldValue: not decimal.Zero, NewValue: decimal.Zero }:
+ ((TextBox)s).MouseWheel -= OnMouseWheel;
+ break;
+ }
}
- /// Инкремент колёсика мышки
+ /// Установить значение свойства MouseWheelIncrement
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static void SetMouseWheelIncrement(DependencyObject d, decimal value) => d.SetValue(MouseWheelIncrementProperty, value);
- /// Инкремент колёсика мышки
+ /// Получить значение свойства MouseWheelIncrement
public static decimal GetMouseWheelIncrement(DependencyObject d) => (decimal)d.GetValue(MouseWheelIncrementProperty);
#endregion
@@ -170,15 +180,16 @@ private static void OnMouseWheelIncrementChanged(DependencyObject s, DependencyP
typeof(TextBoxEx),
new(0.1m));
- /// Инкремент колёсика мышки
+ /// Установить значение свойства MouseWheelIncrementCtrlRatio
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static void SetMouseWheelIncrementCtrlRatio(DependencyObject d, decimal value) => d.SetValue(MouseWheelIncrementCtrlRatioProperty, value);
- /// Инкремент колёсика мышки
+ /// Получить значение свойства MouseWheelIncrementCtrlRatio
public static decimal GetMouseWheelIncrementCtrlRatio(DependencyObject d) => (decimal)d.GetValue(MouseWheelIncrementCtrlRatioProperty);
#endregion
+ /// Обработчик события прокрутки колёсика мыши в TextBox
private static void OnMouseWheel(object Sender, MouseWheelEventArgs E)
{
if (Sender is not TextBox { Text: var text } text_block) return;
@@ -195,4 +206,58 @@ private static void OnMouseWheel(object Sender, MouseWheelEventArgs E)
var new_value_text = new_value.ToString(CultureInfo.InvariantCulture);
text_block.Text = new_value_text;
}
+
+ #region Attached property AutoSelectAll : bool - Автовыбор всего содержимого TextBox
+
+ /// Авто выбор всего содержимого TextBox
+ public static readonly DependencyProperty AutoSelectAllProperty =
+ DependencyProperty.RegisterAttached(
+ "AutoSelectAll",
+ typeof(bool),
+ typeof(TextBoxEx),
+ new(false, OnAutoSelectAllChanged));
+
+ /// Обработчик изменения свойства AutoSelectAll
+ private static void OnAutoSelectAllChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is not TextBox text_box) return;
+
+ if ((bool)e.NewValue)
+ {
+ text_box.PreviewMouseDown += OnTextBoxPreviewMouseDown;
+ text_box.GotKeyboardFocus += OnTextBoxGotKeyboardFocus;
+ }
+ else
+ {
+ text_box.PreviewMouseDown -= OnTextBoxPreviewMouseDown;
+ text_box.GotKeyboardFocus -= OnTextBoxGotKeyboardFocus;
+ }
+ }
+
+ /// Обработчик события предварительного нажатия мыши в TextBox
+ private static void OnTextBoxPreviewMouseDown(object sender, MouseButtonEventArgs e)
+ {
+ if (sender is not TextBox text_box || text_box.IsKeyboardFocusWithin) return;
+
+ text_box.Focus();
+ e.Handled = true;
+ }
+
+ /// Обработчик события получения фокуса клавиатуры в TextBox
+ private static void OnTextBoxGotKeyboardFocus(object sender, RoutedEventArgs e)
+ {
+ if (sender is not TextBox text_box) return;
+
+ text_box.SelectAll();
+ e.Handled = true;
+ }
+
+ /// Установить значение свойства AutoSelectAll
+ [AttachedPropertyBrowsableForType(typeof(TextBox))]
+ public static void SetAutoSelectAll(DependencyObject d, bool value) => d.SetValue(AutoSelectAllProperty, value);
+
+ /// Получить значение свойства AutoSelectAll
+ public static bool GetAutoSelectAll(DependencyObject d) => (bool)d.GetValue(AutoSelectAllProperty);
+
+ #endregion
}
diff --git a/Tests/MathCore.WPF.Tests/MathCore.WPF.Tests.csproj b/Tests/MathCore.WPF.Tests/MathCore.WPF.Tests.csproj
index df6c2a9..5bf2908 100644
--- a/Tests/MathCore.WPF.Tests/MathCore.WPF.Tests.csproj
+++ b/Tests/MathCore.WPF.Tests/MathCore.WPF.Tests.csproj
@@ -10,9 +10,9 @@
-
-
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Tests/MathCore.WPF.WindowTest/App.xaml b/Tests/MathCore.WPF.WindowTest/App.xaml
index 218626a..3c1362d 100644
--- a/Tests/MathCore.WPF.WindowTest/App.xaml
+++ b/Tests/MathCore.WPF.WindowTest/App.xaml
@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:MathCore.WPF.WindowTest"
- StartupUri="TestWindow5.xaml">
+ StartupUri="TestWindow7.xaml">
diff --git a/Tests/MathCore.WPF.WindowTest/App.xaml.cs b/Tests/MathCore.WPF.WindowTest/App.xaml.cs
index c2eb576..3a43487 100644
--- a/Tests/MathCore.WPF.WindowTest/App.xaml.cs
+++ b/Tests/MathCore.WPF.WindowTest/App.xaml.cs
@@ -45,8 +45,8 @@ protected override async void OnStartup(StartupEventArgs e)
base.OnStartup(e);
await host.StartAsync();
- var main_thread_cancellation = __MainThreadWatcherCancellation.Token;
- _ = Task.Run(() => MainThreadLoadingWatcher(main_thread_cancellation), main_thread_cancellation);
+ //var main_thread_cancellation = __MainThreadWatcherCancellation.Token;
+ //_ = Task.Run(() => MainThreadLoadingWatcher(main_thread_cancellation), main_thread_cancellation);
}
private static readonly CancellationTokenSource __MainThreadWatcherCancellation = new();
diff --git a/Tests/MathCore.WPF.WindowTest/MathCore.WPF.WindowTest.csproj b/Tests/MathCore.WPF.WindowTest/MathCore.WPF.WindowTest.csproj
index aa1ad62..544ab5e 100644
--- a/Tests/MathCore.WPF.WindowTest/MathCore.WPF.WindowTest.csproj
+++ b/Tests/MathCore.WPF.WindowTest/MathCore.WPF.WindowTest.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/Tests/MathCore.WPF.WindowTest/TestWindow6.xaml b/Tests/MathCore.WPF.WindowTest/TestWindow6.xaml
index 92ecae7..9db5524 100644
--- a/Tests/MathCore.WPF.WindowTest/TestWindow6.xaml
+++ b/Tests/MathCore.WPF.WindowTest/TestWindow6.xaml
@@ -1,15 +1,139 @@
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/MathCore.WPF.WindowTest/TestWindow7.xaml b/Tests/MathCore.WPF.WindowTest/TestWindow7.xaml
new file mode 100644
index 0000000..7066717
--- /dev/null
+++ b/Tests/MathCore.WPF.WindowTest/TestWindow7.xaml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/MathCore.WPF.WindowTest/TestWindow7.xaml.cs b/Tests/MathCore.WPF.WindowTest/TestWindow7.xaml.cs
new file mode 100644
index 0000000..8572bc0
--- /dev/null
+++ b/Tests/MathCore.WPF.WindowTest/TestWindow7.xaml.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace MathCore.WPF.WindowTest
+{
+ ///
+ /// Логика взаимодействия для TestWindow7.xaml
+ ///
+ public partial class TestWindow7 : Window
+ {
+ public TestWindow7()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Tests/MathCore.WPF.WindowTest/ViewModels/StudentInfoViewModel.cs b/Tests/MathCore.WPF.WindowTest/ViewModels/StudentInfoViewModel.cs
new file mode 100644
index 0000000..6533183
--- /dev/null
+++ b/Tests/MathCore.WPF.WindowTest/ViewModels/StudentInfoViewModel.cs
@@ -0,0 +1,66 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Windows.Data;
+using System.Windows.Input;
+
+using MathCore.WPF.Commands;
+using MathCore.WPF.ViewModels;
+
+namespace MathCore.WPF.WindowTest.ViewModels;
+
+public class StudentInfoViewModel : ViewModel
+{
+ #region LastName : string - Фамилия
+
+ /// Фамилия
+ public string LastName { get; set => Set(ref field, value); } = "Фамилия-1";
+
+ #endregion
+
+ #region FirstName : string - Имя
+
+ /// Имя
+ public string FirstName { get; set => Set(ref field, value); } = "Имя-1";
+
+ #endregion
+
+ #region Patronymic : string - Отчество
+
+ /// Отчество
+ public string Patronymic { get; set => Set(ref field, value); } = "Отчество-1";
+
+ #endregion
+
+ #region Age : int - Отчество
+
+ /// Отчество
+ public int Age { get; set => Set(ref field, value, v => v is > 0 and < 123); } = 18;
+
+ #endregion
+
+ #region Group : string - Группа
+
+ /// Группа
+ public string Group { get; set => Set(ref field, value); } = "Группа-1";
+
+ #endregion
+
+ #region Command SaveCommand : BindingGroup - Принять изменения
+
+ /// Принять изменения
+ [field: AllowNull, MaybeNull]
+ public ICommand SaveCommand => field ??= Command.New(
+ p => p.CommitEdit(),
+ p => p is not null);
+
+ #endregion
+
+ #region Command CancelCommand : BindingGroup - Отменить изменения
+
+ /// Отменить изменения
+ [field: AllowNull, MaybeNull]
+ public ICommand CancelCommand => field ??= Command.New(
+ p => p.CancelEdit(),
+ p => p is { IsDirty: true });
+
+ #endregion
+}
\ No newline at end of file
diff --git a/Tests/MathCore.WPF.WindowTest/ViewModels/TestWindow6ViewModel.cs b/Tests/MathCore.WPF.WindowTest/ViewModels/TestWindow6ViewModel.cs
index 04b74c0..3415861 100644
--- a/Tests/MathCore.WPF.WindowTest/ViewModels/TestWindow6ViewModel.cs
+++ b/Tests/MathCore.WPF.WindowTest/ViewModels/TestWindow6ViewModel.cs
@@ -16,5 +16,10 @@ public class TestWindow6ViewModel : TitledViewModel
public ObservableCollection Values { get; } = [];
-
+ #region Student : StudentInfoViewModel - Студент
+
+ /// Студент
+ public StudentInfoViewModel Student { get; set => Set(ref field, value); } = new();
+
+ #endregion
}
diff --git a/Tests/MathCore.WPF.WindowTest/ViewModels/TestWindow7ViewModel.cs b/Tests/MathCore.WPF.WindowTest/ViewModels/TestWindow7ViewModel.cs
new file mode 100644
index 0000000..3366a35
--- /dev/null
+++ b/Tests/MathCore.WPF.WindowTest/ViewModels/TestWindow7ViewModel.cs
@@ -0,0 +1,49 @@
+using System.Windows.Markup;
+
+using MathCore.WPF.ViewModels;
+
+namespace MathCore.WPF.WindowTest.ViewModels;
+
+[MarkupExtensionReturnType(typeof(TestWindow7ViewModel))]
+public class TestWindow7ViewModel() : TitledViewModel("Поток событий")
+{
+ #region Events : SelectableCollection - События
+
+ /// События
+ public SelectableCollection Events { get; set => Set(ref field, value); } = [.. GenerateEvents()];
+
+ private static IEnumerable GenerateEvents()
+ {
+ for (var i = 0; i < 10; i++)
+ yield return new($"Event-{i + 1}")
+ {
+ Time = DateTime.Now.AddMinutes(i * -10),
+ Description =
+ $"""
+ Event {i + 1} description
+ 123
+ 32112312
+ 123123
+ """
+ };
+ }
+
+ #endregion
+}
+
+public class TestEventViewModel(string EventName) : TitledViewModel(EventName)
+{
+ #region Time : DateTime - Время
+
+ /// Время
+ public DateTime Time { get; set => Set(ref field, value); }
+
+ #endregion
+
+ #region Description : string - Описание
+
+ /// Описание
+ public string Description { get; set => Set(ref field, value); } = null!;
+
+ #endregion
+}
\ No newline at end of file