How to make Items in ItemsControl scrollable? [duplicate] - wpf

I followed this small "tutorial" on how to add a scrollbar to an ItemsControl, and it works in Designer view, but not when I compile and execute the program (only the first few items show up, and no scrollbar to view more - even when VerticalScrollbarVisibility is set to "Visible" instead of "Auto").
Any idea on how to solve this?
This is the code I use to show my items (normally I work with Databinding, but to see the items in my Designer I added them manually):
<ItemsControl x:Name="itemCtrl" Style="{DynamicResource UsersControlStyle}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Top">
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<uc:UcSpeler />
<uc:UcSpeler />
<uc:UcSpeler />
<uc:UcSpeler />
<uc:UcSpeler />
</ItemsControl>
And this is my Template:
<Style x:Key="UsersControlStyle" TargetType="{x:Type ItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
<ScrollViewer VerticalScrollBarVisibility="Visible">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

To get a scrollbar for an ItemsControl, you can host it in a ScrollViewer like this:
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl>
<uc:UcSpeler />
<uc:UcSpeler />
<uc:UcSpeler />
<uc:UcSpeler />
<uc:UcSpeler />
</ItemsControl>
</ScrollViewer>

You have to modify the control template instead of ItemsPanelTemplate:
<ItemsControl >
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer x:Name="ScrollViewer" Padding="{TemplateBinding Padding}">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
Maybe, your code does not working because StackPanel has own scrolling functionality. Try to use StackPanel.CanVerticallyScroll property.

Put your ScrollViewer in a DockPanel and set the DockPanel MaxHeight property
[...]
<DockPanel MaxHeight="700">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemSource ="{Binding ...}">
[...]
</ItemsControl>
</ScrollViewer>
</DockPanel>
[...]

Related

WPF Disable mousewheel on ScrollViewer and let children handle it

I am trying to implement an ItemsControl which contains a collection of graphing components. The component should be able to use the MouseWheel event to zoom, but it appears that the Scrollbar is capturing and handling the event first. Most of the articles I have found here and here speak to letting parents handle the wheel, but I need the child to handle the wheel, and I can't figure out the right hooks. Here is my TiledGrid, which is an ItemsControl at its core:
<ItemsControl x:Class="Grapes.Common.Controls.TiledGrid.TiledGrid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Grapes.Common.Controls.TiledGrid"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" x:Name="itemsGrid"
ScrollViewer.CanContentScroll="True" >
<!--ItemTemplate="{Binding ItemTemplate}"-->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1"
Padding="3" Margin="3" >
<HeaderedContentControl
HeaderTemplate="{Binding HeaderTemplate,ElementName=itemsGrid}" Header="{Binding}"
ContentTemplate="{Binding ItemTemplate,ElementName=itemsGrid}" Content="{Binding}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
>
<HeaderedContentControl.Template>
<ControlTemplate TargetType="HeaderedContentControl">
<DockPanel>
<ContentPresenter DockPanel.Dock="Top" ContentSource="Header" />
<ContentPresenter />
</DockPanel>
</ControlTemplate>
</HeaderedContentControl.Template>
</HeaderedContentControl>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:UniformGridPanel Rows="{Binding RowCount,ElementName=itemsGrid}"
Columns="{Binding ColumnCount,ElementName=itemsGrid}"
>
</local:UniformGridPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate>
<Border
BorderThickness="{TemplateBinding Border.BorderThickness}"
Padding="{TemplateBinding Control.Padding}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True">
<ScrollViewer
Padding="{TemplateBinding Control.Padding}"
Focusable="False" FlowDirection="RightToLeft" CanContentScroll="True" SnapsToDevicePixels="True"
>
<ItemsPresenter FlowDirection="LeftToRight">
</ItemsPresenter>
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
What can I do to stop the ScrollViewer in the ItemsControl.Template from handling the MouseWheel event?
Thanks in Advance!
Mike.

WPF ItemsControl scrollbar

Using ItemsControl to display a collection of items on a Canvas.
Probelm is that I can't see all the items on my screen (need to use Scrollbars), I've checked this post out and tried the same but it doesn't work for me, the Scrollbar is shown but disabled. My XAML:
<Grid>
<DockPanel>
<ScrollViewer>
<ItemsControl ItemsSource={Binding MyCollection}>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
....
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</Grid>
ItemsControl by default doesn't have ScrollViewer in it's Template unlike ListBox.
Get rid of the outer scrollViewer and set the Template of ItemsControl to contain ScrollViewer. Also, I don't see any usage of DockPanel when you already wrap ItemsControl inside Grid.
Change layout something like this:
<Grid>
<ItemsControl ItemsSource={Binding MyCollection}>
<ItemsControl.Template>
<ControlTemplate>
<Border
BorderThickness="{TemplateBinding Border.BorderThickness}"
Padding="{TemplateBinding Control.Padding}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True">
<ScrollViewer
Padding="{TemplateBinding Control.Padding}"
Focusable="False">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding
UIElement.SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
....
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>

Load large data set with WPF ContextMenu for RichTextBox

