我正在使用MVVMLight创建问卷并在渲染InkCanvas控件时遇到内存问题。以下是我正在使用的一个淡化示例:
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());
}
RAM在每个页面加载时出现峰值,很明显,分配给InkCanvas的内存永远不会被释放。在大约~125个InkCanvas控件呈现的第三个左右页面上,应用程序抛出System.OutOfMemoryException。
我的问题是,为什么这些控制措施不会被解除分配?我怎样才能手动释放内存?如果我注释掉InkCanvas,问卷就可以了,而且Children.Clear()似乎正在清理TextBlocks或任何其他控件而没有问题。
更新
因此在与@Grace Feng合作之后,我尝试重构我的方法并使用带有数据模板的ListView,而不是从我的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();
}
}
不幸的是,即使使用ListView数据模板方法,我仍然遇到相同的内存不足错误。想法?
答案 0 :(得分:1)
在渲染大约~125个InkCanvas控件的第三页左右,应用程序抛出System.OutOfMemoryException。
我刚刚转载了这个问题,是的,你是对的。
我的问题是,为什么这些控件没有被解除分配?
从您的Questionnaire.xaml.cs中的代码,我认为您动态地将questionVm.Question
和InkCanvas
的新实例添加到名为&#34; questionnaireForm&#34;的父控件中,并且在执行此操作之前,清除此父控件的子项。没有&#34;解除分配&#34;加载数据时的操作,因此这些控件都不会被释放。
我怎样才能手动释放内存?如果我注释掉InkCanvas,问卷就可以了,而且Children.Clear()似乎正在清理TextBlocks或任何其他控件而没有问题。
如果您手动释放内存,则需要删除其中的一些InkCanvas,或者我认为您可以正确执行的操作是使用UI虚拟化来减少加载数据时的内存丢失。
在UWP APP中,有两个控件已经具有UI虚拟化功能,ListView and GridView。我只用超过125个空InkCnavas
实例测试这两个控件。项目的GridView
大小适应项目中的布局,因此当InkCanvas
为空时,它仍会一次加载所有数据,内存错误仍会发生。但默认情况下ListView
控件将占用一行来保存其项目,UI Virtualization在这里工作正常。
我看到您根据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正确适用于您的情况。
答案 1 :(得分:0)
问题出在这一行
foreach (var questionVm in questionnaireVm.CurrentQuestions)
{
questionnaireForm.Children.Add(questionVm.Question);
if(questionVm.HandwritingControl != null)
questionnaireForm.Children.Add(new InkCanvas()); //everytime you are creating a new object
}
尝试创建InkCanvas的一个对象,并在foreach循环中每次使用它。您可以在构造函数或类级别
中创建对象