2016-04-04 2 views
2

Я использую MVVMLight для создания вопросника и проблем с памятью при рендеринге элементов управления InkCanvas. Вот разбавленные пример того, что я работаю с:UWP InkCanvas Out Of Memory

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, вопросник прекрасен, и Child.Clear(), похоже, очищает TextBlocks или любые другие элементы управления без проблем.

UPDATE

Таким образом, после работы с @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. Мысли?

ответ

1

На третьей странице, где отображаются примерно 125 элементов управления InkCanvas, приложение генерирует исключение System.OutOfMemoryException.

Я просто воспроизвел эту проблему, и да, вы правы в этом.

Вопрос в том, почему эти средства управления не освобождены?

Из кода в вашем Questionnaire.xaml.cs, я думаю, что вы динамичны добавление questionVm.Question и новый экземпляр InkCanvas для управления родительского названия «questionnaireForm», и прежде чем делать это, вы освобождаете ребенок этого родительский контроль. Во время загрузки ваших данных нет действия «deallocating», поэтому ни один из этих элементов управления не будет освобожден.

И как я могу вручную освободить память? Если я прокомментирую InkCanvas, вопросник прекрасен, и Child.Clear(), похоже, очищает TextBlocks или любые другие элементы управления без проблем.

Если вручную освободить память, вам нужно будет удалить некоторые из этих InkCanvas, или я думаю, что вы можете сделать прямо для этого сценария использует виртуализацию пользовательского интерфейса, чтобы уменьшить потерю памяти при загрузке данных.

В UWP APP есть два элемента управления, у которых уже есть функция виртуализации UI, ListView and GridView. Я просто тестирую эти два элемента управления с более чем 125 экземплярами пустых InkCnavas. Размер элемента GridView адаптивен к его компоновке в элементе, поэтому, когда InkCanvas пуст, он все равно будет загружать все данные одновременно, ошибка в памяти будет продолжаться. Но управление ListView по умолчанию займет одну строку для хранения своих элементов, здесь виртуализация пользовательского интерфейса работает отлично.

И я увидел, что вы добавляете 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% правильно для вашего дела.

+0

Грейс, это фантастическое решение и будет отмечено как ответ. У меня был другой вопрос для вас по этому поводу. Наша структура данных намного сложнее, когда существует большое количество вопросов/InkCanvas/другие элементы управления. Лучшим решением было бы создать DataTemplate для каждой уникальной комбинации? Я чувствую, что должен быть способ сделать это более управляемым данными. С помощью grid-подхода я просто рисую каждую строку на основе данных, данных для каждого вопроса. Как бы вы занялись более сложным сценарием? –

+0

@jagsrocknfl, вы имеете в виду, что у вас есть более двух стилей для всех вопросов? –

+0

Да, каждый вопрос представляет собой, по существу, любую комбинацию: Text/InkCanvas & Radio Button/Checkbox/etc. Это все данные, и каждый вопрос может иметь все элементы управления или только текст. Итак, используя шаблоны данных, нам нужен новый шаблон для каждой комбинации? Или просто используйте один шаблон, где все они имеют значение NULL? –

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 и использовать его каждый раз, когда внутри цикла Еогеаспа. Вы можете создать объект в конструкторе или на уровне класса

+0

Apoorv, используя один InkCanvas, не будет работать. Мне нужен один вопрос. –