none
ListBoxのサムネイル一覧の中に区切り線を挿入するには RRS feed

  • 質問

  • WPFコントロールについて質問させていただきます。

    ListBoxの中に指定フォルダ内の写真データをサムネイル表示する画面を作成中なのですが、

    下図のように、Exifデータの撮影日をもとにソートし、日別で区切り線を挿入する仕様を実現するには

    どのような手段が考えられますでしょうか。

    撮影日(Labelなど)→ListBox1→次の撮影日→ListBox2…と動的に生成していくのかな、と予想していますが、

    WPFの動的生成にはこれから着手する段階です。WPFコントロールの扱いに不慣れなため、

    なにかよい方法があったり、根本から間違っている、というご指摘がありましたら、ご教示のほどお願い致します。

    ■イメージ ここから

    2014.08.28-------------------------------

    [img] [img] [img] [img] [img] [img]

    [img] [img] [img]

    2014.08.27-------------------------------

    [img] [img] [img]

    2014.08.26-------------------------------

    [img] [img] [img] [img] [img] [img]

    [img] [img] [img]

    2014-08.25……

    ……

    ■イメージ ここまで

    環境はWin7、Visual C#(2010)です。よろしくお願い致します。

    2014年8月28日 14:19

回答

  • ListBoxが1個だけでも内部でグループして表示させることができます。
    そして、そのグループのヘッダ部分に横線を配置してやります。
    あとは、元の画像データの日時からグループ化と認識してもらえるコレクションを作り、そのコレクションをListBoxのItemsSourceに設定すればグループ化して表示してくれます。

    <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="300">
    
        <Grid>
            <ListBox ItemsSource="{Binding Path=PhotoCollectionView}"
                     ScrollViewer.HorizontalScrollBarVisibility="Hidden" > <!-- Wrapを行うには縦スクロールバーを非表示-->
    
                <ListBox.GroupStyle >
                    <GroupStyle >
    
                        <GroupStyle.HeaderTemplate>
                            <DataTemplate>
                                <DockPanel >
                                    <!-- グループ名 -->
                                    <TextBlock Text="{Binding Path=Name,StringFormat=yyyy/MM/dd}" />
                                    <!--TextBlockを抜いた幅横線を引く-->
                                    <Line VerticalAlignment="Center" Margin="5,0,0,0"
                                          X1="0" X2="{Binding Path=ActualWidth,RelativeSource={RelativeSource Mode=Self}}"
                                          Y1="0" Y2="0" 
                                          Stroke="Black" StrokeThickness="1" />
                                </DockPanel>
                            </DataTemplate>
                        </GroupStyle.HeaderTemplate>
    
                        <GroupStyle.Panel>
                            <ItemsPanelTemplate>
                                <!-- グループは縦方向にスタック -->
                                <VirtualizingStackPanel /> 
                            </ItemsPanelTemplate>
                        </GroupStyle.Panel>
    
                    </GroupStyle>
                </ListBox.GroupStyle>
    
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <!-- グループの子要素は横縦のラップ。ListBoxのスクロールバーを抜いた幅で -->
                        <WrapPanel HorizontalAlignment="Left" 
                                   Width="{Binding Path=ActualWidth,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ScrollContentPresenter}}}"/>
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
    
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <!-- 適当な子要素 -->
                        <StackPanel Width="50" Margin="2">
                            <Border BorderBrush="Gray" BorderThickness="1">
                                <Image Width="50" Height="50" Stretch="Uniform" Source="{Binding Path=Image}" />
                            </Border>
                            <TextBlock Text="{Binding Path=Name}" MaxWidth="100"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Window>
    using System;
    using System.Windows;
    using System.Windows.Data;
    using System.ComponentModel;
    namespace WpfApplication1
    {
         public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                ViewModel d = new ViewModel();
                for (int h = 1; h < 9; h++)
                {
                    d.Photos.Add(new PhotoItem() { Date = new DateTime(2014, 8, 28, h, 0, 0), Name = "x" + h + ".png" });
                }
                for (int h = 1; h <3; h++)
                {
                    d.Photos.Add(new PhotoItem() { Date = new DateTime(2014, 8, 27, h, 0, 0), Name = "y" + h + ".png" });
                }
                for (int h = 1; h < 10; h++)
                {
                    d.Photos.Add(new PhotoItem() { Date = new DateTime(2014, 8, 26, h, 0, 0), Name = "z" + h + ".png" });
                }
    
                d.GroupMode = ViewModel.GroupModes.Date;
    
                this.DataContext = d;
            }
        }
    
        class PhotoItem
        {
            public string Name { get; set; }
            public Uri Image { get; set; }
            public DateTime Date { get; set; }
        }
    
        class ViewModel
        {
            /// <summary>元のコレクション</summary>
            public System.Collections.ObjectModel.ObservableCollection<PhotoItem> Photos
            {
                get
                {
                    if (_Photos == null)
                    {
                        _Photos = new System.Collections.ObjectModel.ObservableCollection<PhotoItem>();
                    }
                    return _Photos;
                }
            }
            private System.Collections.ObjectModel.ObservableCollection<PhotoItem> _Photos;
    
            /// <summary>グループ化や並べ替え用のコレクション</summary>
            public ICollectionView PhotoCollectionView
            {
                get
                {
                    if (_PhotoCollectionView == null)
                    {//元のコレクションからグループ化と並べ替えができるコレクションを作る
                        _PhotoCollectionView = CollectionViewSource.GetDefaultView(this.Photos);
                    }
                    return _PhotoCollectionView;
                }
            }
            private ICollectionView _PhotoCollectionView;
    
            public enum GroupModes
            {
                None,
                Date,
            }
            /// <summary>並べ替えかた</summary>
            public GroupModes GroupMode
            {
                get
                {
                    return _GroupMode;
                }
                set
                {
                    this.PhotoCollectionView.GroupDescriptions.Clear();
                    _GroupMode = value;
                    switch (value)
                    {
                    case GroupModes.Date:
                        this.PhotoCollectionView.GroupDescriptions.Add(new PropertyGroupDescription("Date",new DateTimeToDateConverter()));//日にちでグループ化
                        this.PhotoCollectionView.SortDescriptions.Add(new SortDescription("Date", ListSortDirection.Descending));//グループは日にちの逆順で並べ替え
                        break;
                    }
                }
    
            }private GroupModes _GroupMode;
        }
    
        /// <summary>日時から日のみ取り出すコンバーター</summary>
        [ValueConversion(typeof(DateTime),typeof(DateTime))]
        class DateTimeToDateConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                return ((DateTime)value).Date;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    }
    




    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答の候補に設定 星 睦美 2014年9月1日 6:57
    • 回答としてマーク 星 睦美 2014年9月8日 1:06
    2014年8月28日 16:33
  • 基本的にはgekkaさんと同じですが、一応、私のブログも紹介しておきます(笑) できるだけシンプルなコードにしていますので、グルーピングの参考の一つになれば幸いです。

    ■[WPF] CollectionViewSourceを使ったグルーピングあれこれ【その1】
    http://d.hatena.ne.jp/trapemiya/20120803/1343963471


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

    • 回答の候補に設定 星 睦美 2014年9月1日 6:57
    • 回答としてマーク 星 睦美 2014年9月8日 1:06
    2014年8月29日 2:23
    モデレータ

