Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MathCore.WPF.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Brel/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Bself/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Headered/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=hwnd/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Inits/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Interp/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=MVVM/@EntryIndexedValue">True</s:Boolean>
Expand Down
11 changes: 11 additions & 0 deletions MathCore.WPF/Commands/BindingGroupCommit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Windows.Data;

namespace MathCore.WPF.Commands;

/// <summary>Команда для подтверждения изменений в BindingGroup, если они были изменены</summary>
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();
}
11 changes: 11 additions & 0 deletions MathCore.WPF/Commands/BindingGroupRollback.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Windows.Data;

namespace MathCore.WPF.Commands;

/// <summary>Команда для отмены изменений в BindingGroup, если они были изменены</summary>
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();
}
17 changes: 10 additions & 7 deletions MathCore.WPF/Extensions/WindowExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Window> action)
public static void ForWindowFromChild<T>(this T ChildDependencyObject, Action<Window> 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<Window> action)
public static void ForWindowFromTemplate<T>(this T TemplateFrameworkElement, Action<Window> 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)
Expand All @@ -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)
Expand Down
14 changes: 8 additions & 6 deletions MathCore.WPF/MathCore.WPF.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>preview</LangVersion>
<!--<RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis>-->
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
</PropertyGroup>

<PropertyGroup>
<Version>0.0.48.1</Version>
<Version>0.0.48.2</Version>
<PackageReleaseNotes>
Добавлены стили для кнопок и полей ввода текста в стилистике Bootstrap 5
</PackageReleaseNotes>
Expand All @@ -49,24 +51,24 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MathCore" Version="0.0.93.1" />
<PackageReference Include="MathCore" Version="0.0.93.2" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='net4.6.1-windows'">
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.6.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="[1.1.39]" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='net4.7-windows'">
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.6.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='net4.8-windows'">
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.6.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
</ItemGroup>

Expand All @@ -75,7 +77,7 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='net5.0-windows'">
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.6.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="[1.1.39]" />
</ItemGroup>

Expand Down
113 changes: 89 additions & 24 deletions MathCore.WPF/TextBoxEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,40 +20,42 @@ public static class TextBoxEx
typeof(TextBoxEx),
new(default(bool), OnUpdateBindingOnEnterChanged));

/// <summary>Обработчик изменения свойства UpdateBindingSourceOnEnter</summary>
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;
else
text_box.KeyDown -= OnTextBoxKeyDown;
}

/// <summary>Обработчик события нажатия клавиши в TextBox</summary>
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;
}

/// <summary>Обновить привязку при нажатии Enter</summary>
/// <summary>Установить значение свойства UpdateBindingSourceOnEnter</summary>
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static void SetUpdateBindingSourceOnEnter(DependencyObject d, bool value) => d.SetValue(UpdateBindingSourceOnEnterProperty, value);

/// <summary>Обновить привязку при нажатии Enter</summary>
/// <summary>Получить значение свойства UpdateBindingSourceOnEnter</summary>
public static bool GetUpdateBindingSourceOnEnter(DependencyObject d) => (bool)d.GetValue(UpdateBindingSourceOnEnterProperty);

#endregion
Expand All @@ -66,21 +68,23 @@ private static void OnTextBoxKeyDown(object Sender, KeyEventArgs E)
"ValidateInputScope",
typeof(bool),
typeof(TextBoxEx),
new(default(bool), OnValidateInputScopeChanged));
new(false, OnValidateInputScopeChanged));

/// <summary>Обработчик изменения свойства ValidateInputScope</summary>
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;
else
text_box.PreviewTextInput -= ValidateScopes_OnPreviewTextInput;
}

/// <summary>Обработчик события предварительного ввода текста в TextBox</summary>
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;
Expand All @@ -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):
Expand All @@ -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;
}

/// <summary>Проверять корректность правил InputScope</summary>
/// <summary>Установить значение свойства ValidateInputScope</summary>
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static void SetValidateInputScope(DependencyObject d, bool value) => d.SetValue(ValidateInputScopeProperty, value);

