VirtualizingStackPanel isn't virtualizing - c#

I have a virtualizing stack panel in my ItemsControl, in a scroll viewer. It doesn't seem to be virtualizing:
<Page
x:Class="IWalker.Views.FullTalkAsStripView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:IWalker.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ScrollViewer x:Name="theScrollViewer" VerticalScrollMode="Disabled" HorizontalScrollMode="Auto" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto">
<ItemsControl x:Name="SlideStrip">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:PDFPageUserControl Margin="0 0 5 0" Height="{Binding Path=ActualHeight, ElementName=SlideStrip, Mode=OneWay}" ViewModel="{Binding}" RespectRenderingDimension="Vertical" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Page>
The PDFPageUserControl prints out a little debug message when it is instantiated - and I always get one even when I get more than 50 items. The SlideStrip.ItemsSource is databound, so I don't think that is the problem (via ReactiveUI):
this.OneWayBind(ViewModel, x => x.Pages, y => y.SlideStrip.ItemsSource);
Where Pages is a ReactiveUI list.
What have I missed? Do I need to set IsVirtualizing to true? Where is that attached property in this case (since this isn't a ListBox)?
BTW, I will never have a large number of controls. The issue is that each control holds onto an image, and a large one at that (e.g. full screen on a high DPI display). All I really want to do is release the image when each PDF control is not visible, and re-load when it is. So there may be a better way to solve my problem.

Related

How to STOP ListView from expanding to content

I'm trying to make my xaml squeeze list items (images) to fit the initial window size, but they cant be fixed size because i want to scale them up as i increase the size of the window. Something like a ViewBox would do.
I load images from 2 folders (software and hardware). Number and size of the images will wary in the runtime so i want to make items be the same size regardless of images size or number of items.
That's why i used uniform grid as a items panel template.
But, this is the result I'm getting...
The ListView loads images in their full size and expands itself to fit them in, cutting of some of the items in the process.
This is my xaml:
<Window x:Class="WPF_UI_Testing.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:WPF_UI_Testing"
mc:Ignorable="d"
Title="MainWindow" Height="460" Width="640">
<Grid>
<ListView x:Name="listview1">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"
FontWeight="Bold" FontSize="18" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"></StackPanel>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid ></UniformGrid>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding problemImage}"/>
<TextBlock Text="{Binding ImageName}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
Is there a way to prevent ListView from expanding beyond window borders when populated with content?
EDIT:
I packed the entire solution with some dummy data and images if anyone wants to have a go at this...
https://drive.google.com/file/d/1IWqxSR3kpsVdCm5Qcgn6QZbZhbYz52n2/view?usp=sharing
The UniformGrid does basically what you want. The idea to use it as item panel is also correct. The only problem that arises, aside from using item groups, is the fact that the ListView wraps its panel into a ScrollViewer, which results in items or the UniformGrid to resize differently as the ScrollViewer gives the panel no size restrictions. UniformGrid needs to be hosted in a fixed size container in order to be able to calculate its children's max sizes.
You should either use the ItemsControl
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:DetailItem}">
<StackPanel>
<Image Source="{Binding problemImage}"/>
<TextBlock Text="{Binding ImageName}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
or override the ControlTemplate of ListView and remove the ScrollViewer (if you need its additional features of ListView):
<ListView>
<ListView.Template>
<ControlTemplate TargetType="ListView">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<ItemsPresenter />
</Border>
</ControlTemplate>
</ListView.Template>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:DetailItem}">
<StackPanel>
<Image Source="{Binding problemImage}"/>
<TextBlock Text="{Binding ImageName}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The limitations of this approach is when using groups to display the items. UniformGrid should handle GroupItem (nested items) not as expected.
As said in my previous comment, if you want to group items, you need to extend a panel of your choice to manually arrange GroupItem and it's children (nested ItemsPresenter). As you think about how to calculate sizes you may realizes that it is more complicated to calculate group item sizes with dynamic grouped item sizes.
I recommend to let go the grouping and use one of the above solutions or use grouping and embrace the ScrollViewer.

