Reputation: 682
I have a DataGrid
in a ScrollViewer
. I have one of two problems:
When the width is shrunk and the item text is cut off (the H2
column isn't wide enough), the scroll bar doesn't appear. However, the two columns that I have are sized appropriately and can't be resized off-screen. The XAML is (basically):
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<DataGrid Width="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType={x:Type ScrollViewer}}}">
<DataGrid.Columns>
<DataGridTextColumn Header="H1" Width="40" MinWidth="35"/>
<DataGridTextColumn Header="H2" Width="*" MinWidth="47"/>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
When I have a working scrollbar, the columns can be resized so that the right one can be out of view (the center divider can be moved far enough right that the entire column is out of view). XAML:
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid>
<DataGrid Width="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType={x:Type Grid}}}">
<DataGrid.Columns>
<DataGridTextColumn Header="H1" Width="40" MinWidth="35"/>
<DataGridTextColumn Header="H2" Width="*" MinWidth="47"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</ScrollViewer>
(Note: in both cases, I have excluded the itemsource
as I don't believe it is necessary for the question, I can add that, as well as the backend code, if needed).
I imagine that this has to do with the element that the DataGrid
is bound to (maybe the Grid
element doesn't resize appropriately, I have tried binding the width of the Grid
to the ScrollViewer
but the problem is the same as (1)).
I have also noticed that the scroll bar does appear (both instances) when the vertical content is cut off and the vertical scroll bar appears (both appear at the same time). Any suggestions to have the scrollbar and ability to ensure that the columns doesn't go off-screen?
Upvotes: 1
Views: 1828
Reputation: 176
Here's what I came up with by making use of VisualTreeHelper
.
Create a method that can search the Visual Tree for a child of a particular type. I found a method for looking for a parent (the reverse direction) that I was able to modify. https://stackoverflow.com/a/636456/1640271.
public static T FindVisualChild<T>(DependencyObject parent)
where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
DependencyObject childObject = VisualTreeHelper.GetChild(parent, i);
if (childObject == null) return null;
if (childObject is T obj)
{
return obj;
}
else
{
return FindVisualChild<T>(childObject);
}
}
return null;
}
My code behind included creating one constant, one class variable, and using two Window
event triggers.
Constant
(imposes a constraint on the Window
width to ensure that at least some portion of the second columns can always be seen).
private const double _item2MinViewableWidth = 100;
Variable
(used to capture the Grid
from the Visual Tree that is inside the ScrollView
of the DataGrid
).
private Grid _innerGrid;
Events
private void Window_ContentRendered(object sender, EventArgs e)
{
_innerGrid = UIHelper.FindVisualChild<Grid>(dataGrid);
item2Column.MinWidth = _item2MinViewableWidth;
item1Column.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
item2Column.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
MaxWidth = ActualWidth;
_innerGrid.MinWidth = item1Column.ActualWidth + _item2MinViewableWidth;
_innerGrid.Width = item1Column.ActualWidth + _item2MinViewableWidth;
UpdateLayout();
var mainGridInitWidth = mainGrid.ActualWidth;
MinWidth = ActualWidth;
_innerGrid.Width = Double.NaN;
mainGrid.Width = mainGridInitWidth;
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.WidthChanged && _innerGrid != null)
{
var widthDetla = e.NewSize.Width - e.PreviousSize.Width;
mainGrid.Width += widthDetla;
}
if (Math.Abs(ActualWidth - MaxWidth) < 1)
{
dataGrid.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
}
else
{
dataGrid.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
}
}
Here's my XAML for good measure:
<Window
x:Class="WPFSandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPFSandbox"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Height="450"
ContentRendered="Window_ContentRendered"
ResizeMode="CanResize"
SizeChanged="Window_SizeChanged"
SizeToContent="Width">
<Grid x:Name="mainGrid">
<DataGrid x:Name="dataGrid"
AutoGenerateColumns="False"
CanUserResizeColumns="False"
HeadersVisibility="Column"
IsReadOnly="True"
ItemsSource="{Binding MyItems}">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter
Property="SeparatorVisibility"
Value="Hidden" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTextColumn x:Name="item1Column"
Binding="{Binding Item1}"
Header="Item1" />
<DataGridTextColumn x:Name="item2Column"
Binding="{Binding Item2}"
Header="Item2" />
</DataGrid.Columns>
</DataGrid>
</Grid>
Hopefully, this is in line with what you need.
Upvotes: 1
Reputation: 666
The problem is that the width of the DataGrid is bound to the width of the ScrollViewer
<DataGrid Width={Binding ActualWidth, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}">
The DataGrid is never going to be wider than the ScrollViewer so the scrollbar will not be enabled.
Setting HorizontalAlignment to 'Stretch' will achieve the layout that I think you are looking for and allow scrolling. So the DataGrid element ought to be:
<DataGrid HorizontalAlignment="Stretch">
<DataGrid.Columns>
<DataGridTextColumn Header="H1" Width="40" MinWidth="35"/>
<DataGridTextColumn Header="H2" Width="*" MinWidth="47"/>
</DataGrid.Columns>
</DataGrid>
Upvotes: 1