WPF VirtualizingStackPanel not redrawing on horizontal resize - c#

I have an ItemsControl with a vertical VirtualizingStackPanel that has items with wrapping TextBlocks. Horizontally resizing the parent container fails to re-draw the elements below a certain size, only redrawing once you've scrolled up/down. However above a certain size the redrawing happens correctly. This only seems to be an issue when Virtualization is enabled.
I've created a simple example which demonstrates the issue here...
<Window x:Class="WpfTesting.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfTesting"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:MainWindow}"
Title="MainWindow" Height="450" Width="800">
<ItemsControl ItemsSource="{Binding LongStrings}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="5"
Padding="5"
Background="Gray"
BorderBrush="Black"
BorderThickness="2"
CornerRadius="8">
<TextBlock TextWrapping="Wrap"
Text="{Binding}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer CanContentScroll="True"
Focusable="False">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</Window>
CodeBehind...
public partial class MainWindow : Window
{
public List<string> LongStrings { get; }
public MainWindow()
{
InitializeComponent();
DataContext = this;
LongStrings = Enumerable.Range(1, 20)
.Select(_ => new string(Enumerable.Repeat('a', 1000).ToArray()))
.ToList();
}
}

I feel a little silly that the solution is so simple but you can fix it relatively easily by binding the Width property of the Item you're displaying to the ActualWidth property of the VirtualizingStackPanel.
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="{Binding ActualWidth, ElementName=StackPanel}">
<Border Margin="5"
Padding="5"
Background="Gray"
BorderBrush="Black"
BorderThickness="2"
CornerRadius="8">
<TextBlock TextWrapping="Wrap"
Text="{Binding}"/>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel x:Name="StackPanel"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
Frankly I'm convinced that the rendering behaviour of the VirtualizingStackPanel is a bug given just how odd it is.

Related

WPF show series of image use ItemsControl

Here is my XAML I want to show series of image use DataTempalte in ItemsControl,
when I run the program the screen show only one image. I can't find what's wrong with it.
<Grid>
<ScrollViewer HorizontalScrollBarVisibility="Visible">
<ItemsControl Width="1024" Height="658" ItemsSource="{Binding ImageSet}" ScrollViewer.HorizontalScrollBarVisibility="Visible">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Width="1024" Height="658" Orientation="Horizontal">
<Image x:Name="rectangle" Source="{Binding Img}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
Can anybody help me to find out, appreciate!
So, correct me if I am wrong, but I think you are trying to scroll horizontally through your pictures. You have your ScrollViewer set to scroll horizontally but the ItemsControl inside does not have a horizontal orientation by default. This is why you are only seeing one picture. Try changing the default ItemsPanel and see if you get better results. Something like this:
<Grid>
<ScrollViewer HorizontalScrollBarVisibility="Visible">
<ItemsControl Width="1024"
Height="658"
ItemsSource="{Binding ImageSet}"
ScrollViewer.HorizontalScrollBarVisibility="Visible">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Img}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
This was a great resource for me when I was first trying to understand the ItemsControl in WPF: http://drwpf.com/blog/itemscontrol-a-to-z/

Aligment of Items in nested ItemsControls

