Jelajahi Sumber

File-scoped namespaces

Daniel Chalmers 3 tahun lalu
induk
melakukan
1efb30a39e

+ 35 - 36
DesktopClock/App.xaml.cs

@@ -3,44 +3,43 @@ using System.Reflection;
 using System.Windows;
 using DesktopClock.Properties;
 
-namespace DesktopClock
+namespace DesktopClock;
+
+/// <summary>
+/// Interaction logic for App.xaml
+/// </summary>
+public partial class App : Application
 {
-    /// <summary>
-    /// Interaction logic for App.xaml
-    /// </summary>
-    public partial class App : Application
-    {
-        public static Assembly Assembly { get; } = Assembly.GetExecutingAssembly();
-        public static string Title { get; } = Assembly.GetCustomAttribute<AssemblyTitleAttribute>().Title;
+    public static Assembly Assembly { get; } = Assembly.GetExecutingAssembly();
+    public static string Title { get; } = Assembly.GetCustomAttribute<AssemblyTitleAttribute>().Title;
 
-        // https://www.materialui.co/colors
-        public static IReadOnlyList<Theme> Themes { get; } = new Theme[]
-        {
-            new Theme("White", "#FFFFFF", "#000000"),
-            new Theme("Black", "#000000", "#9E9E9E"),
-            new Theme("Grey", "#9E9E9E", "#000000"),
-            new Theme("Red", "#ff5252", "#212121"),
-            new Theme("Pink", "#FF4081", "#212121"),
-            new Theme("Purple", "#E040FB", "#212121"),
-            new Theme("Deep Purple", "#7C4DFF", "#212121"),
-            new Theme("Indigo", "#536DFE", "#212121"),
-            new Theme("Blue", "#448AFF", "#212121"),
-            new Theme("Light Blue", "#40C4FF", "#212121"),
-            new Theme("Cyan", "#18FFFF", "#212121"),
-            new Theme("Teal", "#64FFDA", "#212121"),
-            new Theme("Green", "#69F0AE", "#212121"),
-            new Theme("Light Green", "#B2FF59", "#212121"),
-            new Theme("Lime", "#EEFF41", "#212121"),
-            new Theme("Yellow", "#FFFF00", "#212121"),
-            new Theme("Amber", "#FFD740", "#212121"),
-            new Theme("Orange", "#FFAB40", "#212121"),
-            new Theme("Deep Orange", "#FF6E40", "#212121"),
-        };
+    // https://www.materialui.co/colors
+    public static IReadOnlyList<Theme> Themes { get; } = new Theme[]
+    {
+        new Theme("White", "#FFFFFF", "#000000"),
+        new Theme("Black", "#000000", "#9E9E9E"),
+        new Theme("Grey", "#9E9E9E", "#000000"),
+        new Theme("Red", "#ff5252", "#212121"),
+        new Theme("Pink", "#FF4081", "#212121"),
+        new Theme("Purple", "#E040FB", "#212121"),
+        new Theme("Deep Purple", "#7C4DFF", "#212121"),
+        new Theme("Indigo", "#536DFE", "#212121"),
+        new Theme("Blue", "#448AFF", "#212121"),
+        new Theme("Light Blue", "#40C4FF", "#212121"),
+        new Theme("Cyan", "#18FFFF", "#212121"),
+        new Theme("Teal", "#64FFDA", "#212121"),
+        new Theme("Green", "#69F0AE", "#212121"),
+        new Theme("Light Green", "#B2FF59", "#212121"),
+        new Theme("Lime", "#EEFF41", "#212121"),
+        new Theme("Yellow", "#FFFF00", "#212121"),
+        new Theme("Amber", "#FFD740", "#212121"),
+        new Theme("Orange", "#FFAB40", "#212121"),
+        new Theme("Deep Orange", "#FF6E40", "#212121"),
+    };
 
-        private void Application_Exit(object sender, ExitEventArgs e)
-        {
-            if (!Settings.Default.CheckIfModifiedExternally())
-                Settings.Default.Save();
-        }
+    private void Application_Exit(object sender, ExitEventArgs e)
+    {
+        if (!Settings.Default.CheckIfModifiedExternally())
+            Settings.Default.Save();
     }
 }

+ 40 - 41
DesktopClock/DateTimeUtil.cs

@@ -2,53 +2,52 @@
 using System.Collections.Generic;
 using System.Linq;
 
-namespace DesktopClock
+namespace DesktopClock;
+
+public static class DateTimeUtil
 {
-    public static class DateTimeUtil
+    /// <summary>
+    /// A collection of DateTime formatting strings.
+    /// </summary>
+    public static IReadOnlyList<string> DateTimeFormats { get; } = new[]
     {
-        /// <summary>
-        /// A collection of DateTime formatting strings.
-        /// </summary>
-        public static IReadOnlyList<string> DateTimeFormats { get; } = new[]
-        {
-            "M",
-            "dddd, MMMM dd",
-            "dddd, MMMM dd, HH:mm",
-            "dddd, MMM dd, HH:mm",
-            "dddd, MMM dd, HH:mm:ss",
-            "ddd, MMMM dd, HH:mm",
-            "ddd, MMMM dd, HH:mm:ss",
-            "ddd, MMM dd, HH:mm",
-            "ddd, MMM dd, HH:mm:ss",
-            "ddd, MMM dd, HH:mm K",
-            "d",
-            "g",
-            "G",
-            "t",
-            "T",
-            "O",
-        };
+        "M",
+        "dddd, MMMM dd",
+        "dddd, MMMM dd, HH:mm",
+        "dddd, MMM dd, HH:mm",
+        "dddd, MMM dd, HH:mm:ss",
+        "ddd, MMMM dd, HH:mm",
+        "ddd, MMMM dd, HH:mm:ss",
+        "ddd, MMM dd, HH:mm",
+        "ddd, MMM dd, HH:mm:ss",
+        "ddd, MMM dd, HH:mm K",
+        "d",
+        "g",
+        "G",
+        "t",
+        "T",
+        "O",
+    };
 
-        /// <summary>
-        /// Common date time formatting strings and an example string for each.
-        /// </summary>
-        public static IReadOnlyDictionary<string, string> DateTimeFormatsAndExamples { get; } =
-            DateTimeFormats.ToDictionary(f => f, f => DateTimeOffset.Now.ToString(f));
+    /// <summary>
+    /// Common date time formatting strings and an example string for each.
+    /// </summary>
+    public static IReadOnlyDictionary<string, string> DateTimeFormatsAndExamples { get; } =
+        DateTimeFormats.ToDictionary(f => f, f => DateTimeOffset.Now.ToString(f));
 
-        public static IReadOnlyCollection<TimeZoneInfo> TimeZones { get; } = TimeZoneInfo.GetSystemTimeZones();
+    public static IReadOnlyCollection<TimeZoneInfo> TimeZones { get; } = TimeZoneInfo.GetSystemTimeZones();
 
-        public static bool TryGetTimeZoneById(string timeZoneId, out TimeZoneInfo timeZoneInfo)
+    public static bool TryGetTimeZoneById(string timeZoneId, out TimeZoneInfo timeZoneInfo)
+    {
+        try
+        {
+            timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
+            return true;
+        }
+        catch (TimeZoneNotFoundException)
         {
-            try
-            {
-                timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
-                return true;
-            }
-            catch (TimeZoneNotFoundException)
-            {
-                timeZoneInfo = null;
-                return false;
-            }
+            timeZoneInfo = null;
+            return false;
         }
     }
 }

+ 61 - 62
DesktopClock/MainViewModel.cs

@@ -5,78 +5,77 @@ using DesktopClock.Properties;
 using GalaSoft.MvvmLight;
 using GalaSoft.MvvmLight.CommandWpf;
 
-namespace DesktopClock
+namespace DesktopClock;
+
+public class MainViewModel : ViewModelBase
 {
-    public class MainViewModel : ViewModelBase
+    private readonly SystemClockTimer _systemClockTimer;
+    private TimeZoneInfo _timeZone;
+
+    public MainViewModel()
     {
-        private readonly SystemClockTimer _systemClockTimer;
-        private TimeZoneInfo _timeZone;
+        _systemClockTimer = new SystemClockTimer();
+        _systemClockTimer.SecondChanged += SystemClockTimer_SecondChanged;
 
-        public MainViewModel()
-        {
-            _systemClockTimer = new SystemClockTimer();
-            _systemClockTimer.SecondChanged += SystemClockTimer_SecondChanged;
+        _timeZone = SettingsHelper.GetTimeZone();
+
+        Settings.Default.PropertyChanged += Settings_PropertyChanged;
 
-            _timeZone = SettingsHelper.GetTimeZone();
+        _systemClockTimer.Start();
+    }
 
-            Settings.Default.PropertyChanged += Settings_PropertyChanged;
+    /// <summary>
+    /// The current date and time in the selected time zone or countdown as a formatted string.
+    /// </summary>
+    public string CurrentTimeOrCountdownString =>
+        IsCountdown ?
+        (Settings.Default.CountdownTo - DateTimeOffset.Now).ToString(Settings.Default.Format) :
+        CurrentTimeInSelectedTimeZone.ToString(Settings.Default.Format);
 
-            _systemClockTimer.Start();
-        }
+    /// <summary>
+    /// Sets app theme to parameter's value.
+    /// </summary>
+    public ICommand SetThemeCommand { get; } = new RelayCommand<Theme>((t) => Settings.Default.Theme = t);
 
-        /// <summary>
-        /// The current date and time in the selected time zone or countdown as a formatted string.
-        /// </summary>
-        public string CurrentTimeOrCountdownString =>
-            IsCountdown ?
-            (Settings.Default.CountdownTo - DateTimeOffset.Now).ToString(Settings.Default.Format) :
-            CurrentTimeInSelectedTimeZone.ToString(Settings.Default.Format);
-
-        /// <summary>
-        /// Sets app theme to parameter's value.
-        /// </summary>
-        public ICommand SetThemeCommand { get; } = new RelayCommand<Theme>((t) => Settings.Default.Theme = t);
-
-        /// <summary>
-        /// Sets format string in settings to parameter's string.
-        /// </summary>
-        public ICommand SetFormatCommand { get; } = new RelayCommand<string>((f) => Settings.Default.Format = f);
-
-        /// <summary>
-        /// Sets time zone ID in settings to parameter's time zone ID.
-        /// </summary>
-        public ICommand SetTimeZoneCommand { get; } = new RelayCommand<TimeZoneInfo>(SettingsHelper.SetTimeZone);
-
-        /// <summary>
-        /// The current date and time in the selected time zone.
-        /// </summary>
-        private DateTimeOffset CurrentTimeInSelectedTimeZone => TimeZoneInfo.ConvertTime(DateTimeOffset.Now, _timeZone);
-
-        /// <summary>
-        /// Should the clock be a countdown?
-        /// </summary>
-        private bool IsCountdown => Settings.Default.CountdownTo > DateTimeOffset.MinValue;
-
-        private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e)
-        {
-            switch (e.PropertyName)
-            {
-                case nameof(Settings.Default.TimeZone):
-                    _timeZone = SettingsHelper.GetTimeZone();
-                    UpdateTimeString();
-                    break;
-
-                case nameof(Settings.Default.Format):
-                    UpdateTimeString();
-                    break;
-            }
-        }
+    /// <summary>
+    /// Sets format string in settings to parameter's string.
+    /// </summary>
+    public ICommand SetFormatCommand { get; } = new RelayCommand<string>((f) => Settings.Default.Format = f);
 
-        private void SystemClockTimer_SecondChanged(object sender, EventArgs e)
+    /// <summary>
+    /// Sets time zone ID in settings to parameter's time zone ID.
+    /// </summary>
+    public ICommand SetTimeZoneCommand { get; } = new RelayCommand<TimeZoneInfo>(SettingsHelper.SetTimeZone);
+
+    /// <summary>
+    /// The current date and time in the selected time zone.
+    /// </summary>
+    private DateTimeOffset CurrentTimeInSelectedTimeZone => TimeZoneInfo.ConvertTime(DateTimeOffset.Now, _timeZone);
+
+    /// <summary>
+    /// Should the clock be a countdown?
+    /// </summary>
+    private bool IsCountdown => Settings.Default.CountdownTo > DateTimeOffset.MinValue;
+
+    private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e)
+    {
+        switch (e.PropertyName)
         {
-            UpdateTimeString();
+            case nameof(Settings.Default.TimeZone):
+                _timeZone = SettingsHelper.GetTimeZone();
+                UpdateTimeString();
+                break;
+
+            case nameof(Settings.Default.Format):
+                UpdateTimeString();
+                break;
         }
+    }
 
