平々毎々(アーカイブ)

はてなダイアリーのアーカイブです。

ItemsControlの動作を追う

ItemsControlの派生クラスでいろいろ調べた。
ItemsControlはおおむねこんな動きをしているのだろう。(簡単のため、ResourceDictionaryなどは無視している)

Size MeasureOverride(Size availableSize)
{
  ItemsPresenter presenter = 【ビジュアルツリーからItemsPresenterを取得】;
  Panel panel = this.ItemsPanel;
  if (panel == null)
  {
    panel = new StackPanel();
  }
  【presenterにpanelを追加】;
  foreach(object item in this.ItemsSource)
  {
    if (this.IsItemItsOwnContainerOverride(item))
    {
      panel.Children.Add(item);
    }
    else
    {
      DependencyObject element = this.GetContainerForItemOverride();
      this.PrepareContainerForItemOverride(element, item);
      panel.Children.Add(element);
    }
  }
  【ビジュアルツリーの子要素】.Measure();
  return 【ビジュアルツリーの子要素】.DesirableSize;
}

DependencyObject GetContainerForItemOverride()
{
  return new ContentPresenter();
}

void PrepareContainerForItemOverride(element, item)
{
  ContentPresenter presenter = element as ContentPresenter;
  if (presenter != null)
  {
    presenter.ContentTemplate = this.ItemTemplate;
    presenter.Content = item;
  }
}

そして、ContentPresenterはおおむねこんな動きをしているのだろう。

Size MeasureOverride(Size availableSize)
{
  if (this.ContentTemplate != null)
  {
    DependencyObject content = this.ContentTemplate.LoadContent();
    FrameworkElement element = content as FrameworkElement;
    if (element != null)
    {
      element.DataContext = this.Content;
      element.Measure();
      return element.DesirableSize;
    }
  }
}

とあるカスタムコントロールを作りたいのだが、やりたいことを実現するにはContentPresenterのMeasureを呼ぶ前にContentTemplate.LoadContentを呼び出しておかないといけないようだ。PrepareContainerForItemOverrideあたりでLoadContentを呼び出して、結果は使い捨てようかな。

(追記)ContentTemplate.LoadContentでButtonを取得したんだけど、Measureの結果が(0, 0)。何が足りない?