让ViewModel知道用户何时使用MVVM在XAML中滚动到ListView的末尾

让ViewModel知道用户何时使用MVVM在XAML中滚动到ListView的末尾,xaml,listview,windows-phone-8.1,mvvm-light,fetch,Xaml,Listview,Windows Phone 8.1,Mvvm Light,Fetch,有没有一个简单的方法可以做到这一点?互联网上有一些示例,但不适用于MVVM Windows Phone 8.1运行时应用程序 DataContext="{Binding Main, Source={StaticResource Locator}}" Loaded="MainPage_OnLoaded"> <Grid> <ListView ItemsSource="{Binding ItemsCollection}" x:Name="L

有没有一个简单的方法可以做到这一点?互联网上有一些示例,但不适用于MVVM Windows Phone 8.1运行时应用程序

    DataContext="{Binding Main, Source={StaticResource Locator}}" Loaded="MainPage_OnLoaded">  
    <Grid>
        <ListView  ItemsSource="{Binding ItemsCollection}" x:Name="ListV"  VerticalAlignment="Center" Height="200" HorizontalAlignment="Center" >

        </ListView>
    </Grid>
</common:PageBase>

我的ListView最初有20个项目,当用户滚动到最后时,需要加载另外20个项目。但是,如何让我的ViewModel知道已到达列表视图的末尾?

如果您的案例在到达列表末尾时正在加载更多项目以创建“延迟加载”,请查看
ISupportIncrementalLoading
界面。它是一个在集合中实现的接口,
ListView
在到达结束时自动使用它加载更多项目。下面是一个例子:

    DataContext="{Binding Main, Source={StaticResource Locator}}" Loaded="MainPage_OnLoaded">  
    <Grid>
        <ListView  ItemsSource="{Binding ItemsCollection}" x:Name="ListV"  VerticalAlignment="Center" Height="200" HorizontalAlignment="Center" >

        </ListView>
    </Grid>
</common:PageBase>
我个人不喜欢这种方法,因为有一个知道如何加载数据的集合。到达列表末尾时,Tareq Ateik触发一个事件,创建了一个
扩展ListViewControl
()。因此,当触发事件时,在ViewModel中处理它(如果使用Caliburn.Micro,则可以直接“订阅”事件),或者使用消息传递向ViewModel发送消息

    DataContext="{Binding Main, Source={StaticResource Locator}}" Loaded="MainPage_OnLoaded">  
    <Grid>
        <ListView  ItemsSource="{Binding ItemsCollection}" x:Name="ListV"  VerticalAlignment="Center" Height="200" HorizontalAlignment="Center" >

        </ListView>
    </Grid>
</common:PageBase>

或者只是在代码隐藏中处理事件,并从代码隐藏中调用ViewModel上所需的方法

基于工具箱中实现的帮助器类,您可以获取对
ListView
控件内垂直
滚动条的引用,然后将事件处理程序附加到该控件,以帮助您检测何时到达
ListView
的底部,util知道将在页面的代码隐藏中实现的所有内容(这不会违反任何mvvm规则,因为这是一个与视图相关的逻辑),一旦到达底部,我们将使用mvvm light
Messenger
类向页面的视图模型发送一条
通知消息,下面介绍如何分步骤执行:

    DataContext="{Binding Main, Source={StaticResource Locator}}" Loaded="MainPage_OnLoaded">  
    <Grid>
        <ListView  ItemsSource="{Binding ItemsCollection}" x:Name="ListV"  VerticalAlignment="Center" Height="200" HorizontalAlignment="Center" >

        </ListView>
    </Grid>
</common:PageBase>
步骤1

    DataContext="{Binding Main, Source={StaticResource Locator}}" Loaded="MainPage_OnLoaded">  
    <Grid>
        <ListView  ItemsSource="{Binding ItemsCollection}" x:Name="ListV"  VerticalAlignment="Center" Height="200" HorizontalAlignment="Center" >

        </ListView>
    </Grid>
</common:PageBase>
将以下帮助器类(来自Xaml toolkit)添加到您的项目中,它有一些扩展方法,可以让您访问滚动条:

public static class VisualTreeHelperExtensions
{
    public static T GetFirstDescendantOfType<T>(this DependencyObject start) where T : DependencyObject
    {
        return start.GetDescendantsOfType<T>().FirstOrDefault();
    }

