WPF ItemsControl scrollbar - wpf

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>

Related

How to make Items in ItemsControl scrollable? [duplicate]

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>
[...]

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.

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

Unwanted scroll on click on last item in ItemsControl

Case: ItemsControl with Virtualizing enabled (performance). Contains checkboxes/textboxes/etc. However, clicking on checkbox/textbox on last visible item causes itemscontrol to scroll down instead of selecting the checkbox/textbox.
With virtualizing disabled, everything works fine...
I have tried a lot of things (using ListView, wrapping it in larger panel, etc.). No luck.
ItemsSource is set in code behind to a large dataset of testitems with properties Name and ID.
<Window x:Class="ListViewTestApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="650" Width="525" Background="AliceBlue">
<ItemsControl x:Name="MainListView" VirtualizingStackPanel.IsVirtualizing="True"
ScrollViewer.CanContentScroll="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid DataContext="{Binding}" Width="525">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Height="50" Grid.Row="0" Content="{Binding Path=Name}"/>
<CheckBox Height="50" Content="Checkbox" Grid.Row="1" />
<TextBox Height="50" Grid.Row="2" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</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">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>

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