英文:
Xamarin Forms: Scroll to is not working when reversing the collection data
问题
我在页面顶部有3个选项卡,我使用ListView
列出了一些数据。当我点击选项卡时,我需要滚动到ListView
的末尾,也就是滚动到最后一项。在将集合设置给ListView
之前,我将其反转。我使用以下代码来滚动到最后一项:
settingslistview.ScrollTo(tab1settingList[tab1settingList.Count - 1], ScrollToPosition.End, false);
如果不反转数据,这个方法可以正常工作。但是当反转数据时,滚动就不起作用了。
我在下面添加了我的演示项目的示例代码:
MainPage.xaml:
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ScrollToDemo.MainPage">
<StackLayout>
<Grid
Margin="10,0,10,0"
VerticalOptions="StartAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="33*" />
<ColumnDefinition Width="34*" />
<ColumnDefinition Width="33*" />
</Grid.ColumnDefinitions>
<StackLayout
Grid.Column="0"
HorizontalOptions="FillAndExpand"
Margin="2,0"
VerticalOptions="FillAndExpand"
Orientation="Vertical">
<Label
x:Name="tab1_label"
FontAttributes="Bold"
Text="Tab1"
Style="{StaticResource style1}"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
Tapped="Tab1Tapped"
NumberOfTapsRequired="1">
</TapGestureRecognizer>
</StackLayout.GestureRecognizers>
</StackLayout>
<StackLayout
Grid.Column="1"
HorizontalOptions="FillAndExpand"
Margin="2,0"
VerticalOptions="FillAndExpand"
Orientation="Vertical">
<Label
x:Name="tab2_label"
IsEnabled="False"
Text="Tab2"
Style="{StaticResource style1}"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
Tapped="Tab2Tapped"
NumberOfTapsRequired="1">
</TapGestureRecognizer>
</StackLayout.GestureRecognizers>
</StackLayout>
<StackLayout
Grid.Column="2"
HorizontalOptions="FillAndExpand"
Margin="2,0"
VerticalOptions="FillAndExpand"
Orientation="Vertical">
<Label
x:Name="tab3_label"
IsEnabled="False"
Text="Tab3"
Style="{StaticResource style1}"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
Tapped="Tab3Tapped"
NumberOfTapsRequired="1">
</TapGestureRecognizer>
</StackLayout.GestureRecognizers>
</StackLayout>
</Grid>
<ListView
x:Name="settingslistview"
HasUnevenRows="True"
SelectionMode="None"
SeparatorColor="#cecece"
SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Frame
HasShadow="False"
Padding="8"
CornerRadius="{OnIdiom Phone=20, Tablet=30}"
BorderColor="#bdbdbd"
Margin="5"
BackgroundColor="{Binding BGColor}">
<StackLayout
VerticalOptions="FillAndExpand"
Margin="5,0,5,0"
Orientation="Horizontal">
<Label
Text="{Binding Title}"
HorizontalOptions="StartAndExpand"
VerticalOptions="CenterAndExpand"
TextColor="{Binding TextColor}">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>18</OnIdiom.Phone>
<OnIdiom.Tablet>27</OnIdiom.Tablet>
<OnIdiom.Desktop>18</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
<Frame.HeightRequest>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>30</OnIdiom.Phone>
<OnIdiom.Tablet>45</OnIdiom.Tablet>
<OnIdiom.Desktop>30</OnIdiom.Desktop>
</OnIdiom>
</Frame.HeightRequest>
</Frame>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Footer>
<Label/>
</ListView.Footer>
</ListView>
</StackLayout>
</ContentPage>
MainPage.xaml.cs:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace ScrollToDemo
{
public partial class MainPage : ContentPage
{
public ObservableCollection<SettingPageItems> tab1settingList;
public ObservableCollection<SettingPageItems> tab2settingList;
public ObservableCollection<SettingPageItems> tab3settingList;
public MainPage()
{
InitializeComponent();
tab1settingList = new ObservableCollection<SettingPageItems>();
tab2settingList = new ObservableCollection<SettingPageItems>();
tab3settingList = new ObservableCollection<SettingPageItems>();
Tab1Items();
Tab2Items();
Tab3Items();
}
private void Tab1Tapped(object sender, EventArgs e)
{
tab1_label.FontAttributes = FontAttributes.Bold;
tab2_label.FontAttributes = FontAttributes.None;
tab3_label.FontAttributes = FontAttributes.None;
settingslistview.ItemsSource = tab1settingList.Reverse();
if (tab1settingList.Count != 0)
{
Device.BeginInvokeOnMainThread(() =>
{
settingslistview.ScrollTo(tab1settingList[tab1settingList.Count - 1], ScrollToPosition.End, false);
});
}
}
private void Tab2Tapped(object sender, EventArgs e)
{
tab1_label.FontAttributes = FontAttributes.None;
tab2_label.FontAttributes = FontAttributes.Bold;
tab3_label.FontAttributes = FontAttributes.None;
settingslistview.ItemsSource = tab2settingList.Reverse();
if (tab2settingList.Count != 0)
{
Device.BeginInvokeOnMainThread(() =>
{
settingslistview.ScrollTo(tab2settingList[tab2settingList.Count - 1], ScrollToPosition.End, false);
});
}
}
private void Tab3Tapped(object sender, EventArgs e)
{
tab1_label.FontAttributes = FontAttributes.None;
tab2_label.FontAttributes = FontAttributes.None;
tab3_label.FontAttributes = FontAttributes.Bold;
settingslistview.ItemsSource = tab3settingList.Reverse();
if (tab3settingList.Count != 0)
{
Device.BeginInvokeOnMainThread(() =>
{
settingslistview.ScrollTo(tab3settingList[tab3settingList.Count - 1], ScrollToPosition.End, false);
});
}
}
public void Tab1Items()
{
for(int i =1; i<= 20; i++)
{
tab1settingList.Add(new SettingPageItems() { Title = "Option"+i, BGColor = Color.FromHex("#e4e4e4"), TextColor = Color.Black });
}
settingslistview.ItemsSource = tab1settingList.Reverse();
}
public void Tab2Items()
{
for (int i = 21; i <= 40; i++)
{
tab2settingList.Add(new SettingPageItems() { Title = "Option" + i, BGColor = Color.FromHex("#FFEBEE"), TextColor = Color.Black });
}
}
public void Tab3Items()
{
for (int i = 41; i <= 60; i++)
{
tab3settingList.Add(new SettingPageItems() { Title = "Option" + i, BGColor = Color.FromHex("#E8EAF6"), TextColor = Color.Black });
}
}
}
public class SettingPageItems : INotifyPropertyChanged
{
public string Title { get; set; }
private Color bgColor;
public Color BGColor
{
set
{
if (value != null)
{
bgColor = value;
NotifyPropertyChanged();
}
}
get
{
return bgColor;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private Color textColor;
public Color TextColor
{
set
{
if (value != null)
{
textColor = value;
NotifyPropertyChanged();
}
}
get
{
return textColor;
}
}
private string imageSource;
public string ImageSource
{
set
{
if (value != null)
{
imageSource = value;
NotifyPropertyChanged();
}
}
get
{
return imageSource;
}
}
}
}
App.xaml:
<?xml version="1.0" encoding="utf-8"?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ScrollToDemo.App">
<Application.Resources>
<Style x:Key="style1" TargetType="Label">
<Setter Property="HorizontalTextAlignment" Value="Center"/>
<Setter Property="HorizontalOptions" Value="CenterAndExpand"/>
<Setter Property="VerticalTextAlignment" Value="Center"/>
<Setter Property="TextColor" Value="Black" />
<Setter Property="FontSize">
<OnIdiom
x:TypeArguments="x:Double"
Phone="18"
Tablet="27"
Desktop="18"/>
</Setter>
</Style>
</Application.Resources>
</Application>
我在这里上传了一个示例演示。当切换到其他选项卡时,我需要滚动到列表视图的最后一项。当前切换时,它位于页面顶部。
英文:
I have 3 tabs on the top of the page where I am listing some data using ListView
. When I tap on a tab I need to scroll to the end of the listview, I mean I need to scroll to the last item of the ListView
. I am reversing the collection before setting it to the ListView
. I used below codes to scroll to last item:
settingslistview.ScrollTo(tab1settingList[tab1settingList.Count - 1], ScrollToPosition.End, false);
This is working fine if we not reverse the data. But when reversing the scrolling is not working.
I am adding the sample codes of my demo project below:
MainPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ScrollToDemo.MainPage">
<StackLayout>
<Grid
Margin="10,0,10,0"
VerticalOptions="StartAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="33*" />
<ColumnDefinition Width="34*" />
<ColumnDefinition Width="33*" />
</Grid.ColumnDefinitions>
<StackLayout
Grid.Column="0"
HorizontalOptions="FillAndExpand"
Margin="2,0"
VerticalOptions="FillAndExpand"
Orientation="Vertical">
<Label
x:Name="tab1_label"
FontAttributes="Bold"
Text="Tab1"
Style="{StaticResource style1}"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
Tapped="Tab1Tapped"
NumberOfTapsRequired="1">
</TapGestureRecognizer>
</StackLayout.GestureRecognizers>
</StackLayout>
<StackLayout
Grid.Column="1"
HorizontalOptions="FillAndExpand"
Margin="2,0"
VerticalOptions="FillAndExpand"
Orientation="Vertical">
<Label
x:Name="tab2_label"
IsEnabled="False"
Text="Tab2"
Style="{StaticResource style1}"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
Tapped="Tab2Tapped"
NumberOfTapsRequired="1">
</TapGestureRecognizer>
</StackLayout.GestureRecognizers>
</StackLayout>
<StackLayout
Grid.Column="2"
HorizontalOptions="FillAndExpand"
Margin="2,0"
VerticalOptions="FillAndExpand"
Orientation="Vertical">
<Label
x:Name="tab3_label"
IsEnabled="False"
Text="Tab3"
Style="{StaticResource style1}"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
Tapped="Tab3Tapped"
NumberOfTapsRequired="1">
</TapGestureRecognizer>
</StackLayout.GestureRecognizers>
</StackLayout>
</Grid>
<ListView
x:Name="settingslistview"
HasUnevenRows="True"
SelectionMode="None"
SeparatorColor="#cecece"
SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Frame
HasShadow="False"
Padding="8"
CornerRadius="{OnIdiom Phone=20, Tablet=30}"
BorderColor="#bdbdbd"
Margin="5"
BackgroundColor="{Binding BGColor}">
<StackLayout
VerticalOptions="FillAndExpand"
Margin="5,0,5,0"
Orientation="Horizontal">
<Label
Text="{Binding Title}"
HorizontalOptions="StartAndExpand"
VerticalOptions="CenterAndExpand"
TextColor="{Binding TextColor}">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>18</OnIdiom.Phone>
<OnIdiom.Tablet>27</OnIdiom.Tablet>
<OnIdiom.Desktop>18</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
<Frame.HeightRequest>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>30</OnIdiom.Phone>
<OnIdiom.Tablet>45</OnIdiom.Tablet>
<OnIdiom.Desktop>30</OnIdiom.Desktop>
</OnIdiom>
</Frame.HeightRequest>
</Frame>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Footer>
<Label/>
</ListView.Footer>
</ListView>
</StackLayout>
</ContentPage>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace ScrollToDemo
{
public partial class MainPage : ContentPage
{
public ObservableCollection<SettingPageItems> tab1settingList;
public ObservableCollection<SettingPageItems> tab2settingList;
public ObservableCollection<SettingPageItems> tab3settingList;
public MainPage()
{
InitializeComponent();
tab1settingList = new ObservableCollection<SettingPageItems>();
tab2settingList = new ObservableCollection<SettingPageItems>();
tab3settingList = new ObservableCollection<SettingPageItems>();
Tab1Items();
Tab2Items();
Tab3Items();
}
private void Tab1Tapped(object sender, EventArgs e)
{
tab1_label.FontAttributes = FontAttributes.Bold;
tab2_label.FontAttributes = FontAttributes.None;
tab3_label.FontAttributes = FontAttributes.None;
settingslistview.ItemsSource = tab1settingList.Reverse();
if (tab1settingList.Count != 0)
{
Device.BeginInvokeOnMainThread(() =>
{
settingslistview.ScrollTo(tab1settingList[tab1settingList.Count - 1], ScrollToPosition.End, false);
});
}
}
private void Tab2Tapped(object sender, EventArgs e)
{
tab1_label.FontAttributes = FontAttributes.None;
tab2_label.FontAttributes = FontAttributes.Bold;
tab3_label.FontAttributes = FontAttributes.None;
settingslistview.ItemsSource = tab2settingList.Reverse();
if (tab2settingList.Count != 0)
{
Device.BeginInvokeOnMainThread(() =>
{
settingslistview.ScrollTo(tab2settingList[tab2settingList.Count - 1], ScrollToPosition.End, false);
});
}
}
private void Tab3Tapped(object sender, EventArgs e)
{
tab1_label.FontAttributes = FontAttributes.None;
tab2_label.FontAttributes = FontAttributes.None;
tab3_label.FontAttributes = FontAttributes.Bold;
settingslistview.ItemsSource = tab3settingList.Reverse();
if (tab3settingList.Count != 0)
{
Device.BeginInvokeOnMainThread(() =>
{
settingslistview.ScrollTo(tab3settingList[tab3settingList.Count - 1], ScrollToPosition.End, false);
});
}
}
public void Tab1Items()
{
for(int i =1; i<= 20; i++)
{
tab1settingList.Add(new SettingPageItems() { Title = "Option"+i, BGColor = Color.FromHex("#e4e4e4"), TextColor = Color.Black });
}
settingslistview.ItemsSource = tab1settingList.Reverse();
}
public void Tab2Items()
{
for (int i = 21; i <= 40; i++)
{
tab2settingList.Add(new SettingPageItems() { Title = "Option" + i, BGColor = Color.FromHex("#FFEBEE"), TextColor = Color.Black });
}
}
public void Tab3Items()
{
for (int i = 41; i <= 60; i++)
{
tab3settingList.Add(new SettingPageItems() { Title = "Option" + i, BGColor = Color.FromHex("#E8EAF6"), TextColor = Color.Black });
}
}
}
}
public class SettingPageItems : INotifyPropertyChanged
{
public string Title { get; set; }
private Color bgColor;
public Color BGColor
{
set
{
if (value != null)
{
bgColor = value;
NotifyPropertyChanged();
}
}
get
{
return bgColor;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private Color textColor;
public Color TextColor
{
set
{
if (value != null)
{
textColor = value;
NotifyPropertyChanged();
}
}
get
{
return textColor;
}
}
private string imageSource;
public string ImageSource
{
set
{
if (value != null)
{
imageSource = value;
NotifyPropertyChanged();
}
}
get
{
return imageSource;
}
}
}
App.xaml
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ScrollToDemo.App">
<Application.Resources>
<Style x:Key="style1" TargetType="Label">
<Setter Property="HorizontalTextAlignment" Value="Center"/>
<Setter Property="HorizontalOptions" Value="CenterAndExpand"/>
<Setter Property="VerticalTextAlignment" Value="Center"/>
<Setter Property="TextColor" Value="Black" />
<Setter Property="FontSize">
<OnIdiom
x:TypeArguments="x:Double"
Phone="18"
Tablet="27"
Desktop="18"/>
</Setter>
</Style>
</Application.Resources>
</Application>
I have uploaded a sample demo here. I need to scroll to the last item of the listview when switching to other tabs. Currently it is on top of the page when switching.
答案1
得分: 1
看一下下面的代码:
private void Tab1Tapped(object sender, EventArgs e)
{
...
settingslistview.ItemsSource = tab1settingList.Reverse();
if (tab1settingList.Count != 0)
{
Device.BeginInvokeOnMainThread(() =>
{
settingslistview.ScrollTo(tab1settingList[tab1settingList.Count - 1], ScrollToPosition.End, false);
});
}
}
你使用了 tab1settingList.Reverse()
并将其设置为 ItemsSource
。Reverse
方法返回一个反转的 IEnumerable
,但它不会修改原始集合。这意味着 tab1settingList
的顺序没有改变。所以当你使用 ScrollTo
方法时,tab1settingList
中的最后一项仍然是 option20
。
你可以像这样做:
private void Tab1Tapped(object sender, EventArgs e)
{
...
ObservableCollection<SettingPageItems> reversedtab1settingList = new ObservableCollection<SettingPageItems>(tab1settingList.Reverse());
settingslistview.ItemsSource = reversedtab1settingList;
if (tab1settingList.Count != 0)
{
Device.BeginInvokeOnMainThread(() =>
{
settingslistview.ScrollTo(reversedtab1settingList[reversedtab1settingList.Count - 1], ScrollToPosition.End, false);
});
}
}
更多信息,请参考 Enumerable.Reverse<TSource>(IEnumerable<TSource>) 方法。
希望能对你有所帮助!
英文:
Look at the following code:
private void Tab1Tapped(object sender, EventArgs e)
{
...
settingslistview.ItemsSource = tab1settingList.Reverse();
if (tab1settingList.Count != 0)
{
Device.BeginInvokeOnMainThread(() =>
{
settingslistview.ScrollTo(tab1settingList[tab1settingList.Count - 1], ScrollToPosition.End, false);
});
}
}
You use tab1settingList.Reverse();
and set it to ItemsSource. The Reverse method returns a reversed IEnumerable but it doesn't modify the origin collection. That means tab1settingList
's order is not changed. So when you use scrollto
method, the last item in tab1settingList
is still option20.
You may do something like this:
private void Tab1Tapped(object sender, EventArgs e)
{
...
ObservableCollection<SettingPageItems> reversedtab1settingList = new ObservableCollection<SettingPageItems>(tab1settingList.Reverse());
settingslistview.ItemsSource = reversedtab1settingList;
if (tab1settingList.Count != 0)
{
Device.BeginInvokeOnMainThread(() =>
{
settingslistview.ScrollTo(reversedtab1settingList[reversedtab1settingList.Count - 1], ScrollToPosition.End, false);
});
}
}
For more info, you may refer to Enumerable.Reverse<TSource>(IEnumerable<TSource>) Method
Hope it helps!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论