【问题标题】:UWP InkCanvas Out Of MemoryUWP InkCanvas 内存不足
【发布时间】:2016-04-04 16:09:13
【问题描述】:

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

【问题讨论】:

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


    【解决方案1】:

    在第三页左右呈现大约 125 个 InkCanvas 控件时,应用会抛出 System.OutOfMemoryException。

    我刚刚重现了这个问题,是的,你是对的。

    我的问题是,为什么不释放这些控件?

    从 Questionnaire.xaml.cs 中的代码来看,我认为您正在将 questionVm.QuestionInkCanvas 的新实例动态添加到名为“questionnaireForm”的父控件中,在执行此操作之前,您清除了这个父控件。在加载数据期间没有“解除分配”操作,因此这些控件都不会被解除分配。

    如何手动释放内存?如果我注释掉 InkCanvas,则调查问卷很好,并且 Children.Clear() 似乎正在清理 TextBlocks 或任何其他控件而没有问题。

    如果您手动释放内存,您将需要删除其中的一些 InkCanvas,或者我认为您可以做的就是在这种情况下使用 UI 虚拟化来减少加载数据时的内存损失。

    在UWP APP中,有两个控件已经具备UI虚拟化功能,ListView and GridView。我只是用超过 125 个空 InkCnavas 实例测试这两个控件。 GridView 的Item 的大小是根据它在item 中的布局自适应的,所以当InkCanvas 为空时,它仍然会一次加载所有数据,仍然会出现内存不足的错误。但是ListView 控件默认会占用一行来保存它的项目,UI 虚拟化在这里可以正常工作。

    我看到您在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>
    

    并以后面的代码为例:

    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% 正确。

    【讨论】:

    • Grace,这是一个很棒的解决方案,将被标记为答案。关于这个,我还有一个问题要问你。我们的数据结构要复杂得多,其中有各种各样的问题/InkCanvas/其他控件。最好的解决方案是为每个独特的组合创建一个 DataTemplate 吗?我觉得好像必须有一种方法可以使这种数据更受数据驱动。使用网格方法,我只需根据为每个问题提供的数据呈现每一行。您将如何应对更复杂的情况?
    • @jagsrocknfl,你的意思是所有问题都有两种以上的样式?
    • 是的,每个问题基本上都是以下的任意组合:文本/InkCanvas 和单选按钮/复选框等。这是所有数据驱动的,每个问题可能有所有控件或只有文本。那么使用数据模板我们是否需要为每个组合创建一个新模板?还是只使用一个模板,它们都可以为空?
    • 如果是这样,我会尝试创建一个usercontrol,它使用所有需要的控件和最复杂的样式,并在DataTemplate中使用这个控件,我将一起使用转换器来判断孩子基于数据的用户控件内部控件的可见性。这意味着,创建一个 Usercontrol 并将其中的子控件的 Visibility 属性与转换器绑定。或者忘记用户控件,只需在数据模板中设计您的布局并使用与转换器的绑定来使控件可见或不可见。
    • 能否请您重新打开聊天讨论,好像这个已经关闭了
    【解决方案2】:

    问题出在这一行

    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 循环中使用它。您可以在构造函数或类级别创建对象

    【讨论】:

    • Apoorv,使用一个 InkCanvas 将不起作用。我每个问题都需要一个。
    猜你喜欢
    • 1970-01-01
    • 2023-03-22
    • 2018-01-16
    • 2016-07-23
    • 1970-01-01
    • 2017-11-27
    • 2018-11-09
    • 1970-01-01
    • 2014-10-15
    相关资源
    最近更新 更多