すべての返信

  • ListBoxが1個だけでも内部でグループして表示させることができます。
    そして、そのグループのヘッダ部分に横線を配置してやります。
    あとは、元の画像データの日時からグループ化と認識してもらえるコレクションを作り、そのコレクションをListBoxのItemsSourceに設定すればグループ化して表示してくれます。

    <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="300">
    
        <Grid>
            <ListBox ItemsSource="{Binding Path=PhotoCollectionView}"
                     ScrollViewer.HorizontalScrollBarVisibility="Hidden" > <!-- Wrapを行うには縦スクロールバーを非表示-->
    
                <ListBox.GroupStyle >
                    <GroupStyle >
    
                        <GroupStyle.HeaderTemplate>
                            <DataTemplate>
                                <DockPanel >
                                    <!-- グループ名 -->
                                    <TextBlock Text="{Binding Path=Name,StringFormat=yyyy/MM/dd}" />
                                    <!--TextBlockを抜いた幅横線を引く-->
                                    <Line VerticalAlignment="Center" Margin="5,0,0,0"
                                          X1="0" X2="{Binding Path=ActualWidth,RelativeSource={RelativeSource Mode=Self}}"
                                          Y1="0" Y2="0" 
                                          Stroke="Black" StrokeThickness="1" />
                                </DockPanel>
                            </DataTemplate>
                        </GroupStyle.HeaderTemplate>
    
                        <GroupStyle.Panel>
                            <ItemsPanelTemplate>
                                <!-- グループは縦方向にスタック -->
                                <VirtualizingStackPanel /> 
                            </ItemsPanelTemplate>
                        </GroupStyle.Panel>
    
                    </GroupStyle>
                </ListBox.GroupStyle>
    
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <!-- グループの子要素は横縦のラップ。ListBoxのスクロールバーを抜いた幅で -->
                        <WrapPanel HorizontalAlignment="Left" 
                                   Width="{Binding Path=ActualWidth,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ScrollContentPresenter}}}"/>
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
    
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <!-- 適当な子要素 -->
                        <StackPanel Width="50" Margin="2">
                            <Border BorderBrush="Gray" BorderThickness="1">
                                <Image Width="50" Height="50" Stretch="Uniform" Source="{Binding Path=Image}" />
                            </Border>
                            <TextBlock Text="{Binding Path=Name}" MaxWidth="100"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Window>
    using System;
    using System.Windows;
    using System.Windows.Data;
    using System.ComponentModel;
    namespace WpfApplication1
    {
         public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                ViewModel d = new ViewModel();
                for (int h = 1; h < 9; h++)
                {
                    d.Photos.Add(new PhotoItem() { Date = new DateTime(2014, 8, 28, h, 0, 0), Name = "x" + h + ".png" });
                }
                for (int h = 1; h <3; h++)
                {
                    d.Photos.Add(new PhotoItem() { Date = new DateTime(2014, 8, 27, h, 0, 0), Name = "y" + h + ".png" });
                }
                for (int h = 1; h < 10; h++)
                {
                    d.Photos.Add(new PhotoItem() { Date = new DateTime(2014, 8, 26, h, 0, 0), Name = "z" + h + ".png" });
                }
    
                d.GroupMode = ViewModel.GroupModes.Date;
    
                this.DataContext = d;
            }
        }
    
        class PhotoItem
        {
            public string Name { get; set; }
            public Uri Image { get; set; }
            public DateTime Date { get; set; }
        }
    
        class ViewModel
        {
            /// <summary>元のコレクション</summary>
            public System.Collections.ObjectModel.ObservableCollection<PhotoItem> Photos
            {
                get
                {
                    if (_Photos == null)
                    {
                        _Photos = new System.Collections.ObjectModel.ObservableCollection<PhotoItem>();
                    }
                    return _Photos;
                }
            }
            private System.Collections.ObjectModel.ObservableCollection<PhotoItem> _Photos;
    
            /// <summary>グループ化や並べ替え用のコレクション</summary>
            public ICollectionView PhotoCollectionView
            {
                get
                {
                    if (_PhotoCollectionView == null)
                    {//元のコレクションからグループ化と並べ替えができるコレクションを作る
                        _PhotoCollectionView = CollectionViewSource.GetDefaultView(this.Photos);
                    }
                    return _PhotoCollectionView;
                }
            }
            private ICollectionView _PhotoCollectionView;
    
            public enum GroupModes
            {
                None,
                Date,
            }
            /// <summary>並べ替えかた</summary>
            public GroupModes GroupMode
            {
                get
                {
                    return _GroupMode;
                }
                set
                {
                    this.PhotoCollectionView.GroupDescriptions.Clear();
                    _GroupMode = value;
                    switch (value)
                    {
                    case GroupModes.Date:
                        this.PhotoCollectionView.GroupDescriptions.Add(new PropertyGroupDescription("Date",new DateTimeToDateConverter()));//日にちでグループ化
                        this.PhotoCollectionView.SortDescriptions.Add(new SortDescription("Date", ListSortDirection.Descending));//グループは日にちの逆順で並べ替え
                        break;
                    }
                }
    
            }private GroupModes _GroupMode;
        }
    
        /// <summary>日時から日のみ取り出すコンバーター</summary>
        [ValueConversion(typeof(DateTime),typeof(DateTime))]
        class DateTimeToDateConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                return ((DateTime)value).Date;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    }
    




    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答の候補に設定 星 睦美 2014年9月1日 6:57
    • 回答としてマーク 星 睦美 2014年9月8日 1:06
    2014年8月28日 16:33
  • 基本的にはgekkaさんと同じですが、一応、私のブログも紹介しておきます(笑) できるだけシンプルなコードにしていますので、グルーピングの参考の一つになれば幸いです。

    ■[WPF] CollectionViewSourceを使ったグルーピングあれこれ【その1】
    http://d.hatena.ne.jp/trapemiya/20120803/1343963471


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

    • 回答の候補に設定 星 睦美 2014年9月1日 6:57
    • 回答としてマーク 星 睦美 2014年9月8日 1:06
    2014年8月29日 2:23
    モデレータ