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つ書いただけなので断定はできませんが、なんか深入りしても無駄なような気にはなってしまいました。 バグって事でひとつ。