From ab76f24eb9ec4e5afb92a0f480e1ba0d64f9e288 Mon Sep 17 00:00:00 2001 From: Jincheng You Date: Fri, 17 Feb 2023 19:20:39 +0000 Subject: [PATCH 1/7] Mitigate flyout position issue when ComboBox is aligned to the right --- Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml b/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml index 2bfc2468..a7089620 100644 --- a/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml +++ b/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml @@ -45,13 +45,14 @@ @@ -151,7 +152,7 @@ Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" - FlowDirection="RightToLeft" + Width="500" ItemsSource="{Binding JavaRuntimes}" SelectedItem="{Binding CurrentJavaRuntime, Mode=TwoWay}"> From d6bea84972fdf17bd65e8575e7f7eaa71433705b Mon Sep 17 00:00:00 2001 From: Jincheng You Date: Sat, 18 Feb 2023 16:13:29 +0000 Subject: [PATCH 2/7] Initial work on calculating ComboBox width based on its content --- .../SetComboBoxWidthFromItemsBehavior.cs | 115 ++++++++++++++++++ .../Views/Pages/Settings/Launch.xaml | 5 + 2 files changed, 120 insertions(+) create mode 100644 Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs diff --git a/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs b/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs new file mode 100644 index 00000000..060d8038 --- /dev/null +++ b/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Threading; +using Microsoft.UI.Dispatching; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Automation.Peers; +using Microsoft.UI.Xaml.Automation.Provider; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.Xaml.Interactivity; +using Windows.Foundation; + +namespace Natsurainko.FluentLauncher.Behaviors +{ + class SetComboBoxWidthFromItemsBehavior : Behavior + { + public static readonly DependencyProperty SetComboBoxWidthFromItemsProperty = + DependencyProperty.RegisterAttached + ( + "SetComboBoxWidthFromItems", + typeof(bool), + typeof(SetComboBoxWidthFromItemsBehavior), + new PropertyMetadata(false, OnSetComboBoxWidthFromItemsPropertyChanged) + ); + + public bool SetComboBoxWidthFromItems + { + get => (bool)GetValue(SetComboBoxWidthFromItemsProperty); + set => SetValue(SetComboBoxWidthFromItemsProperty, value); + } + + protected override void OnAttached() + { + AssociatedObject.Loaded += OnComboBoxLoaded; + } + + private static void OnSetComboBoxWidthFromItemsPropertyChanged( + DependencyObject dpo, + DependencyPropertyChangedEventArgs e) + { + ComboBox comboBox = ((SetComboBoxWidthFromItemsBehavior)dpo).AssociatedObject; + bool newValue = (bool)e.NewValue; + bool oldValue = (bool)e.OldValue; + + if (comboBox != null && newValue != oldValue) + { + if (newValue == true) + { + comboBox.Loaded += OnComboBoxLoaded; + } + else + { + comboBox.Loaded -= OnComboBoxLoaded; + } + } + } + + private static void OnComboBoxLoaded(object sender, RoutedEventArgs e) + { + ComboBox comboBox = (ComboBox) sender; + + comboBox.DispatcherQueue.TryEnqueue(() => { comboBox.SetWidthFromItems(); }); + } + } + + public static class ComboBoxExtensionMethods + { + public static void SetWidthFromItems(this ComboBox comboBox) + { + double comboBoxWidth = 60; // size of combobox without content + + // Create the peer and provider to expand the comboBox in code behind. + ComboBoxAutomationPeer peer = new ComboBoxAutomationPeer(comboBox); + IExpandCollapseProvider provider = (IExpandCollapseProvider)peer.GetPattern(PatternInterface.ExpandCollapse); + + EventHandler eventHandler = null; + eventHandler = new EventHandler((_, _) => + { + if (comboBox.IsDropDownOpen //&& + /*comboBox.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated*/) + { + double width = 0; + + // Get the container of the item + foreach (var item in comboBox.Items) + { + //TODO: combobox.items are not necessarily ComboBoxItems + // var cont = comboBox.ContainerFromIndex(0); + // var a = comboBox.ItemContainerGenerator.ContainerFromItem(item); + TextBlock comboBoxItem = new TextBlock { Text = item.ToString() }; + comboBoxItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + if (comboBoxItem.DesiredSize.Width > width) + { + width = comboBoxItem.DesiredSize.Width; + } + } + comboBox.Width = comboBoxWidth + width; + // Remove the event handler. + // comboBox.ItemContainerGenerator.StatusChanged -= eventHandler; + comboBox.DropDownOpened -= eventHandler; + provider.Collapse(); + } + }); + + // comboBox.ItemContainerGenerator.StatusChanged += eventHandler; + comboBox.DropDownOpened += eventHandler; + + // Expand the comboBox to generate all its ComboBoxItem's. + provider.Expand(); + } + } +} diff --git a/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml b/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml index a7089620..51fabdee 100644 --- a/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml +++ b/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml @@ -8,7 +8,9 @@ xmlns:local="using:Natsurainko.FluentLauncher.Views.Pages.Settings" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="using:Natsurainko.FluentLauncher.ViewModels.Pages.Settings" + xmlns:behaviors ="using:Natsurainko.FluentLauncher.Behaviors" Background="Transparent" + xmlns:i="using:Microsoft.Xaml.Interactivity" mc:Ignorable="d"> @@ -55,6 +57,9 @@ VerticalAlignment="Center" ItemsSource="{Binding GameFolders}" SelectedItem="{Binding CurrentGameFolder, Mode=TwoWay}"> + + + From df73bf225b3118c94ea3475c871bb4b4df1be042 Mon Sep 17 00:00:00 2001 From: Jincheng You Date: Sat, 18 Feb 2023 23:43:53 +0000 Subject: [PATCH 3/7] Determine Width of ComboBox using ItemContainerGenerator --- .../SetComboBoxWidthFromItemsBehavior.cs | 61 ++++++------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs b/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs index 060d8038..bfa427f3 100644 --- a/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs +++ b/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs @@ -61,55 +61,34 @@ private static void OnSetComboBoxWidthFromItemsPropertyChanged( private static void OnComboBoxLoaded(object sender, RoutedEventArgs e) { ComboBox comboBox = (ComboBox) sender; - - comboBox.DispatcherQueue.TryEnqueue(() => { comboBox.SetWidthFromItems(); }); + comboBox.DispatcherQueue.TryEnqueue(() => { SetComboBoxWidth(comboBox); }); } - } - public static class ComboBoxExtensionMethods - { - public static void SetWidthFromItems(this ComboBox comboBox) + /// + /// Set the width of a ComboBox to the longest item in its drop down menu + /// + /// Target + private static void SetComboBoxWidth(ComboBox comboBox) { - double comboBoxWidth = 60; // size of combobox without content - - // Create the peer and provider to expand the comboBox in code behind. - ComboBoxAutomationPeer peer = new ComboBoxAutomationPeer(comboBox); - IExpandCollapseProvider provider = (IExpandCollapseProvider)peer.GetPattern(PatternInterface.ExpandCollapse); + // Open ComboBox drop down and prepare ItemContainerGenerator + comboBox.IsDropDownOpen = true; + comboBox.ItemContainerGenerator.StartAt(new GeneratorPosition(0, 0), GeneratorDirection.Forward, true); - EventHandler eventHandler = null; - eventHandler = new EventHandler((_, _) => + double maxWidth = 0; + ComboBoxItem? item; + while ((item = comboBox.ItemContainerGenerator.GenerateNext(out _) as ComboBoxItem) != null) { - if (comboBox.IsDropDownOpen //&& - /*comboBox.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated*/) + item.Measure(new Windows.Foundation.Size(double.PositiveInfinity, double.PositiveInfinity)); + var size = item.DesiredSize; + if (size.Width > maxWidth) { - double width = 0; - - // Get the container of the item - foreach (var item in comboBox.Items) - { - //TODO: combobox.items are not necessarily ComboBoxItems - // var cont = comboBox.ContainerFromIndex(0); - // var a = comboBox.ItemContainerGenerator.ContainerFromItem(item); - TextBlock comboBoxItem = new TextBlock { Text = item.ToString() }; - comboBoxItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); - if (comboBoxItem.DesiredSize.Width > width) - { - width = comboBoxItem.DesiredSize.Width; - } - } - comboBox.Width = comboBoxWidth + width; - // Remove the event handler. - // comboBox.ItemContainerGenerator.StatusChanged -= eventHandler; - comboBox.DropDownOpened -= eventHandler; - provider.Collapse(); + maxWidth = size.Width; } - }); - - // comboBox.ItemContainerGenerator.StatusChanged += eventHandler; - comboBox.DropDownOpened += eventHandler; + } - // Expand the comboBox to generate all its ComboBoxItem's. - provider.Expand(); + comboBox.Width = maxWidth + 20; // This constant adds more space to include the drop down button and paddings + comboBox.ItemContainerGenerator.Stop(); + comboBox.IsDropDownOpen = false; } } } From 35e43b74a1dee7b2b34dba91a727aa53d0b1cfe7 Mon Sep 17 00:00:00 2001 From: Jincheng You Date: Sun, 19 Feb 2023 00:01:49 +0000 Subject: [PATCH 4/7] Fix event registration to ensure correct behavior when SetComboBoxWidthFromItemsProperty is changed --- .../SetComboBoxWidthFromItemsBehavior.cs | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs b/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs index bfa427f3..a6324dfb 100644 --- a/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs +++ b/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs @@ -11,7 +11,7 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; using Microsoft.Xaml.Interactivity; -using Windows.Foundation; +using Windows.Foundation.Collections; namespace Natsurainko.FluentLauncher.Behaviors { @@ -35,13 +35,26 @@ public bool SetComboBoxWidthFromItems protected override void OnAttached() { AssociatedObject.Loaded += OnComboBoxLoaded; + AssociatedObject.Items.VectorChanged += Items_VectorChanged; + } + + public void Items_VectorChanged(IObservableVector sender, IVectorChangedEventArgs e) + { + SetComboBoxWidth(AssociatedObject); + } + + private static void OnComboBoxLoaded(object sender, RoutedEventArgs e) + { + SetComboBoxWidth((ComboBox)sender); } private static void OnSetComboBoxWidthFromItemsPropertyChanged( DependencyObject dpo, DependencyPropertyChangedEventArgs e) { - ComboBox comboBox = ((SetComboBoxWidthFromItemsBehavior)dpo).AssociatedObject; + SetComboBoxWidthFromItemsBehavior behavior = (SetComboBoxWidthFromItemsBehavior)dpo; + ComboBox comboBox = behavior.AssociatedObject; + bool newValue = (bool)e.NewValue; bool oldValue = (bool)e.OldValue; @@ -50,20 +63,17 @@ private static void OnSetComboBoxWidthFromItemsPropertyChanged( if (newValue == true) { comboBox.Loaded += OnComboBoxLoaded; + comboBox.Items.VectorChanged += behavior.Items_VectorChanged; + } else { comboBox.Loaded -= OnComboBoxLoaded; + comboBox.Items.VectorChanged -= behavior.Items_VectorChanged; } } } - private static void OnComboBoxLoaded(object sender, RoutedEventArgs e) - { - ComboBox comboBox = (ComboBox) sender; - comboBox.DispatcherQueue.TryEnqueue(() => { SetComboBoxWidth(comboBox); }); - } - /// /// Set the width of a ComboBox to the longest item in its drop down menu /// From f11b9747b70b77b2b4c139fca0d6159f34324e34 Mon Sep 17 00:00:00 2001 From: Jincheng You Date: Sun, 19 Feb 2023 00:07:13 +0000 Subject: [PATCH 5/7] Fix appearance issue when there is no content --- .../Behaviors/SetComboBoxWidthFromItemsBehavior.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs b/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs index a6324dfb..528cc67b 100644 --- a/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs +++ b/Natsurainko.FluentLauncher/Behaviors/SetComboBoxWidthFromItemsBehavior.cs @@ -96,7 +96,8 @@ private static void SetComboBoxWidth(ComboBox comboBox) } } - comboBox.Width = maxWidth + 20; // This constant adds more space to include the drop down button and paddings + maxWidth += 20; // This constant adds more space to include the drop down button and paddings + comboBox.Width = maxWidth > 70 ? maxWidth : 70; // Ensures a MinWidth of 70 when there is no content; Less width will result in issues with appearance comboBox.ItemContainerGenerator.Stop(); comboBox.IsDropDownOpen = false; } From 405d8c5ce07e0aef1176410cd74a7ff008f18e6b Mon Sep 17 00:00:00 2001 From: Jincheng You Date: Sun, 19 Feb 2023 00:33:17 +0000 Subject: [PATCH 6/7] Bind MaxWidth to a grid column --- Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml b/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml index 51fabdee..ac28f67c 100644 --- a/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml +++ b/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml @@ -43,7 +43,7 @@ - + From 676e25099cf6fe638cbf44e831365975bc96d03a Mon Sep 17 00:00:00 2001 From: Jincheng You Date: Sun, 19 Feb 2023 00:34:58 +0000 Subject: [PATCH 7/7] Remove width setting for debug --- Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml b/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml index ac28f67c..430bfe6d 100644 --- a/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml +++ b/Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml @@ -53,7 +53,6 @@