none
关于GridView数据分组后快速定位到组的问题 RRS feed

  • 问题

  • 想实现一种功能:

        把数据源绑定到GridView,并按照规则分成了多组(比如10组)。这样第1屏可能直显示前两组,后面的数据需要滚动才能显示。现在,假如提供一个按钮,在按钮的单击事件中,想把特定的组(比如第7组)显示在当前,而不需要拖动滚动条。

       我看到GridView有个ScrollIntoView方法,但需要item参数,感觉效率不太高,有没有一种根据“组名”直接定位到指定的位置的方法呢?或都其他高效的方式实现这种功能?

    2012年4月1日 7:18

答案

  • 没有根据组名直接滚动的,与其自己实现一个,还不如 ScrollIntoView 方法。

    其他方法,有通过SctollViewer来滚动的:

           
            ScrollViewer scrollviewer= GetVisualChild<ScrollViewer>(ItemGridView);
            scrollviewer.ScrollToHorizontalOffset(...);
            
            ......
    
            public static T GetVisualChild<T>(DependencyObject parent) where T : DependencyObject
            {
                T child = default(T);
                int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
                for (int i = 0; i < numVisuals; i++)
                {
                    DependencyObject d = (DependencyObject)VisualTreeHelper.GetChild(parent, i);
                    child = d as T;
                    if (child == null)
                        child = GetVisualChild<T>(d);
                    if (child != null)
                        break;
                }
                return child;
            }

    通过可视树找到GridViewer的ScrollViewer,然后让其滚动。

    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年4月2日 9:05
    版主

