none
Expander のHeaderTemplateでBinding RRS feed

  • 質問

  • 初歩的な質問ですみません。

    ユーザーコントロール内のExpanderのヘッダーにTextBoxを配置し、DataContext(ViewModel)のStringプロパティとバインドさせたいと考えています。
    そこで、ExpanderにDataTemplateを設定し、TextBoxを配置して、ViewModelのプロパティとバインドさせるため、下記のように記述したのですが、バインドできず、空のTextBoxが表示されてしまいました。
    (TitleStringというのは、ViewModelのString型のプロパティです。set 時にOnPropertyChanged("TitleString"); を行っています)


    <UserControl>
        <UserControl.Resources>
            <DataTemplate x:Key="ExpanderDataTemplate">
                <Grid>
                    <TextBox Text="{Binding  Path=TitleString }"/>
                </Grid>
            </DataTemplate>
        </UserControl.Resources>
    
        <Grid x:Name="LayoutRoot">
            <Expander HeaderTemplate="{DynamicResource ExpanderDataTemplate}">
                (Expanderの中身)
            </Expander>
        </Grid>
    </UserControl>
    

    試しに、下記のようにExpanderの中身としてTextBoxを配置し、UserControlのDataContext(ViewModel)のプロパティをバインドさせると中身が反映されますので、このユーザーコントロールのDataContextには正しく値が設定されているようです。

    <TextBox x:Name="Title1" Text="{Binding TitleString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    
    
    
    



    DataTemplateの書式が間違ってますか?

    おわかりになりましたら、ご指摘ください。よろしくお願いします。
    • 編集済み NIM5 2009年8月4日 15:54
    2009年8月4日 13:10

回答

  • 以下が参考になると思います。

    How can I have nested data templates?
    http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/634fffa3-7e70-4cc3-9a1c-5405e4747e2a


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    • 回答としてマーク NIM5 2009年8月5日 6:55
    2009年8月4日 16:30
    モデレータ
  • Header の方に Path がいらないんじゃないですかね。
    <Expander ... Header="{Binding}" ...>

    <TextBox Text="{Binding Path=TitleString, ...}"/>

    Header に TitleString をバインドしてしまうと、TextBox が参照できるのは String オブジェクトそのものになってしまって書き戻すにもどこに書き戻せばいいのか分からないことになってしまいます。。
    • 回答としてマーク NIM5 2009年8月5日 6:55
    2009年8月5日 6:30

すべての返信

  • 以下が参考になると思います。

    How can I have nested data templates?
    http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/634fffa3-7e70-4cc3-9a1c-5405e4747e2a


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    • 回答としてマーク NIM5 2009年8月5日 6:55
    2009年8月4日 16:30
    モデレータ
  • 返信ありがとうございます。
    ご指摘いただいたページや、HeaderedContentControl.Header のヘルプなどを参考にして試してみたところ、下記のように記述すると、DataContextのプロパティに応じてExpander内のTextBlockの文字が更新されました。

    <UserControl.Resources>
      <DataTemplate x:Key="ExpanderDataTemplate">
        <TextBox Text="{Binding Mode=OneWay}" />
      </DataTemplate>
    </UserControl.Resources>
    
    <Grid x:Name="LayoutRoot">
      <Expander HeaderTemplate="{DynamicResource ExpanderDataTemplate}" Header="{Binding TitleString}">
        (中身)
      </Expander>

    しかし、DataContext → ExpanderのTextBox.Text の一方向バインドのため、Expander内のTextBoxに文字を入力しても、DataContextのプロパティには反映されません。
    そこで、双方向バインドにすべく、下記のように書き直したところ、{"両方向のバインドには、Path または XPath が必要です。"} という例外が発生してしまいました。

    <DataTemplate x:Key="ExpanderDataTemplate">
      <TextBox Text="{Binding Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </DataTemplate>
    


    バインド先のPathというのは、この場合何を指定すればいいでしょうか?
    バインドしたいDataContextのプロパティは、String TitleString;  なので、

    {Binding Path = TitleString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

    としてみましたが、空のTextBoxが表示されてしまいました。
    • 編集済み NIM5 2009年8月5日 5:53
    2009年8月5日 5:49
  • Path=. と指定してみて下さい。
    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2009年8月5日 5:57
    モデレータ
  • 早速の返信ありがとうございます。

    <DataTemplate x:Key="ExpanderDataTemplate">
      <TextBox Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </DataTemplate>
    上記のように記述したところ、例外は発生しませんでしたが、片方向バインドになってしまいました。
    つまり、DataContext → ExpanderのTextBox は 反映されるのですが、ExpanderのTextBox → DataContext は反映されません。
    フォーカスを外しても反映されてないので、アップデートのタイミングというわけでもなさそうです。

    ↓もやってみましたが、なぜか OneWayになってしまいますね・・・

      <TextBox Text="{Binding Path=.}"/>
      <TextBox Text="{Binding Path=., Mode=Default}"/>

    なにか、別の所で問題があるのかと思い、XAMLのみでシンプルなテストをしてみました。
    全コードは↓です。

    <Window
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	x:Class="WPF_Expander.MainWindow"
    	x:Name="Window"
    	Title="MainWindow"
    	mc:Ignorable="d">
    
    	<Window.Resources>
    		<DataTemplate x:Key="DataTemplate1">
    			<TextBox Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    		</DataTemplate>
    	</Window.Resources>
    
    	<StackPanel x:Name="LayoutRoot">
    		<Expander Header="{Binding Text, ElementName=MainWndText, Mode=Default}" HeaderTemplate="{DynamicResource DataTemplate1}">
    			<Grid/>
    		</Expander>
    		<TextBox x:Name="MainWndText" Text="TextBox" />
    	</StackPanel>
    </Window>

    これでもやはり、 MainWindowのTextBox → ExpanderのTextBox は反映されますが、ExpanderのTextBox → MainWindowのTextBox は反映されません・・・
    Binding Path=. を、Binding . としても結果は同じでした。

    2009年8月5日 6:29
  • Header の方に Path がいらないんじゃないですかね。
    <Expander ... Header="{Binding}" ...>

    <TextBox Text="{Binding Path=TitleString, ...}"/>

    Header に TitleString をバインドしてしまうと、TextBox が参照できるのは String オブジェクトそのものになってしまって書き戻すにもどこに書き戻せばいいのか分からないことになってしまいます。。
    • 回答としてマーク NIM5 2009年8月5日 6:55
    2009年8月5日 6:30
  • ありがとうございます。
    Hongliang さんのおっしゃるとおり、Header ではPathを指定せず、DataTemplate でPathを指定したところ、思った通りの動作になりました。

    ↓のテストでも、実際のアプリでも、どちらでもうまく双方向バインドしてくれました。

    <Window
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	x:Class="WPF_Expander.MainWindow"
    	x:Name="Window"
    	Title="MainWindow"
    	mc:Ignorable="d">
    
    	<Window.Resources>
    		<DataTemplate x:Key="DataTemplate1">
    			<TextBox Text="{Binding Text, ElementName=MainWndText,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    		</DataTemplate>
    	</Window.Resources>
    
    	<StackPanel x:Name="LayoutRoot">
    		<Expander Header="{Binding}" HeaderTemplate="{DynamicResource DataTemplate1}">
    			<Grid/>
    		</Expander>
    		<TextBox x:Name="MainWndText" Text="TextBox" />
    		<TextBox x:Name="MainWndText2" Text="{Binding Text, ElementName=MainWndText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    	</StackPanel>
    </Window>
    Hongliangさん、trapemiyaさん、ありがとうございました。
    2009年8月5日 6:55