-        private void UpdateTimeString() => RaisePropertyChanged(nameof(CurrentTimeOrCountdownString));
+    private void SystemClockTimer_SecondChanged(object sender, EventArgs e)
+    {
+        UpdateTimeString();
     }
+
+    private void UpdateTimeString() => RaisePropertyChanged(nameof(CurrentTimeOrCountdownString));
 }

+ 37 - 38
DesktopClock/MainWindow.xaml.cs

@@ -3,55 +3,54 @@ using System.Windows;
 using System.Windows.Input;
 using DesktopClock.Properties;
 
-namespace DesktopClock
+namespace DesktopClock;
+
+/// <summary>
+/// Interaction logic for MainWindow.xaml
+/// </summary>
+public partial class MainWindow : Window
 {
-    /// <summary>
-    /// Interaction logic for MainWindow.xaml
-    /// </summary>
-    public partial class MainWindow : Window
+    public MainWindow()
     {
-        public MainWindow()
-        {
-            InitializeComponent();
-        }
+        InitializeComponent();
+    }
 
-        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
+    private void Window_MouseDown(object sender, MouseButtonEventArgs e)
+    {
+        if (e.ChangedButton == MouseButton.Left)
         {
-            if (e.ChangedButton == MouseButton.Left)
-            {
-                DragMove();
-            }
+            DragMove();
         }
+    }
 
-        private void CopyToClipboard() =>
-            Clipboard.SetText(TimeTextBlock.Text);
+    private void CopyToClipboard() =>
+        Clipboard.SetText(TimeTextBlock.Text);
 
-        private void Window_MouseDoubleClick(object sender, MouseButtonEventArgs e)
-        {
-            CopyToClipboard();
-        }
+    private void Window_MouseDoubleClick(object sender, MouseButtonEventArgs e)
+    {
+        CopyToClipboard();
+    }
 
-        private void MenuItemCopy_OnClick(object sender, RoutedEventArgs e)
-        {
-            CopyToClipboard();
-        }
+    private void MenuItemCopy_OnClick(object sender, RoutedEventArgs e)
+    {
+        CopyToClipboard();
+    }
 
-        private void MenuItemSettings_OnClick(object sender, RoutedEventArgs e)
-        {
-            Settings.Default.Save();
+    private void MenuItemSettings_OnClick(object sender, RoutedEventArgs e)
+    {
+        Settings.Default.Save();
 
-            // Open settings file in notepad.
-            Process.Start("notepad", Settings.Path);
-        }
+        // Open settings file in notepad.
+        Process.Start("notepad", Settings.Path);
+    }
 
-        private void MenuItemCheckForUpdates_OnClick(object sender, RoutedEventArgs e)
-        {
-            Process.Start("https://github.com/danielchalmers/DesktopClock/releases");
-        }
+    private void MenuItemCheckForUpdates_OnClick(object sender, RoutedEventArgs e)
+    {
+        Process.Start("https://github.com/danielchalmers/DesktopClock/releases");
+    }
 
-        private void MenuItemExit_OnClick(object sender, RoutedEventArgs e)
-        {
-            Close();
-        }
+    private void MenuItemExit_OnClick(object sender, RoutedEventArgs e)
+    {
+        Close();
     }
 }

