views:

911

answers:

2

For my WPF Toolkit DataGrid I use the following custom column header style:

<Style x:Name="ColumnStyle" x:Key="ColumnHeaderStyle" TargetType="my:DataGridColumnHeader">
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <StackPanel Orientation="Vertical" HorizontalAlignment="Stretch" Background="LightYellow">
                    <TextBlock Text="{Binding Name}" HorizontalAlignment="Stretch" TextAlignment="Left" Background="LightGreen" />
                    <TextBlock Text="{Binding Data}" HorizontalAlignment="Stretch" TextAlignment="Right" Background="LightBlue" />
                </StackPanel>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

The header's DataContext is set in code and the Name and Data string properties are displayed correctly using the initial width of the DataGrid like this:

---------------
|Name         |
|         Data|
---------------

However, when I resize the column, the header display does not reflow, but instead remains the same:

--------------------
|Name              |
|         Data     |
--------------------

where as I expected it to looks like this:

--------------------
|Name              |
|              Data|
--------------------

What do I need to do to get the desired behavior above?

Similarly, the header content doesn't seem to stretch in the vertical direction either.

Update: Adding

    <Setter Property="VerticalAlignment">
        <Setter.Value>Bottom</Setter.Value>
    </Setter>

to the Style seems to properly align the header to the bottom. Unfortunately, setting the HorizontalAlignment property in the fashion to 'Stretch' doesn't seem to do what I am looking for.

Details for Repro: Below are code snippets that demonstrate the behavior.

Window1.xaml:

<Window x:Class="GridTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300" xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit"&gt;
    <Window.Resources>
        <Style x:Name="ColumnStyle" x:Key="ColumnHeaderStyle" TargetType="my:DataGridColumnHeader">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <DockPanel>
                            <TextBlock DockPanel.Dock="Left" Text="{Binding Name}" />
                            <TextBlock DockPanel.Dock="Right" Text="{Binding Data}" />
                        </DockPanel>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Name="RowHeaderStyle" x:Key="RowHeaderStyle" TargetType="my:DataGridRowHeader">
            <Setter Property="Content" Value="{Binding}" />
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Path=Content.Name, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type my:DataGridRowHeader}}}" 
                                       VerticalAlignment="Center"/>
                            <TextBlock Padding="5">|</TextBlock>
                            <TextBlock Text="{Binding Path=Content.Data, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type my:DataGridRowHeader}}}"
                                       VerticalAlignment="Center"/>
                        </StackPanel>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <my:DataGrid Name="dg" 
                     ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}"
                     RowHeaderStyle="{StaticResource RowHeaderStyle}"
                     HeadersVisibility="All">
        </my:DataGrid>
    </Grid>
</Window>

and the code-behind in Window1.xaml.cs

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
using Microsoft.Windows.Controls;
using SLModel;

namespace GridTest
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            this.Loaded += new RoutedEventHandler(Window1_Loaded);
        }

        void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            Inputs = new List<Input>();
            Outputs = new List<Output>();

            Input i1 = new Input() { Name = "I 1", Data = "data 1" };
            Input i2 = new Input() { Name = "I 2", Data = "data 2" };
            Input i3 = new Input() { Name = "I 3", Data = "data 3" };


            Inputs.Add(i1); Inputs.Add(i2);

            Output o1 = new Output() { Name = "O 1", Data = "data 1" };
            Output o2 = new Output() { Name = "O 2", Data = "data 2" };
            Output o3 = new Output() { Name = "O 3", Data = "data 3" };

            Outputs.Add(o1); Outputs.Add(o2); Outputs.Add(o3);

            Relationship r1 = new Relationship() { Formula = "F1" };
            Relationship r2 = new Relationship() { Formula = "F2" };
            Relationship r3 = new Relationship() { Formula = "F3" };
            Relationship r4 = new Relationship() { Formula = "F4" };
            Relationship r5 = new Relationship() { Formula = "F5" };
            Relationship r6 = new Relationship() { Formula = "F6" };


            i1.Relationships.Add(r1);
            i1.Relationships.Add(r2);
            i2.Relationships.Add(r3);
            i2.Relationships.Add(r4);
            i3.Relationships.Add(r5);
            i3.Relationships.Add(r6);

            CreateColumn(o1, 0);
            CreateColumn(o2, 1);
            CreateColumn(o3, 2);

            dg.Items.Add(i1);
            dg.Items.Add(i2);
            dg.Items.Add(i3);
            dg.ColumnWidth = DataGridLength.SizeToHeader;
        }

        private void CreateColumn(Output output, int index)
        {
            Binding textBinding = new Binding();
            textBinding.Path = new PropertyPath(string.Format("Relationships[{0}].Formula", index));
            textBinding.Mode = BindingMode.TwoWay;

            DataGridTextColumn tc = new DataGridTextColumn();
            tc.Binding = textBinding;
            dg.Columns.Add(tc);
            tc.Header = output;
        }

        private List<Output> Outputs { get; set; }
        private List<Input> Inputs { get; set; }
    }
}

