Last Updated 2011/09/21
ListView クラスの View プロパティには ViewBase クラスから派生するクラスを設定しますが、WPF 標準のクラスは GridView クラスしかありません。画像ファイルをリスト表示するにはどうすればいいのでしょうか。このページではその答えを提供します。
System.Windows.Forms.ListView コントロールのほうには View プロパティがあって、大きなアイコン表示や詳細表示にするモードを切り替えることができます。System.Windows.Controls.ListView コントロールのほうにも View プロパティはありますが、簡単な方法で表示方法を切り替えることはできません。これは View プロパティに設定できる WPF 標準のクラスが GridView クラスしかないからです。下図は、GridView クラスを使う例ですが、クラス名から想像できるようにデータをグリッド状に表示します。
GridView クラスのほかに画像をリスト表示するクラスを標準で用意してくれていたら問題ないのですが、なければ作るほかありません。
System.Windows.Controls.ListView コントロールの View プロパティに設定できるクラスは ViewBase クラスから派生するクラスに限られますから、このクラスから派生するクラスを作成することになります。
ダラダラと説明するだけではしかたありませんので、コードを示しましょう。クラス名は IconView とします。以下のコードを IconView.cs ファイルに保存してください。
using System; using System.Windows; using System.Windows.Controls; namespace emanual.Wpf.Controls { public class IconView : ViewBase { public static readonly DependencyProperty ItemWidthProperty = WrapPanel.ItemWidthProperty.AddOwner(typeof(IconView)); // 画像の表示は Image コントロールを使うが、その幅を設定・取得するプロパティ // この幅で WrapPanel コントロール内における配置方法が決まる public double ItemWidth { get { return (double)GetValue(ItemWidthProperty); } set { SetValue(ItemWidthProperty, value); } } protected override object DefaultStyleKey { get { return new ComponentResourceKey(typeof(IconView), "IconViewStyle"); } } protected override object ItemContainerDefaultStyleKey { get { return new ComponentResourceKey(typeof(IconView), "IconViewItemStyle"); } } } // end of IconView class } // end of namespace
次に、クラスのビジュアルな部分(スタイル)を定義するコードを Generic.xaml 内に書き、Themes フォルダに格納します。プロジェクト内に Themes フォルダがなければ作ってください。
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:emanual.Wpf.Controls"> <Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:IconView}, ResourceId=IconViewStyle}" TargetType="ListView" BasedOn="{StaticResource {x:Type ListBox}}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Margin="{TemplateBinding Margin}"> <ScrollViewer Margin="{TemplateBinding Padding}"> <WrapPanel ItemWidth="{Binding (ListView.View).ItemWidth, RelativeSource={RelativeSource AncestorType=ListView}}" IsItemsHost="True" MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}" Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"> </WrapPanel> </ScrollViewer> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:IconView}, ResourceId=IconViewItemStyle}" TargetType="ListViewItem" BasedOn="{StaticResource {x:Type ListBoxItem}}"> <Setter Property="Padding" Value="1"/> <Setter Property="Margin" Value="2"/> <Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <Border Background="White"> <Image Margin="2" Source="{Binding}" Stretch="UniformToFill"/> </Border> </DataTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
下図は、このクラスを使って作ったアプリケーションを起動したところです。[Stock Icon] は Windows が保持するストックアイコンをリストアップします。[Extract Icon] を選択すると、ファイルを選択するダイアログボックスを表示しますので、適当な EXE ファイルまたは DLL ファイルを選択してください。たとえば、Windows ディレクトリにある Explorer.exe とか System32 ディレクトリにある ImageRes.dll または Shell32.dl です。[Show Images] を選択すると、複数のファイルを選択できるダイアログボックスを表示しますので、JPEG などの画像ファイルを複数選択してください。[表示] は画像を表示するサイズを指定します。
アプリケーションのソースコードは「アイコン」のページにあります。
さて、IconView クラスを定義するリソースディクショナリはなぜ Generic.xaml 内に書き込まなければならないのか。これを解明するのは結構時間がかかりました。
まず、Themes フォルダですが、これは Visual Studio が特別なフォルダだと認識するための決まったフォルダ名です。また、Generic.xaml という名前も決まっています。つまり、Themes フォルダ内に格納でき、認識可能なファイル名は決まっていて、一つは Windows Theme を定義する XAML ファイルで、もう一つが Generic.xaml です。
Windows Theme を定義する XAML ファイル名は以下にリストアップするものだけが認識可能です。
これらに加えて、Generic.xaml だけを認識します。それら以外の XAML ファイルを置いても読み込みません。
次に、IconView クラスのスタイルの定義を Generic.xaml に書き込まなければならない理由ですが、ViewBase クラスの継承関係を見れば分かります。つまり、ViewBase クラスはコントロールではありませんから、Application オブジェクトを持たないし、親コントロールを持つこともできません。したがって、Application や Window のリソースとすることができないというわけです。上のコードを見てもらうと、ComponentResourceKey クラスを使っているのはそのためです。
ComponentResourceKey クラスを使うクラスの場合、通常はアプリケーションのアセンブリとは異なる外部アセンブリ(DLL ファイルと考えてよい)として作成することになるため、Themes フォルダ内に格納する Generic.xaml 内にコードを書くルールに準じることになっているものと想像します。いずれにしろ、Visual Studio の内部的な問題なので、どうしてこうしなければならないかの理由を考えても詮無いことです。
−以上−