I didn't know how to properly word my issue in a limited title so I will try to explain it as good as I can. I made gifs for showcases but noticed after I don't have enough reputation points.
EDIT: I have enough reputation now for gifs!
I have a WPF application where I make use of DataGrid to display a list of ViewModels. This DataGrid is populated with data from a folder that you can open with a FolderBrowserDialog attached to a button. When you've set the path before, the application remembers it and on startup it will populate the DataGrid automatically.
This means there are two states: opening the application without the DataGrid populated, and opening the application with the DataGrid populated. This is important to note for later on.
The problem that I'm trying to solve is that whenever the DataGrid is populated with data, I need all my columns to auto resize to the width of their content. However, the last column's width needs to fill all the remaining empty space so the row inside of the DataGrid is selectable throughout the whole width of the DataGrid. At the same time, when you resize the application to where the DataGrid's width becomes narrower than the total width of the columns combined, a horizontal scrollbar needs to appear so you can scroll through the colums with their width still at the same size as their content.
This needs to happen not only when the DataGrid gets populated with Data through the button, but also when the application starts with the data automatically populating the DataGrid.
My difficulties have been finding a solution that works for any situation.
I have tried many approaches but I'll name the most succesful ones:
Approach 1: Set width of all columns to auto, with the last column to fill ( * )
Code:
<Grid Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="3"
Background="#f7f5f2"
Margin="10">
<DataGrid x:Name="ModList"
ItemsSource="{Binding Mods}"
Style="{DynamicResource DataGridStyle1}"
CellStyle="{DynamicResource DataGridCellStyle1}"
ColumnHeaderStyle="{DynamicResource DataGridColumnHeaderStyle1}"
RowStyle="{DynamicResource DataGridRowStyle1}"
RowDetailsTemplate="{DynamicResource DataGridRowDetailsTemplate1}"
Margin="10"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.SelectDroppedItems="True"
dd:DragDrop.DropHandler="{Binding}"
dd:DragDrop.DropTargetAdornerBrush="#484D54">
<DataGrid.Columns>
<DataGridTemplateColumn x:Name="DataGridColumnEnabled"
Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="CheckBoxIsEnabled"
IsChecked="{Binding IsEnabled, UpdateSourceTrigger=PropertyChanged}"
Command="{Binding DataContext.ToggleCheckBoxCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn x:Name="DataGridColumnLoadorder"
Header="Loadorder"
IsReadOnly="True"
Width="Auto"
Binding="{Binding LoadOrder, UpdateSourceTrigger=PropertyChanged}"
CanUserSort="False"/>
<DataGridTextColumn x:Name="DataGridColumnMod"
Header="Mod"
IsReadOnly="True"
Width="Auto"
Binding="{Binding DisplayName}"
CanUserSort="False"
VirtualizingPanel.VirtualizationMode="Standard"/>
<DataGridTemplateColumn x:Name="DataGridColumnNotification"
IsReadOnly="True"
Width="Auto"
CanUserSort="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="/WarningIcon.png"
Height="16"
Width="16"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Cursor="Help"
Visibility="{Binding HasConflicts}">
<ToolTipService.ToolTip>
<ToolTip Content="Mod(s) detected altering the same asset(s). 
This may be intentional, please check before committing loadorder."/>
</ToolTipService.ToolTip>
</Image>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn x:Name="DataGridColumnAuthor"
Header="Author"
IsReadOnly="True"
Width="Auto"
Binding="{Binding Author}"
CanUserSort="False"/>
<DataGridTextColumn x:Name="DataGridColumnVersion"
Header="Version"
IsReadOnly="True"
Width="Auto"
Binding="{Binding Version}"
CanUserSort="False"/>
<DataGridTextColumn x:Name="DataGridColumnSource"
Header="Source"
IsReadOnly="True"
Width="*"
Binding="{Binding Source}"
CanUserSort="False"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
Pro's:
Rows are selectable throughout the DataGrid no matter the width.

