トップ回答者
ListBoxのサムネイル一覧の中に区切り線を挿入するには

質問
-
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)です。よろしくお願い致します。
回答
-
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!)
-
基本的にはgekkaさんと同じですが、一応、私のブログも紹介しておきます(笑) できるだけシンプルなコードにしていますので、グルーピングの参考の一つになれば幸いです。
■[WPF] CollectionViewSourceを使ったグルーピングあれこれ【その1】
http://d.hatena.ne.jp/trapemiya/20120803/1343963471★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
すべての返信
-
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!)
-
基本的にはgekkaさんと同じですが、一応、私のブログも紹介しておきます(笑) できるだけシンプルなコードにしていますので、グルーピングの参考の一つになれば幸いです。
■[WPF] CollectionViewSourceを使ったグルーピングあれこれ【その1】
http://d.hatena.ne.jp/trapemiya/20120803/1343963471★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/