最简单的方法来模拟无限滚动选择器与UWP
问题描述:
我想设计一个控件,允许用户滚动上下滚动向上或向下列表的类似感觉的数字范围(例如0-2000)。最简单的方法来模拟无限滚动选择器与UWP
我试过在Flyout
里面使用ListView
或GridView
这个,但是加载几千个项目实在太慢了。
现在我想最好的方式可能是简单地模拟列表滚动而不实际有任何项目(即无限滚动选择器)。任何有关使用什么控制作为实现这样一个无限选择器的基础的建议?
答
我试图在Flyout中使用ListView或GridView来实现此目的,但是它仅仅为加载几千个项目太慢。
根据您的要求,您不必一次创建所有项目。 您可以通过执行ISupportIncrementalLoading
接口按需要加载其余项目。它允许轻松地创建一个集合,在用户即将到达用户界面上可用项目的末尾时,以增量方式加载数据。使用它,我们可以获得一个快速的流体滚动,同时加载一大组记录。
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>();
}
}
用法
<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();
}
答
这里环绕虚拟滚轮。它只保留5个项目,所以你可以添加很多。为了进行选择,您可以在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);
}
}
}
只需添加您的ItemControl并调用SetPosition进行滚动。 应该是这样的:
+0
非常有趣的方法!虽然希望看到GitHub回购。 :P –
这是之前,我真的希望它会工作,我从来没有见过一个非常有趣的方法。不幸的是,它仍然悬挂着我的“Flyout”。通过设置断点,我发现'LoadMoreItemsAsync'方法在快速连续调用中首先使用'count = 1',然后是3,12,48,192等,直到整个序列没有被展开,甚至没有我尝试滚动列表。 – glopes
更有趣的是,如果我将'ListView'放在'Flyout'之外,这种奇怪的行为就不会发生,所以看起来渴望的展开来自'Flyout'类。 – glopes
是的,我没有在Flyout中放置listview。我认为在Flyout中渲染太多物品是非理性的设计。 –