    public static IEnumerable<T> GetDescendantsOfType<T>(this DependencyObject start) where T : DependencyObject
    {
        return start.GetDescendants().OfType<T>();
    }

    public static IEnumerable<DependencyObject> GetDescendants(this DependencyObject start)
    {
        var queue = new Queue<DependencyObject>();
        var count = VisualTreeHelper.GetChildrenCount(start);

        for (int i = 0; i < count; i++)
        {
            var child = VisualTreeHelper.GetChild(start, i);
            yield return child;
            queue.Enqueue(child);
        }

        while (queue.Count > 0)
        {
            var parent = queue.Dequeue();
            var count2 = VisualTreeHelper.GetChildrenCount(parent);

            for (int i = 0; i < count2; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                yield return child;
                queue.Enqueue(child);
            }
        }
    }

    public static T GetFirstAncestorOfType<T>(this DependencyObject start) where T : DependencyObject
    {
        return start.GetAncestorsOfType<T>().FirstOrDefault();
    }

    public static IEnumerable<T> GetAncestorsOfType<T>(this DependencyObject start) where T : DependencyObject
    {
        return start.GetAncestors().OfType<T>();
    }

    public static IEnumerable<DependencyObject> GetAncestors(this DependencyObject start)
    {
        var parent = VisualTreeHelper.GetParent(start);

        while (parent != null)
        {
            yield return parent;
            parent = VisualTreeHelper.GetParent(parent);
        }
    }

    public static bool IsInVisualTree(this DependencyObject dob)
    {
        return Window.Current.Content != null && dob.GetAncestors().Contains(Window.Current.Content);
    }

    public static Rect GetBoundingRect(this FrameworkElement dob, FrameworkElement relativeTo = null)
    {
        if (relativeTo == null)
        {
            relativeTo = Window.Current.Content as FrameworkElement;
        }

        if (relativeTo == null)
        {
            throw new InvalidOperationException("Element not in visual tree.");
        }

        if (dob == relativeTo)
            return new Rect(0, 0, relativeTo.ActualWidth, relativeTo.ActualHeight);

        var ancestors = dob.GetAncestors().ToArray();

        if (!ancestors.Contains(relativeTo))
        {
            throw new InvalidOperationException("Element not in visual tree.");
        }

        var pos =
            dob
                .TransformToVisual(relativeTo)
                .TransformPoint(new Point());
        var pos2 =
            dob
                .TransformToVisual(relativeTo)
                .TransformPoint(
                    new Point(
                        dob.ActualWidth,
                        dob.ActualHeight));

        return new Rect(pos, pos2);
    }
}
    DataContext="{Binding Main, Source={StaticResource Locator}}" Loaded="MainPage_OnLoaded">  
    <Grid>
        <ListView  ItemsSource="{Binding ItemsCollection}" x:Name="ListV"  VerticalAlignment="Center" Height="200" HorizontalAlignment="Center" >

        </ListView>
    </Grid>
</common:PageBase>
步骤3

    DataContext="{Binding Main, Source={StaticResource Locator}}" Loaded="MainPage_OnLoaded">  
    <Grid>
        <ListView  ItemsSource="{Binding ItemsCollection}" x:Name="ListV"  VerticalAlignment="Center" Height="200" HorizontalAlignment="Center" >

        </ListView>
    </Grid>
</common:PageBase>
在页面代码隐藏中添加所需的处理程序

    DataContext="{Binding Main, Source={StaticResource Locator}}" Loaded="MainPage_OnLoaded">  
    <Grid>
        <ListView  ItemsSource="{Binding ItemsCollection}" x:Name="ListV"  VerticalAlignment="Center" Height="200" HorizontalAlignment="Center" >

        </ListView>
    </Grid>
