Can a WPF Datagrid control behave like a WinForms DataGridControl with respect to column layout?

Basically, I want WPF DataGrid to control the layout of its columns exactly as WinForms DataGridView does

And, more specifically, here is the behavior I'm looking for:

  • The grid control should occupy the space that it gave (i.e. no matter how much space in its parent element is available). Here, I mean only the control, not the columns.

  • Created columns (automatically or manually) may or may not occupy all this space.

  • If there is extra space left after creating the columns, the last column should not be expanded to fill this space

  • If there is extra space left after creating the columns, you do not need to create an empty column in which there is nothing to fill this extra space.

From what I can say, in WPF, the last two markers seem mutually exclusive, and you have to choose one or the other. Has anyone found a way to do both? I searched quite a bit and did not find absolutely what I was looking for, however, all the posts that I find tend to be a couple of years, so I hope someone guessed about it.

EDIT: sa_ddam213, here's a quick xaml project that I put together to check your suggestion.

<Window x:Class="DataGridFix.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:DataGridFix" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <ObjectDataProvider x:Key="data" ObjectType="{x:Type local:TestObject}" MethodName="GetTestData" /> </Window.Resources> <StackPanel> <DataGrid HorizontalAlignment="Left" ColumnWidth="Auto" Height="150" VerticalAlignment="Top" ItemsSource="{Binding Source={StaticResource data}}" /> </StackPanel> </Window> 

And here is the code behind:

namespace DataGridFix {public class TestObject {public int Id {get; set; } public string Name {get; set; }

  public static List<TestObject> GetTestData() { var items = new List<TestObject>(); items.Add(new TestObject() { Id = 1, Name = "Joe" }); items.Add(new TestObject() { Id = 2, Name = "Matt" }); items.Add(new TestObject() { Id = 3, Name = "Hal" }); return items; } } 

}

Indeed, the only remarkable thing that I see from your suggestion is to set HorizontalAlignment to the left. I did this and tried to set ColumnWidth for different settings, but had the same problem with each (except * of course ... technically I can ruin this, but I will not delve into it).

If you use the mouse to expand any of the columns, and then reduce the size of the column, an empty placeholder column will appear. The only difference I noticed from your post is that you put your DataGrids in the StackPanel, since you had more than one of them. I tried this just for this, but the same result. If you see any other difference between what I'm doing and what you suggested, let me know.

+6
source share
2 answers

To get the behavior you need, you probably have to modify the DataGrid management template.

Take a look at the code. I'm pretty close to the WinForms DataGridView look that I think.

To remove an extra column, you must remove the fill column from the DataGridColumnHeadersPresenter . I just commented on this. The rest is just the default template.

Another modification relates to the DataGrid. template DataGrid.
By setting HorizontalAlignment="Left" to ScrollContentPresenter , the rows no longer occupy the entire width of the control.

These are the only changes I made to the default templates.

enter image description here

 <Style TargetType="{x:Type DataGridColumnHeadersPresenter}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}"> <Grid> <!-- Remove this filler column --> <!--<DataGridColumnHeader x:Name="PART_FillerColumnHeader" IsHitTestVisible="False" />--> <ItemsPresenter /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="{x:Type DataGrid}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGrid}"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="True" Padding="{TemplateBinding Padding}"> <ScrollViewer Focusable="false" Name="DG_ScrollViewer"> <ScrollViewer.Template> <ControlTemplate TargetType="{x:Type ScrollViewer}"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Button Command="{x:Static DataGrid.SelectAllCommand}" Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=CellsPanelHorizontalOffset}" Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type DataGrid}, ResourceId=DataGridSelectAllButtonStyle}}" Focusable="false" Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.All}}" /> <DataGridColumnHeadersPresenter Grid.Column="1" Name="PART_ColumnHeadersPresenter" Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.Column}}"/> <!-- Set HorizontalAlignment="Left" to have the rows only take up the width they need and not fill the entire width of the DataGrid --> <ScrollContentPresenter HorizontalAlignment="Left" x:Name="PART_ScrollContentPresenter" Grid.Row="1" Grid.ColumnSpan="2" CanContentScroll="{TemplateBinding CanContentScroll}" /> <ScrollBar Grid.Row="1" Grid.Column="2" Name="PART_VerticalScrollBar" Orientation="Vertical" Maximum="{TemplateBinding ScrollableHeight}" ViewportSize="{TemplateBinding ViewportHeight}" Value="{Binding Path=VerticalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/> <Grid Grid.Row="2" Grid.Column="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=NonFrozenColumnsViewportHorizontalOffset}"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <ScrollBar Grid.Column="1" Name="PART_HorizontalScrollBar" Orientation="Horizontal" Maximum="{TemplateBinding ScrollableWidth}" ViewportSize="{TemplateBinding ViewportWidth}" Value="{Binding Path=HorizontalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/> </Grid> </Grid> </ControlTemplate> </ScrollViewer.Template> <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> </ScrollViewer> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> 

UPDATE
It really looks like the difference between .NET 4 and .NET 4.5.
I am developing on a Windows 8 computer with Visual Studio 2012, since as a test I tried to target .NET 4 to see if I can reproduce the wrong behavior. But it still worked fine.

Of course, I tried to run the application on another computer with .NET 4 installed, and empty lines appeared here when the column size increased and then the size was smaller.

The problem is that DataGridRows not behaving correctly. When working on a computer with .NET 4 installed, they retain their current size when the column is reduced. On .NET 4.5, they are changing as expected.

The new solution to get the behavior you need is actually much simpler than the previous one.

Just setting HorizontalAlignment on the DataGridRows to left and deleting the filler column, it works with both .NET 4 and .NET 4.5. And there is no need to replace the entire DataGrid template.

 <Style TargetType="DataGridRow"> <Setter Property="HorizontalAlignment" Value="Left" /> </Style> <Style TargetType="DataGridColumnHeadersPresenter"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}"> <Grid> <ItemsPresenter /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> 
+7
source

There are many layout options for columns in WPF, it's just a matter of choosing what you want to display.

  • Pixel
  • SizeToCells
  • SizeToHeader
  • Auto
  • Proportional (*)

And if you set HorizontalAlignment to Left , the Size of the DataGrid will resize to fit its contents based on the ColumnWidth that you selected.

Here is an example of some available column options

  <StackPanel> <DataGrid HorizontalAlignment="Left" ColumnWidth="100" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" /> <DataGrid HorizontalAlignment="Left" ColumnWidth="SizeToCells" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" /> <DataGrid HorizontalAlignment="Left" ColumnWidth="SizeToHeader" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" /> <DataGrid HorizontalAlignment="Left" ColumnWidth="Auto" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" /> <DataGrid HorizontalAlignment="Left" ColumnWidth="*" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" /> </StackPanel> 

enter image description here

+5
source

All Articles