Skip to content

Commit 79c7e42

Browse files
committed
Комментарии
1 parent f867afc commit 79c7e42

7 files changed

Lines changed: 296 additions & 276 deletions

MathCore.WPF/LanguageManager.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44

55
namespace MathCore.WPF;
66

7+
/// <summary>Менеджер переключения языка ввода</summary>
78
public class LanguageManager : DependencyObject
89
{
910
#region InputCulture
1011

11-
/// <summary></summary>
12+
/// <summary>Свойство зависимости для текущей культуры ввода</summary>
1213
public static readonly DependencyProperty InputCultureProperty =
1314
DependencyProperty.Register(
1415
nameof(InputCulture),
@@ -17,16 +18,18 @@ public class LanguageManager : DependencyObject
1718
new(InputLanguageManager.Current.CurrentInputLanguage, (s, e) => ChangeCulture((CultureInfo)e.NewValue)));
1819

1920

20-
/// <summary></summary>
21+
/// <summary>Текущая культура ввода</summary>
2122
public CultureInfo InputCulture { get => (CultureInfo)GetValue(InputCultureProperty); set => SetValue(InputCultureProperty, value); }
2223

2324
#endregion
2425

2526
#region Singleton
2627

2728
private static volatile LanguageManager __Manager;
29+
2830
private static readonly object __ManagerSyncRoot = new();
2931

32+
/// <summary>Текущий экземпляр менеджера языка ввода</summary>
3033
public static LanguageManager Current
3134
{
3235
get
@@ -46,5 +49,7 @@ public static LanguageManager Current
4649

4750
private void OnLanguageChanged(object Sender, InputLanguageEventArgs E) => InputCulture = E.NewLanguage;
4851

52+
/// <summary>Изменить текущую культуру ввода</summary>
53+
/// <param name="culture">Новая культура ввода</param>
4954
private static void ChangeCulture(CultureInfo culture) => InputLanguageManager.Current.CurrentInputLanguage = culture;
5055
}

MathCore.WPF/NestedBinding.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@
66
namespace MathCore.WPF;
77

88
/// <summary>Расширение разметки для создания вложенных привязок.</summary>
9+
/// <example>
10+
/// Пример использования вложенных привязок в XAML:
11+
/// <code><![CDATA[
12+
/// <TextBlock>
13+
/// <TextBlock.Text>
14+
/// <wpf:NestedBinding Converter="{StaticResource OuterConverter}">
15+
/// <Binding Path="Property1"/>
16+
/// <wpf:NestedBinding Converter="{StaticResource InnerConverter}">
17+
/// <Binding Path="Property2"/>
18+
/// <Binding Path="Property3"/>
19+
/// </wpf:NestedBinding>
20+
/// </wpf:NestedBinding>
21+
/// </TextBlock.Text>
22+
/// </TextBlock>
23+
/// ]]></code>
24+
/// В этом примере InnerConverter сначала преобразует Property2 и Property3,
25+
/// затем OuterConverter преобразует Property1 и результат InnerConverter.
26+
/// </example>
927
[Copyright("adeptuss", url = "https://habr.com/ru/post/277157/")]
1028
[ContentProperty(nameof(Bindings))]
1129
[MarkupExtensionReturnType(typeof(object))]

MathCore.WPF/NestedBindingConverter.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,85 @@
33

44
namespace MathCore.WPF;
55

6+
/// <summary>Преобразователь значений для вложенных привязок</summary>
7+
/// <remarks>
8+
/// Этот класс используется внутри <see cref="NestedBinding"/> для обработки дерева вложенных привязок.
9+
/// Преобразователь рекурсивно обходит дерево и применяет соответствующие конвертеры к значениям.
10+
/// </remarks>
11+
/// <example>
12+
/// Этот класс создаётся автоматически при использовании <see cref="NestedBinding"/> и не требует ручного создания.
13+
/// Пример пользовательских конвертеров для использования с вложенными привязками:
14+
/// <code><![CDATA[
15+
/// // Конвертер для объединения строк
16+
/// public class StringConcatConverter : IMultiValueConverter
17+
/// {
18+
/// public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
19+
/// {
20+
/// return string.Join(" ", values.Select(v => v?.ToString() ?? ""));
21+
/// }
22+
///
23+
/// public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
24+
/// {
25+
/// throw new NotSupportedException();
26+
/// }
27+
/// }
28+
///
29+
/// // Конвертер для математических операций
30+
/// public class MathConverter : IMultiValueConverter
31+
/// {
32+
/// public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
33+
/// {
34+
/// if (values.Length != 2) return 0;
35+
/// var a = System.Convert.ToDouble(values[0]);
36+
/// var b = System.Convert.ToDouble(values[1]);
37+
/// return a + b;
38+
/// }
39+
///
40+
/// public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
41+
/// {
42+
/// throw new NotSupportedException();
43+
/// }
44+
/// }
45+
/// ]]></code>
46+
/// Использование в XAML:
47+
/// <code><![CDATA[
48+
/// <TextBlock>
49+
/// <TextBlock.Text>
50+
/// <wpf:NestedBinding Converter="{StaticResource StringConcatConverter}">
51+
/// <Binding Path="FirstName"/>
52+
/// <wpf:NestedBinding Converter="{StaticResource MathConverter}">
53+
/// <Binding Path="Value1"/>
54+
/// <Binding Path="Value2"/>
55+
/// </wpf:NestedBinding>
56+
/// </wpf:NestedBinding>
57+
/// </TextBlock.Text>
58+
/// </TextBlock>
59+
/// ]]></code>
60+
/// В этом примере MathConverter сначала суммирует Value1 и Value2, затем StringConcatConverter объединяет FirstName с результатом суммы.
61+
/// </example>
662
public class NestedBindingConverter(NestedBindingsTree tree) : IMultiValueConverter
763
{
64+
/// <summary>Дерево вложенных привязок</summary>
865
private NestedBindingsTree Tree { get; } = tree;
966

67+
/// <summary>Преобразует значения привязок в значение целевого типа</summary>
68+
/// <param name="values">Массив значений, полученных от привязок</param>
69+
/// <param name="TargetType">Целевой тип преобразования</param>
70+
/// <param name="parameter">Параметр преобразователя</param>
71+
/// <param name="culture">Культура для преобразования</param>
72+
/// <returns>Преобразованное значение</returns>
1073
public object? Convert(object[]? values, Type TargetType, object? parameter, CultureInfo culture)
1174
{
1275
var value = GetTreeValue(Tree, values, TargetType, culture);
1376
return value;
1477
}
1578

79+
/// <summary>Рекурсивно вычисляет значение для узла дерева вложенных привязок</summary>
80+
/// <param name="tree">Узел дерева вложенных привязок</param>
81+
/// <param name="values">Массив значений, полученных от привязок</param>
82+
/// <param name="TargetType">Целевой тип преобразования</param>
83+
/// <param name="culture">Культура для преобразования</param>
84+
/// <returns>Преобразованное значение для данного узла дерева</returns>
1685
private static object? GetTreeValue(NestedBindingsTree tree, object[]? values, Type TargetType, CultureInfo culture)
1786
{
1887
var objects = new object[tree.Nodes.Count];
@@ -28,5 +97,12 @@ public class NestedBindingConverter(NestedBindingsTree tree) : IMultiValueConver
2897
return value;
2998
}
3099

100+
/// <summary>Обратное преобразование не поддерживается</summary>
101+
/// <param name="value">Значение для обратного преобразования</param>
102+
/// <param name="TargetTypes">Целевые типы</param>
103+
/// <param name="parameter">Параметр преобразователя</param>
104+
/// <param name="culture">Культура для преобразования</param>
105+
/// <returns>Массив преобразованных значений</returns>
106+
/// <exception cref="NotSupportedException">Обратное преобразование не поддерживается</exception>
31107
public object?[]? ConvertBack(object? value, Type[] TargetTypes, object? parameter, CultureInfo culture) => throw new NotSupportedException();
32108
}

MathCore.WPF/NestedBindingNode.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,32 @@
11
namespace MathCore.WPF;
22

3+
/// <summary>Узел дерева вложенных привязок, представляющий ссылку на значение в массиве привязок</summary>
4+
/// <remarks>
5+
/// Этот класс используется внутри <see cref="NestedBinding"/> для построения дерева вложенных привязок.
6+
/// Каждый узел хранит индекс значения в массиве, передаваемом в конвертер.
7+
/// Класс создаётся автоматически и не предназначен для прямого использования.
8+
/// </remarks>
9+
/// <example>
10+
/// Класс создаётся автоматически при обработке <see cref="NestedBinding"/>.
11+
/// Например, при следующей структуре привязок:
12+
/// <code><![CDATA[
13+
/// <wpf:NestedBinding Converter="{StaticResource OuterConverter}">
14+
/// <Binding Path="Property1"/> <!-- NestedBindingNode с Index=0 -->
15+
/// <Binding Path="Property2"/> <!-- NestedBindingNode с Index=1 -->
16+
/// <wpf:NestedBinding Converter="{StaticResource InnerConverter}">
17+
/// <Binding Path="Property3"/> <!-- NestedBindingNode с Index=2 -->
18+
/// <Binding Path="Property4"/> <!-- NestedBindingNode с Index=3 -->
19+
/// </wpf:NestedBinding>
20+
/// </wpf:NestedBinding>
21+
/// ]]></code>
22+
/// Будет создано дерево, где для каждой обычной привязки создаётся <see cref="NestedBindingNode"/> с соответствующим индексом.
23+
/// </example>
324
public class NestedBindingNode(int index)
425
{
26+
/// <summary>Индекс значения в массиве значений привязок</summary>
527
public int Index => index;
628

29+
/// <summary>Возвращает строковое представление узла</summary>
30+
/// <returns>Строковое представление индекса узла</returns>
731
public override string ToString() => index.ToString();
832
}

MathCore.WPF/NestedBindingsTree.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,49 @@
33

44
namespace MathCore.WPF;
55

6+
/// <summary>Дерево вложенных привязок, представляющее иерархическую структуру конвертеров и привязок</summary>
7+
/// <remarks>
8+
/// Этот класс используется внутри <see cref="NestedBinding"/> для построения дерева вложенных привязок.
9+
/// Каждый узел дерева хранит конвертер и коллекцию дочерних узлов, которые могут быть как простыми
10+
/// ссылками на значения (<see cref="NestedBindingNode"/>), так и вложенными деревьями (<see cref="NestedBindingsTree"/>).
11+
/// Класс создаётся автоматически и не предназначен для прямого использования.
12+
/// </remarks>
13+
/// <example>
14+
/// Класс создаётся автоматически при обработке <see cref="NestedBinding"/>.
15+
/// Например, при следующей XAML-разметке:
16+
/// <code><![CDATA[
17+
/// <wpf:NestedBinding Converter="{StaticResource OuterConverter}">
18+
/// <Binding Path="FirstName"/>
19+
/// <Binding Path="LastName"/>
20+
/// <wpf:NestedBinding Converter="{StaticResource InnerConverter}">
21+
/// <Binding Path="Age"/>
22+
/// <Binding Path="Country"/>
23+
/// </wpf:NestedBinding>
24+
/// </wpf:NestedBinding>
25+
/// ]]></code>
26+
/// Будет создано следующее дерево:
27+
/// - Корневое дерево (NestedBindingsTree) с OuterConverter
28+
/// - Узел (NestedBindingNode) с Index=0 для FirstName
29+
/// - Узел (NestedBindingNode) с Index=1 для LastName
30+
/// - Вложенное дерево (NestedBindingsTree) с InnerConverter
31+
/// - Узел (NestedBindingNode) с Index=2 для Age
32+
/// - Узел (NestedBindingNode) с Index=3 для Country
33+
/// </example>
634
public class NestedBindingsTree() : NestedBindingNode(-1)
735
{
36+
/// <summary>Конвертер, используемый для преобразования значений дочерних узлов</summary>
837
public IMultiValueConverter Converter { get; set; }
938

39+
/// <summary>Параметр, передаваемый конвертеру</summary>
1040
public object ConverterParameter { get; set; }
1141

42+
/// <summary>Культура, используемая для преобразования значений</summary>
1243
public CultureInfo ConverterCulture { get; set; }
1344

45+
/// <summary>Коллекция дочерних узлов дерева</summary>
46+
/// <remarks>
47+
/// Каждый элемент может быть либо <see cref="NestedBindingNode"/> (ссылка на значение привязки),
48+
/// либо <see cref="NestedBindingsTree"/> (вложенное поддерево с собственным конвертером).
49+
/// </remarks>
1450
public List<NestedBindingNode> Nodes { get; } = [];
1551
}

MathCore.WPF/PasswordBoxHelper.cs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,18 @@
55

66
namespace MathCore.WPF;
77

8-
/// <summary>
9-
/// Creates a bindable attached property for the <see cref="PasswordBox.SecurePassword"/> property.
10-
/// </summary>
8+
/// <summary>Создаёт привязываемое присоединённое свойство для <see cref="PasswordBox.SecurePassword"/></summary>
119
public static class PasswordBoxHelper
1210
{
13-
// an attached behavior won't work due to view model validation not picking up the right control to adorn
11+
/// <summary>Присоединённое свойство для привязки защищённого пароля</summary>
12+
// присоединённое поведение не подходит из-за неверной привязки к элементу для валидации // кратко по делу
1413
public static readonly DependencyProperty SecurePasswordBindingProperty = DependencyProperty
1514
.RegisterAttached(
1615
"ShadowSecurePassword",
1716
typeof(SecureString),
1817
typeof(PasswordBoxHelper),
1918
new FrameworkPropertyMetadata(
20-
new SecureString(),
19+
new SecureString(),
2120
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
2221
AttachedPropertyValueChanged));
2322

@@ -28,15 +27,21 @@ public static class PasswordBoxHelper
2827
typeof(PasswordBoxHelper),
2928
new());
3029

30+
/// <summary>Устанавливает значение защищённого пароля</summary>
31+
/// <param name="element">Элемент для установки значения</param>
32+
/// <param name="SecureString">Значение защищённого пароля</param>
3133
public static void SetSecurePassword(PasswordBox element, SecureString SecureString) => element.SetValue(SecurePasswordBindingProperty, SecureString);
3234

35+
/// <summary>Возвращает значение защищённого пароля</summary>
36+
/// <param name="element">Элемент для чтения значения</param>
37+
/// <returns>Значение защищённого пароля</returns>
3338
public static SecureString GetSecurePassword(PasswordBox element) => (SecureString)element.GetValue(SecurePasswordBindingProperty)!;
3439

3540
private static void AttachedPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
3641
{
37-
// we'll need to hook up to one of the element's events
38-
// in order to allow the GC to collect the control, we'll wrap the event handler inside an object living in an attached property
39-
// don't be tempted to use the Unloaded event as that will be fired even when the control is still alive and well (e.g. switching tabs in a tab control)
42+
// требуется подписка на событие элемента для обновления значения // кратко по делу
43+
// обработчик обёрнут в объект присоединённого свойства ради корректной сборки мусора // кратко по делу
44+
// событие Unloaded использовать нельзя, оно срабатывает при переключении вкладок // кратко по делу
4045
var password_box = (PasswordBox)d;
4146
if (password_box.GetValue(__PasswordBindingMarshallerProperty) is not PasswordBindingMarshaller binding_marshaller)
4247
{
@@ -47,7 +52,7 @@ private static void AttachedPropertyValueChanged(DependencyObject d, DependencyP
4752
binding_marshaller.UpdatePasswordBox(e.NewValue as SecureString);
4853
}
4954

50-
/// <summary>Encapsulated event logic</summary>
55+
/// <summary>Инкапсулированная логика обработки событий</summary>
5156
private class PasswordBindingMarshaller
5257
{
5358
private readonly PasswordBox _PasswordBox;
@@ -67,10 +72,10 @@ public void UpdatePasswordBox(SecureString NewPassword)
6772
_IsMarshalling = true;
6873
try
6974
{
70-
// setting up the SecuredPassword won't trigger a visual update so we'll have to use the Password property
75+
// установка SecurePassword не обновляет визуальное значение, требуется Password // кратко по делу
7176
_PasswordBox.Password = SecureStringToString(NewPassword);
7277

73-
// you may try the statement below, however the benefits are minimal security wise (you still have to extract the unsecured password for copying)
78+
// можно использовать копирование, но выигрыш по безопасности минимален // кратко по делу
7479
//newPassword.CopyInto(_passwordBox.SecurePassword);
7580
}
7681
finally
@@ -95,7 +100,7 @@ private static string SecureStringToString(SecureString value)
95100

96101
private void PasswordBoxPasswordChanged(object sender, RoutedEventArgs e)
97102
{
98-
// copy the password into the attached property
103+
// копирование пароля в присоединённое свойство // кратко по делу
99104
if (_IsMarshalling)
100105
return;
101106

0 commit comments

Comments
 (0)