Search code examples
wpfformsdatagridwindowexpander

How to add button at the right corner of Expander - WPF DataGrid


I would like to know how to put button to the right part of my expander, here is picture of how does it look when my expander is collapsed (I need buttons no matter is it expanded or not).

enter image description here

I need to get this, so when user click on button I must get ID of order and do some action! enter image description here

Here is my code:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="10*" />
    </Grid.ColumnDefinitions>

    <DataGrid IsSynchronizedWithCurrentItem="False" Grid.Column="0" VerticalGridLinesBrush="Transparent"  RowHeaderWidth="0" CanUserAddRows="False" AutoGenerateColumns="False"  x:Name="datagrid1" Margin="10,150,8,50" Background="Transparent" RowBackground="#FF494949"  VerticalContentAlignment="Center" HorizontalContentAlignment="Center" ItemsSource="{Binding}">
        <DataGrid.Resources>
            <Style TargetType="{x:Type DataGridColumnHeader}">
                <Setter Property="Background" Value="Black"/>
                <Setter Property="Opacity" Value="0.5"/>
                <Setter Property="Foreground" Value="White"/>
                <Setter Property="HorizontalContentAlignment" Value="Center" />
                <Setter Property="FontSize" Value="15"/>
                <Setter Property="FontFamily" Value="Arial"/>
                <Setter Property="Height" Value="50"/>
            </Style>


    <Style x:Key="TextInCellCenter" TargetType="{x:Type TextBlock}" >
        <Setter Property="TextAlignment" Value="Center"/>
    </Style>

    <Style TargetType="{x:Type TextBlock}" x:Key="RightAligElementStyle">
        <Setter Property="TextAlignment" Value="Right"/>
    </Style>

    <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#83D744"/>

    </DataGrid.Resources>

        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Title}"       Header="Product Name"   Width="0.25*"   Foreground="White" FontSize="20"  FontFamily="Verdana" />
            <DataGridTextColumn Binding="{Binding Quantity}"    ElementStyle="{StaticResource TextInCellCenter}"     Header="Quantity"    Foreground="White"     Width="0.15*"       FontSize="20" FontFamily="Verdana" />
            <DataGridTextColumn Binding="{Binding User}"        ElementStyle="{StaticResource TextInCellCenter}"     Header="User"        Foreground="White"     Width="0.20*"       FontSize="20" FontFamily="Verdana" />
        </DataGrid.Columns>

    <DataGrid.GroupStyle>
        <!-- Style for groups at top level. -->
        <GroupStyle>
            <GroupStyle.ContainerStyle>
                <Style TargetType="{x:Type GroupItem}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type GroupItem}">
                                <Expander IsExpanded="True"  Background="#83D744">
                                    <Expander.Header>
                                        <DockPanel Height="50">
                                            <TextBlock FontWeight="Bold" FontSize="20" Height="25"  Foreground="Black" Text="{Binding Path=Name,StringFormat= OrderNumber:# {0}}" />
                                        </DockPanel>
                                    </Expander.Header>
                                    <Expander.Content>
                                        <ItemsPresenter />
                                    </Expander.Content>
                                </Expander>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </GroupStyle.ContainerStyle>
        </GroupStyle>
    </DataGrid.GroupStyle>
</DataGrid>

Thanks guys, Cheers

After Joe's answer I achieved this, but how come there is no button on last item in a DataGrid, take a look at this screenshot: enter image description here

CODE:

<Grid>
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="10*" />
</Grid.ColumnDefinitions>

<DataGrid IsSynchronizedWithCurrentItem="False" Grid.Column="0" VerticalGridLinesBrush="Transparent"  RowHeaderWidth="0" CanUserAddRows="False" AutoGenerateColumns="False"  x:Name="datagrid1" Margin="10,150,8,50" Background="Transparent" RowBackground="#FF494949"  VerticalContentAlignment="Center" HorizontalContentAlignment="Center" ItemsSource="{Binding}">
    <DataGrid.Resources>
        <Style TargetType="{x:Type DataGridColumnHeader}">
            <Setter Property="Background" Value="Black"/>
            <Setter Property="Opacity" Value="0.5"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="HorizontalContentAlignment" Value="Center" />
            <Setter Property="FontSize" Value="15"/>
            <Setter Property="FontFamily" Value="Arial"/>
            <Setter Property="Height" Value="50"/>
        </Style>


<Style x:Key="TextInCellCenter" TargetType="{x:Type TextBlock}" >
    <Setter Property="TextAlignment" Value="Center"/>
</Style>

<Style TargetType="{x:Type TextBlock}" x:Key="RightAligElementStyle">
    <Setter Property="TextAlignment" Value="Right"/>
</Style>

<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#83D744"/>

