我正在使用MVVMLight来创建调查表,并且在渲染InkCanvas控件时遇到内存问题。这是我正在使用的示例的简化示例:
问题病毒
public Question Question { get; set; }
public HandwritingControl HandwritingControl { get; set; }
问卷调查表
public List<QuestionVm> currentQuestions;
public List<QuestionVm> CurrentQuestions
{
get { return currentQuestions; }
set
{
currentQuestions = value;
RaisePropertyChanged();
}
}
问卷调查表
//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());
}
RAM在每次页面加载时都会增加,很明显分配给InkCanvas的内存永远不会被释放。在第三页左右的页面上,当大约呈现125个InkCanvas控件时,该应用程序将引发System.OutOfMemoryException。
我的问题是,为什么不取消分配这些控件?以及如何手动释放内存?如果我将InkCanvas注释掉,则问卷很好,而Children.Clear()似乎正在清理TextBlocks或任何其他控件而没有问题。
更新
因此,在使用@Grace Feng之后,我尝试重构我的方法,并使用带有数据模板的ListView,而不是从xaml.cs创建网格。
问卷
<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>
问卷调查表
private void buttonNext_Click(object sender, RoutedEventArgs e)
{
//Validate & goto next page
if (questionnaireVm.CurrentPageIsValid())
{
questionnaireVm.CurrentQuestions.Clear();
questionnaireVm.LoadNextPage();
}
}
不幸的是,即使使用ListView数据模板方法,我仍然遇到相同的内存不足错误。有什么想法吗?
最佳答案
在第三页左右的页面上,当大约呈现125个InkCanvas控件时,该应用程序将引发System.OutOfMemoryException。
我只是重现了这个问题,是的,您是正确的。
我的问题是,为什么不取消分配这些控件?
从Questionnaire.xaml.cs中的代码中,我认为您正在动态地将questionVm.Question
和InkCanvas
的新实例动态添加到名为“ questionnaireForm”的父控件中,并且在执行此操作之前,请清除此父控件的子级。在加载数据期间没有“取消分配”操作,因此这些控件都不会被释放。
以及如何手动释放内存?如果我将InkCanvas注释掉,则问卷很好,而Children.Clear()似乎正在清理TextBlocks或任何其他控件而没有问题。
如果您手动释放内存,则需要删除其中一些InkCanvas,或者我认为您可以针对这种情况做的正确的事情是使用UI虚拟化来减少加载数据时的内存丢失。
在UWP APP中,已经有两个具有UI虚拟化功能的控件ListView and GridView。我只是用超过125个空InkCnavas
实例测试这两个控件。 Item的大小GridView
适应于其在项目中的布局,因此,当InkCanvas
为空时,它仍将立即加载所有数据,仍然会发生内存不足错误。但是默认情况下,ListView
控件将占用一行内容,UI虚拟化在这里可以正常工作。
而且我看到您在InkCanvas
的基础上添加了questionVm.HandwritingControl != null
,所以这是一种解决方法,例如,您可以像这样设计Questionnaire.xaml:
<Page.Resources>
<DataTemplate x:Key="NoInkCanvasDataTemplate">
<TextBlock Text="{Binding Questions}" />
</DataTemplate>
<DataTemplate x:Key="InkCanvasDataTemplate">
<StackPanel>
<TextBlock Text="{Binding Questions}" />
<InkCanvas></InkCanvas>
</StackPanel>
</DataTemplate>
<local:CustomDataTemplateSelector x:Key="InkCanvasDataTemplateSelector" NoInkCanvas="{StaticResource NoInkCanvasDataTemplate}" InkCanvas="{StaticResource InkCanvasDataTemplate}"></local:CustomDataTemplateSelector>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView x:Name="listView" ItemTemplateSelector="{StaticResource InkCanvasDataTemplateSelector}" />
</Grid>
并在后面的代码中例如:
private ObservableCollection<CurrentQuestions> questions = new ObservableCollection<CurrentQuestions>();
public MainPage()
{
this.InitializeComponent();
listView.ItemsSource = questions;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
questions.Clear();
foreach (var questionVm in questionnaireVm.CurrentQuestions)
{
//Add your data here
}
}
并创建一个CustomDataTemplateSelector类,如下所示:
public class CustomDataTemplateSelector : DataTemplateSelector
{
public DataTemplate NoInkCanvas
{
get;
set;
}
public DataTemplate InkCanvas
{
get;
set;
}
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var canvas = item as HandwritingControl;
if (canvas == null)
{
return this.NoInkCanvas;
}
else
{
return InkCanvas;
}
}
}
简而言之,您可以使用
ListView
控件及其ItemTemplateSelector
来完成此操作,因为我没有您的所有代码,因此上述代码只是示例,并非100%正确地适合您的情况。关于c# - UWP InkCanvas内存不足,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36407655/