From 00f9518c08fc360471f5b5f664f2790c792dca37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AE=8B=E8=B1=A1=E4=B9=8B=E5=BD=A2?= <976999484@qq.com> Date: Tue, 26 Mar 2024 09:46:28 +0800 Subject: [PATCH 1/4] fix(NumberBox): fix value property --- src/Wpf.Ui.Demo.Simple/MainWindow.xaml | 6 + src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs | 19 +- src/Wpf.Ui/Controls/NumberBox/NumberBox.cs | 216 +++++++++------------ 3 files changed, 114 insertions(+), 127 deletions(-) diff --git a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml index 4d430b511..7c46fb618 100644 --- a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml +++ b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml @@ -33,6 +33,7 @@ + @@ -96,5 +97,10 @@ + + + + + diff --git a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs index 6692de3f9..aed58e08d 100644 --- a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs +++ b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs @@ -3,6 +3,8 @@ // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. +using System.ComponentModel; + using Wpf.Ui.Demo.Simple.Views.Pages; namespace Wpf.Ui.Demo.Simple; @@ -10,8 +12,21 @@ namespace Wpf.Ui.Demo.Simple; /// /// Interaction logic for MainWindow.xaml /// -public partial class MainWindow +public partial class MainWindow : INotifyPropertyChanged { + private double _value; + + public double Value + { + get => _value; + + set + { + _value = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value))); + } + } + public MainWindow() { DataContext = this; @@ -22,4 +37,6 @@ public MainWindow() Loaded += (_, _) => RootNavigation.Navigate(typeof(DashboardPage)); } + + public event PropertyChangedEventHandler? PropertyChanged; } diff --git a/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs b/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs index 208954d9c..431583438 100644 --- a/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs +++ b/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs @@ -5,6 +5,7 @@ // This Source Code is partially based on the source code provided by the .NET Foundation. +using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; @@ -21,12 +22,10 @@ namespace Wpf.Ui.Controls; /// //[ToolboxItem(true)] //[ToolboxBitmap(typeof(NumberBox), "NumberBox.bmp")] -public class NumberBox : Wpf.Ui.Controls.TextBox +public class NumberBox : TextBox { private bool _valueUpdating; - private bool _textUpdating; - /// /// Property for . /// @@ -37,7 +36,7 @@ public class NumberBox : Wpf.Ui.Controls.TextBox new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, - OnValuePropertyChanged, + new(OnValueChanged), null, false, UpdateSourceTrigger.LostFocus @@ -131,7 +130,7 @@ public class NumberBox : Wpf.Ui.Controls.TextBox nameof(NumberFormatter), typeof(INumberFormatter), typeof(NumberBox), - new PropertyMetadata(null, OnNumberFormatterPropertyChanged) + new PropertyMetadata(DefaultNumberFormatter, OnNumberFormatterChanged) ); /// @@ -144,6 +143,39 @@ public class NumberBox : Wpf.Ui.Controls.TextBox typeof(NumberBox) ); + private static readonly ValidateNumberFormatter DefaultNumberFormatter = GetRegionalSettingsAwareDecimalFormatter(); + + private static ValidateNumberFormatter GetRegionalSettingsAwareDecimalFormatter() + { + return new ValidateNumberFormatter(); + } + + private static void OnNumberFormatterChanged( + DependencyObject d, + DependencyPropertyChangedEventArgs e + ) + { + if (e.NewValue is INumberParser) + { + return; + } + + throw new ArgumentException( + $"{nameof(NumberFormatter)} must implement {typeof(INumberParser)}", + nameof(NumberFormatter) + ); + } + + private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not NumberBox numberBox) + { + return; + } + + numberBox.OnValueChanged(d, (double?)e.OldValue); + } + /// /// Gets or sets the numeric value of a . /// @@ -254,8 +286,6 @@ static NumberBox() public NumberBox() : base() { - NumberFormatter ??= GetRegionalSettingsAwareDecimalFormatter(); - DataObject.AddPastingHandler(this, OnClipboardPaste); } @@ -286,8 +316,9 @@ protected override void OnKeyUp(KeyEventArgs e) case Key.Enter: if (TextWrapping != TextWrapping.Wrap) { - ValidateInput(); + UpdateValueFromText(); MoveCaretToTextEnd(); + TriggerValueUpdate(); } break; @@ -325,29 +356,46 @@ protected override void OnTemplateButtonClick(string? parameter) } /// - protected override void OnLostFocus(RoutedEventArgs e) + protected override void OnTextChanged(TextChangedEventArgs e) { - base.OnLostFocus(e); - - ValidateInput(); + UpdateValueFromText(); + base.OnTextChanged(e); } - /// - //protected override void OnTextChanged(System.Windows.Controls.TextChangedEventArgs e) - //{ - // base.OnTextChanged(e); + private void UpdateValueFromText() + { + if (_valueUpdating) + { + return; + } + + var text = Text.Trim(); + + if (String.IsNullOrEmpty(text)) + { + SetCurrentValue(ValueProperty, null); + + return; + } + + SetCurrentValue(ValueProperty, GetParser().ParseDouble(text)); + } - // //if (new string[] { ",", ".", " " }.Any(s => Text.EndsWith(s))) - // // return; + private INumberParser GetParser() + { + return (NumberFormatter as INumberParser) ?? DefaultNumberFormatter; + } - // //if (!_textUpdating) - // // UpdateValueToText(); - //} + private void TriggerValueUpdate() + { + GetBindingExpression(ValueProperty).UpdateSource(); + RaiseEvent(new RoutedEventArgs(ValueChangedEvent)); + } /// protected override void OnTemplateChanged( - System.Windows.Controls.ControlTemplate oldTemplate, - System.Windows.Controls.ControlTemplate newTemplate + ControlTemplate oldTemplate, + ControlTemplate newTemplate ) { base.OnTemplateChanged(oldTemplate, newTemplate); @@ -355,11 +403,12 @@ System.Windows.Controls.ControlTemplate newTemplate // If Text has been set, but Value hasn't, update Value based on Text. if (String.IsNullOrEmpty(Text) && Value != null) { - UpdateValueToText(); + SetCurrentValue(ValueProperty, null); } else { - UpdateTextToValue(); + UpdateValueFromText(); + TriggerValueUpdate(); } } @@ -377,22 +426,29 @@ protected virtual void OnValueChanged(DependencyObject d, double? oldValue) var newValue = Value; - if (newValue > Maximum) + if (Equals(newValue, oldValue)) { - SetCurrentValue(ValueProperty, Maximum); + return; } - if (newValue < Minimum) + if (newValue > Maximum) { - SetCurrentValue(ValueProperty, Minimum); + newValue = Maximum; } - - if (!Equals(newValue, oldValue)) + else if (newValue < Minimum) { - RaiseEvent(new RoutedEventArgs(ValueChangedEvent)); + newValue = Minimum; } - UpdateTextToValue(); + SetCurrentValue(ValueProperty, newValue); + UpdateValueFromText(); + + RaiseEvent(new RoutedEventArgs(ValueChangedEvent)); + + if (GetParser().ParseDouble(Text) != Value) + { + SetCurrentValue(TextProperty, NumberFormatter.FormatDouble(Value)); + } _valueUpdating = false; } @@ -408,7 +464,7 @@ protected virtual void OnClipboardPaste(object sender, DataObjectPastingEventArg return; } - ValidateInput(); + UpdateValueFromText(); } private void StepValue(double? change) @@ -420,9 +476,6 @@ private void StepValue(double? change) ); #endif - // Before adjusting the value, validate the contents of the textbox so we don't override it. - ValidateInput(); - var newValue = Value ?? 0; if (change is not null) @@ -435,97 +488,8 @@ private void StepValue(double? change) MoveCaretToTextEnd(); } - private void UpdateTextToValue() - { - _textUpdating = true; - - // text = value - var newText = String.Empty; - - if (Value is not null) - { - newText = NumberFormatter.FormatDouble(Math.Round((double)Value, MaxDecimalPlaces)); - } - - SetCurrentValue(TextProperty, newText); - - _textUpdating = false; - } - - private void UpdateValueToText() - { - ValidateInput(); - } - - private void ValidateInput() - { - var text = Text.Trim(); - - if (String.IsNullOrEmpty(text)) - { - SetCurrentValue(ValueProperty, null); - - return; - } - - var numberParser = NumberFormatter as INumberParser; - var value = numberParser!.ParseDouble(text); - - if (value is null || Equals(Value, value)) - { - UpdateTextToValue(); - - return; - } - - if (value > Maximum) - { - value = Maximum; - } - - if (value < Minimum) - { - value = Minimum; - } - - SetCurrentValue(ValueProperty, value); - - UpdateTextToValue(); - } - private void MoveCaretToTextEnd() { CaretIndex = Text.Length; } - - private INumberFormatter GetRegionalSettingsAwareDecimalFormatter() - { - return new ValidateNumberFormatter(); - } - - private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is not NumberBox numberBox) - { - return; - } - - numberBox.OnValueChanged(d, (double?)e.OldValue); - } - - private static void OnNumberFormatterPropertyChanged( - DependencyObject d, - DependencyPropertyChangedEventArgs e - ) - { - if (e.NewValue is INumberParser) - { - return; - } - - throw new ArgumentException( - $"{nameof(NumberFormatter)} must implement {typeof(INumberParser)}", - nameof(NumberFormatter) - ); - } } From d4f07434e3a0f90941871c881850b929f8a629a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AE=8B=E8=B1=A1=E4=B9=8B=E5=BD=A2?= <976999484@qq.com> Date: Tue, 26 Mar 2024 09:46:28 +0800 Subject: [PATCH 2/4] fix(NumberBox): fix value property --- src/Wpf.Ui.Demo.Simple/MainWindow.xaml | 9 +- src/Wpf.Ui/Controls/NumberBox/NumberBox.cs | 96 +++++++------------ .../NumberBox/ValidateNumberFormatter.cs | 12 +-- 3 files changed, 38 insertions(+), 79 deletions(-) diff --git a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml index 7c46fb618..0bfe37d75 100644 --- a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml +++ b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml @@ -87,12 +87,9 @@ Grid.Row="0" Icon="pack://application:,,,/Assets/applicationIcon-256.png" /> - + diff --git a/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs b/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs index 431583438..3626a7cdf 100644 --- a/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs +++ b/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs @@ -36,7 +36,7 @@ public class NumberBox : TextBox new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, - new(OnValueChanged), + OnValueChanged, null, false, UpdateSourceTrigger.LostFocus @@ -60,7 +60,7 @@ public class NumberBox : TextBox nameof(SmallChange), typeof(double), typeof(NumberBox), - new PropertyMetadata(1.0d) + new PropertyMetadata(1.0) ); /// @@ -70,7 +70,7 @@ public class NumberBox : TextBox nameof(LargeChange), typeof(double), typeof(NumberBox), - new PropertyMetadata(10.0d) + new PropertyMetadata(10.0) ); /// @@ -123,6 +123,8 @@ public class NumberBox : TextBox new PropertyMetadata(NumberBoxValidationMode.InvalidInputOverwritten) ); + private static readonly ValidateNumberFormatter DefaultNumberFormatter = GetRegionalSettingsAwareDecimalFormatter(); + /// /// Property for . /// @@ -143,8 +145,6 @@ public class NumberBox : TextBox typeof(NumberBox) ); - private static readonly ValidateNumberFormatter DefaultNumberFormatter = GetRegionalSettingsAwareDecimalFormatter(); - private static ValidateNumberFormatter GetRegionalSettingsAwareDecimalFormatter() { return new ValidateNumberFormatter(); @@ -282,13 +282,6 @@ static NumberBox() MinLinesProperty.OverrideMetadata(typeof(NumberBox), new FrameworkPropertyMetadata(1)); } - /// - public NumberBox() - : base() - { - DataObject.AddPastingHandler(this, OnClipboardPaste); - } - /// protected override void OnKeyUp(KeyEventArgs e) { @@ -317,6 +310,7 @@ protected override void OnKeyUp(KeyEventArgs e) if (TextWrapping != TextWrapping.Wrap) { UpdateValueFromText(); + ValidateValue(Value); MoveCaretToTextEnd(); TriggerValueUpdate(); } @@ -355,30 +349,9 @@ protected override void OnTemplateButtonClick(string? parameter) Focus(); } - /// - protected override void OnTextChanged(TextChangedEventArgs e) - { - UpdateValueFromText(); - base.OnTextChanged(e); - } - private void UpdateValueFromText() { - if (_valueUpdating) - { - return; - } - - var text = Text.Trim(); - - if (String.IsNullOrEmpty(text)) - { - SetCurrentValue(ValueProperty, null); - - return; - } - - SetCurrentValue(ValueProperty, GetParser().ParseDouble(text)); + SetCurrentValue(ValueProperty, GetParser().ParseDouble(Text.Trim())); } private INumberParser GetParser() @@ -386,12 +359,6 @@ private INumberParser GetParser() return (NumberFormatter as INumberParser) ?? DefaultNumberFormatter; } - private void TriggerValueUpdate() - { - GetBindingExpression(ValueProperty).UpdateSource(); - RaiseEvent(new RoutedEventArgs(ValueChangedEvent)); - } - /// protected override void OnTemplateChanged( ControlTemplate oldTemplate, @@ -408,29 +375,41 @@ ControlTemplate newTemplate else { UpdateValueFromText(); - TriggerValueUpdate(); + ValidateValue(Value); } } + protected override void OnLostFocus(RoutedEventArgs e) + { + UpdateValueFromText(); + ValidateValue(Value); + base.OnLostFocus(e); + } + /// /// Is called when in this changes. /// protected virtual void OnValueChanged(DependencyObject d, double? oldValue) { - if (_valueUpdating) + var newValue = Value; + + if (Equals(newValue, oldValue)) { return; } - _valueUpdating = true; - - var newValue = Value; + ValidateValue(newValue); + } - if (Equals(newValue, oldValue)) + private void ValidateValue(double? newValue) + { + if (_valueUpdating) { return; } + _valueUpdating = true; + if (newValue > Maximum) { newValue = Maximum; @@ -441,30 +420,19 @@ protected virtual void OnValueChanged(DependencyObject d, double? oldValue) } SetCurrentValue(ValueProperty, newValue); - UpdateValueFromText(); - - RaiseEvent(new RoutedEventArgs(ValueChangedEvent)); + RaiseEvent(new(ValueChangedEvent)); - if (GetParser().ParseDouble(Text) != Value) + // Correct the text from value + if (Value is null && Text.Length > 0) { - SetCurrentValue(TextProperty, NumberFormatter.FormatDouble(Value)); + SetCurrentValue(TextProperty, String.Empty); } - - _valueUpdating = false; - } - - /// - /// Is called when something is pasted in this . - /// - protected virtual void OnClipboardPaste(object sender, DataObjectPastingEventArgs e) - { - // TODO: Fix clipboard - if (sender is not NumberBox) + else if (GetParser().ParseDouble(Text.Trim()) != Value) { - return; + SetCurrentValue(TextProperty, NumberFormatter.FormatDouble(Value)); } - UpdateValueFromText(); + _valueUpdating = false; } private void StepValue(double? change) diff --git a/src/Wpf.Ui/Controls/NumberBox/ValidateNumberFormatter.cs b/src/Wpf.Ui/Controls/NumberBox/ValidateNumberFormatter.cs index 65a302a44..6a7063cf4 100644 --- a/src/Wpf.Ui/Controls/NumberBox/ValidateNumberFormatter.cs +++ b/src/Wpf.Ui/Controls/NumberBox/ValidateNumberFormatter.cs @@ -34,25 +34,19 @@ public string FormatUInt(uint? value) /// public double? ParseDouble(string? value) { - Double.TryParse(value, out double d); - - return d; + return Double.TryParse(value, out var d) ? d : null; } /// public int? ParseInt(string? value) { - Int32.TryParse(value, out int i); - - return i; + return Int32.TryParse(value, out var d) ? d : null; } /// public uint? ParseUInt(string? value) { - UInt32.TryParse(value, out uint ui); - - return ui; + return UInt32.TryParse(value, out var d) ? d : null; } private static string GetFormatSpecifier() From 919c6f81e36bf882c88680c21b96e59c6aee82f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AE=8B=E8=B1=A1=E4=B9=8B=E5=BD=A2?= <976999484@qq.com> Date: Fri, 29 Mar 2024 09:59:27 +0800 Subject: [PATCH 3/4] fix(numberbox): fix compile errors and warnings --- src/Wpf.Ui.Demo.Simple/MainWindow.xaml | 6 ------ src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs | 13 ------------- src/Wpf.Ui/Controls/NumberBox/NumberBox.cs | 20 +++++++++----------- 3 files changed, 9 insertions(+), 30 deletions(-) diff --git a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml index 0bfe37d75..2cfcb6c63 100644 --- a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml +++ b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml @@ -33,7 +33,6 @@ - @@ -94,10 +93,5 @@ - - - - - diff --git a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs index aed58e08d..acb77b768 100644 --- a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs +++ b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs @@ -14,19 +14,6 @@ namespace Wpf.Ui.Demo.Simple; /// public partial class MainWindow : INotifyPropertyChanged { - private double _value; - - public double Value - { - get => _value; - - set - { - _value = value; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value))); - } - } - public MainWindow() { DataContext = this; diff --git a/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs b/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs index 3626a7cdf..43b2d34b4 100644 --- a/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs +++ b/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs @@ -2,7 +2,6 @@ // If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. - // This Source Code is partially based on the source code provided by the .NET Foundation. using System.Windows.Controls; @@ -20,8 +19,8 @@ namespace Wpf.Ui.Controls; /// /// Represents a control that can be used to display and edit numbers. /// -//[ToolboxItem(true)] -//[ToolboxBitmap(typeof(NumberBox), "NumberBox.bmp")] +// [ToolboxItem(true)] +// [ToolboxBitmap(typeof(NumberBox), "NumberBox.bmp")] public class NumberBox : TextBox { private bool _valueUpdating; @@ -80,7 +79,7 @@ public class NumberBox : TextBox nameof(Maximum), typeof(double), typeof(NumberBox), - new PropertyMetadata(Double.MaxValue) + new PropertyMetadata(double.MaxValue) ); /// @@ -90,7 +89,7 @@ public class NumberBox : TextBox nameof(Minimum), typeof(double), typeof(NumberBox), - new PropertyMetadata(Double.MinValue) + new PropertyMetadata(double.MinValue) ); /// @@ -231,7 +230,7 @@ public double Minimum } /// - /// Gets or sets whether the control will accept and evaluate a basic formulaic expression entered as input. + /// Gets or sets a value indicating whether the control will accept and evaluate a basic formulaic expression entered as input. /// public bool AcceptsExpression { @@ -312,7 +311,6 @@ protected override void OnKeyUp(KeyEventArgs e) UpdateValueFromText(); ValidateValue(Value); MoveCaretToTextEnd(); - TriggerValueUpdate(); } break; @@ -368,7 +366,7 @@ ControlTemplate newTemplate base.OnTemplateChanged(oldTemplate, newTemplate); // If Text has been set, but Value hasn't, update Value based on Text. - if (String.IsNullOrEmpty(Text) && Value != null) + if (string.IsNullOrEmpty(Text) && Value != null) { SetCurrentValue(ValueProperty, null); } @@ -391,7 +389,7 @@ protected override void OnLostFocus(RoutedEventArgs e) /// protected virtual void OnValueChanged(DependencyObject d, double? oldValue) { - var newValue = Value; + double? newValue = Value; if (Equals(newValue, oldValue)) { @@ -425,7 +423,7 @@ private void ValidateValue(double? newValue) // Correct the text from value if (Value is null && Text.Length > 0) { - SetCurrentValue(TextProperty, String.Empty); + SetCurrentValue(TextProperty, string.Empty); } else if (GetParser().ParseDouble(Text.Trim()) != Value) { @@ -444,7 +442,7 @@ private void StepValue(double? change) ); #endif - var newValue = Value ?? 0; + double newValue = Value ?? 0; if (change is not null) { From 4b819fa1baf92109bbcd1b758df0cd15ae4f6c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AE=8B=E8=B1=A1=E4=B9=8B=E5=BD=A2?= <976999484@qq.com> Date: Fri, 29 Mar 2024 10:05:08 +0800 Subject: [PATCH 4/4] chore(demo): recover previous demo code --- src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs index acb77b768..6692de3f9 100644 --- a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs +++ b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml.cs @@ -3,8 +3,6 @@ // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. -using System.ComponentModel; - using Wpf.Ui.Demo.Simple.Views.Pages; namespace Wpf.Ui.Demo.Simple; @@ -12,7 +10,7 @@ namespace Wpf.Ui.Demo.Simple; /// /// Interaction logic for MainWindow.xaml /// -public partial class MainWindow : INotifyPropertyChanged +public partial class MainWindow { public MainWindow() { @@ -24,6 +22,4 @@ public MainWindow() Loaded += (_, _) => RootNavigation.Navigate(typeof(DashboardPage)); } - - public event PropertyChangedEventHandler? PropertyChanged; }