</common:PageBase>
    public MainPage()
    {
        InitializeComponent();
    }


    private void MainPage_OnLoaded(object sender, RoutedEventArgs e)
    {
        var scrollViewer = ListV.GetFirstDescendantOfType<ScrollViewer>();
        var scrollbars = scrollViewer.GetDescendantsOfType<ScrollBar>().ToList();
        var verticalBar = scrollbars.FirstOrDefault(x => x.Orientation == Orientation.Vertical);
        if (verticalBar != null)
            verticalBar.Scroll += BarScroll;
    }
    async void BarScroll(object sender, ScrollEventArgs e)
    {
        if (e.ScrollEventType != ScrollEventType.EndScroll) return;

        var bar = sender as ScrollBar;
        if (bar == null)
            return;

        System.Diagnostics.Debug.WriteLine("Scrolling ended");

        if (e.NewValue >= bar.Maximum)
        {
            System.Diagnostics.Debug.WriteLine("We are at the bottom");
            Messenger.Default.Send<NotificationMessage>(new NotificationMessage("LoadMore"));                
        }
        else
        {
            System.Diagnostics.Debug.WriteLine("We are away from the bottom");
        }
    }
}
步骤4

    DataContext="{Binding Main, Source={StaticResource Locator}}" Loaded="MainPage_OnLoaded">  
    <Grid>
        <ListView  ItemsSource="{Binding ItemsCollection}" x:Name="ListV"  VerticalAlignment="Center" Height="200" HorizontalAlignment="Center" >

        </ListView>
    </Grid>
</common:PageBase>
在页面的视图模型中,添加在收到通知消息后将新项目加载到ListView的代码

    DataContext="{Binding Main, Source={StaticResource Locator}}" Loaded="MainPage_OnLoaded">  
    <Grid>
        <ListView  ItemsSource="{Binding ItemsCollection}" x:Name="ListV"  VerticalAlignment="Center" Height="200" HorizontalAlignment="Center" >

        </ListView>
    </Grid>
</common:PageBase>
public class MainViewModel : ViewModelBase
{
    private ObservableCollection<String> _itemsCollection = new ObservableCollection<string>()
    {
        "Item 1",
        "Item 2",
        "Item 3",
        "Item 4"        
    };

    public ObservableCollection<String> ItemsCollection
    {
        get
        {
            return _itemsCollection;
        }

        set
        {
            if (_itemsCollection == value)
            {
                return;
            }

            _itemsCollection = value;
            RaisePropertyChanged();
        }
    }
    public async Task LoadMoreItems()
    {
        ItemsCollection.Add("Item " + ItemsCollection.Count);
        ItemsCollection.Add("Item " + ItemsCollection.Count);
        ItemsCollection.Add("Item " + ItemsCollection.Count);
        ItemsCollection.Add("Item " + ItemsCollection.Count);
        ItemsCollection.Add("Item " + ItemsCollection.Count);

    }
    public MainViewModel()
    {   
        Messenger.Default.Register<NotificationMessage>(this, async m =>
        {
            switch (m.Notification)
            {
                case "LoadMore":
                    await LoadMoreItems();
                    break;

            }
        });
    }  
}

剩下的就保留原样。

@Igor Kulman好的,我会看一看,谢谢。但实际上,我唯一需要的是在到达ListView的末尾时向我的ViewModel发送一些东西,然后从那里开始,我使用不同的startindex执行另一个api调用。然后使用ExtendedListViewControl,当列表的末尾被删除时,它会触发一个事件reached@IgorKulman可以在MVVM灯中使用MoreDataRequested吗?类似于:@IgorKulman在Windows Phone 8.1运行时中没有EvenToCommand,在这里它们使用行为,但“MoreDataRequested”不是受支持的事件名,如加载、点击或取消选中。“MoreDataRequested”=ExtendedListView.MoreDataRequested和所有其他事件,如“Loaded”、“Tapped”=UIElement。(事件名称)。嘿,这看起来很棒是的!但当我滚动到底部时,什么也没发生。应用程序从未出现在“async void BarScroll(object sender,ScrollEventArgs e)”中。顺便说一句,这段代码会自动滚动到列表视图的底部?嗨@拜仁,显然我在发布答案之前已经尝试过我描述的方法,它在我这方面非常有效。唯一的区别是我在windows phone应用程序上使用的是winrt store应用程序(由于我的机器存在一些硬件限制),并且我发布的代码应该可以在商店和手机应用程序上运行,我会将示例项目上载到oneDrive,以便您可以获得一个更好的示例项目进行自定义,然后告诉我问题是否仍然存在。如果(出纳员%2)==0)和(verticalBar.Value>=verticalBar.Maximum)),因此现在它只触发一次事件:D。无论如何,感谢您提供的代码!
    DataContext="{Binding Main, Source={StaticResource Locator}}" Loaded="MainPage_OnLoaded">  
    <Grid>
        <ListView  ItemsSource="{Binding ItemsCollection}" x:Name="ListV"  VerticalAlignment="Center" Height="200" HorizontalAlignment="Center" >

        </ListView>
    </Grid>
</common:PageBase>