如何滚动到添加到 MVVM Light 列表视图的新项目

How to Scroll to a new item added to a list view in MVVM Light

我有一个项目,其中有一个 ListView,当用户单击“新建”按钮时,新对象将添加到 ListView 的底部。我试过使用内容样式 class 但没有用。我只需要一些可以滚动到所选项目的东西。下面是我的代码:

查看:

<ListView Margin="103,0,0,10" ScrollViewer.CanContentScroll="True"  Height="87" SelectedItem="{Binding SelectedSession, Mode=TwoWay}" ItemsSource="{Binding SessionCollection}">
    <ListView.Resources>
        <Style TargetType="{x:Type GridViewColumnHeader}">
            <Setter Property="HorizontalContentAlignment" Value="Left" />
        </Style>
    </ListView.Resources>
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Session Name" Width="180"  DisplayMemberBinding="{Binding SessionName, Mode=TwoWay}" />
            <GridViewColumn Header="Operator Name" Width="180" DisplayMemberBinding="{Binding OperatorName, Mode=TwoWay}"/>
            <GridViewColumn Header="Session Date" Width="180" DisplayMemberBinding="{Binding SessionDate, Mode=TwoWay}"/>
        </GridView>
    </ListView.View>
</ListView>

查看新按钮的模型代码:

public void NewSession()
{
    Session newSession = new Session();

    SessionCollection.Add(newSession);
    SelectedSession = newSession;
    SessionDate = DateTime.Now;
    StartTime = DateTime.Now;
    EndTime = DateTime.Now;
    ProjectManager.Instance.CurrentSession = null;
}

public ObservableCollection<Session> SessionCollection
{
    get
    {
        if (currentDatabaseProj.Sessions == null)
        {
            return currentDatabaseProj.Sessions;
        }
        else
        {
            return currentDatabaseProj.Sessions;
        }
    }
    private set
    {
        currentDatabaseProj.Sessions = value;
        IsNewProjClicked = true;
        this.RaisePropertyChanged("SessionCollection");
    }
}

具有行为的工作示例

xaml

<Window x:Class="ListView.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:listView="clr-namespace:ListView"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Button Content="Add New" Grid.Row="0" Click="Button_Click"></Button>
    <ListView Grid.Row="1" ItemsSource="{Binding MySessionList}" SelectedItem="{Binding SelectedSession, Mode=TwoWay}">
        <i:Interaction.Behaviors>
            <listView:ScrollIntoViewBehavior/>
        </i:Interaction.Behaviors>
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Name" Width="200" DisplayMemberBinding="{Binding Name, Mode=TwoWay}" />
            </GridView>
        </ListView.View>
    </ListView>
</Grid>
</Window>

cs

public partial class MainWindow : Window
{
    private Viewmodel _data;
    public MainWindow()
    {
        InitializeComponent();
        _data = new Viewmodel();
        this.DataContext = _data;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        _data.AddNew();
    }
}

public class Viewmodel : INotifyPropertyChanged
{
    private MySession _selectedSession;
    public ObservableCollection<MySession> MySessionList { get; set; }

    public Viewmodel()
    {
        this.MySessionList = new ObservableCollection<MySession>();

        //fill with some data
        for (int i = 0; i < 20; i++)
        {
            this.MySessionList.Add(new MySession(){Name =  "Index : " + i});
        }
    }

    public MySession SelectedSession
    {
        get { return _selectedSession; }
        set
        {
            _selectedSession = value; 
            OnPropertyChanged();
        }
    }

    //should be a Command for MVVM but for quick and dirty
    public void AddNew()
    {
        var toAdd = new MySession() {Name = "New Added " + DateTime.Now.ToLongTimeString()};
        this.MySessionList.Add(toAdd);
        this.SelectedSession = toAdd;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class MySession
{
    public string Name { get; set; }
}

//ADD REFERENCE:  System.Windows.Interactivity
public class ScrollIntoViewBehavior : Behavior<ListBox>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
    }

    private void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var ctrl = sender as ListBox;

        if (ctrl == null)
            return;


        if (ctrl.SelectedItem != null)
        {
            ctrl.Dispatcher.BeginInvoke(
                DispatcherPriority.Render,
                new Action(() =>
                {
                    ctrl.UpdateLayout();
                    ctrl.ScrollIntoView(ctrl.SelectedItem);
                }));
        }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
    }
}

后面代码中的一个简单处理程序应该可以解决问题 (我简化了您的代码以使其清晰)

    <Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <ListView x:Name="listView" Margin="10" ScrollViewer.CanContentScroll="True"   SelectedItem="{Binding SelectedSession, Mode=TwoWay}" ItemsSource="{Binding SessionCollection}">
        <ListView.Resources>
            <Style TargetType="{x:Type GridViewColumnHeader}">
                <Setter Property="HorizontalContentAlignment" Value="Left" />
            </Style>
        </ListView.Resources>
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Session Name" Width="180"  DisplayMemberBinding="{Binding}" />   
            </GridView>
        </ListView.View>
    </ListView>
    <Button Click="UIElement_NewElementHandler" Grid.Row="1" Content="Add Item" Command="{Binding AddItem}"></Button>
</Grid>

并在后面的视图代码中添加以下处理程序:

  private void UIElement_NewElementHandler(object sender, RoutedEventArgs routedEventArgs)
    {            
        var border = VisualTreeHelper.GetChild(listView, 0) as Decorator;            
        var scrollViewer = border.Child as ScrollViewer;
        scrollViewer.ScrollToBottom();
    }

ps:向下滚动到新添加的项目与 ViewRelated 相关,代码隐藏中使用了视图模型属性的 none,因此我相信您不会违反任何 mvvm 规则那。