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

Posted

技术标签:

【中文标题】如何滚动到添加到 MVVM Light 列表视图中的新项目【英文标题】:How to Scroll to a new item added to a list view in MVVM Light 【发布时间】:2015-01-20 21:06:49 【问题描述】:

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

查看:

<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");
    

【问题讨论】:

如果我猜对了,当单击“新按钮”时,您正在将所选项目设置为新元素? 是的。完全正确。 设置SelectedItem 不会自动聚焦它。请参阅this 主题以获取更多信息,如何设置它。 Herdo 并不真正适用于 MVVM。 【参考方案1】:

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

    <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:向下滚动到新添加的项目是与视图相关的,代码隐藏中没有使用任何视图模型属性,所以我相信你这样做并没有违反任何 mvvm 规则。

【讨论】:

【参考方案2】:

行为示例

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;
    

【讨论】:

有一个问题,我的新会话代码会在单击按钮的地方引发问题吗?我在哪里创建一个新的会话对象并设置 SelectedSession = newSession;? 我在哪里可以得到行为添加我在我的 上的 xaml 中收到一个错误,即 wpf 不支持 ScrollIntoViewBehavior? 我明白这一点,但 Visual Studio 2013 给了我不支持 Behaviors.Scrollintoviewbehavior 的错误。 是的,在 .net 4.5 上不是。嗯 是的,但行为的 xaml 代码抛出错误。

以上是关于如何滚动到添加到 MVVM Light 列表视图中的新项目的主要内容,如果未能解决你的问题,请参考以下文章

使用绑定时滚动 Xamarin Forms 列表视图

如何过滤列表视图中的内部子元素以及使用 Jquery 在移动应用程序中将字母滚动条添加到列表视图的任何简单方法

带有 MVVM Light 的标签栏控制器导航 Xamarin

在 WPF MVVM Light 中多次绑定到 RelayCommand

如何自动滚动到网格视图的末尾?

如何在 WPF / MVVM 中对绑定到相同实例的两个列表视图进行不同选择