I have two ItemsControls nested within a single ItemsControl. Each are placed next to each other in Grid Columns with horizontally oriented StackPanel ItemsPanelTemplates so their contents are layer out horizontally.
While I want the two ItemsControls to occupy the full width of the parent, (50:50), I want the items within them to be right, and left aligned respectively... so they both are centred, something like (excuse my attempt at ASCII art):
| LH ItemsControl | RH ItemsControl |
| [][][][]|[][][] |
Here's my code so far, I have been tweaking the HorizontalAlignment attributes but if I get them to occupy the centre, then the two StackPanels don't fill the full width of the parent.
<ItemsControl ItemsSource="{Binding Things}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ItemsControl Grid.Column="0" ItemsSource="{Binding LeftThings}" HorizontalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Background="LightPink" HorizontalAlignment="Stretch" Height="37"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<ItemsControl Grid.Column="1" ItemsSource="{Binding RightThings}" HorizontalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Background="LightBlue" HorizontalAlignment="Stretch" Height="37"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Any ideas?
Rich
Setting the Background property on the ItemsControl rather than the StackPanel and setting Orientation to Left and Right respectively on the StackPanel gave me the effect I was after:
<ItemsControl ItemsSource="{Binding Things}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ItemsControl Grid.Column="0" ItemsSource="{Binding LeftThings}" HorizontalAlignment="Stretch" Background="LightPink">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Height="37"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<ItemsControl Grid.Column="1" ItemsSource="{Binding RightThings}" HorizontalAlignment="Stretch" Background="LightBlue">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Height="37"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Use FlowDirection=RightToLeft on left stackpanel, and FlowDirection=LeftToRight in DataTemplate control. I made a sample. Below code can be used as is :
MainWindow.xaml
<Window x:Class="WpfItemsControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="407.895" Width="884.211">
<Grid>
<ItemsControl ItemsSource="{Binding Names}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ItemsControl ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ItemsControl}}" Grid.Column="0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Background="Red" HorizontalAlignment="Stretch" FlowDirection="RightToLeft">
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Background="DarkGoldenrod" FontSize="25" FontWeight="Bold" Foreground="Gray" Text="{Binding}" FlowDirection="LeftToRight"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ItemsControl}}" Grid.Column="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Background="Red" HorizontalAlignment="Stretch" FlowDirection="LeftToRight">
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Background="DarkGoldenrod" FontSize="25" FontWeight="Bold" Foreground="Gray" Text="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
MainWindow.xaml.cs
namespace WpfItemsControl
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
try
{
InitializeComponent();
this.DataContext = new DataStore();
}
catch (Exception ex)
{
Debug.WriteLine(ex.InnerException.Message);
}
}
}
public class DataStore
{
public List<string> Names { get; set; }
public DataStore()
{
Names = new List<string>();
Names.Add(">");
Names.Add(">");
Names.Add(">");
Names.Add(">");
Names.Add(">");
}
}
}
This code puts both side items in center, and stretches both stackpanels.

A WPF ItemsControl and WrapPanel walk into a bar

I have a ItemsControl that displays a bunch of UserControl's inside of a WrapPanel. This works perfectly, unless I have a bunch of UserControls, and then the overflow is rendered off screen, and I can't access it. My goal is to have the WrapPanel wrap horizontally, but once the controls are off the screen, to present a scroll bar, and this seems to not work for me.
<ItemsControl ItemsSource="{Binding Servers, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1" Margin="5,5,5,5">
<local:ServerControl DataContext="{Binding }" /> <!-- The actual UserControl -->
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
When the application first starts, this is what it looks like. What you can't see if that there should be 14 boxes viewed. The WrapPanel is doing its job, but it's rendered outside of the bounds of the window.
This shows all the UserControls, but I had to expand the window to be able to see them all.
Any help would be greatly appreciated.
Full XAML:
<Window x:Class="ServerMonitor.Wpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ServerMonitor.Wpf"
xmlns:models="clr-namespace:ServerMonitor.Wpf.Models"
xmlns:System="clr-namespace:System;assembly=mscorlib"
Title="Leading Hedge Server Monitor" Height="350" Width="800">
<Window.DataContext>
<models:MainWindowViewModel>
<models:MainWindowViewModel.MachineNames>
<!-- Test Servers -->
<System:String>T009</System:String>
<System:String>T010</System:String>
<System:String>T011</System:String>
<System:String>T012</System:String>
</models:MainWindowViewModel.MachineNames>
</models:MainWindowViewModel>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Menu Grid.Row="0">
</Menu>
<ItemsControl Grid.Row="1" ItemsSource="{Binding Servers, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1" Margin="5,5,5,5">
<local:ServerControl DataContext="{Binding }" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</Grid>
</Window>
<ItemsControl ItemsSource="{Binding Servers, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1" Margin="5,5,5,5">
<local:ServerControl DataContext="{Binding }" /> <!-- The actual UserControl -->
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
OR
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Servers, Mode=OneWay}">
...
<ItemsControl/>
</ScrollViewer>
Change you second row height to *
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <-- This is what you want -->
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
Setting a RowDefinition to Auto means it will calculate the cumulative DesiredHeight of all child elements in that row and assign it to the Height property of RowDefinition. So, as your WrapPanel grows, it will apply the height to that row and stretch out your parent Grid.

