【问题标题】:Autoscroll ListView in WPF using MVVM使用 MVVM 在 WPF 中自动滚动 ListView
【发布时间】:2014-05-30 20:56:23
【问题描述】:

我尝试让列表在添加新行时自动滚动到末尾。

这是一个我想要集成的简单 MVVM 示例:

有一个按钮可以在单击时将行添加到列表中。

型号代码:

public class Student
{
    public string Lastname {get; set;}
    public string Firstname {get; set;}

    public Student(string lastname, string firstname) {
        this.Lastname = lastname;
        this.Firstname = firstname;

    }
}

public class StudentsModel: ObservableCollection<Student>
{
    private static object _threadLock = new Object();
    private static StudentsModel current = null;

    public static StudentsModel Current {
        get {
            lock (_threadLock)
            if (current == null)
                current = new StudentsModel();

            return current;
        }
    }

    private StudentsModel() {

        for (int i = 1; i <= 50; i++)
        {
            Student aStudent = new Student("Student " + i.ToString(), "Student " + i.ToString());
            Add(aStudent);
        }
    }

    public void AddAStudent(String lastname, string firstname) {
        Student aNewStudent = new Student(lastname, firstname);
        Add(aNewStudent);
    }
}

ViewModel 代码:

public class MainViewModel : ViewModelBase
{

    public StudentsModel Students { get; set; }

    public MainViewModel()
    {
        Students = StudentsModel.Current;
    }

    private ICommand _AddStudent;
    public ICommand AddStudent
    {
        get
        {
            if (_AddStudent == null)
            {
                _AddStudent = new DelegateCommand(delegate()
                {
                    Students.AddAStudent("New Student lastname", "New Student firstname");
                });
            }

            return _AddStudent;
        }
    }

查看代码:

<Window x:Class="demo.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:demo.Commands">
<Grid>
    <ListView  Grid.Row="2" BorderBrush="White" ItemsSource="{Binding Path=Students}"
               HorizontalAlignment="Stretch">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Lastname" DisplayMemberBinding="{Binding Path=Lastname}" />
                <GridViewColumn Header="Firstname" DisplayMemberBinding="{Binding Path=Firstname}" />

            </GridView>
        </ListView.View>
    </ListView >
    <Button Content="Add" Command="{Binding AddStudent}" Margin="601.94,36.866,96.567,419.403" />
</Grid>

谢谢

【问题讨论】:

    标签: c# wpf listview mvvm autoscroll


    【解决方案1】:

    我编写了一个简单的 AttachedProperty,用于在绑定的 ObservableCollection 发生变化时自动滚动到底部:

    public class AutoScroller : Behavior<ScrollViewer>
    {
        public object AutoScrollTrigger 
        {
            get { return (object)GetValue( AutoScrollTriggerProperty ); }
            set { SetValue( AutoScrollTriggerProperty, value ); }
        }
    
        public static readonly DependencyProperty AutoScrollTriggerProperty = 
            DependencyProperty.Register(  
                "AutoScrollTrigger", 
                typeof( object ), 
                typeof( AutoScroller ), 
                new PropertyMetadata( null, ASTPropertyChanged ) );
    
        private static void ASTPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs args )
        {
            var ts = d as AutoScroller;            
            if( ts == null )
                return;
    
            // must be attached to a ScrollViewer
            var sv = ts.AssociatedObject as ScrollViewer;
    
            // check if we are attached to an ObservableCollection, in which case we
            // will subscribe to CollectionChanged so that we scroll when stuff is added/removed
            var ncol = args.NewValue as INotifyCollectionChanged;
            // new event handler
            if( ncol != null )
                ncol.CollectionChanged += ts.OnCollectionChanged;
    
            // remove old eventhandler
            var ocol = args.OldValue as INotifyCollectionChanged;
            if( ocol != null )
                ocol.CollectionChanged -= ts.OnCollectionChanged;
    
    
            // also scroll to bottom when the bound object itself changes
            if( sv != null && ts.AutoScroll )
                sv.ScrollToBottom();
        }
    
        private void OnCollectionChanged(object sender, EventArgs args)
        {
            App.Current.Dispatcher.Invoke(delegate {
                (this.AssociatedObject as ScrollViewer).ScrollToBottom();
            });
        }
    }
    

    注意:我使用 Rx 订阅 CollectionChanged 事件,但这可以通过正常的 .NET 事件处理加上 Dispatcher.Invoke 来获得 UI 线程上的 .ScrollToBottom() 调用。

    另请注意:这个附加属性在一个名为 TouchScroller 的 Behavior 类中,它也可以做其他事情,但它可以简化为一个简单的附加属性。

    编辑:

    要使用它,您只需在 xaml 中绑定视图模型中的属性:

    <ScrollViewer ...>
        <i:Interaction.Behaviors>
            <util:TouchScroller AutoScrollTrigger="{Binding Students}" />
        </i:Interaction.Behaviors>
        ...
    </ScrollViewer>
    

    EDIT2:

    我编辑了代码以包含完整的行为。我使用行为而不是简单的带有附加行为的静态类,因为它可以访问.AssociatedObject,这是您需要调用.ScrollToBottom() 的ScrollViewer。如果不使用行为,您将不得不手动跟踪这些对象。我还删除了 Rx 订阅并添加了简单的事件处理程序和 Dispatcher.Invoke。

    这是我使用的精简和修改版本,但我还没有测试过这个版本。

    【讨论】:

    • 感谢您的回答。我是 .NET 的初学者,我不知道如何做这两件事。 - 将此代码集成到行为类中 - 使用普通 NET 事件订阅 CollectionChanged 事件。你能解释一下我该怎么做吗?
    • AutoScroller 似乎没有 OnCollectionChanged 方法
    • @Sinaesthetic 你是什么意思?它就在课程结束时。
    • 这是处理程序。 ScrollViewer 似乎没有该事件
    • 它不附加到 ScrollViewer 上的事件,仅当 AutoScrollTrigger 属性附加到实现 INotifyColectionChanged 的​​对象时才使用(参见 ASTPropertyChanged 方法)。这让它在附加的集合(edit2 中的学生)添加或删除项目时做出响应。
    猜你喜欢
    • 1970-01-01
    • 2013-06-23
    • 2010-09-17
    • 2011-07-06
    • 1970-01-01
    • 1970-01-01
    • 2011-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多