【问题标题】:UWP Combobox items scrolls to mid for no reasonUWP Combobox 项目无缘无故滚动到中间
【发布时间】: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。

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

【问题讨论】:

    标签: c# xaml uwp combobox scrollviewer


    【解决方案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 方法来改变位置。例如:

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

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

    【讨论】:

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