+ 243 - 244
DesktopClock/OutlinedTextBlock.cs

@@ -6,296 +6,295 @@ using System.Windows.Documents;
 using System.Windows.Markup;
 using System.Windows.Media;
 
-namespace DesktopClock
+namespace DesktopClock;
+
+// https://stackoverflow.com/a/35262509
+[ContentProperty("Text")]
+public class OutlinedTextBlock : FrameworkElement
 {
-    // https://stackoverflow.com/a/35262509
-    [ContentProperty("Text")]
-    public class OutlinedTextBlock : FrameworkElement
+    private void UpdatePen()
     {
-        private void UpdatePen()
+        _Pen = new Pen(Stroke, StrokeThickness)
         {
-            _Pen = new Pen(Stroke, StrokeThickness)
-            {
-                DashCap = PenLineCap.Round,
-                EndLineCap = PenLineCap.Round,
-                LineJoin = PenLineJoin.Round,
-                StartLineCap = PenLineCap.Round
-            };
-
-            InvalidateVisual();
-        }
+            DashCap = PenLineCap.Round,
+            EndLineCap = PenLineCap.Round,
+            LineJoin = PenLineJoin.Round,
+            StartLineCap = PenLineCap.Round
+        };
 
-        public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
-          "Fill",
-          typeof(Brush),
-          typeof(OutlinedTextBlock),
-          new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender));
+        InvalidateVisual();
+    }
 
-        public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
-          "Stroke",
-          typeof(Brush),
-          typeof(OutlinedTextBlock),
-          new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender, StrokePropertyChangedCallback));
+    public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
+      "Fill",
+      typeof(Brush),
+      typeof(OutlinedTextBlock),
+      new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender));
 
-        private static void StrokePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
-        {
-            (dependencyObject as OutlinedTextBlock)?.UpdatePen();
-        }
+    public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
+      "Stroke",
+      typeof(Brush),
+      typeof(OutlinedTextBlock),
+      new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender, StrokePropertyChangedCallback));
 
