diff --git a/samples/SettingsNavigationTest/MainPage.xaml b/samples/SettingsNavigationTest/MainPage.xaml
index 28f6a20..0f07243 100644
--- a/samples/SettingsNavigationTest/MainPage.xaml
+++ b/samples/SettingsNavigationTest/MainPage.xaml
@@ -32,6 +32,71 @@
+
+
+
+
+
+
+
+ Option 1
+ Option 2
+ Option 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -46,5 +111,6 @@
+
diff --git a/samples/SettingsNavigationTest/MainWindow.xaml b/samples/SettingsNavigationTest/MainWindow.xaml
index fb1453d..2a5f816 100644
--- a/samples/SettingsNavigationTest/MainWindow.xaml
+++ b/samples/SettingsNavigationTest/MainWindow.xaml
@@ -9,13 +9,13 @@
ui:ThemeManager.IsThemeAware="True"
ui:WindowHelper.SystemBackdropType="Mica"
ui:WindowHelper.UseModernWindowStyle="True"
- Title="SettingsNavigationTest" Height="450" Width="800">
+ Title="SettingsNavigationTest" Height="756" Width="800">
-
+
diff --git a/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard.Properties.cs b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard/SettingsCard.Properties.cs
similarity index 95%
rename from source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard.Properties.cs
rename to source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard/SettingsCard.Properties.cs
index c5cbc98..2f7557e 100644
--- a/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard.Properties.cs
+++ b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard/SettingsCard.Properties.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System;
using System.Windows;
namespace iNKORE.UI.WPF.Modern.Controls
@@ -69,7 +70,7 @@ public partial class SettingsCard
nameof(ContentAlignment),
typeof(ContentAlignment),
typeof(SettingsCard),
- new PropertyMetadata(defaultValue: ContentAlignment.Right));
+ new PropertyMetadata(defaultValue: ContentAlignment.Right, (d, e) => ((SettingsCard)d).OnContentAlignmentPropertyChanged((ContentAlignment)e.OldValue, (ContentAlignment)e.NewValue)));
///
/// The backing for the property.
@@ -177,6 +178,12 @@ protected virtual void OnIsActionIconVisiblePropertyChanged(bool oldValue, bool
{
OnActionIconChanged();
}
+
+ protected virtual void OnContentAlignmentPropertyChanged(ContentAlignment oldValue, ContentAlignment newValue)
+ {
+ this.UpdateContentAlignmentState();
+ }
+
}
public enum ContentAlignment
@@ -194,5 +201,4 @@ public enum ContentAlignment
///
Vertical
}
-
}
\ No newline at end of file
diff --git a/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard.cs b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard/SettingsCard.cs
similarity index 83%
rename from source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard.cs
rename to source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard/SettingsCard.cs
index 07714dd..12a963a 100644
--- a/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard.cs
+++ b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard/SettingsCard.cs
@@ -54,7 +54,6 @@ static SettingsCard()
internal static readonly DependencyPropertyDescriptor IsPressedPropertyDescriptior = DependencyPropertyDescriptor.FromProperty(IsPressedProperty, typeof(SettingsCard));
internal static readonly DependencyPropertyDescriptor IsMouseOverPropertyDescriptior = DependencyPropertyDescriptor.FromProperty(IsMouseOverProperty, typeof(SettingsCard));
-
///
/// Creates a new instance of the class.
///
@@ -83,15 +82,22 @@ public override void OnApplyTemplate()
SetAccessibleContentName();
IsEnabledChanged += OnIsEnabledChanged;
+ SizeChanged += SettingsCard_SizeChanged;
// RegisterPropertyChangedCallback(ContentProperty, OnContentChanged);
}
+ private void SettingsCard_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ this.UpdateContentAlignmentState();
+ }
+
private static void ContentProperty_ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is SettingsCard control)
{
control.OnContentChanged(e.OldValue, e.NewValue);
+ control.UpdateContentVisibilityStates();
}
}
@@ -136,6 +142,23 @@ private void DisableButtonInteraction()
//PreviewKeyUp -= Control_PreviewKeyUp;
}
+ protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
+ {
+ base.OnMouseLeftButtonDown(e);
+
+ if (!this.IsClickEnabled)
+ e.Handled = false;
+ }
+ protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
+ {
+ base.OnMouseLeftButtonUp(e);
+
+ if (!this.IsClickEnabled)
+ e.Handled = false;
+ }
+
+
+
//private void Control_PreviewKeyUp(object sender, KeyEventArgs e)
//{
// if (e.Key == Key.Enter || e.Key == Key.Space) // || e.Key == Key.GamepadA
@@ -168,7 +191,7 @@ private void DisableButtonInteraction()
//protected override void OnMouseDown(MouseButtonEventArgs e)
//{
// base.OnMouseDown(e);
-
+
// if (IsClickEnabled && IsEnabled)
// {
// this.IsPressed = true;
@@ -196,7 +219,7 @@ private void DisableButtonInteraction()
//protected override void OnMouseLeave(MouseEventArgs e)
//{
// base.OnMouseLeave(e);
-
+
// if (IsClickEnabled && IsEnabled)
// VisualStateManager.GoToState(this, NormalState, true);
//}
@@ -228,6 +251,61 @@ private void UpdatePointerState()
VisualStateManager.GoToState(this, state, true);
}
+ private void UpdateContentAlignmentState()
+ {
+ // Manually go to states, adapted from:
+ // https://github.com/CommunityToolkit/Windows/blob/main/components/SettingsControls/src/SettingsCard/SettingsCard.xaml#L304-353
+
+ string state = null;
+
+ if (this.ContentAlignment == ContentAlignment.Left)
+ {
+ state = LeftState;
+ }
+ else if (this.ContentAlignment == ContentAlignment.Vertical)
+ {
+ state = VerticalState;
+ }
+ else
+ {
+ var SettingsCardWrapNoIconThreshold = this.FindResource("SettingsCardWrapNoIconThreshold") as double?;
+ var SettingsCardWrapThreshold = this.FindResource("SettingsCardWrapThreshold") as double?;
+
+ if (SettingsCardWrapThreshold != null && SettingsCardWrapThreshold != null)
+ {
+ if (this.ActualWidth < SettingsCardWrapNoIconThreshold)
+ {
+ state = RightWrappedNoIconState;
+ }
+ else if (this.ActualWidth < SettingsCardWrapThreshold)
+ {
+ state = RightWrappedState;
+ }
+ else
+ {
+ state = RightState;
+ }
+ }
+ }
+
+ if (state != null)
+ {
+ VisualStateManager.GoToState(this, state, true);
+ }
+ }
+
+ public void UpdateContentVisibilityStates()
+ {
+ // Manually go to states, adapted from:
+ // https://github.com/CommunityToolkit/Windows/blob/main/components/SettingsControls/src/SettingsCard/SettingsCard.xaml#L369
+
+ var state = this.Content == null || this.Content as string == ""
+ ? nameof(Visibility.Collapsed)
+ : nameof(Visibility.Visible);
+
+ VisualStateManager.GoToState(this, state, true);
+ }
+
///
/// Creates AutomationPeer
///
diff --git a/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard.xaml b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard/SettingsCard.xaml
similarity index 84%
rename from source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard.xaml
rename to source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard/SettingsCard.xaml
index adc18d3..58a73cc 100644
--- a/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard.xaml
+++ b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard/SettingsCard.xaml
@@ -99,16 +99,19 @@
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
-
+
-
+
+
+
@@ -214,18 +236,23 @@
-
-
-
-
-
-
+
+
+
+
+ 1
+
+
+
+
-
+
-
+
+
+
@@ -235,23 +262,38 @@
+
-
+
+
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
-
+
@@ -259,17 +301,36 @@
-
-
-
-
-
-
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
-
+
-
-
+
+
+
+
+
+ 0,5,0,0
+
+
@@ -283,7 +344,7 @@
-
+
diff --git a/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCardAutomationPeer.cs b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard/SettingsCardAutomationPeer.cs
similarity index 100%
rename from source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCardAutomationPeer.cs
rename to source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsCard/SettingsCardAutomationPeer.cs
diff --git a/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsExpander/SettingsExpander.Events.cs b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsExpander/SettingsExpander.Events.cs
new file mode 100644
index 0000000..1fd22e5
--- /dev/null
+++ b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsExpander/SettingsExpander.Events.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace iNKORE.UI.WPF.Modern.Controls
+{
+ public partial class SettingsExpander
+ {
+ ///
+ /// Fires when the SettingsExpander is opened
+ ///
+ public event EventHandler? Expanded;
+
+ ///
+ /// Fires when the expander is closed
+ ///
+ public event EventHandler? Collapsed;
+ }
+}
diff --git a/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsExpander/SettingsExpander.ItemsControl.cs b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsExpander/SettingsExpander.ItemsControl.cs
new file mode 100644
index 0000000..a146e04
--- /dev/null
+++ b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsExpander/SettingsExpander.ItemsControl.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+using System.Windows;
+using System.Windows.Markup;
+using System.Collections;
+
+namespace iNKORE.UI.WPF.Modern.Controls
+{
+
+ //// Implement properties for ItemsControl like behavior.
+ public partial class SettingsExpander
+ {
+ public IList Items
+ {
+ get { return (IList)GetValue(ItemsProperty); }
+ set { SetValue(ItemsProperty, value); }
+ }
+
+ public static readonly DependencyProperty ItemsProperty =
+ DependencyProperty.Register(nameof(Items), typeof(IList), typeof(SettingsExpander), new PropertyMetadata(null, OnItemsConnectedPropertyChanged));
+
+ public object ItemsSource
+ {
+ get { return (object)GetValue(ItemsSourceProperty); }
+ set { SetValue(ItemsSourceProperty, value); }
+ }
+
+ public static readonly DependencyProperty ItemsSourceProperty =
+ DependencyProperty.Register(nameof(ItemsSource), typeof(object), typeof(SettingsExpander), new PropertyMetadata(null, OnItemsConnectedPropertyChanged));
+
+ public object ItemTemplate
+ {
+ get { return (object)GetValue(ItemTemplateProperty); }
+ set { SetValue(ItemTemplateProperty, value); }
+ }
+
+ public static readonly DependencyProperty ItemTemplateProperty =
+ DependencyProperty.Register(nameof(ItemTemplate), typeof(object), typeof(SettingsExpander), new PropertyMetadata(null));
+
+ public StyleSelector ItemContainerStyleSelector
+ {
+ get { return (StyleSelector)GetValue(ItemContainerStyleSelectorProperty); }
+ set { SetValue(ItemContainerStyleSelectorProperty, value); }
+ }
+
+ public static readonly DependencyProperty ItemContainerStyleSelectorProperty =
+ DependencyProperty.Register(nameof(ItemContainerStyleSelector), typeof(StyleSelector), typeof(SettingsExpander), new PropertyMetadata(null));
+
+ private static void OnItemsConnectedPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
+ {
+ if (dependencyObject is SettingsExpander expander && expander._itemsRepeater is not null)
+ {
+ var datasource = expander.ItemsSource;
+
+ if (datasource is null)
+ {
+ datasource = expander.Items;
+ }
+
+ expander._itemsRepeater.ItemsSource = datasource;
+ }
+ }
+
+ private void ItemsRepeater_ElementPrepared(ItemsRepeater sender, ItemsRepeaterElementPreparedEventArgs args)
+ {
+ if (ItemContainerStyleSelector != null &&
+ args.Element is FrameworkElement element &&
+ element.ReadLocalValue(FrameworkElement.StyleProperty) == DependencyProperty.UnsetValue)
+ {
+ // TODO: Get item from args.Index?
+ element.Style = ItemContainerStyleSelector.SelectStyle(null, element);
+ }
+ }
+ }
+}
diff --git a/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsExpander/SettingsExpander.Properties.cs b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsExpander/SettingsExpander.Properties.cs
new file mode 100644
index 0000000..75833d4
--- /dev/null
+++ b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsExpander/SettingsExpander.Properties.cs
@@ -0,0 +1,170 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+using System.Windows.Markup;
+using System.Windows;
+
+namespace iNKORE.UI.WPF.Modern.Controls
+{
+ // Licensed to the .NET Foundation under one or more agreements.
+ // The .NET Foundation licenses this file to you under the MIT license.
+ // See the LICENSE file in the project root for more information.
+
+ [ContentProperty(name:nameof(Content))]
+ public partial class SettingsExpander
+ {
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
+ nameof(CornerRadius), typeof(CornerRadius), typeof(SettingsExpander), new PropertyMetadata(default(CornerRadius)));
+
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
+ nameof(Header),
+ typeof(object),
+ typeof(SettingsExpander),
+ new PropertyMetadata(defaultValue: null));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
+ nameof(Description),
+ typeof(object),
+ typeof(SettingsExpander),
+ new PropertyMetadata(defaultValue: null));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty HeaderIconProperty = DependencyProperty.Register(
+ nameof(HeaderIcon),
+ typeof(IconElement),
+ typeof(SettingsExpander),
+ new PropertyMetadata(defaultValue: null));
+
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
+ nameof(Content),
+ typeof(object),
+ typeof(SettingsExpander),
+ new PropertyMetadata(defaultValue: null));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty ItemsHeaderProperty = DependencyProperty.Register(
+ nameof(ItemsHeader),
+ typeof(UIElement),
+ typeof(SettingsExpander),
+ new PropertyMetadata(defaultValue: null));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty ItemsFooterProperty = DependencyProperty.Register(
+ nameof(ItemsFooter),
+ typeof(UIElement),
+ typeof(SettingsExpander),
+ new PropertyMetadata(defaultValue: null));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register(
+ nameof(IsExpanded),
+ typeof(bool),
+ typeof(SettingsExpander),
+ new PropertyMetadata(defaultValue: false, (d, e) => ((SettingsExpander)d).OnIsExpandedPropertyChanged((bool)e.OldValue, (bool)e.NewValue)));
+
+ ///
+ ///
+ ///
+ /// Gets or sets the Header.
+ ///
+ public object Header
+ {
+ get => (object)GetValue(HeaderProperty);
+ set => SetValue(HeaderProperty, value);
+ }
+
+ ///
+ /// Gets or sets the Description.
+ ///
+#pragma warning disable CS0109 // Member does not hide an inherited member; new keyword is not required
+ public new object Description
+#pragma warning restore CS0109 // Member does not hide an inherited member; new keyword is not required
+ {
+ get => (object)GetValue(DescriptionProperty);
+ set => SetValue(DescriptionProperty, value);
+ }
+
+ ///
+ /// Gets or sets the HeaderIcon.
+ ///
+ public IconElement HeaderIcon
+ {
+ get => (IconElement)GetValue(HeaderIconProperty);
+ set => SetValue(HeaderIconProperty, value);
+ }
+
+ ///
+ /// Gets or sets the Content.
+ ///
+ public object Content
+ {
+ get => (object)GetValue(ContentProperty);
+ set => SetValue(ContentProperty, value);
+ }
+
+ ///
+ /// Gets or sets the ItemsFooter.
+ ///
+ public UIElement ItemsHeader
+ {
+ get => (UIElement)GetValue(ItemsHeaderProperty);
+ set => SetValue(ItemsHeaderProperty, value);
+ }
+
+ ///
+ /// Gets or sets the ItemsFooter.
+ ///
+ public UIElement ItemsFooter
+ {
+ get => (UIElement)GetValue(ItemsFooterProperty);
+ set => SetValue(ItemsFooterProperty, value);
+ }
+
+ ///
+ /// Gets or sets the IsExpanded state.
+ ///
+ public bool IsExpanded
+ {
+ get => (bool)GetValue(IsExpandedProperty);
+ set => SetValue(IsExpandedProperty, value);
+ }
+ protected virtual void OnIsExpandedPropertyChanged(bool oldValue, bool newValue)
+ {
+ OnIsExpandedChanged(oldValue, newValue);
+
+ if (newValue)
+ {
+ Expanded?.Invoke(this, EventArgs.Empty);
+ }
+ else
+ {
+ Collapsed?.Invoke(this, EventArgs.Empty);
+ }
+ }
+ }
+}
diff --git a/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsExpander/SettingsExpander.cs b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsExpander/SettingsExpander.cs
new file mode 100644
index 0000000..83ece7d
--- /dev/null
+++ b/source/iNKORE.UI.WPF.Modern.Controls/Controls/SettingsControls/SettingsExpander/SettingsExpander.cs
@@ -0,0 +1,89 @@
+using System.Collections.Generic;
+using System.Windows.Automation.Peers;
+using System.Windows.Automation;
+using System.Windows;
+
+namespace iNKORE.UI.WPF.Modern.Controls
+{
+ // Licensed to the .NET Foundation under one or more agreements.
+ // The .NET Foundation licenses this file to you under the MIT license.
+ // See the LICENSE file in the project root for more information.
+
+ //// Note: ItemsRepeater will request all the available horizontal space: https://github.com/microsoft/microsoft-ui-xaml/issues/3842
+ [TemplatePart(Name = PART_ItemsRepeater, Type = typeof(ItemsRepeater))]
+ [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(FrameworkElement))]
+ public partial class SettingsExpander : System.Windows.Controls.Control
+ {
+ private const string PART_ItemsRepeater = "PART_ItemsRepeater";
+
+ private ItemsRepeater? _itemsRepeater;
+
+ ///
+ /// The SettingsExpander is a collapsable control to host multiple SettingsCards.
+ ///
+ public SettingsExpander()
+ {
+ this.DefaultStyleKey = typeof(SettingsExpander);
+ Items = new List