none
Listbox表示されているIndexの取得

    質問

  • 大変お世話になります。

    ListBox に表示されている先頭のItem のIndex を取得する方法はありますでしょうか?以前 .net FrameWork1.1,2 の時にはListbox にTopIndex メンバがありListBox に最初に表示される項目のインデックスを取得する事ができていました。WPF(FrameWork4)では、現在Listboxに表示されているItemが何なのか取得する事は出来ますでしょうか?

    どうぞよろしくお願いいたします。

    2012年8月2日 7:18

回答

  • >>SelectedIndex,SelectedItem につきましては動作は確認していたのですが、私が欲しかったのは、今表示されている項目出した。

    すみません、完全に質問の意図を取り違えてました(苦笑)

    お書きの通り、ItemsPanelを別途指定するとVirtualizingStackPanelが生成されなくなり、仮想モードが使えなくなります。
    とりあえず、先のコードを元にサンプルを作ってみました。多分、ご希望の動作になっていると思います。

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            
            <ListBox Grid.Row="0" Height="140" HorizontalAlignment="Left" Margin="81,51,0,0" Name="listBox1" VerticalAlignment="Top" Width="133" ScrollViewer.ScrollChanged="ScrollChanged" >
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
                
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}" TextWrapping="Wrap" Width="50"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
    
            <TextBlock Grid.Row="1" Name="textBlock"/>
    
        </Grid>
    </Window>

        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                List<string> list = new List<string>();
                list.Add("11111111111111");
                list.Add("2222222222222");
                list.Add("33333333333333");
                list.Add("444444444444");
                list.Add("555555555555");
                list.Add("6666666666666");
                list.Add("77777777777777");
                list.Add("8888888888888");
                list.Add("99999999999999");
                list.Add("00000000000000");
                list.Add("1111111111111");
    
                listBox1.ItemsSource = list;
            }
    
            private void ScrollChanged(object sender, ScrollChangedEventArgs e)
            {
                ScrollViewer viewer = FindVisualChild<ScrollViewer>(listBox1);
                StackPanel panel = FindVisualChild<StackPanel>(listBox1);
                if (listBox1.Items.Count > 0 && viewer != null)
                {
                    int offset =
                      (panel.Orientation == Orientation.Horizontal) ? (int)viewer.HorizontalOffset : (int)viewer.VerticalOffset;
                    var item = listBox1.Items[offset];
    
                    textBlock.Text = item as string;
                }
            }
    
            private static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(obj, i);
    
                    if (child is T)
                    {
                        return (T)child;
                    }
                    else
                    {
                        child = FindVisualChild<T>(child);
                        if (child != null)
                        {
                            return (T)child;
                        }
                    }
                }
                return null;
            }
        }

    VirtualizingStackPanelではなく、ScrollViewerから直接Offset値を取得するということです。
    なお、VirtualizingStackPane.IsVirtualizing添付プロパティをFalseにするとピクセル単位のスクロールモードとなり、Offset値の意味合いが異なってきますので注意してください。
    また、冒頭にも書いたとおり仮想モードがOffになっているので見えない領域のItemも生成されています。Item数が多くなるとパフォーマンスにも影響しますので、その点もご注意ください。



    2012年8月3日 5:19
  • > FindVisualChildの中で VirtualizingStackPanel が見つけられない状態になります。御助言いただける事がありましたら是非お願いいたします。

    <ItemsPanelTemplate >
            <StackPanel Orientation="Horizontal"/>
    </ItemsPanelTemplate>

    ここを

    <ItemsPanelTemplate >
            <VirtualizingStackPanel Orientation="Horizontal"/>
    </ItemsPanelTemplate>
    に変えてみればいいと思います。


    ひらぽん http://d.hatena.ne.jp/hilapon/


    2012年8月3日 5:29
    モデレータ

