简体   繁体   English

UWP InkCanvas 内存不足

[英]UWP InkCanvas Out Of Memory

I'm using MVVMLight to create a questionnaire and running into memory issues when rendering the InkCanvas controls.我正在使用 MVVMLight 创建调查问卷并在呈现 InkCanvas 控件时遇到内存问题。 Here's a watered down example of what I am working with:这是我正在使用的一个淡化示例:

QuestionVm问题Vm

public Question Question { get; set; }
public HandwritingControl HandwritingControl { get; set; }

QuestionnaireVm问卷Vm

public List<QuestionVm> currentQuestions;
public List<QuestionVm> CurrentQuestions
{
    get { return currentQuestions; }
    set
    {
        currentQuestions = value;
        RaisePropertyChanged();
    }
}

Questionnaire.xaml.cs问卷.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. RAM 在每个页面加载时都会出现峰值,很明显,分配给 InkCanvas 的内存永远不会被释放。 On the third or so page when roughly ~125 InkCanvas controls are rendered, the app throws a System.OutOfMemoryException.在呈现大约 125 个 InkCanvas 控件的第三个左右的页面上,应用程序抛出 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.如果我注释掉 InkCanvas,问卷就很好,Children.Clear() 似乎正在清理 TextBlocks 或任何其他控件,没有问题。

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.因此,在与 @Grace Feng 合作后,我尝试重构我的方法并使用带有数据模板的 ListView,而不是从我的 xaml.cs 创建网格。

Questionnaire.xaml问卷.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问卷.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.不幸的是,即使使用 ListView 数据模板方法,我仍然遇到相同的内存不足错误。 Thoughts?想法?

On the third or so page when roughly ~125 InkCanvas controls are rendered, the app throws a System.OutOfMemoryException.在呈现大约 125 个 InkCanvas 控件的第三个左右的页面上,应用程序抛出 System.OutOfMemoryException。

I just reproduced this problem, and yes you are right about this.我刚刚重现了这个问题,是的,你是对的。

My question is, why aren't these controls being deallocated?我的问题是,为什么不释放这些控件?

From the code in your Questionnaire.xaml.cs, I think you are dynamic adding questionVm.Question and a new instance of InkCanvas to the parent control named "questionnaireForm", and before doing this, you clear the children of this parent control.从您的 Questionnaire.xaml.cs 中的代码来看,我认为您正在将questionVm.QuestionInkCanvas的新实例动态添加到名为“questionnaireForm”的父控件中,在此之前,您需要清除该父控件的子控件。 There is no "deallocating" action during you load your data, so none of these controls will be 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.如果我注释掉 InkCanvas,问卷就很好,Children.Clear() 似乎正在清理 TextBlocks 或任何其他控件,没有问题。

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.如果您手动释放内存,您将需要删除其中的一些 InkCanvas,或者我认为您可以做的正确的事情是使用 UI 虚拟化来减少加载数据时的内存损失。

In an UWP APP, there are two controls which have UI virtualization function already, ListView and GridView .在一个 UWP APP 中,已经有两个控件具有 UI 虚拟化功能, ListView 和 GridView I just test these two controls with over 125 instance of empty InkCnavas .我只是用超过 125 个空InkCnavas实例测试这两个控件。 The Item's size of GridView is adaptive to its layout in the item, so when the InkCanvas is empty, it will still load all data at once, the out of memory error will still happen. GridView的Item的大小是根据它在item中的布局自适应的,所以当InkCanvas为空的时候,还是会一次性加载所有的数据,还是会出现内存不足的错误。 But the ListView control by default will take one row to hold its items, the UI Virtualization works fine here.但是默认情况下ListView控件将占用一行来保存其项目,UI 虚拟化在这里工作正常。

And I saw that you add InkCanvas based on the questionVm.HandwritingControl != null , so here is a workaround, you can for example design your Questionnaire.xaml like this:我看到你添加了基于questionVm.HandwritingControl != null InkCanvas ,所以这里有一个解决方法,例如你可以像这样设计你的 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>

And in the code behind for example:例如在后面的代码中:

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
    }
}

And create a CustomDataTemplateSelector class like this:并像这样创建一个 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;
        }
    }
}

In a few words, you can do this with a ListView control and its ItemTemplateSelector , since I don't have all your code, the above code is just a sample, not 100% correctly for your case.简而言之,您可以使用ListView控件及其ItemTemplateSelector来执行此操作,因为我没有您的所有代码,所以上面的代码只是一个示例,对于您的情况并非 100% 正确。

The problem is in this line问题出在这一行

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                    
}

try to create one object of InkCanvas and use it everytime inside the foreach loop.尝试创建 InkCanvas 的一个对象并每次在 foreach 循环中使用它。 You can create the object in the constructor or at the class level您可以在构造函数或类级别创建对象

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM