I have a grid with some data and one of the cells contains rather long strings (and there could be quite a few). So to not use too much of the available window space, I'd like those strings to be scrollable. Vertically works, but whatever I try, I can't get a horizontal scrollbar.
This is my xaml:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="150" Width="250">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" Grid.Column="0">
<ItemsControl ItemsSource="{Binding Path=BoundTexts}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Text}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</Grid>
</Window>
and here's the code behind to test.
using System.Collections.Generic;
using System.Windows;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<BoundClass> temp = new List<BoundClass>();
for (int i = 0; i < 10; i )
{
string t = ""; // just to create some long strings.
for (int j = 0; j < 10; j ) // I know it can be done better.
{
t = $"{j*10:D2}********";
}
temp.Add(new BoundClass(t));
}
BoundTexts = temp.ToArray();
DataContext = this;
}
public BoundClass[] BoundTexts { get; set; }
}
public class BoundClass
{
public string Text { get; set;}
public BoundClass(string text)
{
Text = text;
}
}
}
I know there are a few similar questions on here, but as far as I have seen, they are all shrouded in templates and other complex topics. Also some are answered by "make sure you have a restraining container around it", I think I do by the grid.
CodePudding user response:
Setting ScrollViewer.HorizontalScrollBarVisibility
and ScrollViewer.VerticalScrollBarVisibility
to "Auto"
should do the trick.
Default value for VerticalScrollBarVisibility
is ScrollBarVisibility.Visible
BUT for HorizontalScrollBarVisibility
is ScrollBarVisibility.Disabled
. So it should be even enough to set only HorizontalScrollBarVisibility = "Auto"
<ScrollViewer Grid.Row="0" Grid.Column="0" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
CodePudding user response:
The proper way to add a ScrollViewer
to an ItemsControl
is to put it into the ControlTemplate
to wrap the ItemsPresenter
. This way the ScrollViewer
behaves correctly.
If you don't do it this way (and instead wrap the ItemsControl
) the ScrollViewer
will try to scroll the ItemsControl
and not the items!
But you want the ScrollViewer
to detect when items of the ItemsControl
overflow. Naturally, the ItemsControl
itself will stretch to occupy the available space - it won't overflow and will be displayed correctly (fully visible). But the items do overflow the space of the ItemsControl
. That's why wrapping the ItemsControl
won't work as expected.
Additionally, you must explicitly enable the ScrollViewer.HorizontalScrollBarVisibility
. It's important to know that setting it to Auto
will impact the performance. It's recommended to set it to Hidden
or Visible
.
<ItemsControl Height="200">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer CanContentScroll="True"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<!-- Consider to enable virtualization by adding a VirtualizingStackPanel as host Panel.
Important: don't forget to set ScrollViewer.CanContentScroll to 'True' -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>