With simple classes Input, Output, and Relationship as such:

public class Input { public Input() { Relationships = new ObservableCollection(); }

public string Name { get; set; }
public string Data { get; set; }

public ObservableCollection<Relationship> Relationships { get; set; }

}

public class Output { public Output() { }

public string Name { get; set; }
public string Data { get; set; }

}

public class Relationship { public Relationship() { } public string Formula { get; set; } }

Repro steps:

  1. Launch application

  2. Observe column headers 'O 1data 1', 'O 2data 2', and 'O 3data 3'

  3. Make first column wider by dragging the column separator to the right

  4. Observe that the distance between the 'Name' TextBlock (in this case 'O 1') and the 'Data' TextBlock ('data 1') is not changing, i.e., the 'Data' TextBlock is not 'docked' to the right edge of the column header.

A: 

Use a dock panel instead

<DockPanel Background="LightYellow">
   <TextBlock DockPanel.Dock="Left" Text="{Binding Name}" TextAlignment="Left" Background="LightGreen" />
   <TextBlock DockPanel.Dock="Right" Text="{Binding Data}" HorizontalAlignment="Right" TextAlignment="Right" Background="LightBlue" />
</DockPanel >
Aran Mulholland
That doesn't seem to make any difference. I've added DockPanel.Dock="Top" to each of the elements, but they still won't span the entire column width when the column is resized beyond the initial size.Is there some other attribute I have to set to get things to expand correctly?
Philipp Schmid
sorry left out the DockPanel.Dock="Left" and "Right", edited post accordingly
Aran Mulholland
In my real example, I actually have 4 items that are stacked vertically, so I was setting DockPanel.Dock="Top" for all of them. But that didn't make them expand horizontally.
Philipp Schmid
I believe the real problem is that the DockPanel is not stretching when the DataGridColumn is widened by the user. And therefore the items inside the DockPanel also don't stretch (regardless of their docking strategy, left-right, or top-bottom).
Philipp Schmid
i dont quite understand, why would setting DockPanel.Dock="Top" make them expand horizontally? you have to dock them to the left and right. If you put one button in the header and dont set a size it will fill to take up all available space, right? you should get KaXaml or xamlPad and experiment with this stuff as i think its a layout issue and not related to the datagrid at all. Will my suggestion (code above) work for just the two elements?
Aran Mulholland
i know we have datagrid headers responding to sizing changes correctly, ill check them tommorow when i get to work, have you modified the control template?
Aran Mulholland
I have modified my code to dock left and right. This puts both elements next to each other rather than on top of each other. However, it still doesn't expand as I widen the column manually.
Philipp Schmid
have you modified the control template? if so can i have a look at it
Aran Mulholland
+1  A: 

I suggest replacing your StackPanel with a Grid:

<Style x:Name="ColumnStyle" x:Key="ColumnHeaderStyle" TargetType="my:DataGridColumnHeader">
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Name}" Background="LightGreen" />
                    <TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding Data}" Background="LightBlue" />
                </Grid>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

If that doesn't work, you may need to edit the ControlTemplate for the DataGridColumnHeader. I'm not sure what the default template looks like, but if the ContentPresenter is not located inside a stretchable container (like a Grid), it won't matter if you stretch what's inside the ContentPresenter, it won't stretch. But one thing I'm pretty sure of, StackPanels don't stretch even if you tell them to, so definitely try a Grid in your DataTemplate first.

Update (fixed)

Okay, I dug up the default ControlTemplate for DataGridColumnHeader. It turns out is does use a Grid, so I don't think that is the problem.

The key might be the ContentPresenter:

<ContentPresenter
    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />

You might just need to set HorizontalContentAlignment="Stretch" on your DataGridColumnHeader style.

DanM
@Phillip, I think you're going to need to change the `ControlTemplate` as well. Please see the update to my answer.
DanM
It looks like you grabbed the control template for the row header instead. I'll find the one for the column header and using your instructions will verify what I believe to be the correct solution.Why is an expanding panel not the default I wonder?
Philipp Schmid
Oh, you're right. Sorry about that it. It looks like the column header uses a Grid, not a StackPanel too. Please see my latest update.
DanM
That was it!Adding <Setter Property="HorizontalContentAlignment"> <Setter.Value>Stretch</Setter.Value> </Setter>to my DataGridColumnHeader style did the trick (and it was simple too!).Thanks for your help and persistence!
Philipp Schmid