UWP Combobox 项目无缘无故滚动到中间

Posted

技术标签:

【中文标题】UWP Combobox 项目无缘无故滚动到中间【英文标题】:UWP Combobox items scrolls to mid for no reason 【发布时间】:2021-01-31 05:49:51 【问题描述】:

我注意到,在一个附加了一些 itemsource 的组合框中,当没有选定的项目时,它倾向于滚动到项目的中间而不是从顶部(第一个项目)开始,当一个项目被选中时它会滚动有时滚动到所选项目。

所以我想在没有选择任何项目时滚动到第一个项目。为此,我尝试了以下修复。

代码

    private void ComboBoxKeyboardSelectionBehavior_DropDownOpened(object sender, object e)
    
        var comboBox = (ComboBox) sender;
        if(comboBox.SelectedIndex == -1)
        
            //var scrollviewer = comboBox.GetScrollViewer();
            //scrollviewer.ChangeView(0, 0, null);
            //var allItems = comboBox.Items.ToList();
            //var cccc = comboBox.Items.Count;
            //var firstItem = allItems.First();
            var ci = comboBox.ContainerFromIndex(0) as ComboBoxItem;
            if (ci != null)
            
                ci.StartBringIntoView();
            
        
        else
        
            var ci = comboBox.ContainerFromIndex(comboBox.SelectedIndex) as ComboBoxItem;
            if (ci != null)
            
                ci.StartBringIntoView();
            
        
    

WinRTXamlToolkit.Controls.Extensions 为我提供了获取滚动查看器的选项,然后尝试使用 ChangeView 方法,但这不起作用。我成功地从列表中获得了第一项并使用了 ContainerFromItem 方法,但它返回了 null。所以我也尝试了 ContainerFromIndex 方法并将索引提供为 0 因为这应该是第一项,但它也不起作用。

如果是选定的项目(else 语句),它与ContainerFromIndex(comboBox.SelectedIndex) 一起工作正常,但只是为了测试当我用 ContainerFromItem 尝试它时它返回 null。

仅供参考,此事件是组合框样式的附加行为,但这无关紧要,因为行为在选定项目场景中完美运行。

【问题讨论】:

【参考方案1】:

如果您想在没有选中项时滚动到第一项,则需要更改ComboBoxDropDown 的行为而不是ScrollViewerComboBox

ComboBoxDropDown实际上是Popup,而Popup的显示位置在后面的代码中定义,我们无法访问。一种解决方法是找到Popup并在打开时重新定位它,但是使用这种方法我们需要在每次打开时计算VerticalOffset属性,并且VerticalOffset的不同值有很多场景。

因此,我们建议您自定义一个控件,其行为类似于ComboBox,并且在未选择任何项目时定位到第一个项目。例如:

创建UserControl:

<Button x:Name="rootButton" BorderBrush="Gray" BorderThickness="2" Click="Button_Click" MinWidth="80" Background="Transparent" Padding="0">
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
      Width="Binding ElementName=rootButton, Path=ActualWidth">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="32" />
    </Grid.ColumnDefinitions>
    <TextBlock Text="x:Bind selectedItem, Mode=OneWay" Grid.Column="0" VerticalAlignment="Center" FontSize="15" HorizontalAlignment="Center" />
    <FontIcon Grid.Column="1" FontSize="12" FontFamily="Segoe MDL2 Assets" Glyph="&#xE0E5;" HorizontalAlignment="Right"
           Margin="0,10,10,10" VerticalAlignment="Center" />
</Grid>
<FlyoutBase.AttachedFlyout>
    <MenuFlyout Placement="Bottom" x:Name="menuFlyout">
        <MenuFlyoutItem Text="Item 1" Click="MenuFlyoutItem_Click" />
        <MenuFlyoutItem Text="Item 2" Click="MenuFlyoutItem_Click" />
        <MenuFlyoutItem Text="Item 3" Click="MenuFlyoutItem_Click" />
        <MenuFlyoutItem Text="Item 4" Click="MenuFlyoutItem_Click" />
        <MenuFlyoutItem Text="Item 5" Click="MenuFlyoutItem_Click" />
    </MenuFlyout>
</FlyoutBase.AttachedFlyout>

还有这个 UserControl 后面的代码:

public sealed partial class CustomComboBox : UserControl, INotifyPropertyChanged

    public CustomComboBox()
    
        this.InitializeComponent();
        selectedItem = "";
    

    private string _selectedItem;

    public string selectedItem
    
        get  return _selectedItem; 

        set
        
            _selectedItem = value;
            if (PropertyChanged != null)
            
                PropertyChanged(this, new PropertyChangedEventArgs("selectedItem"));
           
        
    

    public event PropertyChangedEventHandler PropertyChanged;

    private void MenuFlyoutItem_Click(object sender, RoutedEventArgs e)
    
        var item = sender as MenuFlyoutItem;
        selectedItem = item.Text;
    

    private void Button_Click(object sender, RoutedEventArgs e)
    
        FlyoutBase.ShowAttachedFlyout(sender as Button);
    

您可以像这样在其他页面中使用CustomComboBox

<local:CustomComboBox VerticalAlignment="Center" HorizontalAlignment="Center" />

默认情况下,此CustomComboBox 将在其下方显示其DropDown 列表。

此外,您还可以考虑使用ListBox等其他控件直接替换ComboBox来避免这种情况。

更新:

目前ComboBox控件并没有提供相关的API来设置DropDownStyle中的起始位置,但是我们有一个变通方法是在DropDown中获取ScrollViewer,然后调用ChangeView 方法来改变位置。例如:

    定义一个继承自ComboBox 类的自定义组合框以获取ScrollViewer
    public class TestComboBox : ComboBox
    
        public ScrollViewer InternalScrollViewer;
        protected override void OnApplyTemplate()
        
            InternalScrollViewer = GetTemplateChild("ScrollViewer") as ScrollViewer;
            base.OnApplyTemplate();
        
    
    ComboBoxKeyboardSelectionBehavior_DropDownOpened事件处理程序中使用Task.Delay()初始化项目后,调用ChangeView方法改变位置:
    await Task.Delay(50);
    comboBox.InternalScrollViewer.ChangeView(0, 0, 1);

注意,在 XAML 中使用 TestComboBox 而不是 ComboBox

【讨论】:

您的问题解决了吗?如果还没有解决,请随时联系我们。 我希望在样式和行为中找到一个解决方案,这样我们就不需要制作自定义控件,因为我们有大量的组合框,并且制作自定义控件可能不是我们场景的最佳主意。 我们已经有了组合框的样式,我们在 xaml 中做了大量的自定义内容以及一些附加到它的行为。 我更新了答案,您可以检查一下新的解决方法是否可以解决您的问题。

以上是关于UWP Combobox 项目无缘无故滚动到中间的主要内容,如果未能解决你的问题,请参考以下文章

ListBox 总是滚动到打开 ComboBox 的项目

滚动到 UWP 的 ListView 中的新项目

UWP WinUI TreeView 以编程方式滚动到项目

c# UWP TreeView 在 ItemDrag 上自动滚动

Kendo React ComboBox 不会滚动到键入的值

UWP ComboBox 不再具有打开/关闭动画(内部版本 14393)