C# 如何在 MVVM WPF 应用程序中控制 ListBox 的滚动位置
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2292360/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to control the scroll position of a ListBox in a MVVM WPF app
提问by Jobi Joy
I have got a big ListBox with vertical scrolling enabled, my MVVM has New and Edit ICommands. I am adding new item to the end of the collection but I want the scrollbar also to auto position to the End when I call my MVVM-AddCommand. I am also making an item editable(By calling EditCommand with a particular row item) from some other part of the application so that my ListBoxItem getting in to edit mode using DataTrigger, but how will I bring that particular row(ListBoxItem) to the view by adjusting the scroll position.
我有一个启用了垂直滚动的大列表框,我的 MVVM 有新建和编辑 ICommands。我正在向集合的末尾添加新项目,但我希望滚动条也能在调用 MVVM-AddCommand 时自动定位到 End。我还在应用程序的其他部分使项目可编辑(通过使用特定行项目调用 EditCommand),以便我的 ListBoxItem 使用 DataTrigger 进入编辑模式,但是我将如何将该特定行(ListBoxItem)带到视图中通过调整滚动位置。
If I am doing it in the View side I can call listBox.ScrollInToView(lstBoxItem). But what is the best way to solve this common Scroll issue from an MVVM perspective.
如果我在视图端执行此操作,我可以调用 listBox.ScrollInToView(lstBoxItem)。但是从 MVVM 的角度解决这个常见的 Scroll 问题的最佳方法是什么。
采纳答案by Dr. WPF
I typically set IsSynchronizedWithCurrentItem="True"
on the ListBox
. Then I add a SelectionChanged
handler and always bring the selected item into view, with code like this:
我通常设置IsSynchronizedWithCurrentItem="True"
在ListBox
. 然后我添加一个SelectionChanged
处理程序并始终将所选项目带入视图,代码如下:
private void BringSelectionIntoView(object sender, SelectionChangedEventArgs e)
{
Selector selector = sender as Selector;
if (selector is ListBox)
{
(selector as ListBox).ScrollIntoView(selector.SelectedItem);
}
}
From my VM I can get the default collection view and use one of the MoveCurrent*()
methods to ensure that the item being edited is the current item.
从我的 VM 中,我可以获得默认的集合视图并使用其中一种MoveCurrent*()
方法来确保正在编辑的项目是当前项目。
CollectionViewSource.GetDefaultView(_myCollection).MoveCurrentTo(thisItem);
NOTE: Edited to use ListBox.ScrollIntoView()
to accomodate virtualization
注意:编辑ListBox.ScrollIntoView()
用于适应虚拟化
回答by Kelly
Using this in MVVM can be easily accomplished via an attached behavior like so:
在 MVVM 中使用它可以通过附加的行为轻松完成,如下所示:
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace Jarloo.Sojurn.Behaviors
{
public sealed class ScrollIntoViewBehavior : Behavior<ListBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectionChanged += ScrollIntoView;
}
protected override void OnDetaching()
{
AssociatedObject.SelectionChanged -= ScrollIntoView;
base.OnDetaching();
}
private void ScrollIntoView(object o, SelectionChangedEventArgs e)
{
ListBox b = (ListBox) o;
if (b == null)
return;
if (b.SelectedItem == null)
return;
ListBoxItem item = (ListBoxItem) ((ListBox) o).ItemContainerGenerator.ContainerFromItem(((ListBox) o).SelectedItem);
if (item != null) item.BringIntoView();
}
}
}
Then in the View ad this reference at the top:
然后在查看广告顶部的这个参考:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
And do this:
并这样做:
<ListBox ItemsSource="{Binding MyData}" SelectedItem="{Binding MySelectedItem}">
<i:Interaction.Behaviors>
<behaviors:ScrollIntoViewBehavior />
</i:Interaction.Behaviors>
</ListBox>
Now when the SelectedItem changes the behavior will do the BringIntoView() call for you.
现在,当 SelectedItem 更改时,该行为将为您执行 BringIntoView() 调用。
回答by c_wiz_kid
If the above code doesn't work for you, give this a try
如果上面的代码不适合你,试试这个
public class ListBoxExtenders : DependencyObject
{
public static readonly DependencyProperty AutoScrollToCurrentItemProperty = DependencyProperty.RegisterAttached("AutoScrollToCurrentItem", typeof(bool), typeof(ListBoxExtenders), new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged));
public static bool GetAutoScrollToCurrentItem(DependencyObject obj)
{
return (bool)obj.GetValue(AutoScrollToSelectedItemProperty);
}
public static void SetAutoScrollToCurrentItem(DependencyObject obj, bool value)
{
obj.SetValue(AutoScrollToSelectedItemProperty, value);
}
public static void OnAutoScrollToCurrentItemChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
var listBox = s as ListBox;
if (listBox != null)
{
var listBoxItems = listBox.Items;
if (listBoxItems != null)
{
var newValue = (bool)e.NewValue;
var autoScrollToCurrentItemWorker = new EventHandler((s1, e2) => OnAutoScrollToCurrentItem(listBox, listBox.Items.CurrentPosition));
if (newValue)
listBoxItems.CurrentChanged += autoScrollToCurrentItemWorker;
else
listBoxItems.CurrentChanged -= autoScrollToCurrentItemWorker;
}
}
}
public static void OnAutoScrollToCurrentItem(ListBox listBox, int index)
{
if (listBox != null && listBox.Items != null && listBox.Items.Count > index && index >= 0)
listBox.ScrollIntoView(listBox.Items[index]);
}
}
Usage in XAML
XAML 中的用法
<ListBox IsSynchronizedWithCurrentItem="True" extenders:ListBoxExtenders.AutoScrollToCurrentItem="True" ..../>
回答by VahidN
This is the attached property form of the accepted answer:
这是已接受答案的附加属性形式:
using System.Windows;
using System.Windows.Controls;
namespace CommonBehaviors
{
public static class ScrollCurrentItemIntoViewBehavior
{
public static readonly DependencyProperty AutoScrollToCurrentItemProperty =
DependencyProperty.RegisterAttached("AutoScrollToCurrentItem",
typeof(bool), typeof(ScrollCurrentItemIntoViewBehavior),
new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged));
public static bool GetAutoScrollToCurrentItem(DependencyObject obj)
{
return (bool)obj.GetValue(AutoScrollToCurrentItemProperty);
}
public static void OnAutoScrollToCurrentItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var listBox = obj as ListBox;
if (listBox == null) return;
var newValue = (bool)e.NewValue;
if (newValue)
listBox.SelectionChanged += listBoxSelectionChanged;
else
listBox.SelectionChanged -= listBoxSelectionChanged;
}
static void listBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listBox = sender as ListBox;
if (listBox == null || listBox.SelectedItem == null || listBox.Items == null) return;
listBox.Items.MoveCurrentTo(listBox.SelectedItem);
listBox.ScrollIntoView(listBox.SelectedItem);
}
public static void SetAutoScrollToCurrentItem(DependencyObject obj, bool value)
{
obj.SetValue(AutoScrollToCurrentItemProperty, value);
}
}
}
Usage:
用法:
<ListBox ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True"
behaviors:ScrollCurrentItemIntoViewBehavior.AutoScrollToCurrentItem="True">