-        public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
-          "StrokeThickness",
-          typeof(double),
-          typeof(OutlinedTextBlock),
-          new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsRender, StrokePropertyChangedCallback));
-
-        public static readonly DependencyProperty FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(
-          typeof(OutlinedTextBlock),
-          new FrameworkPropertyMetadata(OnFormattedTextUpdated));
-
-        public static readonly DependencyProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(
-          typeof(OutlinedTextBlock),
-          new FrameworkPropertyMetadata(OnFormattedTextUpdated));
-
-        public static readonly DependencyProperty FontStretchProperty = TextElement.FontStretchProperty.AddOwner(
-          typeof(OutlinedTextBlock),
-          new FrameworkPropertyMetadata(OnFormattedTextUpdated));
-
-        public static readonly DependencyProperty FontStyleProperty = TextElement.FontStyleProperty.AddOwner(
-          typeof(OutlinedTextBlock),
-          new FrameworkPropertyMetadata(OnFormattedTextUpdated));
-
-        public static readonly DependencyProperty FontWeightProperty = TextElement.FontWeightProperty.AddOwner(
-          typeof(OutlinedTextBlock),
-          new FrameworkPropertyMetadata(OnFormattedTextUpdated));
-
-        public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
-          "Text",
-          typeof(string),
-          typeof(OutlinedTextBlock),
-          new FrameworkPropertyMetadata(OnFormattedTextInvalidated));
-
-        public static readonly DependencyProperty TextAlignmentProperty = DependencyProperty.Register(
-          "TextAlignment",
-          typeof(TextAlignment),
-          typeof(OutlinedTextBlock),
-          new FrameworkPropertyMetadata(OnFormattedTextUpdated));
-
-        public static readonly DependencyProperty TextDecorationsProperty = DependencyProperty.Register(
-          "TextDecorations",
-          typeof(TextDecorationCollection),
-          typeof(OutlinedTextBlock),
-          new FrameworkPropertyMetadata(OnFormattedTextUpdated));
-
-        public static readonly DependencyProperty TextTrimmingProperty = DependencyProperty.Register(
-          "TextTrimming",
-          typeof(TextTrimming),
-          typeof(OutlinedTextBlock),
-          new FrameworkPropertyMetadata(OnFormattedTextUpdated));
-
-        public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register(
-          "TextWrapping",
-          typeof(TextWrapping),
-          typeof(OutlinedTextBlock),
-          new FrameworkPropertyMetadata(TextWrapping.NoWrap, OnFormattedTextUpdated));
-
-        private FormattedText _FormattedText;
-        private Geometry _TextGeometry;
-        private Pen _Pen;
-
-        public Brush Fill
-        {
-            get { return (Brush)GetValue(FillProperty); }
-            set { SetValue(FillProperty, value); }
-        }
+    private static void StrokePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
+    {
+        (dependencyObject as OutlinedTextBlock)?.UpdatePen();
+    }
 
-        public FontFamily FontFamily
-        {
-            get { return (FontFamily)GetValue(FontFamilyProperty); }
-            set { SetValue(FontFamilyProperty, value); }
-        }
+    public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
+      "StrokeThickness",
+      typeof(double),
+      typeof(OutlinedTextBlock),
+      new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsRender, StrokePropertyChangedCallback));
+
+    public static readonly DependencyProperty FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(
+      typeof(OutlinedTextBlock),
+      new FrameworkPropertyMetadata(OnFormattedTextUpdated));
+
+    public static readonly DependencyProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(
+      typeof(OutlinedTextBlock),
+      new FrameworkPropertyMetadata(OnFormattedTextUpdated));
+
+    public static readonly DependencyProperty FontStretchProperty = TextElement.FontStretchProperty.AddOwner(
+      typeof(OutlinedTextBlock),
+      new FrameworkPropertyMetadata(OnFormattedTextUpdated));
+
+    public static readonly DependencyProperty FontStyleProperty = TextElement.FontStyleProperty.AddOwner(
+      typeof(OutlinedTextBlock),
+      new FrameworkPropertyMetadata(OnFormattedTextUpdated));
+
+    public static readonly DependencyProperty FontWeightProperty = TextElement.FontWeightProperty.AddOwner(
+      typeof(OutlinedTextBlock),
+      new FrameworkPropertyMetadata(OnFormattedTextUpdated));
+
+    public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
+      "Text",
+      typeof(string),
+      typeof(OutlinedTextBlock),
+      new FrameworkPropertyMetadata(OnFormattedTextInvalidated));
+
+    public static readonly DependencyProperty TextAlignmentProperty = DependencyProperty.Register(
+      "TextAlignment",
+      typeof(TextAlignment),
+      typeof(OutlinedTextBlock),
+      new FrameworkPropertyMetadata(OnFormattedTextUpdated));
+
+    public static readonly DependencyProperty TextDecorationsProperty = DependencyProperty.Register(
+      "TextDecorations",
+      typeof(TextDecorationCollection),
+      typeof(OutlinedTextBlock),
+      new FrameworkPropertyMetadata(OnFormattedTextUpdated));
+
+    public static readonly DependencyProperty TextTrimmingProperty = DependencyProperty.Register(
+      "TextTrimming",
+      typeof(TextTrimming),
+      typeof(OutlinedTextBlock),
+      new FrameworkPropertyMetadata(OnFormattedTextUpdated));
+
+    public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register(
+      "TextWrapping",
+      typeof(TextWrapping),
+      typeof(OutlinedTextBlock),
+      new FrameworkPropertyMetadata(TextWrapping.NoWrap, OnFormattedTextUpdated));
+
+    private FormattedText _FormattedText;
+    private Geometry _TextGeometry;
+    private Pen _Pen;
+
+    public Brush Fill
+    {
+        get { return (Brush)GetValue(FillProperty); }
+        set { SetValue(FillProperty, value); }
+    }
 
-        [TypeConverter(typeof(FontSizeConverter))]
-        public double FontSize
-        {
-            get { return (double)GetValue(FontSizeProperty); }
-            set { SetValue(FontSizeProperty, value); }
-        }
+    public FontFamily FontFamily
+    {
+        get { return (FontFamily)GetValue(FontFamilyProperty); }
+        set { SetValue(FontFamilyProperty, value); }
+    }
 
