UWP InkCanvas内存不足

时间:2016-04-04 16:09:14

标签: c# win-universal-app mvvm-light inkcanvas

我正在使用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数据模板方法,我仍然遇到相同的内存不足错误。想法?

2 个答案:

答案 0 :(得分:1)

  

在渲染大约~125个InkCanvas控件的第三页左右,应用程序抛出System.OutOfMemoryException。

我刚刚转载了这个问题,是的,你是对的。

  

我的问题是,为什么这些控件没有被解除分配?

从您的Questionnaire.xaml.cs中的代码,我认为您动态地将questionVm.QuestionInkCanvas的新实例添加到名为&#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循环中每次使用它。您可以在构造函数或类级别

中创建对象