'How to 'wrap' the columns (not the text/content) of a WPF DataGrid?
I'm trying to implement a solution where I can 'wrap' a WPF DataGrid - by that I mean that the entire columns and rows wrap, not their text or content (see image below).
I have a set of data comprised of columns and rows (with column headers) that I want to wrap when constricting the window's width constraints, rather than instead using a horizontal scroll bar which would mean data is presented off-screen.
I had a look at using a WrapPanel as the ItemsPanelTemplate of my DataGrid, however I was not able to build on this further to achieve what I wanted.
<DataGrid.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</DataGrid.ItemsPanel>
If there is an answer that achieves what I want using another control, i.e. a ListView or GridView without compromises, I would be happy with that.
My current solution is to manually modify my ItemsSource and break that up, and then create multiple DataGrids of a pre-determined size, which is not very flexible.
Solution 1:[1]
Testing DataGrid.ItemsPanel and WrapPanel
The DataGrid only displays rows of data. So, if we set WrapPanel.Orientation to "Horizontal":
<DataGrid x:Name="dg" ItemsSource="{Binding _humans}">
<DataGrid.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</DataGrid.ItemsPanel>
</DataGrid>
Each row will be positioned side by side horizontally:

ListView and GridView
If we want to display Property columns separately, we should use a ListView for each property. The GridView will be used for displaying the header. In the XAML document we set a Name for the WrapPanel.
<Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<WrapPanel Name="wraper" Orientation="Horizontal">
</WrapPanel>
</ScrollViewer>
</Grid>
The code behind is implemented as:
public partial class MainWindow : Window
{
private IList<Human> _humans;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
CreateHumans();
wraper.Children.Add(CreateListViewFor("FirstName"));
wraper.Children.Add(CreateListViewFor("LastName"));
wraper.Children.Add(CreateListViewFor("Age"));
wraper.Children.Add(CreateListViewFor("Bday", "Birthday"));
wraper.Children.Add(CreateListViewFor("Salary"));
wraper.Children.Add(CreateListViewFor("Id", "Identification"));
}
private void CreateHumans()
{
_humans = new List<Human>();
for (int i = 10; i < 20; i++)
{
var human = new Human();
human.FirstName = "Zacharias";
human.LastName = "Barnham";
human.Bday = DateTime.Parse("1.3.1990");
human.Id = "ID-1234-zxc";
human.Salary = 2_000_000;
_humans.Add(human);
}
}
private ListView CreateListViewFor(string propertyName, string header)
{
var lv = new ListView();
var gv = new GridView();
lv.ItemsSource = _humans;
lv.View = gv;
lv.SelectionChanged += UpdateSelectionForAllListViews;
gv.Columns.Add(new GridViewColumn() { Header = header, DisplayMemberBinding = new Binding(propertyName), Width = 100 });
return lv;
}
private ListView CreateListViewFor(string propertyName)
{
return CreateListViewFor(propertyName, propertyName);
}
private void UpdateSelectionForAllListViews(object sender, SelectionChangedEventArgs e)
{
int index = (sender as ListView).SelectedIndex;
foreach (var child in wraper.Children)
{
(child as ListView).SelectedIndex = index;
}
}
}
We call our CreateListViewFor() method to provide ListView objects to the WrapPanel. We create a callback for the Selection Changed event to update the selection for each list. The styling is up to you.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | nonameyet |