-        public FontStretch FontStretch
-        {
-            get { return (FontStretch)GetValue(FontStretchProperty); }
-            set { SetValue(FontStretchProperty, value); }
-        }
+    [TypeConverter(typeof(FontSizeConverter))]
+    public double FontSize
+    {
+        get { return (double)GetValue(FontSizeProperty); }
+        set { SetValue(FontSizeProperty, value); }
+    }
 
-        public FontStyle FontStyle
-        {
-            get { return (FontStyle)GetValue(FontStyleProperty); }
-            set { SetValue(FontStyleProperty, value); }
-        }
+    public FontStretch FontStretch
+    {
+        get { return (FontStretch)GetValue(FontStretchProperty); }
+        set { SetValue(FontStretchProperty, value); }
+    }
 
-        public FontWeight FontWeight
-        {
-            get { return (FontWeight)GetValue(FontWeightProperty); }
-            set { SetValue(FontWeightProperty, value); }
-        }
+    public FontStyle FontStyle
+    {
+        get { return (FontStyle)GetValue(FontStyleProperty); }
+        set { SetValue(FontStyleProperty, value); }
+    }
 
-        public Brush Stroke
-        {
-            get { return (Brush)GetValue(StrokeProperty); }
-            set { SetValue(StrokeProperty, value); }
-        }
+    public FontWeight FontWeight
+    {
+        get { return (FontWeight)GetValue(FontWeightProperty); }
+        set { SetValue(FontWeightProperty, value); }
+    }
 
-        public double StrokeThickness
-        {
-            get { return (double)GetValue(StrokeThicknessProperty); }
-            set { SetValue(StrokeThicknessProperty, value); }
-        }
+    public Brush Stroke
+    {
+        get { return (Brush)GetValue(StrokeProperty); }
+        set { SetValue(StrokeProperty, value); }
+    }
 
-        public string Text
-        {
-            get { return (string)GetValue(TextProperty); }
-            set { SetValue(TextProperty, value); }
-        }
+    public double StrokeThickness
+    {
+        get { return (double)GetValue(StrokeThicknessProperty); }
+        set { SetValue(StrokeThicknessProperty, value); }
+    }
 
-        public TextAlignment TextAlignment
-        {
-            get { return (TextAlignment)GetValue(TextAlignmentProperty); }
-            set { SetValue(TextAlignmentProperty, value); }
-        }
+    public string Text
+    {
+        get { return (string)GetValue(TextProperty); }
+        set { SetValue(TextProperty, value); }
+    }
 
-        public TextDecorationCollection TextDecorations
-        {
-            get { return (TextDecorationCollection)GetValue(TextDecorationsProperty); }
-            set { SetValue(TextDecorationsProperty, value); }
-        }
+    public TextAlignment TextAlignment
+    {
+        get { return (TextAlignment)GetValue(TextAlignmentProperty); }
+        set { SetValue(TextAlignmentProperty, value); }
+    }
 
-        public TextTrimming TextTrimming
-        {
-            get { return (TextTrimming)GetValue(TextTrimmingProperty); }
-            set { SetValue(TextTrimmingProperty, value); }
-        }
+    public TextDecorationCollection TextDecorations
+    {
+        get { return (TextDecorationCollection)GetValue(TextDecorationsProperty); }
+        set { SetValue(TextDecorationsProperty, value); }
+    }
 
-        public TextWrapping TextWrapping
-        {
-            get { return (TextWrapping)GetValue(TextWrappingProperty); }
-            set { SetValue(TextWrappingProperty, value); }
-        }
+    public TextTrimming TextTrimming
+    {
+        get { return (TextTrimming)GetValue(TextTrimmingProperty); }
+        set { SetValue(TextTrimmingProperty, value); }
+    }
 
-        public OutlinedTextBlock()
-        {
-            UpdatePen();
-            TextDecorations = new TextDecorationCollection();
-        }
+    public TextWrapping TextWrapping
+    {
+        get { return (TextWrapping)GetValue(TextWrappingProperty); }
+        set { SetValue(TextWrappingProperty, value); }
+    }
 
