I'm developing a Universal Win App and the issue is present on both platforms. I've a ObservableCollection with products, object properties are bound from xaml DataTemplate and ObservableCollection is set as ItemsSource to GridView.
private ObservableCollection<Product> productList = new ObservableCollection<Product>();
Bound products have images too.
<Image
CacheMode="BitmapCache"
Source="{Binding ImageUrl}"
Stretch="Uniform" />
GridView
<ScrollViewer
x:Name="ProductList_GridView_ParentScrollViewer"
VerticalScrollBarVisibility="Hidden"
Grid.Row="1">
<StackPanel
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<Viewbox
AllowDrop="False"
ScrollViewer.HorizontalScrollMode="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollMode="Disabled"
Stretch="UniformToFill">
<GridView
x:Name="ProductList_GridView"
Margin="15,9.5,15,0" />
</Viewbox>
</StackPanel>
</ScrollViewer>
Also I've a kind of pagination setting which make request each time scrollable height is equal to vertical offset. And I'm adding new items to ObservableCollection by doing
foreach (var item in requestResult.products)
{
productList.Add(item);
}
this seems to work fine until i reach certain number of items. After that the app just crashes. My app's memory consumption reaches as high as 800Mb's before crashing on 2GB ram device. On 512Mb ram it crashes a lot earlier.
How can I fix the issue? Do I need to change the way I did this pagination. Or can it be fixed with small changes. I guess the problem is with Image but not sure. Isn't CacheMode="BitmapCache" supposed to fix the memory problem?
Your GridView virtualization is broken because you are wrapping it in a ScrollViewer and all items are rendered at once (and using much more memory).
If you only leave the following XAML you will have no more memory problems:
<GridView
x:Name="ProductList_GridView"
Margin="15,9.5,15,0" />
Related
I'm working on a application where the bulk of my content is presented to users with the built in WPF DataGrid. Here is what my DataGrid looks like. I don't set anything but the RowDefinitions on the parent Grid.
<UserControl xmlns="blah blah blah">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid x:Name="dgrMaterialCollection"
IsReadOnly="True"
ItemsSource="{Binding Path=MyObservableCollection, UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="True"
AutoGeneratingColumn="dgrMaterialCollection_AutoGeneratingColumn"
AutoGeneratedColumns="dgrMaterialCollection_AutoGeneratedColumns"
CanUserResizeColumns="True"
CanUserReorderColumns="True"
CanUserSortColumns="True"
EnableRowVirtualization="True"
EnableColumnVirtualization="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingPanel.VirtualizationMode="Standard"
VirtualizingPanel.IsVirtualizing="True"
SelectionMode="Single"
SelectionUnit="FullRow"
ScrollViewer.CanContentScroll="False"
RowHeaderWidth="0"
Grid.Row="0"
RowHeight="32"
RowDetailsVisibilityMode="Collapsed">
<DataGrid.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DataGridStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- A few value converters -->
</ResourceDictionary>
</DataGrid.Resources>
<!-- Some Manually Declared Columns -->
</DataGrid>
<StackPanel Grid.Row="1">
<SomeTextBoxes/>
</StackPanel>
</Grid>
</UserControl>
When my application displays the DataGrid, there is about a 10 - 20 second delay depending the size of the dataset. During this delay the UI is locked up. Once the data grid is loaded - I can scroll through all few thousand items without any delay. When I say without delay, I mean clicking on the scroll bar and moving it up and down as fast as humanly possible, with no delay in rendering the rows as it scrolls. It seems like the virtualization is not working, and instead the datagrid is creating all the rows at one time instead of while the user is scrolling.
My Datagrid is nested in the following structure (spread out over multiple xaml files). There are no extra ScrollViewers, StackPanels, or anything. Just this.
<RibbonWindow>
<Grid>
<ContentControl> <!-- Populated via a DataTemplate and ContentTemplateSelector -->
<UserControl>
<UserControl>
<UserControl>
<Grid>
<Here is the DataGrid>
Some things I have attempted to speed up the loading
Explicitly set all the column widths (shaved about 10 second off my load time)
Turned off AutoGenerateColumns (didn't make a difference).
Disabling the styles set in the DataGrid's RessourceDictionary (no difference)
Load items into my ObservableCollection on a background thread, using async/await, and other multithreaded strategies. This has made no difference in load times.
Add items to a new ObservableCollection, then setting it to the databound property (so the events aren't triggered)
Add items to the ObservableCollection one at a time (events are triggered upon each add)
Turning virtualization on and off makes no difference in load time.
I know the delay is not caused by populating the ObservableCollection. It starts after the collection is fully populated, and when the WPF DataGridis rendering all the rows/columns. I have virtualization enabled and it still takes forever. I am using the .NET framework 4.5.1 (so the 4.6 issue with Virtualizationis not causing this). There are also long delays when sorting (using built in datagrid sorting) and resizing columns from the UI.
What am I missing here? Is there something I can do to ensure Virtualizationis working? What are some things that might speed this up? If I know Virtualization is working then I may move onto a new strategy, like paging or some kind of lazy loading. Another solution I would be willing to work with is one where the user interface is not blocked/frozen while all the rows are being rendered. That way the users could at least click around on other thing while rows are added in.
The ScrollViewer.CanContentScroll value on the DataGrid element must be set to True in order for row virtualization to work properly. Here is an explanation of why it must be set this way.
Thanks to ChrisW for catching that in the comments on this question.
my idea is to create a listview with images, over each image I would like to insert a InkCanvas.
I tried two ways and both have problems.
First way.
I created the listview with its DataTemplate:
<DataTemplate x:Name="ListViewItemTemplate">
<Grid>
<Image></Image>
<InkCanvas></InkCanvas>
</Grid>
</DataTemplate>
<ItemsPanelTemplate x:Name="ListItemsPanelTemplate">
<VirtualizingStackPanel Orientation="Vertical">
<VirtualizingStackPanel.ChildrenTransitions>
<TransitionCollection/>
</VirtualizingStackPanel.ChildrenTransitions>
</VirtualizingStackPanel>
</ItemsPanelTemplate>
<ListView x:Name="list"
ItemTemplate="{StaticResource ListViewItemTemplate}"
ItemsPanel="{StaticResource ListItemsPanelTemplate}">
</ListView>
c#:
List<Image> pages = new List<Image>();
list.ItemsSource = pages;
In this way, the listview uses virtualization, when I write in a inkview, the stroke is copied every 5 inkview. It's not good.
If I remove the virtualization:
<ItemsPanelTemplate x:Name="ListItemsPanelTemplate">
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
It occurs the problem of the second way. Written below.
Second way:
I created a usercontrol PageFormat for the DataTemplate like this:
<UserControl>
<Grid Background="White">
<Image x:Name="image"></Image>
<InkCanvas x:Name="ink" ></InkCanvas>
</Grid>
</UserControl>
<ListView x:Name="list" ></ListView>
c#:
List<PageFormat> pages= new List<PageFormat>();
list.ItemsSource = pages;
In this way I have the following problem:like in this question
When I create more than 125 InkCanvas I have a memory problem.
I prefer the second way because I can play with the formatting of the page directly in a usercontrol, but I can not solve these problems.
I would like to solve the problem of generating InkCanvas the System.OutOfMemoryException error.
How can I solve it?
Thanks so much.
If you are 100% sure that InkCanvas is the cause of out of memory exceptions, you could do the following pseudo-system:
when a user is not editing an item, it should be persisted to disk/memory and a bitmap rendered and presented out of it.
when a user starts editing an item, de-serialize its data and realize it as an InkCanvas
i.e. use less resource-intensive types wherever it is appropriate
EDIT
Here's the solution to the fact that rendering an InkCanvas to a RenderTargetBitmap will produce a blank image:
https://mtaulty.com/2016/02/16/windows-10-uwp-inkcanvas-and-rendertargetbitmap/
(requires Win2D package)
I'm trying to make an image gallery from pictures library images. Everything is working fine when I have a reduced number of images, but if I have more than 80 images, the phone runs out of memory. I've tried using data virtualization by defining a class which implements the ISupportIncrementalLoading interface. Then I used that class to populate my GridView, but with absolutely no luck: it still is throwing an OutOfMemoryException.
The possibility I'm thinking about is to use random access virtualization, but I haven't found any code template about it.
So,
Could you please explain me how to apply random access virtualization to my list of images?
Or
Could you please explain me how to effectively make an image gallery which retrieves its elements from the Phone's photo gallery?
My XAML code is as follows:
<GridView x:Name="photosGrid" Height="392" Width="400" ItemsSource="{Binding}" Margin="0,0,-0.333,0" SelectionMode="Multiple" Background="Black">
<GridView.ItemTemplate>
<DataTemplate>
<Image Width="90" Height="90" Margin="5" Source="{Binding}" Stretch="UniformToFill"/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
I just have a conceptual question here. First of all I am programming a Windows 8.1 App using C# and XAML as well as the MVVM-Light Toolkit.
I am working on a Schedule at the moment which is backed by a calendar. The calendar should have a "DayView" which consists of a timeline on the left that goes from top to bottom. Now on the right of that timeline I want to show something similar to a rectangle as a visualization for a given task with a height that represents its duration. Every task has a starting time so the top of that rectangle should be placed next to the corresponding entry in the timeline.
Well I hope it is clear what I am trying to say here basically that should look similar to the Calendar-App coming with Windows 8. I will post a screenshot of that below that post.
Now I have tried or thought of multiple things by now that lead to more or less complicated problems. The best approach I had was creating a List a placing it on the right of that timeline. Now that List would contain a huge amount of items (maybe one for each pixel) which will be changed in size. The size then is 0 or in relation to the duration of the task depending on whether there is a one or not.
But that leads to huge problems when it comes to changing an entry or having more than one task for the same time since those items wont overlap. And I don't want to place more than one or two of those per page since the performance-issue would be overkill...
One of the issues I have is that I am using the MVVM-Concept and don't know how to be able to create new UI-Elements such as a rectangle without touching the code-behind. That is why basically the only thing I have thought about by now were lists...
I hope it is clear what I meant.
Thank you very much for your help!
FunkyPeanut
Problem solved using a Grid as the ItemsPanel of an ItemsControl:
<ItemsControl Grid.Column="2" ItemsSource="{Binding Day.ItemsList, Source={StaticResource Locator}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Width="20"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Height="{Binding Height}" Width="20" Margin="{Binding Margin}">
<Grid.Background>
<SolidColorBrush Opacity="{Binding Opacity}" Color="{Binding ColorHash, Converter={StaticResource HexToColorConverter}}"/>
</Grid.Background>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
With the following Data:
ItemsList = new ObservableCollection<object>(tmpPeriodsList.Select((x, i) => new
{
ColorHash = x.ColorHash,
Index = i,
Margin = StartPosition(i),
Opacity = 0.6,
Height = 45,
}));
am trying to show high quality images in phone ,as my image's size is too large app shows memory warning exception. so i tried visualization .but every time the listbox loads it loads whole data
xaml code:
<ListBox x:Name="ImageStack"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Visible"
VirtualizingStackPanel.VirtualizationMode="Recycling"
>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"></VirtualizingStackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Resources>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Image Source="{Binding bimage}" />
<TextBlock Text="{Binding impath}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and i used an IList
ImageStack.ItemsSource = new VirtualSongList();
and inside virtualsonglist class
object IList.this[int index]
{
get
{
// here is where the magic happens, create/load your data on the fly.
// Debug.WriteLine("Requsted item " + index.ToString());
System.Diagnostics.Debug.WriteLine(index.ToString());
return new im { impath = "yyyyyyyyy", bimage = new BitmapImage(new Uri(#"Assets/c"+index+".png", UriKind.Relative)) };
}
set
{
throw new NotImplementedException();
}
}
can anyone suggest a efficient way to display high quality image galley in windows
Use LongListSelector that was built with Virtualization in mind.
Even in that case it might not solve your problem if you are being too aggressive with your image memory.
Since you have limited space in your WP screen try making and showing a thumbnail of your image instead at first using BitmapImage::DecodePixelHeight and BitmapImage::DecodePixelWidth instead of decoding the full thing and therefore storing the full thing in memory. You can still show the full size image inside a details page, one at a time, when the user selects to do so.
Also try experimenting with BitmapImage::CacheOption (or other caching techniques) to see how you can better keep your memory in check.