ScrollViewer disappear automatically in WPF

i have an items control that is bound to an observable collection of videos.I added a vertical scroll bar but but it disappear after the page is loaded.
<ItemsControl x:Name="_imageList" ScrollViewer.CanContentScroll="True" HorizontalAlignment="Right" Margin="-1,0" Width="460" >
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer x:Name="ScrollViewer" Padding="{TemplateBinding Padding}" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="4" Rows="3"/>
<!--<StackPanel Orientation="Horizontal"/>-->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Click="btn_Clicked" Margin="9,9,9,9" BorderThickness="0" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
<Image x:Name="image" Source="{Binding thumbnail}" ClipToBounds="True"/>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This is code behind of my page:
public void Images()
{
var images = new ObservableCollection<Video>();
var wcf = new ServiceReferenceVideo.VideoServiceClient();
link_thumb = new Dictionary<string, string>();
foreach (var item in wcf.GetAllVideos())
{
images.Add(item);
}
_imageList.ItemsSource = images;
}
Try putting the Scrollviewer outside the ItemsControl. Something like...
<ScrollViewer>
<ItemsControl>
</ItemsControl>
</ScrollViewer>
This wont work because your UniformGrid automatically adopts to the available size. Try set a fixed Width or MinWidth like 300 for the buttons in your DataTemplate
Here is a working example:
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="525"
Height="350">
<Grid>
<ItemsControl x:Name="Items">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="4"
IsItemsHost="True"
Rows="3" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button MinWidth="200"
MinHeight="200"
Margin="9"
Content="{Binding }" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Items.ItemsSource = new List<int>(Enumerable.Range(0,100));
}
}

ListView with dynamic raws and columns that stays propetinal aytomatically

I am developing a WPF application.
have a list of user controls
<Grid >
<ListView ItemsSource="{Binding MyList}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" ItemWidth="{Binding (ListView.View).ItemWidth, RelativeSource={RelativeSource AncestorType=ListView}}"
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
ItemHeight="{Binding (ListView.View).ItemHeight, RelativeSource={RelativeSource AncestorType=ListView}}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate >
<DataTemplate >
<Views:MyUserControl Height="auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SnapsToDevicePixels="True"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
And MyUserControl has another list of user controls "DetailUserControl":
<UserControl x:Class="MyUserControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" >
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="45" />
<RowDefinition />
</Grid.RowDefinitions>
<Border Grid.RowSpan="2" >
<StackPanel VerticalAlignment="Top" Orientation="Horizontal" HorizontalAlignment="Center" >
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10" Text="{Binding Title}" />
</StackPanel>
</Border>
<Border Grid.Row="1" Style="{StaticResource SummaryInnerFill}" >
<ItemsControl ItemsSource="{Binding DetailUserControls}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
</Grid>
I used wrap panel to make all the usercontrols to be orderd from left to right one after the other.
I want the layout to be optimized and use as much as it can from the first line, and only then it will move to the second line.
here is an example (sorry for my drawing skills :) )
I have the following layout:
if I change the order inside the tiles I can have the following layout (that contains the same tiles but don't waste space an is much more organized):
Is there a panel I can use that will take care of it?
You can make use of Uniform Control Grid. You can specify Rows and Columns Properties. Controls are added to the grid in the order that they are declared.Its child controls are organised into a tabular structure of rows and columns. Unlike the Grid control, you don't have fine-grained control of the layout.
Example,
<UniformGrid Name="uniformGrid1"
Rows="2"
Columns="3">
<Button Content="blah"
Margin="2" />
<Button Content="blah1"
Margin="2" />
<Button Content="blah2"
Margin="2" />
</UniformGrid>
UniformGrid

Categories

Resources