"Infinite Scrolling" 在 ListView 中 / 避免重入滚动事件
"Infinite Scrolling" in a ListView / Avoiding Re-entrant Scroll Events
我的目标是在 WPF ListView
中模拟 "infinite scrolling"。我用一些不太理想的方法完成了这个任务,我相信有更好的方法来完成它。
by "infinite scrolling" 我的意思是:
假设 ListView
有 20 个项目(已排序 1, 2, 3, 4, ... 17, 18, 19, 20
)。当用户向下滚动一个项目时,ListView
顶部的项目被移除并放置在 ListView
的末尾,因此项目的顺序是 2, 3, 4, 5, ... 18, 19, 20, 1
。现在,如果用户向下滚动两个项目,顶部的两个项目将被删除并放在末尾,因此项目的顺序是 4, 5, 6, 7, ... 20, 1, 2, 3
。现在,类似地,如果用户向上滚动一个项目,ListView
底部的项目将被删除并放在开头,因此项目的顺序是 3, 4, 5, 6, ... 19, 20, 1, 2
.
我已经通过分配给 ScrollViewer
的 ScrollChanged
事件的以下功能完成了这项任务,我希望成为 "infinite":
// sv - the ScrollViewer to which this event handler is listening
// lv - the ListView associated with "sv"
bool handle_scroll = true;
private void inf_scroll(object sender, ScrollChangedEventArgs e) {
if (handle_scroll) {
for (int i = 0; i < e.VerticalChange; i++) {
object tmp = lv.Items[0];
lv.Items.RemoveAt(0);
lv.Items.Add(tmp);
handle_scroll = false;
}
for (int i = 0; i > e.VerticalChange; i--) {
object tmp = lv.Items[lv.Items.Count - 1];
lv.Items.RemoveAt(lv.Items.Count - 1);
lv.Items.Insert(0, tmp);
handle_scroll = false;
}
if(!handle_scroll){
sv.ScrollToVerticalOffset(sv.VerticalOffset - e.VerticalChange);
}
}
else {
handle_scroll = true;
}
}
注意变量 handle_scroll
。我把它放在适当的位置,因为对 sv.ScrollToVerticalOffset
的调用将导致整个 inf_scroll
函数在不存在的情况下被递归调用。
我 知道 在 ScrollChanged
事件处理程序中滚动 ScrollViewer
是不好的做法,所以这就是我问的原因:是否有更好的方法来做到这一点?如何防止对 inf_scroll
的递归调用?有没有更好的方法模拟"infinite scrolling"?
虽然它没有明确避免在 ScrollChanged
处理程序中滚动,但您可以尝试以下操作:
// sv - the ScrollViewer to which this event handler is listening
// lv - the ListView associated with "sv"
private void inf_scroll(object sender, ScrollChangedEventArgs e) {
for (int i = 0; i < e.VerticalChange; i++) {
object tmp = lv.Items[0];
lv.Items.RemoveAt(0);
lv.Items.Add(tmp);
}
for (int i = 0; i > e.VerticalChange; i--) {
object tmp = lv.Items[lv.Items.Count - 1];
lv.Items.RemoveAt(lv.Items.Count - 1);
lv.Items.Insert(0, tmp);
}
lv.ScrollChanged -= inf_scroll; // remove the handler temporarily
sv.ScrollToVerticalOffset(sv.VerticalOffset - e.VerticalChange);
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>{
sv.ScrollChanged += inf_scroll; // add the handler back after the scrolling has occurred to avoid recursive scrolling
}));
}
我的目标是在 WPF ListView
中模拟 "infinite scrolling"。我用一些不太理想的方法完成了这个任务,我相信有更好的方法来完成它。
by "infinite scrolling" 我的意思是:
假设 ListView
有 20 个项目(已排序 1, 2, 3, 4, ... 17, 18, 19, 20
)。当用户向下滚动一个项目时,ListView
顶部的项目被移除并放置在 ListView
的末尾,因此项目的顺序是 2, 3, 4, 5, ... 18, 19, 20, 1
。现在,如果用户向下滚动两个项目,顶部的两个项目将被删除并放在末尾,因此项目的顺序是 4, 5, 6, 7, ... 20, 1, 2, 3
。现在,类似地,如果用户向上滚动一个项目,ListView
底部的项目将被删除并放在开头,因此项目的顺序是 3, 4, 5, 6, ... 19, 20, 1, 2
.
我已经通过分配给 ScrollViewer
的 ScrollChanged
事件的以下功能完成了这项任务,我希望成为 "infinite":
// sv - the ScrollViewer to which this event handler is listening
// lv - the ListView associated with "sv"
bool handle_scroll = true;
private void inf_scroll(object sender, ScrollChangedEventArgs e) {
if (handle_scroll) {
for (int i = 0; i < e.VerticalChange; i++) {
object tmp = lv.Items[0];
lv.Items.RemoveAt(0);
lv.Items.Add(tmp);
handle_scroll = false;
}
for (int i = 0; i > e.VerticalChange; i--) {
object tmp = lv.Items[lv.Items.Count - 1];
lv.Items.RemoveAt(lv.Items.Count - 1);
lv.Items.Insert(0, tmp);
handle_scroll = false;
}
if(!handle_scroll){
sv.ScrollToVerticalOffset(sv.VerticalOffset - e.VerticalChange);
}
}
else {
handle_scroll = true;
}
}
注意变量 handle_scroll
。我把它放在适当的位置,因为对 sv.ScrollToVerticalOffset
的调用将导致整个 inf_scroll
函数在不存在的情况下被递归调用。
我 知道 在 ScrollChanged
事件处理程序中滚动 ScrollViewer
是不好的做法,所以这就是我问的原因:是否有更好的方法来做到这一点?如何防止对 inf_scroll
的递归调用?有没有更好的方法模拟"infinite scrolling"?
虽然它没有明确避免在 ScrollChanged
处理程序中滚动,但您可以尝试以下操作:
// sv - the ScrollViewer to which this event handler is listening
// lv - the ListView associated with "sv"
private void inf_scroll(object sender, ScrollChangedEventArgs e) {
for (int i = 0; i < e.VerticalChange; i++) {
object tmp = lv.Items[0];
lv.Items.RemoveAt(0);
lv.Items.Add(tmp);
}
for (int i = 0; i > e.VerticalChange; i--) {
object tmp = lv.Items[lv.Items.Count - 1];
lv.Items.RemoveAt(lv.Items.Count - 1);
lv.Items.Insert(0, tmp);
}
lv.ScrollChanged -= inf_scroll; // remove the handler temporarily
sv.ScrollToVerticalOffset(sv.VerticalOffset - e.VerticalChange);
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>{
sv.ScrollChanged += inf_scroll; // add the handler back after the scrolling has occurred to avoid recursive scrolling
}));
}