/// <summary>Проверять корректность правил InputScope</summary>
/// <summary>Получить значение свойства ValidateInputScope</summary>
public static bool GetValidateInputScope(DependencyObject d) => (bool)d.GetValue(ValidateInputScopeProperty);

#endregion
Expand All @@ -143,19 +147,25 @@ private static void ValidateScopes_OnPreviewTextInput(object Sender, TextComposi
typeof(TextBoxEx),
new(decimal.Zero, OnMouseWheelIncrementChanged));

/// <summary>Обработчик изменения свойства MouseWheelIncrement</summary>
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;
}
}

/// <summary>Инкремент колёсика мышки</summary>
/// <summary>Установить значение свойства MouseWheelIncrement</summary>
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static void SetMouseWheelIncrement(DependencyObject d, decimal value) => d.SetValue(MouseWheelIncrementProperty, value);

/// <summary>Инкремент колёсика мышки</summary>
/// <summary>Получить значение свойства MouseWheelIncrement</summary>
public static decimal GetMouseWheelIncrement(DependencyObject d) => (decimal)d.GetValue(MouseWheelIncrementProperty);

#endregion
Expand All @@ -170,15 +180,16 @@ private static void OnMouseWheelIncrementChanged(DependencyObject s, DependencyP
typeof(TextBoxEx),
new(0.1m));

/// <summary>Инкремент колёсика мышки</summary>
/// <summary>Установить значение свойства MouseWheelIncrementCtrlRatio</summary>
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static void SetMouseWheelIncrementCtrlRatio(DependencyObject d, decimal value) => d.SetValue(MouseWheelIncrementCtrlRatioProperty, value);

/// <summary>Инкремент колёсика мышки</summary>
/// <summary>Получить значение свойства MouseWheelIncrementCtrlRatio</summary>
public static decimal GetMouseWheelIncrementCtrlRatio(DependencyObject d) => (decimal)d.GetValue(MouseWheelIncrementCtrlRatioProperty);

#endregion

/// <summary>Обработчик события прокрутки колёсика мыши в TextBox</summary>
private static void OnMouseWheel(object Sender, MouseWheelEventArgs E)
{
if (Sender is not TextBox { Text: var text } text_block) return;
Expand All @@ -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

/// <summary>Авто выбор всего содержимого TextBox</summary>
public static readonly DependencyProperty AutoSelectAllProperty =
DependencyProperty.RegisterAttached(
"AutoSelectAll",
typeof(bool),
typeof(TextBoxEx),
new(false, OnAutoSelectAllChanged));

/// <summary>Обработчик изменения свойства AutoSelectAll</summary>
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;
}
}

/// <summary>Обработчик события предварительного нажатия мыши в TextBox</summary>
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;
}

/// <summary>Обработчик события получения фокуса клавиатуры в TextBox</summary>
private static void OnTextBoxGotKeyboardFocus(object sender, RoutedEventArgs e)
{
if (sender is not TextBox text_box) return;

text_box.SelectAll();
e.Handled = true;
}

/// <summary>Установить значение свойства AutoSelectAll</summary>
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static void SetAutoSelectAll(DependencyObject d, bool value) => d.SetValue(AutoSelectAllProperty, value);

/// <summary>Получить значение свойства AutoSelectAll</summary>
public static bool GetAutoSelectAll(DependencyObject d) => (bool)d.GetValue(AutoSelectAllProperty);

#endregion
}
6 changes: 3 additions & 3 deletions Tests/MathCore.WPF.Tests/MathCore.WPF.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

<ItemGroup>
<PackageReference Include="MathCore.TestsExtensions" Version="0.1.44" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.7.3" />
<PackageReference Include="MSTest.TestFramework" Version="3.7.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="MSTest.TestAdapter" Version="3.9.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.9.2" />
<PackageReference Include="coverlet.collector" Version="6.0.4" PrivateAssets="all">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
2 changes: 1 addition & 1 deletion Tests/MathCore.WPF.WindowTest/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
Expand Down
4 changes: 2 additions & 2 deletions Tests/MathCore.WPF.WindowTest/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading
Loading