Easiest way to simulate infinite scrolling selector with UWP

那年仲夏 提交于 2020-01-14 06:06:03

问题


I am trying to design a control that would allow users to scroll up or down a range of numbers (e.g. 0-2000) with a similar feel of scrolling up or down a list.

I tried to use a ListView or GridView inside a Flyout for this, but it's simply too slow just to load a couple thousand items.

Now I'm thinking the best way might be to simply simulate the list scrolling without actually having any items (i.e. an infinite scrolling selector). Any suggestions on what control to use as a base to implement such an infinite selector?


回答1:


Here is looped virtualized scroller. It keeps only 5 items so you could add a lot of them. For selection, you may add an event on Item.

public sealed partial class MainPage
{
    private static int Count = 20;
    private static float ItemSize = 128;

    private Visual _spawnerVisual;
    private readonly List<ItemModel> _items = new List<ItemModel>();
    private readonly List<Item> _selectedItems = new List<Item>();
    private int _index;

    private static readonly Random Random = new Random();

    public MainPage()
    {
        InitializeComponent();
        Loaded += MainPage_Loaded;
    }

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        PopulateItems();
        SetInitialItems();
    }

    private void PopulateItems()
    {
        for (int i = 0; i < Count; i++)
        {
            _items.Add(new ItemModel());
        }
    }

    private void SetInitialItems()
    {
        _index = -5;
        for (int i = 0; i < 5; i++)
        {
            KillAtTop(true);
        }
        _spawnerVisual = ElementCompositionPreview.GetElementVisual(Spawner);
    }

    private void KillAtTop(bool atTop)
    {
        if (atTop)
        {
            if (_selectedItems.Count > 4)
            {
                _selectedItems.RemoveAt(0);
                Spawner.Children.RemoveAt(0);
            }

            var index = GetIndex(_index + 3);

            var item = new Item
            {
                Id = index,
                BackColor = Color.FromArgb(255, (byte)Random.Next(0, 255), (byte)Random.Next(0, 255), (byte)Random.Next(0, 255)),
                ItemSize = ItemSize,
                ItemModel = _items[index],
                OffsetY = (_index + 6) * ItemSize
            };
            _selectedItems.Add(item);
            Spawner.Children.Add(item);
            _index++;
        }
        else
        {
            if (_selectedItems.Count > 4)
            {
                _selectedItems.RemoveAt(_selectedItems.Count - 1);
                Spawner.Children.RemoveAt(Spawner.Children.Count - 1);
            }

            var index = GetIndex(_index - 3);

            var item = new Item
            {
                Id = index,
                BackColor = Color.FromArgb(255, (byte)Random.Next(0, 255), (byte)Random.Next(0, 255), (byte)Random.Next(0, 255)),
                ItemSize = ItemSize,
                ItemModel = _items[index],
                OffsetY = _index * ItemSize
            };
            _selectedItems.Insert(0, item);
            Spawner.Children.Insert(0, item);
            _index--;
        }
    }

    private int GetIndex(int index)
    {
        index = index % _items.Count;
        if (index > _items.Count - 1) return index - _items.Count;
        if (index < 0) return _items.Count + index;
        return index;
    }

    private void Slider_OnSetDelta(float deltaY)
    {
        SetPosition(deltaY);
    }

    public void SetPosition(float offsetY)
    {
        _spawnerVisual.Offset += new Vector3(0, offsetY / 10, 0);

        if (_spawnerVisual.Offset.Y > -ItemSize * _index + ItemSize)
        {
            KillAtTop(false);
        }

        if (_spawnerVisual.Offset.Y < -ItemSize * _index)
        {
            KillAtTop(true);
        }

    }
}

Just add your ItemControl and call SetPosition to scroll. Should look like this:




回答2:


I tried to use a ListView or GridView inside a Flyout for this, but it's simply too slow just to load a couple thousand items.

For your requirement, you do not have to create all the items at once. You can load the remaining items as needed via implement ISupportIncrementalLoading interface. It allows to easily create a collection in which data is loaded incrementally, when a user is about to the end of the items available on the user interface. Using it, we can obtain a fast & fluid scrolling while loading an huge set of records.

public class ItemToShow : ObservableCollection<string>, ISupportIncrementalLoading
{
    public int lastItem = 1;

    public bool HasMoreItems
    {
        get
        {
            if (lastItem == 2000) return false;
            else return true;
        }
    }

    public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
    {
        var progressBar = ((Window.Current.Content as Frame).Content as MainPage).MyProgressBar;
        CoreDispatcher coreDispatcher = Window.Current.Dispatcher;
        return Task.Run<LoadMoreItemsResult>(() =>
       {
           coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
           {
               progressBar.IsIndeterminate = true;
               progressBar.Visibility = Visibility.Visible;
           });
           List<string> items = new List<string>();
           for (var i = 0; i < count; i++)
           {
               items.Add(string.Format("Items{0}", lastItem));
               lastItem++;
               if (lastItem == 2000)
                   break;
           }
           coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
           {
               foreach (string item in items)
               {
                   this.Add(item);
                   progressBar.Visibility = Visibility.Collapsed;
                   progressBar.IsHoldingEnabled = false;
               }
           });
           return new LoadMoreItemsResult() { Count = count };
       }).AsAsyncOperation<LoadMoreItemsResult>();
    }
}

Usage

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ListView
        x:Name="ListViewMain"
        Margin="0,100,0,0"
        Padding="100,0,0,10">
        <ListView.Resources>
            <DataTemplate x:Key="DataTemplateListViewMain">
                <Grid
                    Width="100"
                    Height="100"
                    Background="LightGray">
                    <TextBlock
                        VerticalAlignment="Center"
                        FontSize="18"
                        Text="{Binding}"
                        TextAlignment="Center" />
                </Grid>
            </DataTemplate>
        </ListView.Resources>
        <ListView.ItemTemplate>
            <StaticResource ResourceKey="DataTemplateListViewMain" />
        </ListView.ItemTemplate>
    </ListView>
    <ProgressBar
        x:Name="MyProgressBar"
        Height="10"
        Margin="0,5,0,0"
        VerticalAlignment="Top"
        x:FieldModifier="public"
        Visibility="Collapsed" />
</Grid>

MainPage.xaml.cs

private void Page_Loaded(object sender, RoutedEventArgs e)
 {
     ListViewMain.ItemsSource = new ItemToShow();
 }



来源:https://stackoverflow.com/questions/45495328/easiest-way-to-simulate-infinite-scrolling-selector-with-uwp

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!