全部回复

  • 没有根据组名直接滚动的,与其自己实现一个,还不如 ScrollIntoView 方法。

    其他方法,有通过SctollViewer来滚动的:

           
            ScrollViewer scrollviewer= GetVisualChild<ScrollViewer>(ItemGridView);
            scrollviewer.ScrollToHorizontalOffset(...);
            
            ......
    
            public static T GetVisualChild<T>(DependencyObject parent) where T : DependencyObject
            {
                T child = default(T);
                int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
                for (int i = 0; i < numVisuals; i++)
                {
                    DependencyObject d = (DependencyObject)VisualTreeHelper.GetChild(parent, i);
                    child = d as T;
                    if (child == null)
                        child = GetVisualChild<T>(d);
                    if (child != null)
                        break;
                }
                return child;
            }

    通过可视树找到GridViewer的ScrollViewer,然后让其滚动。

    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年4月2日 9:05
    版主
  • 有没有使用ScrollIntoView方法的实例,我使用的ScrollIntoView方法不启作用!!
    2012年4月5日 10:16
  • 你的Items是通过数据邦定的吗? 如果是的话,这个Item不是简单通过GridView.Items[i]来获得的,而是你的邦定的那个具体Item对象,例如你邦定了10个String的了List,给ScrollIntoView方法的Item应该是List[i]里面的某个Item对象。 

    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年4月6日 7:29
    版主
  • 用的是item对象,但还不是行,不知道是哪儿的问题,如下:

     try
                {
                     Friend friend;//item对象
                    string GroupName = (ComboBoxGroup.SelectedItem as FriendsGroup).GroupName; 

                    for (int i = 0; i < m_FriendsSource.Count; i++)  //m_FriendsSource为FriendGridView绑定的数据源,类型为ObservableCollection<GroupInfoList<object>>,它是按组划分的,关键字为GroupName     

               {
                        if (m_FriendsSource[i].GroupName.ToString() == GroupName)
                        {
                            friend = m_FriendsSource[i][0] as Friend;
                            FriendGridView.ScrollIntoView(friend);
                            break;
                        }
                    }
                }
                catch (Exception ex)
                {

                }

    public class GroupInfoList<T> : ObservableCollection<object>
        {
            /// <summary>
            /// 组名
            /// </summary>
            public object GroupName { get; set; }
        }

    2012年4月9日 2:29
  • 关于ScrollIntoView()我用了一个ListBox控件来测试,发现也没有起到作用!!

    <ListBox x:Name="talkBox" ScrollViewer.HorizontalScrollMode="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="0" BorderThickness="0"/>
    <Button x:Name="btnSend" Content="发送" Click="btnSend_Click"/>

     string item= "aaaaa"              
     talkBox.Items.Add(item);
     talkBox.ScrollIntoView(item);

    点击按钮后talkBox总是显示第一项,不会滚动到新加的哪一项。很费解啊!

    2012年4月10日 9:43
  • 你不要一直往ListBox.Items里面添加一样内容的String, ListBox在Item是内容一样的String的时候是有问题的,它无法判定各个Item分别是谁。 


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us


    • 已编辑 Jie BaoModerator 2012年4月10日 16:05 混淆了,忘了这个是Metro的帖子
    2012年4月10日 10:28
    版主
  • 我换了一种写法:

                    string dd = txtMessage.Text;
                    talkBox.Items.Add(dd);
                    int count = talkBox.Items.Count - 1;
                    talkBox.SelectedIndex = count;
                    talkBox.ScrollIntoView(lvTalk.SelectedItem);

    运行结果是在点击按钮后,偶尔会有滚动到底部,但大部分点击没有滚动。

    2012年4月11日 7:22
  • 请检查是否lvTalk.SelectedItem 的值确实存在或者它存在于你的 talkBox.Items 中,类型是否匹配。 因为虚拟化等原因会影响在这个Item还没有显示之前,你无法控制其进入显示区域。

    所以我还是建议用可视树找到GridViewer的ScrollViewer,然后让其滚动。 调用

    ScrollToHorizontalOffset 或者 ScrollToVerticalOffset 



    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年4月17日 6:38
    版主
  • 所以我还是建议用可视树找到GridViewer的ScrollViewer,然后让其滚动。 调用ScrollToHorizontalOffset 或者 ScrollToVerticalOffset 

    有没有这个方案的例子,不知道如何实现!!!

    2012年4月18日 6:16
  • 没有根据组名直接滚动的,与其自己实现一个,还不如 ScrollIntoView 方法。

    其他方法,有通过SctollViewer来滚动的:

           
            ScrollViewer scrollviewer= GetVisualChild<ScrollViewer>(ItemGridView);
            scrollviewer.ScrollToHorizontalOffset(...);
            
            ......
    
            public static T GetVisualChild<T>(DependencyObject parent) where T : DependencyObject
            {
                T child = default(T);
                int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
                for (int i = 0; i < numVisuals; i++)
                {
                    DependencyObject d = (DependencyObject)VisualTreeHelper.GetChild(parent, i);
                    child = d as T;
                    if (child == null)
                        child = GetVisualChild<T>(d);
                    if (child != null)
                        break;
                }
                return child;
            }

    通过可视树找到GridViewer的ScrollViewer,然后让其滚动。

    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    请问这里面的这段代码用C++该怎么实现呢?
    2012年4月18日 6:19
  • 为了检验ScrollIntoView方法,我整理了下代码,代码应该没有什么问题,但就是出现偶尔滚动的现象,您帮着给看看问题是出在哪儿?谢谢!

    <Grid Background="{StaticResource FeinnoBackgroundBrush}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="120"/>
                <ColumnDefinition Width="880"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
           
            <Grid x:Name="itemDetailGrid" Grid.Column="1"  Margin="40,0,100,0">  
                <Grid.RowDefinitions>
                    <RowDefinition Height="7*"/>
                    <RowDefinition Height="3*"/>
                </Grid.RowDefinitions>
                    <ListView x:Name="lvTalk" ScrollViewer.HorizontalScrollMode="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" SelectionMode="None"/>             
                    <StackPanel Grid.Row="1" VerticalAlignment="Bottom">
                        <TextBox x:Name="txtMessage" Grid.Row="1" Height="100" TextWrapping="Wrap"/>
                    </StackPanel>
            </Grid>
            <StackPanel  Grid.Row="1" Grid.Column="2" VerticalAlignment="Bottom" Margin="10,0,0,0">
                <Button x:Name="btnSend" Content="发送" Click="btnSend_Click"/>
            </StackPanel>
        </Grid>

     private void btnSend_Click(object sender, RoutedEventArgs e)
            {
                try
                {              
                    string dd = txtMessage.Text;
                    lvTalk.Items.Add(dd);
                    int count = lvTalk.Items.Count - 1;
                    lvTalk.SelectedIndex = count;
                    lvTalk.ScrollIntoView(lvTalk.SelectedItem);         
                   
                }
                catch (Exception ex)
                {
               
                }
            }

    2012年4月18日 7:05
  • 写在Header 中,如下:

    public:
      template<typename T>
      T GetVisualChild (DependencyObject ^parent)
      {
        T child = default(T);
        int numVisuals = VisualTreeHelper::GetChildrenCount(parent);
        for(int i=offsetof;i<numVisuals;i++){
          DependencyObject ^d = (DependencyObject^) VisualTreeHelper::GetChild(parent,i);
          child = safe_cast<T>(d);
          if(child == NULL)
            child = GetVisualChild<T>(d);
          else
            break;
          }
        return child;
      };
    


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年4月18日 7:35
    版主
  • 这样就好了:

                lvTalk.ItemContainerGenerator.ItemsChanged += ItemContainerGenerator_ItemsChanged;
            ...
    
            void ItemContainerGenerator_ItemsChanged(object sender, ItemsChangedEventArgs e)
            {
                int count = lvTalk.Items.Count - 1;
                lvTalk.SelectedIndex = count;
                lvTalk.ScrollIntoView(lvTalk.SelectedItem);
            }

    在我们加入一个Item时候这个ItemContainer并没有马上Load好,所以我们这个时候可能无法立刻Scroll到他


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年4月18日 7:58
    版主
  • 这样就好了:

                lvTalk.ItemContainerGenerator.ItemsChanged += ItemContainerGenerator_ItemsChanged;
            ...
    
            void ItemContainerGenerator_ItemsChanged(object sender, ItemsChangedEventArgs e)
            {
                int count = lvTalk.Items.Count - 1;
                lvTalk.SelectedIndex = count;
                lvTalk.ScrollIntoView(lvTalk.SelectedItem);
            }

    在我们加入一个Item时候这个ItemContainer并没有马上Load好,所以我们这个时候可能无法立刻Scroll到他


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    这个已经明白了。
    2012年4月18日 9:33
  • 没有根据组名直接滚动的,与其自己实现一个,还不如 ScrollIntoView 方法。

    其他方法,有通过SctollViewer来滚动的:

           
            ScrollViewer scrollviewer= GetVisualChild<ScrollViewer>(ItemGridView);
            scrollviewer.ScrollToHorizontalOffset(...);
            
            ......
    
            public static T GetVisualChild<T>(DependencyObject parent) where T : DependencyObject
            {
                T child = default(T);
                int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
                for (int i = 0; i < numVisuals; i++)
                {
                    DependencyObject d = (DependencyObject)VisualTreeHelper.GetChild(parent, i);
                    child = d as T;
                    if (child == null)
                        child = GetVisualChild<T>(d);
                    if (child != null)
                        break;
                }
                return child;
            }

    通过可视树找到GridViewer的ScrollViewer,然后让其滚动。

    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us


    这个SctollViewer方法还是不会用,您能不能给个完整点的例子!!
    2012年4月18日 9:36
  • 这个方法是在你的Item加入之后,得到Item的对应ScrollViewer top的位置,然后call scrollviewer.ScrollToHorizontalOffset()到这个位置。 如果你永远是滚动到最低端,你就可以 直接滚动到ScrollViewer的高度位置。 

    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年4月18日 10:01
    版主
  • 你好Bob!使用过程中遇到点问题:

    1.我将你提供的代码做了如下修改,

    public: template<typename T> T GetVisualChild (DependencyObject ^parent) { T child = default(T); int numVisuals = VisualTreeHelper::GetChildrenCount(parent);

    //此处已将offsetof改为0;

    for(int i=0;i<numVisuals;i++){ DependencyObject ^d = (DependencyObject^) VisualTreeHelper::GetChild(parent,i); child = safe_cast<T>(d);

    //此处将NULL改为nullptr

    if(child == nullptr) child = GetVisualChild<T>(d); else break; } return child; };

    //获得ScrollViewer

    ScrollViewer^ scrollviewer= GetVisualChild<ScrollViewer^>(GridView5);
    

    //发生如下编译错误
    \readpage.xaml.h(39): error C2882: 'default' : illegal use of namespace identifier in expression
    1>          readpage.xaml.cpp(98) : see reference to function template instantiation 'T ReadPage::GetVisualChild<Windows::UI::Xaml::Controls::ScrollViewer^>(Windows::UI::Xaml::DependencyObject ^)' being compiled
    1>          with
    1>          [
    1>              T=Windows::UI::Xaml::Controls::ScrollViewer ^
    1>          ]
    1>readpage.xaml.h(39): error C2275: 'T' : illegal use of this type as an expression
    1>          readpage.xaml.cpp(98) : see declaration of 'T'

    2.我想给通过ScrollViewer^ scrollviewer= GetVisualChild<ScrollViewer^>(GridView5);得到的scrollviewer

    添加一个ViewChanged事件响应函数。

    scrollviewer->ViewChanged += ref new EventHandler<ScrollViewerViewChangedEventArgs^>(this,&ReadPage::GridView5_SV_ViewChanged_2);

    //Header里的函数声明

    void GridView5_SV_ViewChanged_2(Windows::UI::Xaml::Controls::ScrollViewerViewChangedEventArgs^ e);

    这样写编译器会提示类型不匹配。请问我该怎么样修改才是正确的呢?谢谢!

    补充:

    如果我将T child = default(T);改成T child = nullptr;

    编译不会出问题,可是link的时候出问题。信息如下,

    1>ReadPage.xaml.obj : error LNK2022: metadata operation failed (80131187) : 重复类型中的方法声明不一致(类型: ReadPage;方法: GetVisualChild<Windows::UI::Xaml::Controls::ScrollViewer ^>): (0x06000066)。
    1>ReadPage.xaml.obj : error LNK2022: metadata operation failed (801311D6) : 重复类型(ReadPage)中的方法数不同: (0x02000016)。
    1>LINK : fatal error LNK1255: link failed because of metadata errors

    • 已编辑 frglig 2012年4月25日 8:05 问题更新
    2012年4月25日 2:22
  • 我是这样做的,在项目中新建一个头文件 VisualTreeHelperEx.h :

    #pragma once
    #include "pch.h"
    
    
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Media;
    
    template<typename T> 
    T GetVisualChild (DependencyObject ^parent)
    {
    	T child = nullptr;
    	int numVisuals = VisualTreeHelper::GetChildrenCount(parent);
    	for(int i=0;i<numVisuals;i++){
    		DependencyObject ^d = (DependencyObject^) VisualTreeHelper::GetChild(parent,i);
    		child = dynamic_cast<T>(d);
    		if(child == nullptr)
    			child = GetVisualChild<T>(d);
    		else
    			break;
    	}
    	return child;
    };
    

    然后调用就OK, 我测试了 dynamic_cast 是OK的。


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年5月1日 6:52
    版主
  • 增加一个问题,不知道是否能实现。

    问题描述:GridView内容分组后,想把最后一组滚动到最左边,(此时scrollviewer.ExtentWidth没有那么宽)

    这种需求能实现吗?如何实现呢?

    2012年5月11日 4:04