WPF: ListBox with WrapPanel, No scroll bars, content scaled with ViewBox, showing all content

Using a WPF ListBox and a WrapPanel, is there a way to use a ViewBox with the internal content scaling, but without having any content trimming or scroll bars? I want to scale all internal items down. The width / heigth of the container can scale, and the number of items is dynamic.
I put together a simple WPF example (without any solutions). Open to sugguestions.
Code Example:
<Window x:Class="WpfListBoxWrapPanel.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"
mc:Ignorable="d"
Title="MainWindow"
Height="230.572"
Width="426.972">
<Grid>
<ListBox ItemsSource="{Binding SomeCollection, FallbackValue=123456123456123456123456123456123456}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="50"
Height="50"
Margin="3">
<Rectangle Fill="LightCoral"></Rectangle>
<TextBlock Text="{Binding}"
Margin="4"></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
Thanks!

WPF control for neural net visualization

I'm developing a Neural net, and for visualization I'm writing a UserControl in WPF.
The following code draws the neurons:
<UserControl x:Class="ExcelAddIn.LogView"
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">
<UserControl.Resources>
<DataTemplate x:Key="ellipse">
<Grid Margin="0,10,0,0">
<Ellipse Width="50" Height="50" Fill="Red" Stroke="Black" StrokeThickness="2"/>
<TextBlock HorizontalAlignment="Center" Text="{Binding Input, StringFormat=N2}" TextAlignment="Center" Margin="0,10,0,0"/>
<TextBlock HorizontalAlignment="Center" Text="{Binding Output, StringFormat=N2}" TextAlignment="Center" Margin="0,22,0,0"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="panel">
<ScrollViewer VerticalScrollBarVisibility="Auto" Width="70" Margin="100,0,0,0">
<ItemsControl x:Name="Items" ItemsSource="{Binding Neurons}" ItemTemplate="{StaticResource ellipse}"/>
</ScrollViewer>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ItemsControl x:Name="Panel" ItemsSource="{Binding Layers}" ItemTemplate="{StaticResource panel}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
I would like to draw lines between the Neurons like this:
So for each neuron in the specific layer should have a line to each neuron in the next layer (drawing all the lines would made it harder to understand, so I didn't draw all the lines on the illustration). I would like to achieve this with binding. Is it possible? (I don't care if my existing code needs to be refactored, if that solves my problem.)
Note: I have a Weight object (a line would represent this weight). One Weight object has reference to two Neurons, a double, that is the weight between them and some other properties. And the weigths can be accessed from the datacontext of the usercontrol.
Thank you.
It seems the Nodes Editor example (from the another answer) should help. It is necessary to create instances of appropriate ViewModels (Node and Connector classes). Please note that Node coordinates must be filled before displaying (see the implementation of the NodesDataSource class).
Additional references to articles/libraries:
NetworkView: A WPF custom control for visualizing and editing networks, graphs and flow-charts.
Graphviz4Net library: provides WPF control that is capable of generating "nice looking" graph layouts).
Graph# library: a graph layout framework, it contains some layout algorithms and a GraphLayout control for WPF applications.

Binding an ItemsControl containing WindowsFormHost items in WPF