-        protected override void OnRender(DrawingContext drawingContext)
-        {
-            EnsureGeometry();
+    public OutlinedTextBlock()
+    {
+        UpdatePen();
+        TextDecorations = new TextDecorationCollection();
+    }
 
-            drawingContext.DrawGeometry(null, _Pen, _TextGeometry);
-            drawingContext.DrawGeometry(Fill, null, _TextGeometry);
-        }
+    protected override void OnRender(DrawingContext drawingContext)
+    {
+        EnsureGeometry();
 
-        protected override Size MeasureOverride(Size availableSize)
-        {
-            EnsureFormattedText();
+        drawingContext.DrawGeometry(null, _Pen, _TextGeometry);
+        drawingContext.DrawGeometry(Fill, null, _TextGeometry);
+    }
 
-            // constrain the formatted text according to the available size
+    protected override Size MeasureOverride(Size availableSize)
+    {
+        EnsureFormattedText();
 
-            var w = availableSize.Width;
-            var h = availableSize.Height;
+        // constrain the formatted text according to the available size
 
-            // the Math.Min call is important - without this constraint (which seems arbitrary, but is the maximum allowable text width), things blow up when availableSize is infinite in both directions
-            // the Math.Max call is to ensure we don't hit zero, which will cause MaxTextHeight to throw
-            _FormattedText.MaxTextWidth = Math.Min(3579139, w);
-            _FormattedText.MaxTextHeight = Math.Max(0.0001d, h);
+        var w = availableSize.Width;
+        var h = availableSize.Height;
 
-            // return the desired size
-            return new Size(Math.Ceiling(_FormattedText.Width), Math.Ceiling(_FormattedText.Height));
-        }
+        // the Math.Min call is important - without this constraint (which seems arbitrary, but is the maximum allowable text width), things blow up when availableSize is infinite in both directions
+        // the Math.Max call is to ensure we don't hit zero, which will cause MaxTextHeight to throw
+        _FormattedText.MaxTextWidth = Math.Min(3579139, w);
+        _FormattedText.MaxTextHeight = Math.Max(0.0001d, h);
 
-        protected override Size ArrangeOverride(Size finalSize)
-        {
-            EnsureFormattedText();
+        // return the desired size
+        return new Size(Math.Ceiling(_FormattedText.Width), Math.Ceiling(_FormattedText.Height));
+    }
 
-            // update the formatted text with the final size
-            _FormattedText.MaxTextWidth = finalSize.Width;
-            _FormattedText.MaxTextHeight = Math.Max(0.0001d, finalSize.Height);
+    protected override Size ArrangeOverride(Size finalSize)
+    {
+        EnsureFormattedText();
 
-            // need to re-generate the geometry now that the dimensions have changed
-            _TextGeometry = null;
+        // update the formatted text with the final size
+        _FormattedText.MaxTextWidth = finalSize.Width;
+        _FormattedText.MaxTextHeight = Math.Max(0.0001d, finalSize.Height);
 
-            return finalSize;
-        }
+        // need to re-generate the geometry now that the dimensions have changed
+        _TextGeometry = null;
 
-        private static void OnFormattedTextInvalidated(DependencyObject dependencyObject,
-          DependencyPropertyChangedEventArgs e)
-        {
-            var outlinedTextBlock = (OutlinedTextBlock)dependencyObject;
-            outlinedTextBlock._FormattedText = null;
-            outlinedTextBlock._TextGeometry = null;
+        return finalSize;
+    }
 
-            outlinedTextBlock.InvalidateMeasure();
-            outlinedTextBlock.InvalidateVisual();
-        }
+    private static void OnFormattedTextInvalidated(DependencyObject dependencyObject,
+      DependencyPropertyChangedEventArgs e)
+    {
+        var outlinedTextBlock = (OutlinedTextBlock)dependencyObject;
+        outlinedTextBlock._FormattedText = null;
+        outlinedTextBlock._TextGeometry = null;
 
-        private static void OnFormattedTextUpdated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
-        {
-            var outlinedTextBlock = (OutlinedTextBlock)dependencyObject;
-            outlinedTextBlock.UpdateFormattedText();
-            outlinedTextBlock._TextGeometry = null;
+        outlinedTextBlock.InvalidateMeasure();
+        outlinedTextBlock.InvalidateVisual();
+    }
 
-            outlinedTextBlock.InvalidateMeasure();
-            outlinedTextBlock.InvalidateVisual();
-        }
+    private static void OnFormattedTextUpdated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
+    {
+        var outlinedTextBlock = (OutlinedTextBlock)dependencyObject;
+        outlinedTextBlock.UpdateFormattedText();
+        outlinedTextBlock._TextGeometry = null;
+
+        outlinedTextBlock.InvalidateMeasure();
+        outlinedTextBlock.InvalidateVisual();
+    }
 
-        private void EnsureFormattedText()
+    private void EnsureFormattedText()
+    {
+        if (_FormattedText != null)
         {
-            if (_FormattedText != null)
-            {
-                return;
-            }
+            return;
+        }
 
 #pragma warning disable CS0618 // Type or member is obsolete
-            _FormattedText = new FormattedText(
-                Text ?? "",
-                CultureInfo.CurrentUICulture,
-                FlowDirection,
-                new Typeface(FontFamily, FontStyle, FontWeight, FontStretch),
-                FontSize,
-                Brushes.Black);
+        _FormattedText = new FormattedText(
+            Text ?? "",
+            CultureInfo.CurrentUICulture,
+            FlowDirection,
+            new Typeface(FontFamily, FontStyle, FontWeight, FontStretch),
+            FontSize,
+            Brushes.Black);
 #pragma warning restore CS0618 // Type or member is obsolete
 
-            UpdateFormattedText();
-        }
+        UpdateFormattedText();
+    }
 
-        private void UpdateFormattedText()
+    private void UpdateFormattedText()
+    {
+        if (_FormattedText == null)
         {
-            if (_FormattedText == null)
-            {
-                return;
-            }
-
-            _FormattedText.MaxLineCount = TextWrapping == TextWrapping.NoWrap ? 1 : int.MaxValue;
-            _FormattedText.TextAlignment = TextAlignment;
-            _FormattedText.Trimming = TextTrimming;
-
-            _FormattedText.SetFontSize(FontSize);
-            _FormattedText.SetFontStyle(FontStyle);
-            _FormattedText.SetFontWeight(FontWeight);
-            _FormattedText.SetFontFamily(FontFamily);
-            _FormattedText.SetFontStretch(FontStretch);
-            _FormattedText.SetTextDecorations(TextDecorations);
+            return;
         }
 
-        private void EnsureGeometry()
-        {
-            if (_TextGeometry != null)
-            {
-                return;
-            }
+        _FormattedText.MaxLineCount = TextWrapping == TextWrapping.NoWrap ? 1 : int.MaxValue;
+        _FormattedText.TextAlignment = TextAlignment;
+        _FormattedText.Trimming = TextTrimming;
 
-            EnsureFormattedText();
-            _TextGeometry = _FormattedText.BuildGeometry(new Point(0, 0));
+        _FormattedText.SetFontSize(FontSize);
+        _FormattedText.SetFontStyle(FontStyle);
+        _FormattedText.SetFontWeight(FontWeight);
+        _FormattedText.SetFontFamily(FontFamily);
+        _FormattedText.SetFontStretch(FontStretch);
+        _FormattedText.SetTextDecorations(TextDecorations);
+    }
+
+    private void EnsureGeometry()
+    {
+        if (_TextGeometry != null)
+        {
+            return;
         }
+
+        EnsureFormattedText();
+        _TextGeometry = _FormattedText.BuildGeometry(new Point(0, 0));
     }
 }

+ 79 - 80
DesktopClock/Properties/Settings.cs

@@ -5,105 +5,104 @@ using System.Windows.Media;
 using Newtonsoft.Json;
 using WpfWindowPlacement;
 
