diff --git a/source/iNKORE.UI.WPF.Modern.Gallery/Assets/Design/Cards.dark.png b/source/iNKORE.UI.WPF.Modern.Gallery/Assets/Design/Cards.dark.png new file mode 100644 index 00000000..4fbec4ac Binary files /dev/null and b/source/iNKORE.UI.WPF.Modern.Gallery/Assets/Design/Cards.dark.png differ diff --git a/source/iNKORE.UI.WPF.Modern.Gallery/Assets/Design/Cards.light.png b/source/iNKORE.UI.WPF.Modern.Gallery/Assets/Design/Cards.light.png new file mode 100644 index 00000000..62b880de Binary files /dev/null and b/source/iNKORE.UI.WPF.Modern.Gallery/Assets/Design/Cards.light.png differ diff --git a/source/iNKORE.UI.WPF.Modern.Gallery/Assets/Design/Dialog.dark.png b/source/iNKORE.UI.WPF.Modern.Gallery/Assets/Design/Dialog.dark.png new file mode 100644 index 00000000..7afd73dd Binary files /dev/null and b/source/iNKORE.UI.WPF.Modern.Gallery/Assets/Design/Dialog.dark.png differ diff --git a/source/iNKORE.UI.WPF.Modern.Gallery/Assets/Design/Dialog.light.png b/source/iNKORE.UI.WPF.Modern.Gallery/Assets/Design/Dialog.light.png new file mode 100644 index 00000000..1d8f13f5 Binary files /dev/null and b/source/iNKORE.UI.WPF.Modern.Gallery/Assets/Design/Dialog.light.png differ diff --git a/source/iNKORE.UI.WPF.Modern.Gallery/DataModel/Data/Controls.Foundation.json b/source/iNKORE.UI.WPF.Modern.Gallery/DataModel/Data/Controls.Foundation.json index 45a7ec64..aa9f3b99 100644 --- a/source/iNKORE.UI.WPF.Modern.Gallery/DataModel/Data/Controls.Foundation.json +++ b/source/iNKORE.UI.WPF.Modern.Gallery/DataModel/Data/Controls.Foundation.json @@ -24,6 +24,27 @@ "Description": "Design guidance and resources", "Items": [ + { + "UniqueId": "Spacing", + "Title": "Spacing", + "Subtitle": "Spacing values and layout examples", + "ImagePath": "ms-appx:///Assets/ControlIcons/DefaultIcon.png", + "ImageIconPath": "ms-appx:///Assets/ControlIcons/DefaultIcon.png", + "Description": "", + "Content": "", + "IncludedInBuild": true, + "IsNew": false, + "IsUpdated": false, + "Docs": [ + { + "Title": "Content design basics", + "Uri": "https://learn.microsoft.com/windows/apps/design/basics/content-basics" + } + ], + "RelatedControls": + [ + ] + }, { "UniqueId": "Typography", "Title": "Typography", @@ -68,4 +89,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/source/iNKORE.UI.WPF.Modern.Gallery/Navigation/NavigationRootPage.xaml b/source/iNKORE.UI.WPF.Modern.Gallery/Navigation/NavigationRootPage.xaml index 61e9a267..c501d8d5 100644 --- a/source/iNKORE.UI.WPF.Modern.Gallery/Navigation/NavigationRootPage.xaml +++ b/source/iNKORE.UI.WPF.Modern.Gallery/Navigation/NavigationRootPage.xaml @@ -121,6 +121,14 @@ + + + + + r.Groups) + .SelectMany(g => g.Items) + .FirstOrDefault(i => i.UniqueId == "Spacing"); + if (spacingItem != null) + { + rootFrame.Navigate(ItemPage.Create(spacingItem)); + } + } else if (selectedItem?.Tag?.ToString() == "Typography") { - // Handle Typography navigation var typographyId = "Typography"; if (_lastItem?.ToString() == typographyId) return; _lastItem = typographyId; - - // Find Typography item from the data source var typographyItem = ControlInfoDataSource.Instance.Realms .SelectMany(r => r.Groups) .SelectMany(g => g.Items) .FirstOrDefault(i => i.UniqueId == "Typography"); - if (typographyItem != null) { rootFrame.Navigate(ItemPage.Create(typographyItem)); diff --git a/source/iNKORE.UI.WPF.Modern.Gallery/Pages/Controls/Foundation/Design/SpacingPage.xaml b/source/iNKORE.UI.WPF.Modern.Gallery/Pages/Controls/Foundation/Design/SpacingPage.xaml new file mode 100644 index 00000000..dc44208f --- /dev/null +++ b/source/iNKORE.UI.WPF.Modern.Gallery/Pages/Controls/Foundation/Design/SpacingPage.xaml @@ -0,0 +1,168 @@ + + + + + + + + + The use of consistently sized spacing and gutters semantically groups an experience into separate components. These values map to our rounded corner logic and together help create a cohesive and usable layout. + A best practice in design is to use a 4px grid. This means that any spacing or sizing should be a multiple of 4. This helps to create a consistent and harmonious layout and these values are easy to scale. + + + Below, you can find a few examples of common layout types with highlighted spacing values (in epx). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/iNKORE.UI.WPF.Modern.Gallery/Pages/Controls/Foundation/Design/SpacingPage.xaml.cs b/source/iNKORE.UI.WPF.Modern.Gallery/Pages/Controls/Foundation/Design/SpacingPage.xaml.cs new file mode 100644 index 00000000..6c397e71 --- /dev/null +++ b/source/iNKORE.UI.WPF.Modern.Gallery/Pages/Controls/Foundation/Design/SpacingPage.xaml.cs @@ -0,0 +1,129 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using iNKORE.UI.WPF.Modern.Controls; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Windows; +using System.Windows.Navigation; +using System.Windows.Media.Imaging; +using System.Windows.Threading; +using iNKORE.UI.WPF.Modern.Gallery.Helpers; +using iNKORE.UI.WPF.Modern; + +namespace iNKORE.UI.WPF.Modern.Gallery.Pages.Controls.Foundation +{ + /// + /// Spacing page showcasing Windows spacing values and layout examples. + /// + public partial class SpacingPage : Page + { + private DispatcherTimer _themeMonitorTimer; + private ElementTheme _lastKnownTheme = ElementTheme.Default; + + public SpacingPage() + { + this.InitializeComponent(); + Loaded += SpacingPage_Loaded; + + ThemeManager.Current.ActualApplicationThemeChanged += OnThemeChanged; + ThemeManager.AddActualThemeChangedHandler(this, OnElementThemeChanged); + + _themeMonitorTimer = new DispatcherTimer + { + Interval = TimeSpan.FromMilliseconds(200) + }; + _themeMonitorTimer.Tick += ThemeMonitorTimer_Tick; + _themeMonitorTimer.Start(); + } + + private void SpacingPage_Loaded(object sender, RoutedEventArgs e) + { + if (NavigationRootPage.Current?.NavigationView != null) + { + NavigationRootPage.Current.NavigationView.Header = "Spacing"; + } + UpdateSpacingImages(); + } + + private void UpdateSpacingImages() + { + if (CardsImage == null || DialogImage == null) return; + + var pageTheme = ThemeManager.GetActualTheme(this); + var parentTheme = ElementTheme.Default; + + var parentElement = this.Parent as FrameworkElement; + while (parentElement != null) + { + var currentParentTheme = ThemeManager.GetActualTheme(parentElement); + if (currentParentTheme != ElementTheme.Default) + { + parentTheme = currentParentTheme; + break; + } + parentElement = parentElement.Parent as FrameworkElement; + } + + var effectiveTheme = pageTheme != ElementTheme.Default ? pageTheme : parentTheme; + var isDarkTheme = effectiveTheme == ElementTheme.Dark || + (effectiveTheme == ElementTheme.Default && ThemeHelper.IsDarkTheme()); + + var cardsImageName = isDarkTheme ? "Cards.dark.png" : "Cards.light.png"; + var dialogImageName = isDarkTheme ? "Dialog.dark.png" : "Dialog.light.png"; + + var cardsUri = new Uri($"pack://application:,,,/iNKORE.UI.WPF.Modern.Gallery;component/Assets/Design/{cardsImageName}"); + var dialogUri = new Uri($"pack://application:,,,/iNKORE.UI.WPF.Modern.Gallery;component/Assets/Design/{dialogImageName}"); + + try + { + var cardsBitmap = new BitmapImage(); + cardsBitmap.BeginInit(); + cardsBitmap.UriSource = cardsUri; + cardsBitmap.CacheOption = BitmapCacheOption.OnLoad; + cardsBitmap.CreateOptions = BitmapCreateOptions.IgnoreImageCache; + cardsBitmap.EndInit(); + cardsBitmap.Freeze(); + CardsImage.Source = cardsBitmap; + + var dialogBitmap = new BitmapImage(); + dialogBitmap.BeginInit(); + dialogBitmap.UriSource = dialogUri; + dialogBitmap.CacheOption = BitmapCacheOption.OnLoad; + dialogBitmap.CreateOptions = BitmapCreateOptions.IgnoreImageCache; + dialogBitmap.EndInit(); + dialogBitmap.Freeze(); + DialogImage.Source = dialogBitmap; + } + catch (Exception ex) + { + Debug.WriteLine($"Failed to load spacing images: {ex.Message}"); + // Fallback to dark images if there's an issue + CardsImage.Source = new BitmapImage(new Uri("pack://application:,,,/iNKORE.UI.WPF.Modern.Gallery;component/Assets/Design/Cards.dark.png")); + DialogImage.Source = new BitmapImage(new Uri("pack://application:,,,/iNKORE.UI.WPF.Modern.Gallery;component/Assets/Design/Dialog.dark.png")); + } + } + + private void OnThemeChanged(ThemeManager sender, object args) + { + UpdateSpacingImages(); + } + + private void OnElementThemeChanged(object sender, RoutedEventArgs e) + { + UpdateSpacingImages(); + } + + private void ThemeMonitorTimer_Tick(object sender, EventArgs e) + { + var currentTheme = ThemeManager.GetActualTheme(this); + if (currentTheme != _lastKnownTheme) + { + _lastKnownTheme = currentTheme; + UpdateSpacingImages(); + Debug.WriteLine($"Theme change detected: {currentTheme}"); + } + } + } +}