8

I define a headertemplate into a wpf groupbox and the databinding doesn't work. I don't understand why.

<GroupBox>
<GroupBox.HeaderTemplate>
            <DataTemplate>
            <StackPanel Orientation="Horizontal" >
                <Image Source="/PopuAssuNetApplication.UI.Control;component/Images/Members.png" Width="24" />
                <TextBlock VerticalAlignment="Center">
                                <TextBlock.Text>
                                        <MultiBinding StringFormat="{x:Static Member=resx:Resources.PersonsInContractGroupBox}"> 
                                            <Binding Path="CurrentContract.Federation" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}">
                                            </Binding>
                                            <Binding Path="CurrentContract.Type" Converter="{StaticResource contractTypeConverter}" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}">
                                            </Binding>
                                            <Binding Path="CurrentContract.Number" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}">
                                            </Binding>
                                        </MultiBinding>
                                    </TextBlock.Text>
                </TextBlock>
                <WpfComponent:WaitControl Margin="7,0,0,0" VerticalAlignment="Top" Width="24" Height="24" MarginCenter="4">
                    <WpfComponent:WaitControl.Style>
                        <Style>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding Path=IsMembersOfContractBusy, UpdateSourceTrigger=PropertyChanged, ElementName=PersonsInContract}" Value="true">
                                    <Setter Property="WpfComponent:WaitControl.Visibility" Value="Visible" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Path=IsMembersOfContractBusy, UpdateSourceTrigger=PropertyChanged, ElementName=PersonsInContract}" Value="false">
                                    <Setter Property="WpfComponent:WaitControl.Visibility" Value="Collapsed" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </WpfComponent:WaitControl.Style>
                </WpfComponent:WaitControl>
            </StackPanel>
                </DataTemplate>
        </GroupBox.HeaderTemplate>

Coolweb
  • 485
  • 1
  • 6
  • 16

5 Answers5

28

The problem is that the HeaderTemplate is used for templating the Header thus within the HeaderTemplate your DataContext is whatever you bind or assign to the Header property of your GroupBox.

Think of the Header property as almost like the DataContext for the header of the control. Normally the DataContext property inherits its value from its parent but since not every control has a Header the Header is blank unless you set it.

By binding your Header explicitly to the current DataContext Header="{Binding}" your example should work as you expect. To help illustrate how this works I've created a simple example below that shows how the Header and DataContext work independently from each other for providing data to either the body or header of the control.

<GroupBox Header="HEADER TEXT" DataContext="BODY TEXT">
    <GroupBox.HeaderTemplate>
        <DataTemplate>
            <Button Content="{Binding}"
                    Background="LightGreen" />
        </DataTemplate>
    </GroupBox.HeaderTemplate>

    <CheckBox HorizontalAlignment="Center"
                VerticalAlignment="Center" Content="{Binding}" />
</GroupBox>

This will yield a GroupBox that looks like the following.

GroupBox with templated header

I think that by default in databinding, wpf always gets data from the DataContext property. Seems not in datatemplate

Your assumption is correct about DataContext and it does work in the DataTemplate as I've demonstrated it's just that in the Header's template the DataContext is the value from the Header Property and not the DataContext itself.

Igor Popov
  • 9,795
  • 7
  • 55
  • 68
jpierson
  • 16,435
  • 14
  • 105
  • 149
  • 2
    +1, this information really helps with how the header works in group boxes! – Mas Sep 09 '11 at 09:43
  • Note that these observations also hold true in general for any HeaderedContentControl as well. – jpierson Feb 27 '13 at 03:49
  • The most important take away from this answer is that you need to set the Header="{Binding}" if you are going to use the HeaderTemplate. – stricq Feb 19 '18 at 21:01
9

The GroupBox does not have a member called "CurrentContract". Most probably, you want to accesss a property called "CurrentContract" from the corresponding ViewModel?! The ViewModel is the GroupBox's DataContext, so you have to change the Binding Paths to something like...

<Binding Path="DataContext.CurrentContract.Type" Converter="{StaticResource contractTypeConverter}" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}">
gehho
  • 9,049
  • 3
  • 45
  • 59
  • 1
    Thanks. I think that by default in databinding, wpf always gets data from the DataContext property. Seems not in datatemplate. – Coolweb Mar 11 '10 at 13:40
  • 1
    Or alternatively you can bind the DataContext to the Header by doing something like Header="{Binding}" in your GroupBox declaration. This way your DataTemplate will work as is. See my Answer for more details. – jpierson Feb 12 '11 at 02:56
5
    <GroupBox >
        <GroupBox.HeaderTemplate>
            <DataTemplate>
                    <RadioButton Content="myR"
                            IsChecked="{Binding rIsChecked, Mode=TwoWay}"
                            DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}}" />
            </DataTemplate>
        </GroupBox.HeaderTemplate>
        <GroupBox.Content>
            <Grid IsEnabled="{Binding rIsChecked}">
            </Grid>
        </GroupBox.Content>
    </GroupBox>

Just propagate the GroupBox DC to the DataTemplate content...works like a charm...

frankidoze
  • 59
  • 1
  • 1
1

The lesson learned above is useful in general for DataTemplates, but I actually found out recently there is a better way to change the header of a groupbox:

<GroupBox>
    <GroupBox.Header>
        <CheckBox IsChecked="{Binding Path=mSomeBoolean}"/>
    </GroupBox.Header>
</GroupBox>

This way there is no need to define a relative source in the bindings.

Also please note this issue with GroupBoxes and the header.

Community
  • 1
  • 1
Andre Luus
  • 3,692
  • 3
  • 33
  • 46
  • Setting visual content directly on the Header property works just fine but there is no reason to avoid the HeaderTemplate if you understand how it works. Please see my answer for an explanation. – jpierson Feb 12 '11 at 03:03
0

This is what worked for me:

<HeaderedContentControl Header="{Binding}" Style="{StaticResource TallHeaderedContentStyle}">
  <HeaderedContentControl.HeaderTemplate>
    <DataTemplate>
      <TextBlock Text="{Binding Path=HeaderText"} />
    </DataTemplate>
  </HeaderedContentControl.HeaderTemplate>
stricq
  • 798
  • 6
  • 18