Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Settings] Fix flyout position when ComboBox is aligned to the right #31

Merged
merged 7 commits into from
Feb 19, 2023
Original file line number Diff line number Diff line change
@@ -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<ComboBox>
{
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<object> eventHandler = null;
eventHandler = new EventHandler<object>((_, _) =>
{
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);
Copy link
Collaborator Author

@gaviny82 gaviny82 Feb 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本来应该用ItemContainerGenerator获取容器宽度的,但是这一组API好像已经弃用了(ItemContainerGenerator Class | MS Learn),使用GenerateNext也总是返回null,不确定是不是控件加载顺序的问题。

现在的方法是new TextBlock然后获取DesiredSize加上一个固定的宽度,对于string类型的ComboBoxItem没问题,如果设置了ItemTemplate就不能用。

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();
}
}
}
5 changes: 5 additions & 0 deletions Natsurainko.FluentLauncher/Views/Pages/Settings/Launch.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
<Page.DataContext>
<vm:Launch />
Expand Down Expand Up @@ -55,6 +57,9 @@
VerticalAlignment="Center"
ItemsSource="{Binding GameFolders}"
SelectedItem="{Binding CurrentGameFolder, Mode=TwoWay}">
<i:Interaction.Behaviors>
<behaviors:SetComboBoxWidthFromItemsBehavior SetComboBoxWidthFromItems="True"/>
</i:Interaction.Behaviors>
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="x:String">
<TextBlock Text="{Binding}" TextTrimming="CharacterEllipsis" />
Expand Down