</DataGrid.Resources>

    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Title}"       Header="Product Name"   Width="0.25*"   Foreground="White" FontSize="20"  FontFamily="Verdana" />
        <DataGridTextColumn Binding="{Binding Quantity}"    ElementStyle="{StaticResource TextInCellCenter}"     Header="Quantity"    Foreground="White"     Width="0.15*"       FontSize="20" FontFamily="Verdana" />
        <DataGridTextColumn Binding="{Binding User}"        ElementStyle="{StaticResource TextInCellCenter}"     Header="User"        Foreground="White"     Width="0.20*"       FontSize="20" FontFamily="Verdana" />
    </DataGrid.Columns>



    <DataGrid.GroupStyle>
        <!-- Style for groups at top level. -->
        <GroupStyle>
            <GroupStyle.ContainerStyle>
                <Style TargetType="{x:Type GroupItem}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type GroupItem}">
                                <Expander IsExpanded="True"  Background="Black" Opacity="0.7">
                                    <Expander.Header>
                                        <DockPanel Height="50" Width="{Binding
                                                          RelativeSource={RelativeSource
                                                          Mode=FindAncestor,
                                                          AncestorType={x:Type Expander}},
                                                          Path=ActualWidth}">
                                            <Button DockPanel.Dock="Right" Content="Test Button" Margin="0,0,28,0"/>
                                            <TextBlock FontWeight="Normal" FontFamily="Verdana" FontSize="20" Height="25" Foreground="#83D744" Text="{Binding Path=Name,StringFormat= Order Number:# {0}}" />
                                        </DockPanel>
                                    </Expander.Header>
                                    <Expander.Content>
                                        <ItemsPresenter />
                                    </Expander.Content>
                                </Expander>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </GroupStyle.ContainerStyle>
        </GroupStyle>
    </DataGrid.GroupStyle>
</DataGrid>

So for strange reason button on last item is not visible...


Solution

  • Ah, I've come across this problem before. If you select the DockPanel component you can quickly see the issue, the container that holds the header content is aligned "Left" instead of "Stretch", so the dock panel aligning something on the right doesn't result in what you want:

    enter image description here

    As the dock panel itself is stuck in "Left" alignment, even if we set it to "Stretch".

    A workaround is to use some bindings so set it's width:

       <Expander.Header>
            <DockPanel Height="50" Width="{Binding
                                    RelativeSource={RelativeSource
                                      Mode=FindAncestor,
                                      AncestorType={x:Type Expander}},
                                    Path=ActualWidth}">
                <Button DockPanel.Dock="Right" Content="Test" Margin="0,0,28,0"/>
                <TextBlock FontWeight="Bold" FontSize="20" Height="25"  Foreground="Black" Text="Heading" />
            </DockPanel>
        </Expander.Header>
    

    Resulting in:

    enter image description here

    However it's not ideal. Notice I've had to pad out the right of the button with Padding="0,0,28,0" otherwise it's pushed off the screen.

    A better solution would be to create your own style for the expander.

    Edit. Given the problems you're facing (which are really weird) I think the best solution is to re-template the expanded so it is correctly structured using a grid. Here's an example:

    <Expander.Template>
        <ControlTemplate TargetType="Expander">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Name="ContentRow" Height="0"/>
                </Grid.RowDefinitions>
    
                <Border Background="Green">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
    
                        <ToggleButton IsChecked="{Binding Path=IsExpanded,Mode=TwoWay,
                                        RelativeSource={RelativeSource TemplatedParent}}">
                            <ToggleButton.Template>
                                <ControlTemplate TargetType="ToggleButton">
                                    <Border Padding="6,4" Background="Transparent">
                                        <Path Name="Arrow"
                                                Fill="Black"
                                                HorizontalAlignment="Center"
                                                VerticalAlignment="Center"
                                                Data="M 0 0 L 8 8 L 16 0 Z"/>
                                    </Border>
                                    <ControlTemplate.Triggers>
                                        <Trigger Property="ToggleButton.IsMouseOver" Value="true">
                                            <Setter TargetName="Arrow" Property="Fill"
                                                    Value="LightGreen" />
                                        </Trigger>
                                        <Trigger Property="IsPressed" Value="true">
                                            <Setter TargetName="Arrow" Property="Fill"
                                                    Value="DarkGreen" />
                                        </Trigger>
                                        <Trigger Property="IsChecked" Value="true">
                                            <Setter TargetName="Arrow" Property="Data"
                                                    Value="M 0 8 L 8 0 L 16 8 Z" />
                                        </Trigger>
                                        <Trigger Property="IsEnabled" Value="False">
                                            <Setter TargetName="Arrow" Property="Opacity"
                                                    Value="0.5" />
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </ToggleButton.Template>
                        </ToggleButton>
                        <ContentPresenter Grid.Column="1"
                                            Margin="4" 
                                            ContentSource="Header" 
                                            RecognizesAccessKey="True" />
                    </Grid>
                </Border>
                <ContentPresenter Grid.Row="1"/>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="IsExpanded" Value="True">
                    <Setter TargetName="ContentRow" Property="Height"
                            Value="{Binding ElementName=Content,Path=DesiredHeight}" />
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Expander.Template>
    

    It means you have to change the way it looks (but it looks like you want to do that anyway). If you notice, the header ContentPresenter is in a grid, so stretches the full length of the Expander. I also had to do a custom toggle button.

    Here's the result:

    enter image description here