I'm into a situation where the number of items of a ContextMenu has gone to a number like 3 thousand. In this scenario, the ContextMenu takes lot of time in loading itself. I'm trying to do UI Virtualization but without any luck.
The sample code is as follows:
<RichTextBox x:Name="FMWpfPopupControlHost" ScrollViewer.CanContentScroll="True" VirtualizingStackPanel.IsVirtualizing="True" HorizontalAlignment="Left" Width="300" Height="600" Focusable="True" Loaded="onLoaded1">
<RichTextBox.ContextMenu >
<ContextMenu VirtualizingStackPanel.IsVirtualizing="True" ItemsSource="{Binding GetLabel, Source ={StaticResource model}}" ScrollViewer.CanContentScroll="True">
<ContextMenu.ItemTemplate>
<DataTemplate>
<TextBlock Initialized="TextBlock_Initialized" Text="{Binding getLabel}"/>
</DataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" IsVirtualizing="True" VirtualizationMode="Recycling"/>
</ItemsPanelTemplate>
</ContextMenu.ItemsPanel>
<ContextMenu.Template>
<ControlTemplate>
<Border BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="{TemplateBinding Control.Padding}" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" SnapsToDevicePixels="True">
<ScrollViewer Padding="{TemplateBinding Control.Padding}" Focusable="False">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
</RichTextBox.ContextMenu>
</RichTextBox>
Please guide me how can I resolve this issue?
Thanks!
Harsh Gupta

How to enable scrolling when listbox is disabled in Silverlight?

I want to disable listbox (not allow user to selected) but scrollable in Silverlight, how can I do that?
How about not using a ListBox. Despite it being the defacto means for displaying a list of items the true purpose of the ListBox is to select something from a list.
A scrollable list can be delivered by styleing the ItemsControl like this:-
<Grid.Resources>
<Style x:Key="ScrollableItemsControl" TargetType="ItemsControl">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ItemsControl">
<Border CornerRadius="2" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer x:Name="ScrollViewer" Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" BorderBrush="Transparent" BorderThickness="0" TabNavigation="{TemplateBinding TabNavigation}">
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
Now you apply this style to an ItemsControl:-
<ItemsControl Style="{StaticResource ScrollableItemsControl}" ItemsSource="{Binding SomeData}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding SomeProperty}" Margin="5" />
<TextBlock Text="{Binding SomeOtherProperty}" Margin="5" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This is a scrollable list but with none of the selection semanitics of the ListBox. Note there are fewer intermediatory elements in the visual tree doing this way than fiddling around with the ListBox control.
Edit
In light of your comment there is an easy way to toggle the selection behaviour of a ListBox without disabling it. You disable the ListBoxItems it contains instead.
<Grid.Resources>
<Style x:Key="DisabledItem" TargetType="ListBoxItem">
<Setter Property="IsEnabled" Value="False" />
</Style>
</Grid.Resources>
<ListBox x:Name="lst" ItemsSource="{StaticResource TestData}" ItemContainerStyle="{StaticResource DisabledItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="5" />
<TextBlock Text="{Binding Age}" Margin="5" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In this case the ListBox starts of with disabled items. Scrolling however is still possible. But the items appear dimmed and there is no hover or selected highlighting going on.
The code below is able to toggle the "disabled" state on and off.
private void Button_Click(object sender, RoutedEventArgs e)
{
if (lst.ItemContainerStyle == null)
lst.ItemContainerStyle = (Style)LayoutRoot.Resources["DisabledItem"];
else
lst.ItemContainerStyle = null;
}

Virtualizing an ItemsControl?

I have an ItemsControl containing a list of data that I would like to virtualize, however VirtualizingStackPanel.IsVirtualizing="True" does not seem to work with an ItemsControl.
Is this really the case or is there another way of doing this that I am not aware of?
To test I have been using the following block of code:
<ItemsControl ItemsSource="{Binding Path=AccountViews.Tables[0]}"
VirtualizingStackPanel.IsVirtualizing="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Initialized="TextBlock_Initialized"
Margin="5,50,5,50" Text="{Binding Path=Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If I change the ItemsControl to a ListBox, I can see that the Initialized event only runs a handful of times (the huge margins are just so I only have to go through a few records), however as an ItemsControl every item gets initialized.
I have tried setting the ItemsControlPanelTemplate to a VirtualizingStackPanel but that doesn't seem to help.
There's actually much more to it than just making the ItemsPanelTemplate use VirtualizingStackPanel. The default ControlTemplate for ItemsControl does not have a ScrollViewer, which is the key to virtualization. Adding to the the default control template for ItemsControl (using the control template for ListBox as a template) gives us the following:
<ItemsControl ItemsSource="{Binding AccountViews.Tables[0]}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Initialized="TextBlock_Initialized"
Text="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsVirtualizing="True"
VirtualizationMode="Recycling" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<ScrollViewer CanContentScroll="True"
Padding="{TemplateBinding Padding}"
Focusable="False">
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
(BTW, a great tool for looking at default control templates is Show Me The Template)
Things to notice:
You have to set ScrollViewer.CanContentScroll="True", see here for why.
Also notice that I put VirtualizingStackPanel.VirtualizationMode="Recycling". This will reduce the numbers of times TextBlock_Initialized is called to however many TextBlocks are visible on the screen. You can read more on UI virtualization here
.
EDIT: Forgot to state the obvious: as an alternate solution, you can just replace ItemsControl with ListBox :)
Also, check out this Optimizing Performance on MSDN page and notice that ItemsControl isn't in the "Controls That Implement Performance Features" table, which is why we need to edit the control template.
Building on DavidN's answer, here is a style you can use on an ItemsControl to virtualise it:
<!--Virtualised ItemsControl-->
<Style x:Key="ItemsControlVirtualizedStyle" TargetType="ItemsControl">
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ItemsControl">
<Border
BorderThickness="{TemplateBinding Border.BorderThickness}"
Padding="{TemplateBinding Control.Padding}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True"
>
<ScrollViewer Padding="{TemplateBinding Control.Padding}" Focusable="False">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I do not like the suggestion to use a ListBox as they allow the selection of rows where you do not necessarily want it.
It is just that the default ItemsPanel isn't a VirtualizingStackPanel. You need to change it:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

Resources