I have a WPF DataGrid
that contains some data. I would like to set the width of the columns such that the content fits in and never gets cropped (instead, a horizontal scroll bar should become visible). Additionally, I want the DataGrid
to fill the whole place available (I am working with a DockPanel
). I am using the following code (simplified):
<DataGrid ItemsSource="{Binding Table}">
<DataGrid.Columns>
<DataGridTextColumn MinWidth="100" Width="Auto" Header="Column 1" Binding="{Binding Property1}" />
<DataGridTextColumn MinWidth="200" Width="Auto" Header="Column 2" Binding="{Binding Property2}" />
</DataGrid.Columns>
</DataGrid>
This apparently does not work out of the box with Width="Auto"
as it always looks something like this:
This obviously looks ugly. I would like to have the whole row selected, or, which would be much better, the columns to fill the whole width, but as one can see, this does not work.
If I use Width="*"
instead, the content of the columns gets cropped which is even worse for me.
I found a similar question here, and a workaround was posted there. This may work, but I am working with the MVVM pattern, so the ItemsSource gets updated in the ViewModel and I cannot think about a way of doing it from there, because I cannot access the ActualWidth
property of the DataGridColumn
. Also, I would like to do it only in XAML if possible.
I would appreciate any help. Thanks!
Edit: As I still don't have a clue what to do about it, I start a small bounty. I would be very happy about a suggestion what one could do about my problem. Thanks again!
Edit 2: After saus' answer I thought about the options again. The problem is that I need to update the Width
and the MinWidth
properties also during the application is running, so not only after loading the window. I already tried to do something like
column.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
column.MinWidth = column.ActualWidth;
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
in some event that is fired when the underlying ItemsSource
of the DataGrid
is updating. However, this does not work, as the ActualWidth
property does not seem to change after setting the Width
on Auto
. Is there an option to somehow "repaint" it in order to get the ActualWidth
property updated? Thanks!
I would suggest using the workaround that you linked to. It does work. In the codebehind of your view, add the following to the constructor after InitializeComponent():
Griddy.Loaded += SetMinWidths; // I named my datagrid Griddy
and define SetMinWidths as:
public void SetMinWidths(object source, EventArgs e )
{
foreach (var column in Griddy.Columns)
{
column.MinWidth = column.ActualWidth;
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
}
}
I'm assuming that the reason you don't want to use this solution is that you believe that MVVM prohibits code in the codebehind. But since this is entirely view specific logic I believe that it is justified in this case. The whole "MVVM prohibits code in the codebehind" thing is a bit of a misconception.
But if you are bound by style guides, or you want this logic to be available for all datagrids in your app, you can make an attached behaviour that does the job like this:
public class SetMinWidthToAutoAttachedBehaviour
{
public static bool GetSetMinWidthToAuto(DependencyObject obj)
{
return (bool)obj.GetValue(SetMinWidthToAutoProperty);
}
public static void SetSetMinWidthToAuto(DependencyObject obj, bool value)
{
obj.SetValue(SetMinWidthToAutoProperty, value);
}
// Using a DependencyProperty as the backing store for SetMinWidthToAuto. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SetMinWidthToAutoProperty =
DependencyProperty.RegisterAttached("SetMinWidthToAuto", typeof(bool), typeof(SetMinWidthToAutoAttachedBehaviour), new UIPropertyMetadata(false, WireUpLoadedEvent));
public static void WireUpLoadedEvent(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var grid = (DataGrid)d;
var doIt = (bool)e.NewValue;
if (doIt)
{
grid.Loaded += SetMinWidths;
}
}
public static void SetMinWidths(object source, EventArgs e)
{
var grid = (DataGrid)source;
foreach (var column in grid.Columns)
{
column.MinWidth = column.ActualWidth;
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
}
}
}
and then for any datagrid that you want to apply this behaviour to, add the following:
<DataGrid ItemsSource="{Binding Table}" local:SetMinWidthToAutoAttachedBehaviour.SetMinWidthToAuto="true">
And then your conscience, as well as your codebehind, will be clear.
Use a TextBlock with text wrapping inside template column:
<DataGrid ItemsSource="{Binding Table}">
<DataGrid.Columns>
<DataGridTextColumn MinWidth="100" Width="Auto" Header="Column 1"
Binding="{Binding Property1}" />
<DataGridTemplateColumn Width="*" Header="Column 2">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Property2}" TextWrapping="Wrap" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
how about this:
for your columns' MinWidth, you setup a binding to the dataGrid's ScrollContent's Width, with a converter that divide this width by the number of columns
you need some code behind for the converter, but this would keep your MVVM structure intact.
might be a long shot though, did not have the time to try it.
Edit: I put some more thought into this and there a pb: it will not work well if you have, say, one huge column and a few small ones. I assumed that all cols are the same width, which is obviously not the case here.
You might want to explore this way though. Using bindings on the Widths with converters is about the only thing I can think of that could work: since you basically have 2 conditions to take into account when calculating a column's width, there will be no easy way to do this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With