diff --git a/sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/AuthenticationButtonView.xaml b/sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/AuthenticationButtonView.xaml
new file mode 100644
index 00000000..5502a7c8
--- /dev/null
+++ b/sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/AuthenticationButtonView.xaml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/AuthenticationButtonView.xaml.cs b/sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/AuthenticationButtonView.xaml.cs
new file mode 100644
index 00000000..98528a99
--- /dev/null
+++ b/sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/AuthenticationButtonView.xaml.cs
@@ -0,0 +1,53 @@
+namespace Atc.Wpf.Sample.SamplesWpfControls.ButtonControls;
+
+public partial class AuthenticationButtonView
+{
+ public AuthenticationButtonView()
+ {
+ InitializeComponent();
+
+ DataContext = this;
+ }
+
+ [DependencyProperty]
+ private bool isBusy;
+
+ [DependencyProperty]
+ private bool isAuthenticated;
+
+ [RelayCommand]
+ private async Task Login()
+ {
+ IsBusy = true;
+
+ await Task
+ .Delay(2_000)
+ .ConfigureAwait(false);
+
+ await Application.Current.Dispatcher
+ .InvokeAsyncIfRequired(() =>
+ {
+ IsAuthenticated = true;
+ IsBusy = false;
+ })
+ .ConfigureAwait(false);
+ }
+
+ [RelayCommand]
+ private async Task Logout()
+ {
+ IsBusy = true;
+
+ await Task
+ .Delay(500)
+ .ConfigureAwait(false);
+
+ await Application.Current.Dispatcher
+ .InvokeAsyncIfRequired(() =>
+ {
+ IsAuthenticated = false;
+ IsBusy = false;
+ })
+ .ConfigureAwait(false);
+ }
+}
\ No newline at end of file
diff --git a/sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/ConnectivityButtonView.xaml b/sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/ConnectivityButtonView.xaml
new file mode 100644
index 00000000..a23bc43b
--- /dev/null
+++ b/sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/ConnectivityButtonView.xaml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/Atc.Wpf.Sample/SamplesWpfTheming/InputButton/ImageToggledButton.xaml.cs b/sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/ConnectivityButtonView.xaml.cs
similarity index 87%
rename from sample/Atc.Wpf.Sample/SamplesWpfTheming/InputButton/ImageToggledButton.xaml.cs
rename to sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/ConnectivityButtonView.xaml.cs
index 3274ca13..9ee3110e 100644
--- a/sample/Atc.Wpf.Sample/SamplesWpfTheming/InputButton/ImageToggledButton.xaml.cs
+++ b/sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/ConnectivityButtonView.xaml.cs
@@ -1,8 +1,8 @@
-namespace Atc.Wpf.Sample.SamplesWpfTheming.InputButton;
+namespace Atc.Wpf.Sample.SamplesWpfControls.ButtonControls;
-public partial class ImageToggledButton
+public partial class ConnectivityButtonView
{
- public ImageToggledButton()
+ public ConnectivityButtonView()
{
InitializeComponent();
diff --git a/sample/Atc.Wpf.Sample/SamplesWpfControls/BaseControls/ImageButtonView.xaml b/sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/ImageButtonView.xaml
similarity index 96%
rename from sample/Atc.Wpf.Sample/SamplesWpfControls/BaseControls/ImageButtonView.xaml
rename to sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/ImageButtonView.xaml
index d3347455..a6e2c83c 100644
--- a/sample/Atc.Wpf.Sample/SamplesWpfControls/BaseControls/ImageButtonView.xaml
+++ b/sample/Atc.Wpf.Sample/SamplesWpfControls/ButtonControls/ImageButtonView.xaml
@@ -1,5 +1,5 @@
+ {
+ IsConnected = true;
+ IsBusy = false;
+ })
+ .ConfigureAwait(false);
+ }
+
+ [RelayCommand]
+ private async Task Disconnect()
+ {
+ IsBusy = true;
+
+ await Task
+ .Delay(500)
+ .ConfigureAwait(false);
+
+ await Application.Current.Dispatcher
+ .InvokeAsyncIfRequired(() =>
+ {
+ IsConnected = false;
+ IsBusy = false;
+ })
+ .ConfigureAwait(false);
+ }
+}
\ No newline at end of file
diff --git a/sample/Atc.Wpf.Sample/SamplesWpfControlsTreeView.xaml b/sample/Atc.Wpf.Sample/SamplesWpfControlsTreeView.xaml
index 1becbff4..f9eb96be 100644
--- a/sample/Atc.Wpf.Sample/SamplesWpfControlsTreeView.xaml
+++ b/sample/Atc.Wpf.Sample/SamplesWpfControlsTreeView.xaml
@@ -15,6 +15,7 @@
IsExpanded="True"
SamplePath="DialogBoxes.StandardDialogBoxView" />
+
-
+
+
+
+
+
+
+
-
+
net9.0-windows
diff --git a/src/Atc.Wpf.Controls/BaseControls/ImageToggledButton.cs b/src/Atc.Wpf.Controls/BaseControls/ImageToggledButton.cs
deleted file mode 100644
index f3a0fdce..00000000
--- a/src/Atc.Wpf.Controls/BaseControls/ImageToggledButton.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-namespace Atc.Wpf.Controls.BaseControls;
-
-public sealed partial class ImageToggledButton : ImageButton
-{
- [DependencyProperty(
- DefaultValue = false,
- PropertyChangedCallback = nameof(OnToggledChanged))]
- private bool isToggled;
-
- [DependencyProperty]
- private object? onContent;
-
- [DependencyProperty]
- private object? offContent;
-
- [DependencyProperty]
- private ImageSource? onImageSource;
-
- [DependencyProperty]
- private ImageSource? offImageSource;
-
- [DependencyProperty(DefaultValue = "")]
- private string onSvgImageSource;
-
- [DependencyProperty(DefaultValue = "")]
- private string offSvgImageSource;
-
- [DependencyProperty]
- private Color? onSvgImageOverrideColor;
-
- [DependencyProperty]
- private Color? offSvgImageOverrideColor;
-
- [DependencyProperty]
- private ICommand? onCommand;
-
- [DependencyProperty]
- private ICommand? offCommand;
-
- static ImageToggledButton()
- {
- DefaultStyleKeyProperty.OverrideMetadata(
- typeof(ImageToggledButton),
- new FrameworkPropertyMetadata(typeof(ImageButton)));
- }
-
- public ImageToggledButton()
- {
- // Initialise with the Off state visual when loaded.
- Loaded += (_, _) => ApplyVisualState(IsToggled);
- }
-
- protected override void OnClick()
- {
- base.OnClick();
-
- IsToggled = !IsToggled;
-
- ExecuteCurrentCommand();
- }
-
- private static void OnToggledChanged(
- DependencyObject d,
- DependencyPropertyChangedEventArgs e)
- {
- var ctl = (ImageToggledButton)d;
- ctl.ApplyVisualState((bool)e.NewValue);
- }
-
- private void ApplyVisualState(
- bool toggled)
- {
- if (toggled)
- {
- // ON state
- if (OnContent is not null)
- {
- SetCurrentValue(ContentProperty, OnContent);
- }
-
- if (OnImageSource is not null)
- {
- SetCurrentValue(ImageSourceProperty, OnImageSource);
- }
-
- if (!string.IsNullOrEmpty(OnSvgImageSource))
- {
- SetCurrentValue(SvgImageSourceProperty, OnSvgImageSource);
- }
-
- if (OnSvgImageOverrideColor is not null)
- {
- SetCurrentValue(SvgImageOverrideColorProperty, OnSvgImageOverrideColor);
- }
- }
- else
- {
- // OFF state
- if (OffContent is not null)
- {
- SetCurrentValue(ContentProperty, OffContent);
- }
-
- if (OffImageSource is not null)
- {
- SetCurrentValue(ImageSourceProperty, OffImageSource);
- }
-
- if (!string.IsNullOrEmpty(OffSvgImageSource))
- {
- SetCurrentValue(SvgImageSourceProperty, OffSvgImageSource);
- }
-
- if (OffSvgImageOverrideColor is not null)
- {
- SetCurrentValue(SvgImageOverrideColorProperty, OffSvgImageOverrideColor);
- }
- }
- }
-
- private void ExecuteCurrentCommand()
- {
- var cmd = IsToggled
- ? OffCommand
- : OnCommand;
-
- if (cmd?.CanExecute(parameter: null) == true)
- {
- cmd.Execute(parameter: null);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Atc.Wpf.Controls/ButtonControls/AuthenticationButton.xaml b/src/Atc.Wpf.Controls/ButtonControls/AuthenticationButton.xaml
new file mode 100644
index 00000000..d746cbd4
--- /dev/null
+++ b/src/Atc.Wpf.Controls/ButtonControls/AuthenticationButton.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Atc.Wpf.Controls/ButtonControls/AuthenticationButton.xaml.cs b/src/Atc.Wpf.Controls/ButtonControls/AuthenticationButton.xaml.cs
new file mode 100644
index 00000000..03de49ed
--- /dev/null
+++ b/src/Atc.Wpf.Controls/ButtonControls/AuthenticationButton.xaml.cs
@@ -0,0 +1,137 @@
+// ReSharper disable InvertIf
+namespace Atc.Wpf.Controls.ButtonControls;
+
+public partial class AuthenticationButton
+{
+ public event RoutedEventHandler? IsAuthenticatedChanged;
+
+ [DependencyProperty(DefaultValue = null)]
+ private ImageLocation? imageLocation;
+
+ [DependencyProperty(DefaultValue = 16)]
+ private int imageWidth;
+
+ [DependencyProperty(DefaultValue = 16)]
+ private int imageHeight;
+
+ [DependencyProperty(DefaultValue = 10d)]
+ private double imageContentSpacing;
+
+ [DependencyProperty(DefaultValue = 0d)]
+ private double imageBorderSpacing;
+
+ [DependencyProperty(DefaultValue = false)]
+ private bool isBusy;
+
+ [DependencyProperty(
+ DefaultValue = false,
+ PropertyChangedCallback = nameof(OnIsAuthenticatedChanged))]
+ private bool isAuthenticated;
+
+ [DependencyProperty]
+ private object? loginContent;
+
+ [DependencyProperty]
+ private object? logoutContent;
+
+ [DependencyProperty]
+ private ImageSource? loginImageSource;
+
+ [DependencyProperty]
+ private ImageSource? logoutImageSource;
+
+ [DependencyProperty(DefaultValue = "")]
+ private string loginSvgImageSource;
+
+ [DependencyProperty(DefaultValue = "")]
+ private string logoutSvgImageSource;
+
+ [DependencyProperty]
+ private Color? loginSvgImageOverrideColor;
+
+ [DependencyProperty]
+ private Color? logoutSvgImageOverrideColor;
+
+ [DependencyProperty]
+ private ICommand? loginCommand;
+
+ [DependencyProperty]
+ private ICommand? logoutCommand;
+
+ public AuthenticationButton()
+ {
+ InitializeComponent();
+
+ SetCurrentValue(ImageLocationProperty, Controls.ImageLocation.Left);
+
+ ThemeManager.Current.ThemeChanged += OnThemeChanged;
+ }
+
+ protected override void OnInitialized(
+ EventArgs e)
+ {
+ base.OnInitialized(e);
+
+ if (LoginContent is null or string { Length: 0 })
+ {
+ SetCurrentValue(LoginContentProperty, Word.Login);
+ }
+
+ if (LogoutContent is null or string { Length: 0 })
+ {
+ SetCurrentValue(LogoutContentProperty, Word.Logout);
+ }
+
+ if (LoginImageSource is null &&
+ string.IsNullOrWhiteSpace(LoginSvgImageSource))
+ {
+ var fgBrush = Application.Current.TryFindResource("AtcApps.Brushes.ThemeForeground");
+
+ var img = (ImageSource?)FontIconImageSourceValueConverter.Instance
+ .Convert(
+ FontMaterialDesignType.Login,
+ typeof(ImageSource),
+ fgBrush,
+ CultureInfo.CurrentUICulture);
+
+ if (img is not null)
+ {
+ SetCurrentValue(LoginImageSourceProperty, img);
+ }
+ }
+
+ if (LogoutImageSource is null &&
+ string.IsNullOrWhiteSpace(LogoutSvgImageSource))
+ {
+ var accentBrush = Application.Current.TryFindResource("AtcApps.Brushes.Accent");
+
+ var img = (ImageSource?)FontIconImageSourceValueConverter.Instance
+ .Convert(
+ FontMaterialDesignType.Logout,
+ typeof(ImageSource),
+ accentBrush,
+ CultureInfo.CurrentUICulture);
+
+ if (img is not null)
+ {
+ SetCurrentValue(LogoutImageSourceProperty, img);
+ }
+ }
+ }
+
+ private void OnThemeChanged(
+ object? sender,
+ ThemeChangedEventArgs e)
+ {
+ SetCurrentValue(LoginContentProperty, Word.Login);
+ SetCurrentValue(LogoutContentProperty, Word.Logout);
+ }
+
+ private static void OnIsAuthenticatedChanged(
+ DependencyObject d,
+ DependencyPropertyChangedEventArgs e)
+ => ((AuthenticationButton)d).RaiseIsAuthenticatedChanged();
+
+ private void RaiseIsAuthenticatedChanged()
+ => IsAuthenticatedChanged?.Invoke(this, new RoutedEventArgs());
+}
\ No newline at end of file
diff --git a/src/Atc.Wpf.Controls/ButtonControls/ConnectivityButton.xaml b/src/Atc.Wpf.Controls/ButtonControls/ConnectivityButton.xaml
new file mode 100644
index 00000000..8b7506c7
--- /dev/null
+++ b/src/Atc.Wpf.Controls/ButtonControls/ConnectivityButton.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Atc.Wpf.Controls/ButtonControls/ConnectivityButton.xaml.cs b/src/Atc.Wpf.Controls/ButtonControls/ConnectivityButton.xaml.cs
new file mode 100644
index 00000000..8f21d8f7
--- /dev/null
+++ b/src/Atc.Wpf.Controls/ButtonControls/ConnectivityButton.xaml.cs
@@ -0,0 +1,137 @@
+// ReSharper disable InvertIf
+namespace Atc.Wpf.Controls.ButtonControls;
+
+public partial class ConnectivityButton
+{
+ public event RoutedEventHandler? IsConnectedChanged;
+
+ [DependencyProperty(DefaultValue = null)]
+ private ImageLocation? imageLocation;
+
+ [DependencyProperty(DefaultValue = 16)]
+ private int imageWidth;
+
+ [DependencyProperty(DefaultValue = 16)]
+ private int imageHeight;
+
+ [DependencyProperty(DefaultValue = 10d)]
+ private double imageContentSpacing;
+
+ [DependencyProperty(DefaultValue = 0d)]
+ private double imageBorderSpacing;
+
+ [DependencyProperty(DefaultValue = false)]
+ private bool isBusy;
+
+ [DependencyProperty(
+ DefaultValue = false,
+ PropertyChangedCallback = nameof(OnIsConnectedChanged))]
+ private bool isConnected;
+
+ [DependencyProperty]
+ private object? connectContent;
+
+ [DependencyProperty]
+ private object? disconnectContent;
+
+ [DependencyProperty]
+ private ImageSource? connectImageSource;
+
+ [DependencyProperty]
+ private ImageSource? disconnectImageSource;
+
+ [DependencyProperty(DefaultValue = "")]
+ private string connectSvgImageSource;
+
+ [DependencyProperty(DefaultValue = "")]
+ private string disconnectSvgImageSource;
+
+ [DependencyProperty]
+ private Color? connectSvgImageOverrideColor;
+
+ [DependencyProperty]
+ private Color? disconnectSvgImageOverrideColor;
+
+ [DependencyProperty]
+ private ICommand? connectCommand;
+
+ [DependencyProperty]
+ private ICommand? disconnectCommand;
+
+ public ConnectivityButton()
+ {
+ InitializeComponent();
+
+ SetCurrentValue(ImageLocationProperty, Controls.ImageLocation.Left);
+
+ ThemeManager.Current.ThemeChanged += OnThemeChanged;
+ }
+
+ protected override void OnInitialized(
+ EventArgs e)
+ {
+ base.OnInitialized(e);
+
+ if (ConnectContent is null or string { Length: 0 })
+ {
+ SetCurrentValue(ConnectContentProperty, Word.Connect);
+ }
+
+ if (DisconnectContent is null or string { Length: 0 })
+ {
+ SetCurrentValue(DisconnectContentProperty, Word.Disconnect);
+ }
+
+ if (ConnectImageSource is null &&
+ string.IsNullOrWhiteSpace(ConnectSvgImageSource))
+ {
+ var fgBrush = Application.Current.TryFindResource("AtcApps.Brushes.ThemeForeground");
+
+ var img = (ImageSource?)FontIconImageSourceValueConverter.Instance
+ .Convert(
+ FontMaterialDesignType.Login,
+ typeof(ImageSource),
+ fgBrush,
+ CultureInfo.CurrentUICulture);
+
+ if (img is not null)
+ {
+ SetCurrentValue(ConnectImageSourceProperty, img);
+ }
+ }
+
+ if (DisconnectImageSource is null &&
+ string.IsNullOrWhiteSpace(DisconnectSvgImageSource))
+ {
+ var accentBrush = Application.Current.TryFindResource("AtcApps.Brushes.Accent");
+
+ var img = (ImageSource?)FontIconImageSourceValueConverter.Instance
+ .Convert(
+ FontMaterialDesignType.Logout,
+ typeof(ImageSource),
+ accentBrush,
+ CultureInfo.CurrentUICulture);
+
+ if (img is not null)
+ {
+ SetCurrentValue(DisconnectImageSourceProperty, img);
+ }
+ }
+ }
+
+ private void OnThemeChanged(
+ object? sender,
+ ThemeChangedEventArgs e)
+ {
+ SetCurrentValue(ConnectContentProperty, Word.Connect);
+ SetCurrentValue(DisconnectContentProperty, Word.Disconnect);
+ }
+
+ private static void OnIsConnectedChanged(
+ DependencyObject d,
+ DependencyPropertyChangedEventArgs e)
+ => ((ConnectivityButton)d).RaiseIsConnectedChanged();
+
+ private void RaiseIsConnectedChanged()
+ => IsConnectedChanged?.Invoke(this, new RoutedEventArgs());
+}
\ No newline at end of file
diff --git a/src/Atc.Wpf.Controls/BaseControls/ImageButton.cs b/src/Atc.Wpf.Controls/ButtonControls/ImageButton.cs
similarity index 98%
rename from src/Atc.Wpf.Controls/BaseControls/ImageButton.cs
rename to src/Atc.Wpf.Controls/ButtonControls/ImageButton.cs
index e7244e9c..1f162838 100644
--- a/src/Atc.Wpf.Controls/BaseControls/ImageButton.cs
+++ b/src/Atc.Wpf.Controls/ButtonControls/ImageButton.cs
@@ -1,4 +1,4 @@
-namespace Atc.Wpf.Controls.BaseControls;
+namespace Atc.Wpf.Controls.ButtonControls;
public partial class ImageButton : Button
{
diff --git a/src/Atc.Wpf.Controls/BaseControls/ImageButton.xaml b/src/Atc.Wpf.Controls/ButtonControls/ImageButton.xaml
similarity index 85%
rename from src/Atc.Wpf.Controls/BaseControls/ImageButton.xaml
rename to src/Atc.Wpf.Controls/ButtonControls/ImageButton.xaml
index 7d3bd52c..a14d16e4 100644
--- a/src/Atc.Wpf.Controls/BaseControls/ImageButton.xaml
+++ b/src/Atc.Wpf.Controls/ButtonControls/ImageButton.xaml
@@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:atc="https://github.com/atc-net/atc-wpf/tree/main/schemas"
xmlns:atcValueConverters="https://github.com/atc-net/atc-wpf/tree/main/schemas/value-converters"
- xmlns:baseControls="clr-namespace:Atc.Wpf.Controls.BaseControls"
+ xmlns:buttonControls="clr-namespace:Atc.Wpf.Controls.ButtonControls"
xmlns:progressing="clr-namespace:Atc.Wpf.Controls.Progressing"
xmlns:valueConverters="clr-namespace:Atc.Wpf.Controls.BaseControls.Internal.ValueConverters">
@@ -18,7 +18,7 @@
@@ -150,7 +150,7 @@
@@ -239,25 +239,25 @@
+ TargetType="{x:Type buttonControls:ImageButton}" />
+ TargetType="{x:Type buttonControls:ImageButton}" />
@@ -265,7 +265,7 @@
@@ -291,7 +291,7 @@
@@ -325,7 +325,7 @@
@@ -359,7 +359,7 @@
@@ -393,7 +393,7 @@
@@ -427,7 +427,7 @@
@@ -461,7 +461,7 @@
@@ -495,7 +495,7 @@
@@ -529,7 +529,7 @@
@@ -563,7 +563,7 @@
@@ -597,7 +597,7 @@
@@ -631,7 +631,7 @@
@@ -665,7 +665,7 @@
@@ -699,7 +699,7 @@