2016-04-04 45 views
2

我正在使用MVVMLight创建问卷并在渲染InkCanvas控件时遇到内存问题。以下是我与工作的淡化例如:UWP 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()); 
} 

内存在每个页面加载时都会出现尖峰,清楚地分配给InkCanvas的内存永远不会被释放。在大约〜125个InkCanvas控件呈现时,在第三页左右,应用程序抛出System.OutOfMemoryException。

我的问题是,为什么不释放这些控件?我怎么手动释放内存?如果我注释掉InkCanvas,问卷很好,而Children.Clear()似乎正在清理TextBlocks或任何其他控件而没有问题。

UPDATE

因此,与@Grace冯工作后,我试图重构我的方法和使用与数据模板一个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数据模板的方法仍然遇到相同的内存不足的错误。思考?

回答

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适应于它在项目中的布局,所以当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%正确。

+0

恩典,这是一个绝妙的解决方案,将被标记为答案。我对你有另一个问题。在存在大量问题/ InkCanvas /其他控件的情况下,我们的数据结构要复杂得多。最好的解决方案是为每个独特的组合创建一个DataTemplate?我觉得好像必须有办法让这种更多的数据驱动。通过网格方法,我只需根据每个问题给出的数据来渲染每一行。你将如何处理更复杂的情况? –

+0

@jagsrocknfl,你的意思是你对所有问题都有两种以上的风格? –

+0

是的,每个问题基本上是以下任意组合:文本/ InkCanvas和单选按钮/复选框/等等。这些都是数据驱动的,每个问题可能包含所有控件或仅包含文本。所以使用数据模板我们需要每个组合的新模板?或者只使用一个模板,它们都是可空的? –

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

+0

Apoorv,使用一个InkCanvas将无法正常工作。我需要每个问题一个。 –