【问题标题】:Getting the first visible group in ListView does not work properly获取 ListView 中的第一个可见组无法正常工作
【发布时间】:2016-06-17 20:00:09
【问题描述】:

我创建了一个示例应用程序,其中我有一个分组的 ListView,并且在滚动列表视图时我想获得 UI 中的顶部可见组(第一个可见组)。但是,当我继续在基于触摸的设备上滚动时,只要我将手指放在设备上,它就会开始显示下一组(在顶部可见组旁边)。如果我移开手指,它会更正值。 这是我的示例应用程序:https://onedrive.live.com/redir?resid=91B2B9D9EA21A110!615&authkey=!AKJV0b_q7g-YZF4&ithint=file%2czip

示例代码:

public sealed partial class MainPage : Page
    {
        public ViewModel MyVM = new ViewModel();
        public MainPage()
        {
            this.InitializeComponent();

            lv.SizeChanged += (s, e) =>
            {
                ScrollViewer sv = FindVisualChildren<ScrollViewer>(lv).FirstOrDefault();
                if (sv != null)
                {
                    sv.ViewChanged += (ss, ee) =>
                    {
                        IEnumerable<TextBlock> tblocks = FindVisualChildren<TextBlock>(lv).Where(x => x.Name == "tbHeader");
                        if (tblocks != null)
                        {
                            foreach (TextBlock tblock in tblocks)
                            {
                                if (IsVisibileToUser(tblock, sv))
                                {
                                    first.Text = tblock.Text;
                                    break;
                                }
                            }
                        }
                    };
                }
            };
        }

        private static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
        {
            if (depObj != null)
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                    if (child != null && child is T)
                    {
                        yield return (T)child;
                    }

                    foreach (T childOfChild in FindVisualChildren<T>(child))
                    {
                        yield return childOfChild;
                    }
                }
            }
        }

        private bool IsVisibileToUser(FrameworkElement element, FrameworkElement container)
        {
            if (element == null || container == null)
                return false;

            if (element.Visibility != Visibility.Visible)
                return false;

            Rect elementBounds = element.TransformToVisual(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
            Rect containerBounds = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);

            return (elementBounds.Top < containerBounds.Bottom && elementBounds.Bottom > containerBounds.Top);
        }
    }

public class ClassA
    {
        public DateTime DateTimePropertyOfClassA { get; set; }
    }

public class ViewModel
    {
        public ViewModel()
        {
            //return a grouped collection:
            Grouped = from x in CollectionOfClassA group x by x.DateTimePropertyOfClassA into grp orderby grp.Key select grp;
        }

        public IList<ClassA> CollectionOfClassA { get; set; } = new List<ClassA>()
        {
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-01-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-03-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-04-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-05-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-06-01")},
            new ClassA(){ DateTimePropertyOfClassA =DateTime.Parse("2016-07-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-08-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-09-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-11-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-12-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-03-01")},
            new ClassA(){ DateTimePropertyOfClassA =DateTime.Parse("2016-06-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-01-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-03-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-03-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-03-01")},
            new ClassA(){ DateTimePropertyOfClassA = DateTime.Parse("2016-03-01")},
            new ClassA(){ DateTimePropertyOfClassA =DateTime.Parse("2016-06-01")}
        };

        public IEnumerable<object> Grouped { get; }
    }

Xaml:

<Page.Resources>
    <CollectionViewSource x:Name="cvs"
                      IsSourceGrouped="True"
                      Source="{x:Bind MyVM.Grouped, Mode=OneWay}"/>
</Page.Resources>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>
    <TextBlock Name="first" HorizontalAlignment="Center" Visibility="Visible" FontSize="12"></TextBlock>
    <ListView Grid.Row="1" Name="lv" ItemsSource="{Binding Source={StaticResource cvs}}" ScrollViewer.VerticalScrollBarVisibility="Visible">
        <ListView.GroupStyle>
            <GroupStyle>
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock x:Name="tbHeader" FontSize="15" FontWeight="Bold" Text="{Binding Key}"/>
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
            </GroupStyle>
        </ListView.GroupStyle>
    </ListView>
</Grid>

我可以做些什么来改进此功能?

【问题讨论】:

  • 考虑添加一些代码 sn-p 而不是链接来下载您的示例应用程序,这将鼓励人们帮助您。

标签: listview uwp


【解决方案1】:

ScrollViewer.ViewChanged event 在滚动过程中会被触发多次,我们无法控制在您的代码中调用IsVisibileToUser 方法的次数。

Rect elementBounds 将在滚动期间动态更改,您可以尝试以下解决方法:将ScrollViewer 中的区域缩小到只有TextBlock 的大小,例如:

private bool IsVisibileToUser(FrameworkElement element, FrameworkElement container)
{
    if (element == null || container == null)
        return false;

    if (element.Visibility != Visibility.Visible)
        return false;

    Rect elementBounds = element.TransformToVisual(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
    Rect containerBounds = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);

    return (elementBounds.Top <= element.ActualHeight && elementBounds.Bottom > containerBounds.Top);
}

【讨论】:

  • 我实际上发布了错误的代码。我已经返回 (elementBounds.Top containerBounds.Top);它仍然有同样的问题。问题是当下一个组被滚动到视图中时,仍然位于顶部的文本块将从 IsVisibleToUser 返回 false,因为它不符合条件 'elementBounds.Bottom > containerBounds.Top',因为 elementBounds.Bottom 为负数且 containerBounds.Top是 0
  • @AshishAgrawal,我知道,我测试了您发布的示例,而不是您发布的代码。请注意,我将返回条件更改为return (elementBounds.Top &lt;= element.ActualHeight &amp;&amp; elementBounds.Bottom &gt; containerBounds.Top);,我只更改了此代码。
  • @AshishAgrawal,这是怎么回事?你测试过我的代码吗?还有什么问题吗?
  • 抱歉延迟回复。我刚刚测试过,它在我的示例应用程序中运行良好。我会继续将它集成到我的实际项目中,看看它是否也能在那里工作。我会更新你。谢谢。
  • 很抱歉再次打扰您。我遇到了这个代码的一个问题,如果我有一个很长的列表(假设我修改 ViewModel 类中的代码以使 CollectionOfClassA 填充有许多组,以便有足够的组来进行轻弹滚动,即点击并滚动一次,然后移开手指,让它随着惯性滚动)。它没有正确更新顶部可见项目。当我向上滚动以查看列表开头的项目时,它会特别发生。当我向下滚动以查看最后的项目时,它几乎可以正常工作。任何帮助表示赞赏。谢谢。
猜你喜欢
  • 1970-01-01
  • 2014-12-07
  • 1970-01-01
  • 2016-03-07
  • 2011-02-24
  • 1970-01-01
  • 2018-01-18
  • 2013-01-10
  • 1970-01-01
相关资源
最近更新 更多