DataTemplateに対応したコントロールの作り方ということで、こちらのサイトを写経させていただきました。
カスタムコントロールの作成
カスタムコントロールを新規作成して、Generic.xamlに適当にStackPanelを追加します。ここにアイテムを追加していくっていう予定です。
<ResourceDictionaryxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WpfApplication4"><Style TargetType="{x:Type local:MyItemControl}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:MyItemControl}"><Border Background="{TemplateBinding Background}"BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="{TemplateBinding BorderThickness}"><StackPanel x:Name="ItemsPanel" /></Border></ControlTemplate></Setter.Value></Setter></Style></ResourceDictionary>
依存プロパティの作成
今回は、データを格納するためのItemsSourceプロパティと、1要素1要素に適用するDataTemplateを設定するItemTemplateプロパティを追加します。
public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } // Using a DependencyProperty as the backing store for ItemsSource. This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(MyItemControl), new PropertyMetadata(null, ItemsSourceChanged)); public DataTemplate ItemTemplate { get { return (DataTemplate)GetValue(ItemTemplateProperty); } set { SetValue(ItemTemplateProperty, value); } } // Using a DependencyProperty as the backing store for ItemTemplate. This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(MyItemControl), new PropertyMetadata(null));
ItemsSourceは変更のタイミングで再描画したいので、変更時のコールバックを設定してます。
OnApplyTemplateとその他のメソッド
ということで、残りの部分です。ItemsSourceChangedは、再描画するためのメソッドを呼ぶだけのシンプル実装です。
privatestaticvoid ItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((MyItemControl)d).RenderItems(); }
ControlTemplateが適用されるタイミングで呼ばれるOnApplyTemplateメソッドも、Panelをテンプレートから取得するしたら再描画するだけです。
private Panel itemsPanel; publicoverridevoid OnApplyTemplate() { base.OnApplyTemplate(); this.itemsPanel = this.GetTemplateChild("ItemsPanel") as Panel; this.RenderItems(); }
そして大事な再描画メソッドのRenderItemsメソッドです。こいつは、DataTemplateのLoadContentメソッドを使ってテンプレートからコントロールの実体を作成して、DataContextに要素をつっこんでからPanelに追加しています。
privatevoid RenderItems() { if (this.itemsPanel == null) { return; } this.itemsPanel.Children.Clear(); if (this.ItemsSource == null) { return; } foreach (var item inthis.ItemsSource) { var elm = this.ItemTemplate.LoadContent() as FrameworkElement; elm.DataContext = item; this.itemsPanel.Children.Add(elm); } }
このメソッドが今回の核ですね。LoadContentメソッドメモメモ。
実行してみる
画面に適当においてみます。
<local:MyItemControl ItemsSource="{Binding}"><local:MyItemControl.ItemTemplate><DataTemplate><TextBlock Text="{Binding Name}" /></DataTemplate></local:MyItemControl.ItemTemplate></local:MyItemControl>
DataContextにはNameプロパティをもったオブジェクトの配列を入れてます。実行すると、こんな感じに表示されます。