I am building a UI element for our WPF application that allows a user to visualize a collection of graphs aligned in a grid format. As I understand, you can use an ItemsControl with a WrapPanel that will nicely align ui elements in a grid format.
The difficulty comes when we try to use a winforms graphing library (zedgraph) to generate the graphs. This means we have to use WindowsFormsHost to display the graph views. I have tried to bind the graph collection using the normal data binding, it doesn't really work:
XAML:
<ItemsControl ItemsSource="{Binding Graphs}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<WindowsFormsHost Margin="5">
<ui:Graph></ui:Graph>
</WindowsFormsHost>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The above code displays nothing, but the Graphs getter is accessed.
Also, even if I cancel the binding and I just insert some random graph views inside the ItemsControl... it still doesn't display anything:
XAML:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<WindowsFormsHost Margin="5">
<ui:Graph></ui:Graph>
</WindowsFormsHost>
<WindowsFormsHost Margin="5">
</WindowsFormsHost>
<ui:Graph></ui:Graph>
</WindowsFormsHost>
<WindowsFormsHost Margin="5">
<ui:Graph></ui:Graph>
</WindowsFormsHost>
</ItemsControl>
To test to make sure the graphing library functions normally, this does indeed display a graph:
<Grid>
<WindowsFormsHost Margin="5">
<ui:Graph></ui:Graph>
</WindowsFormsHost>
</Grid>
Can anyone guide me in the right direction? Much appreciated.
That might be related to layout. winforms is horrible, and requires that you hardcode the sizes of everything.
Try giving a fixed hardcoded Width and Height to the winformshost

WPF Scrollviewer not scrolling... No thumb, only responds to mousewheel

I have a WPF .Net 4 application which contains a button that when clicked opens a new windows :
CreationWindow creationWindow = new CreationWindow();
creationWindow.Owner = this;
CreationWindow.Show();
The window shows fine, but the listbox that it contains (say 100 images as listboxitems for content) does not have a thumb on the scrollbar.
Heres a sample of the content of this 'CreationWindow'
<Window
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:Converters="clr-namespace:Krocr.Client.Converters"
x:Class="Krocr.Client.ComparisonMode"
Title="ComparisonMode" Height="450" Width="700">
<Grid>
<ListBox ItemsSource="{Binding Images}"/>
</Grid>
</Window>
The scrollbar is visible, but I cannot interact with it. The mousewheel does scroll the list.
HOWEVER..
If I add a scrollviewer to my main window, and add some items to it. Subsequent scrollviewers (in new windows) then work correctly...
I haven't altered any styles at all for the listbox or the scrollviewer... Very confused!
Help would be greatly appreciated as it's driving me mad.
EDIT : Added screenshot of problem (cant post images yet as I'm new...)
http://i.stack.imgur.com/XdYSs.png
I figured it out...
It was a crazy visual tree in my mainwindow.xaml, which was breaking the rendering of everything else... Heres the issue :
<Grid Background="#00E5E5E5" Margin="0,75,0,0" Grid.Row="1">
<Viewbox x:Name="docViewBox" Margin="0">
<Grid Margin="5" x:Name="holdingGrid">
<Canvas x:Name="AggLayer" Margin="0" />
<Canvas x:Name="rectCanvas" MouseLeftButtonDown="StartDrag" MouseLeftButtonUp="EndDrag" Background="Transparent"/>
<ListBox x:Name="overlayCanvas" Background="#00FFFFFF" IsHitTestVisible="False" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="{Binding ActualWidth, ElementName=rectCanvas}" Height="{Binding ActualHeight, ElementName=rectCanvas}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
</Viewbox>
<Canvas x:Name="DialogLayer" Margin="0" />
</Grid>
With that commented, it works fine... Also that xaml completely breaks blend, causing random crazy behaviour...
Time to optimize I feel... Thanks for the input :)
EDIT : Infact, all I needed to do was remove the Viewbox and things worked fine... very odd
EDIT 2 : The culprit was the listbox with a canvas itemspanel, this in particular
<Canvas Width="{Binding ActualWidth, ElementName=rectCanvas}" Height="{Binding ActualHeight, ElementName=rectCanvas}"/>
Binding those width and height values was causing the viewbox to get into an infinite scaling loop, which was breaking other things. Silly me...
It's because a Grid by default doesn't constrain the size of its child controls, and so the ListBox doesn't realize it needs to scroll.
The quickest solution is to replace the Grid with a DockPanel, which will constrain its child, but this may need rethinking if you have more child controls to add later!

Categories

Resources