Cons:
Columns get mashed together when resizing and no horizontal scrollbar (it does flicker while resizing which I dont know why that is though).

Populating empty DataGrid with Data causes columns to not auto resize to content.

Approach 2: Same as above but handling MinWidth of columns in code-behind
Code:
Added event to DataGrid in my XAML:
SizeChanged="ModList_SizeChanged"
Code-behind:
public partial class MainWindow : Window
{
// Other code
private void ModList_SizeChanged(object sender, EventArgs e)
{
DataGridColumnEnabled.MinWidth = DataGridColumnEnabled.ActualWidth;
DataGridColumnLoadorder.MinWidth = DataGridColumnLoadorder.ActualWidth;
DataGridColumnMod.MinWidth = DataGridColumnMod.ActualWidth;
DataGridColumnNotification.MinWidth = DataGridColumnNotification.ActualWidth;
DataGridColumnAuthor.MinWidth = DataGridColumnAuthor.ActualWidth;
DataGridColumnVersion.MinWidth = DataGridColumnVersion.ActualWidth;
DataGridColumnSource.MinWidth = 200;
}
}
Pro:
Horizontal scrollbar now appears when resizing window

Con
Populating empty DataGrid with Data still causes columns to not auto resize to content.
Conclusion
The last approach is the one I've been using now. I need to find a solution which basically accomplishes the same pro's, but also automatically resizes all the columns when I populate the DataGrid.
I'm starting to feel like I'm thinking in the wrong direction which is why I've decided to seek help through here.
I really hope someone could provide me with a proper solution to have an auto resizing DataGrid that makes use of a horizontal scrollbar when the window is too small.
Thank you for reading this post and I hope I've structured it in a decent way.
Looking forward to your suggestions!
You must not set the column's width to
*width as this would change the arrangement behavior as it will force to maximize the space for the*column. Hence auto columns will shrink (based on the layout algorithm of theDataGrid.Instead, you must explicitly calculate the width for the last column to make it fill. To preserve the default column sizing (like size to cells or size to headers) you must allow the
DataGidto complete the original layout algorithm. Then finally adjust the last column.For a graceful solution you may want to consider extending the
DataGridso that you can overrideArrangeOverrideto prepend the custom calculations for the last column.The following example extends
DataGridto implement the logic internally. Alternatively, you can move the adjustment logic to an attached behavior or to the event handler of theDataGrid.SizeChangedevent. From a design perspective extendingDataGridis the cleanest solution as the layout logic is properly encapsulated by the element itself.Another (better) solution that avoids the calculations is to make use of the native resize behavior that can be configured by setting the
DataGrid.ColumnWidthproperty:The easiest is to configure the
DataGridto distribute the available space evenly amongst all columns. You do this by settingDataGrid.ColumnWidthproperty to*.If you only want to make the last column to fill the remaining space, you must set the width of all columns except the last to an explicit value or
AutowhileDataGrid.ColumnWidthis set to*.To prevent the columns from shrinking you have to set the
DataGridColumn.MinWidthproperty. This also guarateens that a horizontal scroll bar will appear.And if you don't want to distribute the available space evenly between all columns you must also set the
DataGridCOlumn.Widthproperty.For example, to make only the last column fill the remaining space you must set
DataGrid.ColumnWidthto*, and all columns except the last must have an explicitDataGridColumn.Width(e.g.Auto) and all columns, including the last, must have theirDataGridColumn.MinWidthset. IfDataGridColumn.Widthis set toAutothen theDataGridwill calculate the size based on the bigger width of header and cell.The key is that when
DataGrid.ColumnWidthis set to*or e.g.DataGridLength.SizeToCellsthen theDataGridwill automatically recalculate the layout if the condition has changed.Setting the width locally on the
DataGridColumnresults in a single calculation, the moment theDataGridCellis created.Available space is distributed evenly amongst all columns:
Only particular columns will share the available space. In the following example, only the last column will fill the remaining space: