スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: スポンサー広告

アコーディオンってやつだ

アコーディオンってのはこんな(↓)やつ
jQuery UI - アコーディオン

アコーディオンは、ヘッダーとコンテンツの複数のペアから成ります。ヘッダーをクリックするとそれに対応したコンテンツが表示され、同時に他のペアが折りたたまされて見えなくなります。

このアコーディオン、WPF Toolkitにはついてたのに、.NETの正式リリース時にはなぜか除外されてた。なぜだろ?Windows Applicationでは需要がなかったのだろうか?
これをどうにか再現できないか?と悩んで、結局はExpanderで再現しようと思った。
○STEP1
「Expanderを縦に並べる」ので、StackPanelが良いかとも思ったんだけど、StackPanelって、上に詰める動きなので、思うようなレイアウトにはなってくれない。なので、GridパネルにExpanderを並べる事にした。
<Window x:Class="TawamureDays.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Expander Header="Expander A" Background="LightBlue" IsExpanded="True">
<TextBlock Text="Text int Expander A"/>
</Expander>
<Expander Header="Expander B" Grid.Row="1" Background="LightPink" IsExpanded="False">
<TextBlock Text="Text int Expander B"/>
</Expander>
</Grid>
</Window>

Bをたたんでも、Aのサイズ(高さ)は変わらない。
20130121_1
まあ、RowDefinitionが1:1になっているからだろうけど。
Autoにしたらしたで、余計変になるし…。
20130121_2

○STEP2
あれこれやった結果、RowDefinitionのHeightは、

たたまれている方→Auto
開いている方→1*

が望ましいことがわかった。ただ、どれを開くかはユーザ次第で、開いたExpander以外をすべて閉じる必要がある。
まずは、Style.Triggerでやって見ることにした。
<Window x:Class="TawamureDays.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition>
<RowDefinition.Resources>
<Style TargetType="RowDefinition">
<Setter Property="Height" Value="*"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsExpanded, ElementName=ExpdA}" Value="False">
<Setter Property="Height" Value="Auto"/>
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Resources>
</RowDefinition>
<RowDefinition>
<RowDefinition.Resources>
<Style TargetType="RowDefinition">
<Setter Property="Height" Value="*"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsExpanded, ElementName=ExpdB}" Value="False">
<Setter Property="Height" Value="Auto"/>
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Resources>
</RowDefinition>
</Grid.RowDefinitions>
<Expander Header="Expander A" Background="LightBlue" x:Name="ExpdA">
<TextBlock Text="Text int Expander A"/>
</Expander>
<Expander Header="Expander B" Grid.Row="1" Background="LightPink" x:Name="ExpdB">
<TextBlock Text="Text int Expander B"/>
</Expander>
</Grid>
</Window>
これだととりあえず、手動でうまい具合に開閉してやれば、期待したようなレイアウトにはなってくれる。
20130121_3
でもこれだと、自動で開閉してくれないままだったりする。ヘッダーをクリックされたコンテンツ以外は、すべて自動で閉じてほしいわけで。

○STEP3
↓のように、IsExpandedプロパティをTriggerで切り替えてやればいいのかと思い、そういう設定を実装してみた。
        <Expander Header="Expander A" Background="LightBlue" x:Name="ExpdA">
<Expander.Resources>
<Style TargetType="Expander">
<Style.Triggers>
<DataTrigger Binding="{Binding IsExpanded, ElementName=ExpdB}" Value="True">
<Setter Property="IsExpanded" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsExpanded, ElementName=ExpdB}" Value="False">
<Setter Property="IsExpanded" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Resources>
<TextBlock Text="Text int Expander A"/>
</Expander>
<Expander Header="Expander B" Grid.Row="1" Background="LightPink" x:Name="ExpdB">
<Expander.Resources>
<Style TargetType="Expander">
<Style.Triggers>
<DataTrigger Binding="{Binding IsExpanded, ElementName=ExpdA}" Value="True">
<Setter Property="IsExpanded" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsExpanded, ElementName=ExpdA}" Value="False">
<Setter Property="IsExpanded" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Resources>
<TextBlock Text="Text int Expander B"/>
</Expander>
※Expanderの部分だけを抜粋している。
Aが開かれたら、Bを閉じる。Bが開かれたらAを閉じる。のつもりだったんだけど、これは正常に動作しない。Aを開いても、Bは閉じてくれない。IsExpandedプロパティは、ユーザの手動による開閉には連動しているけど、IsExpandedをFalseに設定したからと言って、閉じてくれるわけではないらしい。さすがにこれが駄目だと、ControlTemplateの組み直しか、EventTrigger系に踏み切るかしかなさそうなんだけど。更に調べたら、良い記事があった。
WPF: Multiple Expander have to collapse if ONE is expanded
ViewModel側との連携は必要となるけど、やりたいことはできる感じかな。
STEP4
実際に実装してみる。
(1)コンバータ作成
using System;
using System.Windows.Data;

namespace TawamureDays {

/// <summary>
/// Expander用コンバータ
/// </summary>
[ValueConversion(typeof(string), typeof(bool))]
public class ExpanderToBoolConverter : System.Windows.Data.IValueConverter {

/// <summary>
/// 値を変換します。
/// </summary>
/// <param name="value">バインディング ソースによって生成された値。</param>
/// <param name="targetType">バインディング ターゲット プロパティの型。</param>
/// <param name="parameter">使用するコンバーター パラメーター</param>
/// <param name="culture">コンバーターで使用するカルチャ。</param>
/// <returns>変換された値</returns>
public object Convert(object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture) {

if (value == null) {
return parameter == null;
}

//パラメータとvalueが同じならTrueになる
return string.Compare(value.ToString(),
parameter == null ? string.Empty :
parameter.ToString(), false) == 0;
}

/// <summary>
/// 値を変換します。
/// </summary>
/// <param name="value">バインディング ターゲットによって生成される値。</param>
/// <param name="targetType">変換後の型。</param>
/// <param name="parameter">使用するコンバーター パラメーター</param>
/// <param name="culture">コンバーターで使用するカルチャ。</param>
/// <returns>変換された値</returns>
public object ConvertBack(object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture) {

if (System.Convert.ToBoolean(value)) {
return parameter;
}

return null;
}
}
}

(2)ViewModel生成→設定
using System.Windows;

namespace TawamureDays {

using TawamureDays.ViewModels;

/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window {

public MainWindow() {
InitializeComponent();
this.DataContext = new MainWindowVM {SelectedExpander = "A"};//初期で開くExpander
return;
}
}

/// <summary>
/// ウィンドウ用ViewModelクラス
/// </summary>
public class MainWindowVM : BaseWindowVM {

/// <summary>現在開かれているExpander(を表す文字列)</summary>
private string selectedExpander_;

/// <summary>
/// 現在開かれているExpander(を表す文字列)を取得|設定します。
/// </summary>
public string SelectedExpander {
get { return selectedExpander_; }
set {
if (selectedExpander_ != value) {
selectedExpander_ = value;
this.NotifyPropertyChanged("SelectedExpander");
}
}
}
}
}

(3)XAML設定
<Window x:Class="TawamureDays.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:local="clr-namespace:TawamureDays">
<Grid>
<Grid.Resources>
<local:ExpanderToBoolConverter x:Key="Expander2Bool"/>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition>
<RowDefinition.Resources>
<Style TargetType="RowDefinition">
<Setter Property="Height" Value="Auto"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedExpander,
Converter={StaticResource Expander2Bool},
ConverterParameter=A}" Value="True">
<Setter Property="Height" Value="1*"/>
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Resources>
</RowDefinition>
<RowDefinition>
<RowDefinition.Resources>
<Style TargetType="RowDefinition">
<Setter Property="Height" Value="Auto"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedExpander,
Converter={StaticResource Expander2Bool},
ConverterParameter=B}" Value="True">
<Setter Property="Height" Value="1*"/>
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Resources>
</RowDefinition>
<RowDefinition>
<RowDefinition.Resources>
<Style TargetType="RowDefinition">
<Setter Property="Height" Value="Auto"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedExpander,
Converter={StaticResource Expander2Bool},
ConverterParameter=C}" Value="True">
<Setter Property="Height" Value="1*"/>
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Resources>
</RowDefinition>
</Grid.RowDefinitions>
<Expander Header="Expander A" Background="LightBlue" x:Name="ExpdA"
IsExpanded="{Binding SelectedExpander,
Converter={StaticResource Expander2Bool},
ConverterParameter=A}">
<TextBlock Text="Text int Expander A"/>
</Expander>
<Expander Header="Expander B" Grid.Row="1" Background="LightPink" x:Name="ExpdB"
IsExpanded="{Binding SelectedExpander,
Converter={StaticResource Expander2Bool},
ConverterParameter=B}">
<TextBlock Text="Text int Expander B"/>
</Expander>
<Expander Header="Expander C" Grid.Row="2" Background="LightYellow" x:Name="ExpdC"
IsExpanded="{Binding SelectedExpander,
Converter={StaticResource Expander2Bool},
ConverterParameter=C}">
<TextBlock Text="Text int Expander C"/>
</Expander>
</Grid>
</Window>

(4)実行
20130121_5
ExpanderBをクリック↓
20130121_6
・RowDefinitionの設定がめんどくさい。
・ExpanderのIsExpandedとほぼ同じではあるので、どうにかならないか?
まあ、カスタムコントロール化したり、ControlTemplate化するのも1つの手ではありそうだけど。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


サイドバー背後固定表示サンプル

当ブログに書かれたソースコードは流用自由です。

バグ、スペルミス等はありうる事です。

ご利用の際は自己責任でお願いしますm(_ _)m

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。