-namespace DesktopClock.Properties
+namespace DesktopClock.Properties;
+
+public sealed class Settings : INotifyPropertyChanged
 {
-    public sealed class Settings : INotifyPropertyChanged
-    {
-        private DateTime _fileLastUsed = DateTime.UtcNow;
+    private DateTime _fileLastUsed = DateTime.UtcNow;
 
-        public static readonly string Path = "DesktopClock.settings";
-        private static readonly Lazy<Settings> _default = new(() => TryLoad() ?? new Settings());
+    public static readonly string Path = "DesktopClock.settings";
+    private static readonly Lazy<Settings> _default = new(() => TryLoad() ?? new Settings());
 
-        private static readonly JsonSerializerSettings _jsonSerializerSettings = new()
-        {
-            Formatting = Formatting.Indented
-        };
+    private static readonly JsonSerializerSettings _jsonSerializerSettings = new()
+    {
+        Formatting = Formatting.Indented
+    };
 
-        private Settings()
-        {
-            var random = new Random();
+    private Settings()
+    {
+        var random = new Random();
 
-            // Random default theme.
-            Theme = App.Themes[random.Next(0, App.Themes.Count)];
-        }
+        // Random default theme.
+        Theme = App.Themes[random.Next(0, App.Themes.Count)];
+    }
 
 #pragma warning disable CS0067 // The event 'Settings.PropertyChanged' is never used
-        public event PropertyChangedEventHandler PropertyChanged;
+    public event PropertyChangedEventHandler PropertyChanged;
 #pragma warning restore CS0067 // The event 'Settings.PropertyChanged' is never used
 
-        public static Settings Default => _default.Value;
-
-        #region "Properties"
-
-        public WindowPlacement Placement { get; set; }
-        public bool Topmost { get; set; } = true;
-        public bool ShowInTaskbar { get; set; } = true;
-        public int Height { get; set; } = 48;
-        public string TimeZone { get; set; } = string.Empty;
-        public string Format { get; set; } = "dddd, MMM dd, HH:mm:ss";
-        public bool BackgroundEnabled { get; set; } = true;
-        public double BackgroundOpacity { get; set; } = 0.90;
-        public Color OuterColor { get; set; }
-        public Color TextColor { get; set; }
-        public string FontFamily { get; set; } = "Arial";
-        public DateTimeOffset CountdownTo { get; set; } = DateTimeOffset.MinValue;
-
-        [JsonIgnore]
-        public Theme Theme
+    public static Settings Default => _default.Value;
+
+    #region "Properties"
+
+    public WindowPlacement Placement { get; set; }
+    public bool Topmost { get; set; } = true;
+    public bool ShowInTaskbar { get; set; } = true;
+    public int Height { get; set; } = 48;
+    public string TimeZone { get; set; } = string.Empty;
+    public string Format { get; set; } = "dddd, MMM dd, HH:mm:ss";
+    public bool BackgroundEnabled { get; set; } = true;
+    public double BackgroundOpacity { get; set; } = 0.90;
+    public Color OuterColor { get; set; }
+    public Color TextColor { get; set; }
+    public string FontFamily { get; set; } = "Arial";
+    public DateTimeOffset CountdownTo { get; set; } = DateTimeOffset.MinValue;
+
+    [JsonIgnore]
+    public Theme Theme
+    {
+        get => new("Custom", TextColor.ToString(), OuterColor.ToString());
+        set
         {
-            get => new("Custom", TextColor.ToString(), OuterColor.ToString());
-            set
-            {
-                TextColor = (Color)ColorConverter.ConvertFromString(value.PrimaryColor);
-                OuterColor = (Color)ColorConverter.ConvertFromString(value.SecondaryColor);
-            }
+            TextColor = (Color)ColorConverter.ConvertFromString(value.PrimaryColor);
+            OuterColor = (Color)ColorConverter.ConvertFromString(value.SecondaryColor);
         }
+    }
 
-        #endregion "Properties"
+    #endregion "Properties"
 
-        /// <summary>
-        /// Saves to the default path.
-        /// </summary>
-        public void Save()
-        {
-            using (var fileStream = new FileStream(Path, FileMode.Create))
-            using (var streamWriter = new StreamWriter(fileStream))
-            using (var jsonWriter = new JsonTextWriter(streamWriter))
-                JsonSerializer.Create(_jsonSerializerSettings).Serialize(jsonWriter, this);
+    /// <summary>
+    /// Saves to the default path.
+    /// </summary>
+    public void Save()
+    {
+        using (var fileStream = new FileStream(Path, FileMode.Create))
+        using (var streamWriter = new StreamWriter(fileStream))
+        using (var jsonWriter = new JsonTextWriter(streamWriter))
+            JsonSerializer.Create(_jsonSerializerSettings).Serialize(jsonWriter, this);
 
-            _fileLastUsed = DateTime.UtcNow;
-        }
+        _fileLastUsed = DateTime.UtcNow;
+    }
 
-        /// <summary>
-        /// Determines if the settings file has been modified externally since the last time it was used.
-        /// </summary>
-        public bool CheckIfModifiedExternally() =>
-            File.GetLastWriteTimeUtc(Path) > _fileLastUsed;
+    /// <summary>
+    /// Determines if the settings file has been modified externally since the last time it was used.
+    /// </summary>
+    public bool CheckIfModifiedExternally() =>
+        File.GetLastWriteTimeUtc(Path) > _fileLastUsed;
 
-        /// <summary>
-        /// Loads from the default path.
-        /// </summary>
-        private static Settings Load()
+    /// <summary>
+    /// Loads from the default path.
+    /// </summary>
+    private static Settings Load()
+    {
+        using (var fileStream = new FileStream(Path, FileMode.Open))
+        using (var streamReader = new StreamReader(fileStream))
+        using (var jsonReader = new JsonTextReader(streamReader))
+            return JsonSerializer.Create(_jsonSerializerSettings).Deserialize<Settings>(jsonReader);
+    }
+
+    /// <summary>
+    /// Returns loaded settings from the default path or null if it fails.
+    /// </summary>
+    private static Settings TryLoad()
+    {
+        try
         {
-            using (var fileStream = new FileStream(Path, FileMode.Open))
-            using (var streamReader = new StreamReader(fileStream))
-            using (var jsonReader = new JsonTextReader(streamReader))
-                return JsonSerializer.Create(_jsonSerializerSettings).Deserialize<Settings>(jsonReader);
+            return Load();
         }
-
-        /// <summary>
-        /// Returns loaded settings from the default path or null if it fails.
-        /// </summary>
-        private static Settings TryLoad()
+        catch
         {
-            try
-            {
-                return Load();
-            }
-            catch
-            {
-                return null;
-            }
+            return null;
         }
     }
 }

+ 13 - 14
DesktopClock/SettingsHelper.cs

@@ -1,20 +1,19 @@
 using System;
 using DesktopClock.Properties;
 
-namespace DesktopClock
+namespace DesktopClock;
+
+public static class SettingsHelper
 {
-    public static class SettingsHelper
-    {
-        /// <summary>
-        /// Gets the time zone selected in settings, or local by default.
-        /// </summary>
-        public static TimeZoneInfo GetTimeZone() =>
-            DateTimeUtil.TryGetTimeZoneById(Settings.Default.TimeZone, out var timeZoneInfo) ? timeZoneInfo : TimeZoneInfo.Local;
+    /// <summary>
+    /// Gets the time zone selected in settings, or local by default.
+    /// </summary>
+    public static TimeZoneInfo GetTimeZone() =>
+        DateTimeUtil.TryGetTimeZoneById(Settings.Default.TimeZone, out var timeZoneInfo) ? timeZoneInfo : TimeZoneInfo.Local;
 
-        /// <summary>
-        /// Selects a time zone to use.
-        /// </summary>
-        public static void SetTimeZone(TimeZoneInfo timeZone) =>
-            Settings.Default.TimeZone = timeZone.Id;
-    }
+    /// <summary>
+    /// Selects a time zone to use.
+    /// </summary>
+    public static void SetTimeZone(TimeZoneInfo timeZone) =>
+        Settings.Default.TimeZone = timeZone.Id;
 }

+ 43 - 44
DesktopClock/SystemClockTimer.cs

@@ -1,53 +1,52 @@
 using System;
 using System.Threading;
 
-namespace DesktopClock
+namespace DesktopClock;
+
+/// <summary>
+/// A timer, synced with the system clock.
+/// </summary>
+public sealed class SystemClockTimer : IDisposable
 {
+    private readonly Timer _timer;
+
+    public SystemClockTimer()
+    {
+        _timer = new Timer(_ => OnTick());
+    }
+
+    /// <summary>
+    /// Occurs after the second of the system clock changes.
+    /// </summary>
+    public event EventHandler SecondChanged;
+
+    /// <summary>
+    /// Number of milliseconds until the next second on the system clock.
+    /// </summary>
+    private int MillisecondsUntilNextSecond => 1000 - DateTimeOffset.Now.Millisecond;
+
     /// <summary>
-    /// A timer, synced with the system clock.
+    /// Releases all resources used by the current instance of <see cref="SystemClockTimer" />.
     /// </summary>
-    public sealed class SystemClockTimer : IDisposable
+    public void Dispose()
+    {
+        _timer.Dispose();
+    }
+
+    public void Start() => ScheduleTickForNextSecond();
+
+    public void Stop() => _timer.Change(Timeout.Infinite, Timeout.Infinite);
+
+    private void OnTick()
     {
-        private readonly Timer _timer;
-
-        public SystemClockTimer()
-        {
-            _timer = new Timer(_ => OnTick());
-        }
-
-        /// <summary>
-        /// Occurs after the second of the system clock changes.
-        /// </summary>
-        public event EventHandler SecondChanged;
-
-        /// <summary>
-        /// Number of milliseconds until the next second on the system clock.
-        /// </summary>
-        private int MillisecondsUntilNextSecond => 1000 - DateTimeOffset.Now.Millisecond;
-
-        /// <summary>
-        /// Releases all resources used by the current instance of <see cref="SystemClockTimer" />.
-        /// </summary>
-        public void Dispose()
-        {
-            _timer.Dispose();
-        }
-
-        public void Start() => ScheduleTickForNextSecond();
-
-        public void Stop() => _timer.Change(Timeout.Infinite, Timeout.Infinite);
-
-        private void OnTick()
-        {
-            ScheduleTickForNextSecond();
-
-            SecondChanged?.Invoke(this, EventArgs.Empty);
-        }
-
-        /// <summary>
-        /// Starts the timer and schedules the tick for the next second on the system clock.
-        /// </summary>
-        private void ScheduleTickForNextSecond() =>
-            _timer.Change(MillisecondsUntilNextSecond, Timeout.Infinite);
+        ScheduleTickForNextSecond();
+
+        SecondChanged?.Invoke(this, EventArgs.Empty);
     }
+
+    /// <summary>
+    /// Starts the timer and schedules the tick for the next second on the system clock.
+    /// </summary>
+    private void ScheduleTickForNextSecond() =>
+        _timer.Change(MillisecondsUntilNextSecond, Timeout.Infinite);
 }

+ 11 - 12
DesktopClock/Theme.cs

@@ -1,16 +1,15 @@
-namespace DesktopClock
+namespace DesktopClock;
+
+public readonly struct Theme
 {
-    public readonly struct Theme
-    {
-        public string Name { get; }
-        public string PrimaryColor { get; }
-        public string SecondaryColor { get; }
+    public string Name { get; }
+    public string PrimaryColor { get; }
+    public string SecondaryColor { get; }
 
-        public Theme(string name, string primaryColor, string secondaryColor)
-        {
-            Name = name;
-            PrimaryColor = primaryColor;
-            SecondaryColor = secondaryColor;
-        }
+    public Theme(string name, string primaryColor, string secondaryColor)
+    {
+        Name = name;
+        PrimaryColor = primaryColor;
+        SecondaryColor = secondaryColor;
     }
 }