すべての返信

  • 現在選択中のItemのIndex及び対応するオブジェクトでしたら、SelectedIndex/SelectedItemというプロパティがあります。
    DependencyPropertyなのでBindingでGet/Setもできますよ。

    http://msdn.microsoft.com/ja-jp/library/system.windows.controls.listbox_properties.aspx

    詳しくは上記リファレンスをどうぞ。

    2012年8月2日 7:40
  • 以下が参考になるのではないかと思います。

    .NET/C# Get First Visible Item of a ListBox
    http://www.dominikschmidt.net/2010/12/netc-get-first-visible-item-of-a-listbox/


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2012年8月2日 7:41
    モデレータ
  • みっと様

    御回答いただきましてありがとうございます。

    SelectedIndex,SelectedItem につきましては動作は確認していたのですが、私が欲しかったのは、今表示されている項目でした。


    2012年8月3日 4:38
  • trapemiya様 大変参考になります。ありがとうございます。

    以下にて、表示されているIndexを取得する事が出来ました。リンク先の一部に大文字、小文字の誤記?があり、変更したところ動作確認できました。ただ、私がやりたいのは、項目を横方向に並べたいのですが、横方向に並べるとうまく動作いたしませんでした。横方向に並べたxaml は次の返信に投稿します。

    xaml

    <ListBox Height="140" HorizontalAlignment="Left" Margin="81,51,0,0" Name="listBox1" VerticalAlignment="Top" Width="133" ScrollViewer.ScrollChanged="ScrollChanged" />

    c#

       private void ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
          VirtualizingStackPanel panel = FindVisualChild<VirtualizingStackPanel>(listBox1);
          if (listBox1.Items.Count > 0 && panel != null)
          {
            int offset = 
              (panel.Orientation == Orientation.Horizontal) ? (int)panel.HorizontalOffset : (int)panel.VerticalOffset;
            var item = listBox1.Items[offset];
          }
        }

        private static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
        {
          for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
          {
            DependencyObject child = VisualTreeHelper.GetChild(obj, i);
     
            if (child is T)
            {
              return (T)child;
            }
            else
            {
              child = FindVisualChild<T>(child);
              if (child != null)
              {
                return (T)child;
              }
            }
          }
          return null;
        }

    2012年8月3日 4:43
  • 先ほどのコードにおいて、Items が垂直方向に並ぶ時には、表示されている先頭のIndexが offset の中に入るようになりました。しかし、Itemsを水平方向に並べると、VirtualizingStackPanelが常にnull となり、offsetを計算する事ができません。以下、Items を水平方向に並べた場合です。基本的な事が分かっていなくて申し訳ないのですが、FindVisualChildの中で VirtualizingStackPanel が見つけられない状態になります。御助言いただける事がありましたら是非お願いいたします。

    xaml(ItemsControl.ItemsPanelを用いて、Itemsを水平方向に並べた)

            <ListBox Height="140" HorizontalAlignment="Left" Margin="81,51,0,0" Name="listBox1" VerticalAlignment="Top" Width="133" ScrollViewer.ScrollChanged="ScrollChanged" >
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ListBox>

    c#(これは動作確認したコードと同じ)

        private void ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
          VirtualizingStackPanel panel = FindVisualChild<VirtualizingStackPanel>(listBox1);
          if (listBox1.Items.Count > 0 && panel != null)
          {
            int offset = 
              (panel.Orientation == Orientation.Horizontal) ? (int)panel.HorizontalOffset : (int)panel.VerticalOffset;
            var item = listBox1.Items[offset];
          }
        }

        private static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
        {
          for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
          {
            DependencyObject child = VisualTreeHelper.GetChild(obj, i);
     
            if (child is T)
            {
              return (T)child;
            }
            else
            {
              child = FindVisualChild<T>(child);
              if (child != null)
              {
                return (T)child;
              }
            }
          }
          return null;
        }

    2012年8月3日 5:11
  • >>SelectedIndex,SelectedItem につきましては動作は確認していたのですが、私が欲しかったのは、今表示されている項目出した。

    すみません、完全に質問の意図を取り違えてました(苦笑)

    お書きの通り、ItemsPanelを別途指定するとVirtualizingStackPanelが生成されなくなり、仮想モードが使えなくなります。
    とりあえず、先のコードを元にサンプルを作ってみました。多分、ご希望の動作になっていると思います。

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            
            <ListBox Grid.Row="0" Height="140" HorizontalAlignment="Left" Margin="81,51,0,0" Name="listBox1" VerticalAlignment="Top" Width="133" ScrollViewer.ScrollChanged="ScrollChanged" >
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
                
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}" TextWrapping="Wrap" Width="50"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
    
            <TextBlock Grid.Row="1" Name="textBlock"/>
    
        </Grid>
    </Window>

        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                List<string> list = new List<string>();
                list.Add("11111111111111");
                list.Add("2222222222222");
                list.Add("33333333333333");
                list.Add("444444444444");
                list.Add("555555555555");
                list.Add("6666666666666");
                list.Add("77777777777777");
                list.Add("8888888888888");
                list.Add("99999999999999");
                list.Add("00000000000000");
                list.Add("1111111111111");
    
                listBox1.ItemsSource = list;
            }
    
            private void ScrollChanged(object sender, ScrollChangedEventArgs e)
            {
                ScrollViewer viewer = FindVisualChild<ScrollViewer>(listBox1);
                StackPanel panel = FindVisualChild<StackPanel>(listBox1);
                if (listBox1.Items.Count > 0 && viewer != null)
                {
                    int offset =
                      (panel.Orientation == Orientation.Horizontal) ? (int)viewer.HorizontalOffset : (int)viewer.VerticalOffset;
                    var item = listBox1.Items[offset];
    
                    textBlock.Text = item as string;
                }
            }
    
            private static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(obj, i);
    
                    if (child is T)
                    {
                        return (T)child;
                    }
                    else
                    {
                        child = FindVisualChild<T>(child);
                        if (child != null)
                        {
                            return (T)child;
                        }
                    }
                }
                return null;
            }
        }

    VirtualizingStackPanelではなく、ScrollViewerから直接Offset値を取得するということです。
    なお、VirtualizingStackPane.IsVirtualizing添付プロパティをFalseにするとピクセル単位のスクロールモードとなり、Offset値の意味合いが異なってきますので注意してください。
    また、冒頭にも書いたとおり仮想モードがOffになっているので見えない領域のItemも生成されています。Item数が多くなるとパフォーマンスにも影響しますので、その点もご注意ください。



    2012年8月3日 5:19
  • > FindVisualChildの中で VirtualizingStackPanel が見つけられない状態になります。御助言いただける事がありましたら是非お願いいたします。

    <ItemsPanelTemplate >
            <StackPanel Orientation="Horizontal"/>
    </ItemsPanelTemplate>

    ここを

    <ItemsPanelTemplate >
            <VirtualizingStackPanel Orientation="Horizontal"/>
    </ItemsPanelTemplate>
    に変えてみればいいと思います。


    ひらぽん http://d.hatena.ne.jp/hilapon/


    2012年8月3日 5:29
    モデレータ
  • ついでに参考のため、修正したコードも挙げておきます。

    using System.Collections.ObjectModel;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    
    namespace WpfApplication1 {
        /// <summary>
        /// MainWindow.xaml の相互作用ロジック
        /// </summary>
        public partial class MainWindow : Window {
            public MainWindow() {
                InitializeComponent();
    
                var list = new ObservableCollection<int>();
                for (int i = 0; i < 1000; i++) { list.Add(i); }
                listBox1.ItemsSource = list;
            }
    
            private void ScrollChanged(object sender, ScrollChangedEventArgs e) {
                VirtualizingStackPanel panel = FindVisualChild<VirtualizingStackPanel>(listBox1);
                if (listBox1.Items.Count > 0 && panel != null) {
                    int offset =
                      (panel.Orientation == Orientation.Horizontal) ? (int)panel.HorizontalOffset : (int)panel.VerticalOffset;
                    var item = listBox1.Items[offset];
                    textBlock1.Text = item.ToString();
                }
            }
    
            private static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) {
                    DependencyObject child = VisualTreeHelper.GetChild(obj, i);
    
                    if (child is T) {
                        return (T)child;
                    }
                    else {
                        child = FindVisualChild<T>(child);
                        if (child != null) {
                            return (T)child;
                        }
                    }
                }
                return null;
            } 
        }
    }
    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="300" Width="300">
        <StackPanel>
            <ListBox Name="listBox1" ScrollViewer.ScrollChanged="ScrollChanged" Height="60">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate >
                        <VirtualizingStackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ListBox>
            <TextBlock Name="textBlock1" Text="TextBlock" />
        </StackPanel>
    </Window>



    ひらぽん http://d.hatena.ne.jp/hilapon/

    2012年8月3日 5:44
    モデレータ
  • レスポンスの良さに感動いたします。ご提示いただきましたソースにより、思い通りの動作をいたしました。誠にありがとうございます。今後ともよろしくお願いいたします。
    2012年8月3日 5:49
  • ひらぽん様

    ご指摘の通り変更したところばっちり動作しました。大変助かりました。ご指導ありがとうございます。今後ともよろしくお願いいたします。

    2012年8月3日 5:51