简体   繁体   English

Scroll ListView To Top On ItemsSource Changed被意外调用

[英]Scroll ListView To Top On ItemsSource Changed Is Called Unexpectedly

I've got a wpf user control project set up similar (for the most part) to the WPF Apps With The Model-View-ViewModel Design Pattern article . 我有一个wpf用户控件项目设置类似(大部分) 使用Model-View-ViewModel设计模式WPF应用程序文章 Basically, I have a headered content control, that houses a tab control. 基本上,我有一个带有标签控件的带标题的内容控件。 The first tab holds search results and any additional tabs are opened by clicking on an item from the search results to see a more detailed view of the item. 第一个选项卡保存搜索结果,通过单击搜索结果中的项目打开任何其他选项卡,以查看项目的更详细视图。

My main problem is that I reuse the search results tab any time someone types in search criteria and runs the search command. 我的主要问题是,只要有人输入搜索条件并运行搜索命令,我就会重复使用搜索结果选项卡。 The scrollviewer does not scroll to the top when the search results collection is repopulated (if the user had scrolled down the list). 重新填充搜索结果集合时滚动查看器不会滚动到顶部(如果用户向下滚动列表)。

Originally I was repopulating the search result item collection in a way that caused the CollectionChanged event to fire. 最初我以一种导致CollectionChanged事件触发的方式重新填充搜索结果项集合。 Then I looked into using an attached property to allow scrolling to the top and it looked as if replacing the entire collection was the way to go (since ItemsControl does not implement the ICollectionNotifyChanged interface). 然后我查看使用附加属性允许滚动到顶部,看起来好像替换整个集合是要走的路(因为ItemsControl没有实现ICollectionNotifyChanged接口)。

So I made the change and all seemed well. 所以我做了改变,一切都很顺利。 Running a new search replaced the search results item collection, which fired the attached property and scrolled the listview back to the top. 运行新搜索替换了搜索结果项集合,该集合触发了附加属性并将列表视图滚动回顶部。 However, I now have a problem that whenever I move to another tab to view a detailed view of an item, it fires the attached property and scrolls the search results on the first tab to the top . 但是,我现在遇到一个问题, 每当我移动到另一个选项卡查看项目的详细视图时, 它会触发附加属性并将第一个选项卡上的搜索结果滚动到顶部 I'm not sure how to address this situation. 我不知道如何解决这种情况。

public static class ScrollToTopBehavior
{
    public static readonly DependencyProperty ScrollToTopProperty =
        DependencyProperty.RegisterAttached
            (
                "ScrollToTop",
                typeof (bool),
                typeof (ScrollToTopBehavior),
                new UIPropertyMetadata(false, OnScrollToTopPropertyChanged)
            );

    public static bool GetScrollToTop(DependencyObject obj)
    {
        return (bool) obj.GetValue(ScrollToTopProperty);
    }

    public static void SetScrollToTop(DependencyObject obj, bool value)
    {
        obj.SetValue(ScrollToTopProperty, value);
    }

    private static void OnScrollToTopPropertyChanged(DependencyObject dpo,
                                                     DependencyPropertyChangedEventArgs e)
    {
        var itemsControl = dpo as ItemsControl;
        if (itemsControl == null) return;

        DependencyPropertyDescriptor dependencyPropertyDescriptor =
            DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof (ItemsControl));
        if (dependencyPropertyDescriptor == null) return;
        if ((bool) e.NewValue)
        {
            dependencyPropertyDescriptor.AddValueChanged(itemsControl, ItemsSourceChanged);
        }
        else
        {
            dependencyPropertyDescriptor.RemoveValueChanged(itemsControl, ItemsSourceChanged);
        }
    }

    private static void ItemsSourceChanged(object sender, EventArgs e)
    {
        var itemsControl = sender as ItemsControl;
        EventHandler eventHandler = null;
        eventHandler = delegate
            {
                if (itemsControl != null &&
                    itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
                {
                    var scrollViewer = VisualTreeHelpers.FindChild<ScrollViewer>(itemsControl);
                    scrollViewer.ScrollToTop();
                    itemsControl.ItemContainerGenerator.StatusChanged -= eventHandler;
                }
            };
        if (itemsControl != null) itemsControl.ItemContainerGenerator.StatusChanged += eventHandler;
    }
}

I've attached this property to a search results user control like so: 我已将此属性附加到搜索结果用户控件,如下所示:

<ListView ...some properties left out for brevity...
              DataContext="{StaticResource SearchResultsViewSource}"
              IsSynchronizedWithCurrentItem="True"
              ItemsSource="{Binding}"
              SelectionMode="Single"
              behaviours:ScrollToTopBehavior.ScrollToTop="True">
...etc...

The search results control is housed inside of another user control that is using a headered content control (I'll provide the details if needed). 搜索结果控件位于另一个使用带标题内容控件的用户控件内(如果需要,我将提供详细信息)。

This is a hack and doesn't resolve your real issue but it will resolve the symptoms. 这是一个黑客,并没有解决你的真正问题,但它将解决症状。
You can detach the your behavior on tabControl.Selecting event, so when you start or go to the search results tab you attach the behavior/property and when you go to other tabs you detach/unregister the behavior. 您可以在tabControl.Selecting事件上分离您的行为,因此当您启动或转到搜索结果选项卡时,您将附加行为/属性,当您转到其他选项卡时,您可以分离/取消注册该行为。

should look like this: 应该是这样的:

yourTab.SelecnionChanged+= new TabControlCancelEventHandler(tabControl_Selecting);

void tabControl_SelecnionChange(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    var current = (sender as TabControl).SelectedValue;

    //check if current is search tab then
    {
      ScrollToTopBehavior.SetScrollToTop(yourList,true);
    }
    else
    {
      ScrollToTopBehavior.SetScrollToTop(yourList,false);
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM