From 0c1fe1d257f36760be5a50823210286b99cea33a Mon Sep 17 00:00:00 2001 From: Anton Zhukov Date: Fri, 28 Nov 2025 23:50:03 +0200 Subject: [PATCH] feat: Use system theme --- AboutBoxWpf/AboutBoxWpf.csproj | 2 +- EventLook/App.xaml | 27 ++-- EventLook/EventLook.csproj | 4 +- .../Extensions/DependencyObjectExtensions.cs | 29 ++++ EventLook/Extensions/TextBoxExtensions.cs | 24 +++ EventLook/View/DateTimePickerCustom.xaml | 50 ++++++ EventLook/View/DateTimePickerCustom.xaml.cs | 147 ++++++++++++++++++ EventLook/View/DetailWindow.xaml | 8 +- EventLook/View/LogPickerWindow.xaml | 6 +- EventLook/View/MainWindow.xaml | 62 ++++---- EventLook/View/SettingsWindow.xaml | 12 +- EventLook/View/TextBoxBehavior.cs | 33 ++-- Tests/Tests.csproj | 2 +- 13 files changed, 331 insertions(+), 75 deletions(-) create mode 100644 EventLook/Extensions/DependencyObjectExtensions.cs create mode 100644 EventLook/Extensions/TextBoxExtensions.cs create mode 100644 EventLook/View/DateTimePickerCustom.xaml create mode 100644 EventLook/View/DateTimePickerCustom.xaml.cs diff --git a/AboutBoxWpf/AboutBoxWpf.csproj b/AboutBoxWpf/AboutBoxWpf.csproj index 4b5d306..176bd24 100644 --- a/AboutBoxWpf/AboutBoxWpf.csproj +++ b/AboutBoxWpf/AboutBoxWpf.csproj @@ -1,7 +1,7 @@  - net8.0-windows10.0.17763.0 + net10.0-windows10.0.17763.0 enable true AnyCPU;x64;x86;ARM64 diff --git a/EventLook/App.xaml b/EventLook/App.xaml index 0b8c5af..63ef6c0 100644 --- a/EventLook/App.xaml +++ b/EventLook/App.xaml @@ -1,3 +1,4 @@ + + xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006" + ThemeMode="System"> - ./Font/WINGDNG3.TTF#Wingdings 3 - - + + ./Font/WINGDNG3.TTF#Wingdings 3 + + + + \ No newline at end of file diff --git a/EventLook/EventLook.csproj b/EventLook/EventLook.csproj index 9de56c0..9f50228 100644 --- a/EventLook/EventLook.csproj +++ b/EventLook/EventLook.csproj @@ -1,6 +1,6 @@  - net8.0-windows10.0.17763.0 + net10.0-windows10.0.17763.0 WinExe true true @@ -50,7 +50,7 @@ - + diff --git a/EventLook/Extensions/DependencyObjectExtensions.cs b/EventLook/Extensions/DependencyObjectExtensions.cs new file mode 100644 index 0000000..9f43f85 --- /dev/null +++ b/EventLook/Extensions/DependencyObjectExtensions.cs @@ -0,0 +1,29 @@ +using System.Windows; +using System.Windows.Media; + +namespace EventLook.Extensions; + +internal static class DependencyObjectExtensions +{ + extension(DependencyObject tree) + { + // https://learn.microsoft.com/en-us/answers/questions/1806319/how-to-remove-the-clear-button-at-the-end-of-the-t + public DependencyObject FindChildElementByName(string sName) + { + for (var i = 0; i < VisualTreeHelper.GetChildrenCount(tree); i++) + { + var child = VisualTreeHelper.GetChild(tree, i); + + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + if (child != null && (child as FrameworkElement)?.Name == sName) + return child; + + var childInSubtree = FindChildElementByName(child, sName); + if (childInSubtree != null) + return childInSubtree; + } + + return null; + } + } +} \ No newline at end of file diff --git a/EventLook/Extensions/TextBoxExtensions.cs b/EventLook/Extensions/TextBoxExtensions.cs new file mode 100644 index 0000000..82beede --- /dev/null +++ b/EventLook/Extensions/TextBoxExtensions.cs @@ -0,0 +1,24 @@ +using System.Windows; +using System.Windows.Controls; + +namespace EventLook.Extensions; + +internal static class TextBoxExtensions +{ + extension(TextBox textBox) + { + // https://learn.microsoft.com/en-us/answers/questions/1806319/how-to-remove-the-clear-button-at-the-end-of-the-t + public void DeleteFluentClearButton() + { + var childButton = textBox.FindChildElementByName("DeleteButton"); + var parentGrid = (childButton as Control)?.Parent; + if (parentGrid != null) + { + if (childButton is UIElement childButtonElement) + { + (parentGrid as Grid)?.Children.Remove(childButtonElement); + } + } + } + } +} \ No newline at end of file diff --git a/EventLook/View/DateTimePickerCustom.xaml b/EventLook/View/DateTimePickerCustom.xaml new file mode 100644 index 0000000..ce0bea1 --- /dev/null +++ b/EventLook/View/DateTimePickerCustom.xaml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/EventLook/View/DateTimePickerCustom.xaml.cs b/EventLook/View/DateTimePickerCustom.xaml.cs new file mode 100644 index 0000000..f6d5d13 --- /dev/null +++ b/EventLook/View/DateTimePickerCustom.xaml.cs @@ -0,0 +1,147 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using CommunityToolkit.Mvvm.Input; +using EventLook.Extensions; + +namespace EventLook.View; + +public sealed partial class DateTimePickerCustom +{ + private bool isUpdating; + + public DateTimePickerCustom() + { + InitializeComponent(); + MoveFocusCommand = new RelayCommand(() => PartTimeText.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next))); + } + + public IRelayCommand MoveFocusCommand { get; } + + #region Value dependency property + + public static readonly DependencyProperty ValueProperty = + DependencyProperty.Register( + nameof(Value), + typeof(DateTime), + typeof(DateTimePickerCustom), + new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged)); + + public DateTime Value + { + get => (DateTime)GetValue(ValueProperty); + set => SetValue(ValueProperty, value); + } + + private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ctrl = (DateTimePickerCustom)d; + ctrl.UpdatePartsFromValue(); + } + + private void UpdatePartsFromValue() + { + if (isUpdating) return; + isUpdating = true; + + SelectedDate = Value; + SelectedTimeString = TimeOnly.FromTimeSpan(Value.TimeOfDay).ToString(TimeFormat); + + isUpdating = false; + } + + #endregion Value dependency property + + #region IsReadonly dependency property + + public static readonly DependencyProperty IsReadOnlyProperty = + DependencyProperty.Register(nameof(IsReadOnly), typeof(bool), typeof(DateTimePickerCustom), new PropertyMetadata(false)); + + public bool IsReadOnly + { + get => (bool)GetValue(IsReadOnlyProperty); + set => SetValue(IsReadOnlyProperty, value); + } + + #endregion IsReadonly dependency property + + #region SelectedDate dependency property + + public static readonly DependencyProperty SelectedDateProperty = + DependencyProperty.Register( + nameof(SelectedDate), + typeof(DateTime), + typeof(DateTimePickerCustom), + new FrameworkPropertyMetadata(DateTime.Now.Date, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedDateChanged)); + + public DateTime SelectedDate + { + get => (DateTime)GetValue(SelectedDateProperty); + set => SetValue(SelectedDateProperty, value); + } + + private static void OnSelectedDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ctrl = (DateTimePickerCustom)d; + + if (ctrl.isUpdating) return; + ctrl.isUpdating = true; + + ctrl.Value = (DateTime)e.NewValue + ctrl.Value.TimeOfDay; + + ctrl.isUpdating = false; + } + + #endregion SelectedDate dependency property + + #region SelectedTimeString dependency property + + private const string TimeFormat = "HH:mm"; + + public static readonly DependencyProperty SelectedTimeStringProperty = + DependencyProperty.Register( + nameof(SelectedTimeString), + typeof(string), + typeof(DateTimePickerCustom), + new FrameworkPropertyMetadata(TimeOnly.MinValue.ToString(TimeFormat), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedTimeStringChanged)); + + public string SelectedTimeString + { + get => (string)GetValue(SelectedTimeStringProperty); + set => SetValue(SelectedTimeStringProperty, value); + } + + private static void OnSelectedTimeStringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ctrl = (DateTimePickerCustom)d; + + if (ctrl.isUpdating) return; + ctrl.isUpdating = true; + + // Allow HH:mm or HHmm format. + if (TimeOnly.TryParse((string)e.NewValue, out var time) + || (e.NewValue is string { Length: 4 } str + && TimeOnly.TryParse(str[..2] + ":" + str[2..], out time))) + { + // Ensure normalized time + ctrl.SelectedTimeString = time.ToString(TimeFormat); + + ctrl.Value = ctrl.Value.Date + time.ToTimeSpan(); + } + + ctrl.isUpdating = false; + } + + #endregion + + private void DateTimePickerCustom_Loaded(object sender, RoutedEventArgs e) + { + UpdatePartsFromValue(); + } + + private void PartTimeText_OnLoaded(object sender, RoutedEventArgs e) + { + ((TextBox)sender).DeleteFluentClearButton(); + } +} \ No newline at end of file diff --git a/EventLook/View/DetailWindow.xaml b/EventLook/View/DetailWindow.xaml index cf14e0a..954ff61 100644 --- a/EventLook/View/DetailWindow.xaml +++ b/EventLook/View/DetailWindow.xaml @@ -8,14 +8,14 @@ FocusManager.FocusedElement="{Binding ElementName=RichTextBoxXml}" Title="Event Details" Height="550" Width="900"> - - + + -