I'm using MVVMLight to create a questionnaire and running into memory issues when rendering the InkCanvas controls. Here's a watered down example of what I am working with:
QuestionVm
public Question Question { get; set; }
public HandwritingControl HandwritingControl { get; set; }
QuestionnaireVm
public List<QuestionVm> currentQuestions;
public List<QuestionVm> CurrentQuestions
{
get { return currentQuestions; }
set
{
currentQuestions = value;
RaisePropertyChanged();
}
}
Questionnaire.xaml.cs
//Clear form & iterate questions
questionnaireForm.Children.Clear();
foreach (var questionVm in questionnaireVm.CurrentQuestions)
{
questionnaireForm.Children.Add(questionVm.Question);
if(questionVm.HandwritingControl != null)
questionnaireForm.Children.Add(new InkCanvas());
}
The RAM spikes on each page load and it's clear the memory allocated to the InkCanvas is never being deallocated. On the third or so page when roughly ~125 InkCanvas controls are rendered, the app throws a System.OutOfMemoryException.
My question is, why aren't these controls being deallocated? And how can I manually free up the memory? If I comment out the InkCanvas, the questionnaire is fine and Children.Clear() appears to be cleaning up the TextBlocks or any other controls without issue.
UPDATE
So after working with @Grace Feng I tried to refactor my approach and use a ListView with a data template rather than creating a grid from my xaml.cs.
Questionnaire.xaml
<ListView Name="questionnaireListView" ItemsSource="{Binding CurrentQuestions, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Question.Text}" />
<TextBlock Text="{Binding Question.Description}" />
<InkCanvas/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Questionnaire.xaml.cs
private void buttonNext_Click(object sender, RoutedEventArgs e)
{
//Validate & goto next page
if (questionnaireVm.CurrentPageIsValid())
{
questionnaireVm.CurrentQuestions.Clear();
questionnaireVm.LoadNextPage();
}
}
Unfortunately I am still experiencing the same out of memory error even using the ListView data template method. Thoughts?
I just reproduced this problem, and yes you are right about this.
From the code in your Questionnaire.xaml.cs, I think you are dynamic adding
questionVm.Question
and a new instance ofInkCanvas
to the parent control named "questionnaireForm", and before doing this, you clear the children of this parent control. There is no "deallocating" action during you load your data, so none of these controls will be deallocated.If you manually free up the memory, you will need to remove some of these InkCanvas, or I think what you can do right is for this scenario is using UI virtualization to reduce the memory loss when you load data.
In an UWP APP, there are two controls which have UI virtualization function already, ListView and GridView. I just test these two controls with over 125 instance of empty
InkCnavas
. The Item's size ofGridView
is adaptive to its layout in the item, so when theInkCanvas
is empty, it will still load all data at once, the out of memory error will still happen. But theListView
control by default will take one row to hold its items, the UI Virtualization works fine here.And I saw that you add
InkCanvas
based on thequestionVm.HandwritingControl != null
, so here is a workaround, you can for example design your Questionnaire.xaml like this:And in the code behind for example:
And create a CustomDataTemplateSelector class like this:
In a few words, you can do this with a
ListView
control and itsItemTemplateSelector
, since I don't have all your code, the above code is just a sample, not 100% correctly for your case.The problem is in this line
try to create one object of InkCanvas and use it everytime inside the foreach loop. You can create the object in the constructor or at the class level