Expanderのカスタムコントロールを作りたくて検索してみたら次のページが見つかりました。
これを元にシンプルなカスタムExpanderを作るとこうなります。 Themes/Generic.xamlは、
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ExpanderTemplateTest"
>
<Style
TargetType="{x:Type local:SimpleExpander}"
BasedOn="{StaticResource {x:Type Expander}}"
>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:SimpleExpander}">
<StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton Name="headerToggle" Content="▼" IsChecked="{Binding Path=IsExpanded,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"/>
<ContentPresenter Name="header" ContentSource="Header"/>
</StackPanel>
<Separator/>
<ContentPresenter Name="content" Visibility="Hidden" Height="0"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="headerToggle" Property="Content" Value="△"/>
<Setter TargetName="content" Property="Visibility" Value="Visible"/>
<Setter
TargetName="content"
Property="Height"
Value="{Binding
ElementName=content,
Path=DesiredHeight
}"
/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
横長失礼。 最初は「横長すぎるのはダメかなぁ」とかって思ってたけどxamlじゃそんなこと言ってられないんですよね。 xamlに限らず、色んなブログとかページとかで色んなスタイルのコード見てたら「ブログでコードを見やすく表示する方法なんてない」ということにも気付きました。 なのでもう多少の横長くらいなら投稿するのに気は使いません。
本題に戻って、SimpleExpander.csは自動生成でできるSimpleExpanderクラスの継承元をControlからExpanderに変えるだけです。
// SimpleExpander.cs
using System.Windows;
using System.Windows.Controls;
namespace ExpanderTemplateTest
{
public class SimpleExpander : Expander
{
static SimpleExpander()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SimpleExpander), new FrameworkPropertyMetadata(typeof(SimpleExpander)));
}
}
}
サンプルコードを動かすと、実行後初めてExpanderを展開させたときに次のエラーが出力されました。
System.Windows.Data Error: 40 : BindingExpression path error: 'DesiredHeight' property not found on 'object' ''ContentPresenter' (Name='content')'. BindingExpression:Path=DesiredHeight; DataItem='ContentPresenter' (Name='content'); target element is 'ContentPresenter' (Name='content'); target property is 'Height' (type 'Double')
出力先はVisual Studioの出力欄です。 エラーは出ていますが、SimpleExpanderはちゃんと動いてます。 「例外で強制停止」とかはありません。 メッセージ内容は「DesiredHeightプロパティが見つからないからバインドできません」とか。
コントロール系のクラスのリファレンスを読むと、DesiredSizeプロパティはあってもDesiredHeightプロパティってのはないですね。 このDesiredHeightプロパティってのは何者でしょう?
エラーメッセージで検索したらこんな投稿が見つかりました。
とりあえずエラーメッセージの回避策は書いてあります。 そしてやはり「a bit frustrating」な人もいる模様。 しかし肝心のDesiredHeightプロパティが何なのかは書いてませんでした。
そこで、簡単なコードを書いてHeightに割り当てられる値を見てみました。 確認用コードのMainWindow.xamlは、
<Window x:Class="ExpanderTemplateTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ExpanderTemplateTest"
Title="ExpanderTemplateTest"
Width="400"
SizeToContent="Height"
ResizeMode="NoResize"
>
<local:SimpleExpander
x:Name="simpleExpander"
Header="test"
LayoutUpdated="SimpleExpander_LayoutUpdated"
>
<StackPanel>
<Label>猫の耳に真珠</Label>
<Label>馬に小判</Label>
<Label>豚に念仏</Label>
</StackPanel>
</local:SimpleExpander>
</Window>
MainWindow.xaml.csはこうなっています。
// MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace ExpanderTemplateTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void SimpleExpander_LayoutUpdated(object sender, EventArgs e)
{
StackPanel panel = (StackPanel)simpleExpander.Content;
ContentPresenter p = (ContentPresenter)VisualTreeHelper.GetParent(panel);
Title = p.Height.ToString();
}
}
}
結果、例のトリガーが動くとHeightにNaNが設定されるのが確認できました。 え? DesiredHeightプロパティって関係ない?
試しに次のように書き換えてみました。
<Setter
TargetName="content"
Property="Height"
Value="{Binding ElementName=content, Path=DesiredHeight}"
/>
↓
<Setter TargetName="content" Property="Height" Value="NaN"/>
挙動変わらず。 そして当然ですが、エラーメッセージはなくなっています。
え~と、つまり...「DesiredHeightってのは実在しないプロパティで、バインド対象に書くのはバグなんだけど結果NaNが入るから動いてました。」ってことですか? 実在しないバインド対象を書くとデフォルト値が入る ... のかな?
簡単なコードを1つ書いただけなので断定はできませんが、なんか深入りしても無駄なような気にはなってしまいました。 バグって事でひとつ。