locked
Список изображений (c#, VS 2012, Metro) RRS feed

  • Вопрос

  • Проблема в следующем.

    Необходимо создать список изображений с пред просмотром. Изображений может быть очень много (более 2000 с цифровика).

    Пытался сделать в ListBox - изображения в Image (в DataTemplate) не грузятся. Нашел способ создания списка через GridView:

    <GridView ItemsSource="{Binding ItemList}" SelectionMode="Multiple" Margin="10,120,10,10" HorizontalContentAlignment="Stretch" >
                <GridView.ItemContainerStyle>
                    <Style TargetType="GridViewItem">
                        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                        <Setter Property="VerticalContentAlignment" Value="Top"/>
                        <Setter Property="HorizontalAlignment" Value="Stretch"/>
                        <Setter Property="VerticalAlignment" Value="Top"/>
                    </Style>
                </GridView.ItemContainerStyle>
                <GridView.ItemTemplate>
                    <DataTemplate>
                        <Grid Height="130" Width="190" Margin="5">
                            <ToolTipService.ToolTip>
                                <Grid Background="{StaticResource ToolTipBackgroundThemeBrush}" Height="180" Width="460">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*"/>
                                        <ColumnDefinition Width="10"/>
                                        <ColumnDefinition Width="210"/>
                                    </Grid.ColumnDefinitions>
                                    <Border BorderBrush="{StaticResource ToolTipBorderThemeBrush}" />
                                    <StackPanel Grid.Column="0">
                                        <TextBlock Text="{Binding Title}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" Foreground="{StaticResource ToolTipForegroundThemeBrush}" FontSize="16" />
                                        <TextBlock Text="{Binding ImageType}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" Foreground="{StaticResource ToolTipForegroundThemeBrush}" />
                                        <TextBlock Text="{Binding ImageDate}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" Foreground="{StaticResource ToolTipForegroundThemeBrush}" />
                                        <TextBlock Text="{Binding Path}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" MaxHeight="80" Foreground="{StaticResource ToolTipForegroundThemeBrush}" />
                                    </StackPanel>
                                    <Image Source="{Binding Image}" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10" MaxWidth="190" MaxHeight="130"/>
                                </Grid>
                            </ToolTipService.ToolTip>
                            <Image Source="{Binding Image}" HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="190" MaxHeight="130" Stretch="Uniform"/>
                        </Grid>
                    </DataTemplate>
                </GridView.ItemTemplate>
            </GridView>
    ImageItems imageItems;
    public MainPage()
    {
        this.DataContext = this.imageItems = new ImageItems();
        this.InitializeComponent();
    }
    
    private async void AddFilesInFolder(object sender, RoutedEventArgs e)
    {
        var fp = new FolderPicker();
        fp.FileTypeFilter.Add(".jpg");
        fp.FileTypeFilter.Add(".jpeg");
        fp.FileTypeFilter.Add(".png");
        fp.FileTypeFilter.Add(".gif");
        fp.FileTypeFilter.Add(".bmp");
        fp.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
        fp.ViewMode = PickerViewMode.Thumbnail;
        var selectFolder = await fp.PickSingleFolderAsync();
        if (selectFolder != null)
        {
            var queryOptions = new QueryOptions(CommonFileQuery.OrderByName, new List<string>() { ".jpg", ".jpeg", ".png", ".gif", ".bmp" });
            StorageFileQueryResult queryResults = selectFolder.CreateFileQueryWithOptions(queryOptions);
            var fif = new Windows.Storage.BulkAccess.FileInformationFactory(queryResults, Windows.Storage.FileProperties.ThumbnailMode.PicturesView, 50, Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale, false);
            IReadOnlyList<StorageFile> fileList = await queryResults.GetFilesAsync();
            foreach (var file in fileList)
            {
                imageItems.ItemList.Add(new ImageItem(file));
            }
        }
    }
    
    public class ImageItems : ObservableCollection<ImageItem>, INotifyPropertyChanged
    {
        public new event PropertyChangedEventHandler PropertyChanged;
    
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    
        private ObservableCollection<ImageItem> itemList = new ObservableCollection<ImageItem>();
        public ObservableCollection<ImageItem> ItemList
        {
            get { return itemList; }
            set
            {
                itemList = value;
                OnPropertyChanged("ItemList");
            }
        }
    }
    
    public class ImageItem : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        private StorageFile imgFile;
        public string Title { get { return imgFile.DisplayName; } }
        public string ImageType { get { return imgFile.DisplayType; } }
        public string ImageDate { get { return imgFile.DateCreated.ToString(System.Globalization.CultureInfo.CurrentCulture); } }
        public string Path { get { return imgFile.Path.Replace(imgFile.Name, string.Empty); } }
        private ImageSource image = null;
        public ImageSource Image
        {
            get { return this.image; }
            private set
            {
                this.image = value;
                this.OnPropertyChanged("Image");
            }
        }
        public ImageItem(StorageFile file)
        {
            imgFile = file;
            SetImage(file);
        }
    
        public async void SetImage(StorageFile file)
        {
            var imageThumbnail = await file.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.PicturesView);//, 40, Windows.Storage.FileProperties.ThumbnailOptions.None);
            BitmapImage imageBitmapImage = new BitmapImage();
            await imageBitmapImage.SetSourceAsync(imageThumbnail);
            Image = imageBitmapImage;
        }
    }

    Но сталкнулся с большоооой проблемой. Так как изображения достаточно большие, уже при загрузке около 150 изображений, объем занимаемой приложением памяти достигает 500-600 Мб (и дальше, соответственно, уверенно растет).

    Каким образом можно реализовать загрузку только видимых изображений или как еще можно решить эту проблему?

    26 марта 2013 г. 7:42

Ответы

  • Попробуйте так:

        <Page.Resources>
                <CollectionViewSource x:Name="ImagesSource"/>
        </Page.Resources>
    
        <GridView x:Name="grid" ItemsSource="{Binding Source={StaticResource ImagesSource}}">
            <GridView.ItemTemplate>
                <DataTemplate>
                    <Grid Height="130" Width="190" Margin="5">
                        <ToolTipService.ToolTip>
                            <Grid Background="{StaticResource ToolTipBackgroundThemeBrush}" Height="180" Width="460">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="10"/>
                                    <ColumnDefinition Width="210"/>
                                </Grid.ColumnDefinitions>
                                <Border BorderBrush="{StaticResource ToolTipBorderThemeBrush}" />
                                <StackPanel Grid.Column="0">
                                    <TextBlock Text="{Binding Title}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" Foreground="{StaticResource ToolTipForegroundThemeBrush}" FontSize="16" />
                                    <TextBlock Text="{Binding ImageType}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" Foreground="{StaticResource ToolTipForegroundThemeBrush}" />
                                    <TextBlock Text="{Binding ImageDate}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" Foreground="{StaticResource ToolTipForegroundThemeBrush}" />
                                    <TextBlock Text="{Binding Path}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" MaxHeight="80" Foreground="{StaticResource ToolTipForegroundThemeBrush}" />
                                </StackPanel>
                                <Image Source="{Binding Image}" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10" MaxWidth="190" MaxHeight="130"/>
                            </Grid>
                        </ToolTipService.ToolTip>
                        <Image Source="{Binding Image}" HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="190" MaxHeight="130" Stretch="Uniform"/>
                    </Grid>
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>
    

    И .cs файл страницы:

        public sealed partial class BlankPage : LayoutAwarePage
        {
            private GeneratorIncrementalLoadingClass<ImageItem> imageItems;
            private IReadOnlyList<StorageFile> fileList;
    
            public BlankPage()
            {
                this.InitializeComponent();
            }
    
            protected override async void LoadState(object navigationParameter, Dictionary<string, object> pageState)
            {
                var fp = new FolderPicker();
                fp.FileTypeFilter.Add(".jpg");
                fp.FileTypeFilter.Add(".jpeg");
                fp.FileTypeFilter.Add(".png");
                fp.FileTypeFilter.Add(".gif");
                fp.FileTypeFilter.Add(".bmp");
                fp.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
                fp.ViewMode = PickerViewMode.Thumbnail;
                var selectFolder = await fp.PickSingleFolderAsync();
                if (selectFolder != null)
                {
                    var queryOptions = new QueryOptions(CommonFileQuery.OrderByName, new[] { ".jpg", ".jpeg", ".png", ".gif", ".bmp" });
                    var storageFileQueryResult = selectFolder.CreateFileQueryWithOptions(queryOptions);
                    fileList = await storageFileQueryResult.GetFilesAsync();
                    imageItems = new GeneratorIncrementalLoadingClass<ImageItem>((uint)fileList.Count, i => new ImageItem(fileList.ElementAt(i)));
                    ImagesSource.Source = imageItems;
                }
            }
        }
    

    У меня никаких ошибок и все картинки грузятся.

    27 марта 2013 г. 19:02
  • Тогда в дополнение ко всему стоит ознакомиться со статьей в MSDN Использование виртуализации для списка или сетки
    28 марта 2013 г. 11:58

Все ответы

  • В WinRT есть новый интерфейс ISupportIncrementalLoading, который позволяет осуществить инкрементальную загрузку элементов списка. Также можете посмотреть вот эту и эту ветки.
    • Изменено Kirill Bessonov 26 марта 2013 г. 8:02
    • Предложено в качестве ответа Abolmasov Dmitry 27 марта 2013 г. 8:01
    26 марта 2013 г. 8:02
  • В WinRT есть новый интерфейс ISupportIncrementalLoading, который позволяет осуществить инкрементальную загрузку элементов списка. Также можете посмотреть вот эту и эту ветки.

    А можно какой-нить простой пример на ISupportIncrementalLoading, а то что-то не пойму.
    26 марта 2013 г. 8:21
  • Так там же есть полный пример XAML data binding sample
    • Предложено в качестве ответа Abolmasov Dmitry 27 марта 2013 г. 8:01
    26 марта 2013 г. 8:32
  • Так там же есть полный пример XAML data binding sample

    Так вот я и не пойму как его интегрировать в код? Это и есть мой список, или это будет доп. класс, или еще как? В XAML надо что-то писать или нет!? )))
    26 марта 2013 г. 8:38
  • В примере есть абстрактный класс IncrementalLoadingBase вы можете наследовать от него свою коллекцию. Так же есть пример GeneratorIncrementalLoadingClass<T> и 8 сценарий в этом проекте.
    26 марта 2013 г. 9:03
  • В общем или я дурак, или одно из двух!

    На основе 8-го сценария сделал свои классы. НО НЕ ЧЕРТА НЕ ВЫХОДИТ.

    При вызове "imageItems = new GeneratorIncrementalLoadingClass<ImageItem>(maxCount, (count) => {" загружается только ток кол-во элементов, которое прописано в "maxCount", при этом, если оно больше кол-ва загруженных изображений (файлов), то:

    если я делаю:

    imageItems = new GeneratorIncrementalLoadingClass<ImageItem>(maxCount, (count) =>
    {
        return new ImageItem(listFiles[count]);
    });

    вылезает ошибка, а если делаю:

    imageItems = new GeneratorIncrementalLoadingClass<ImageItem>(maxCount, (count) =>
    {
        if (listFiles.Count > count)
            return new ImageItem(listFiles[count]);
        else
            return null;
    });

    то после последнего файла в listFiles - добавляются пустые элементы.

    Пробовал через "PrepareContainerForItemOverride", но тут я могу отловить только момент показа элемента (и, например, начать загрузку и отображение картинки), а момент скрытия элемента на могу (чтобы, например, вызвать "...Image = null;")

    Поработав с "GeneratorIncrementalLoadingClass" уже было подумал, что проще было бы не его использовать, а вделать как на сайтах - несколько страниц, на которых выводятся элементы с №1 по №10 на одной, с №11 по №20 на другой и т.д..

    Но я хотел бы, все же, сделать весь список на одной странице (грубо говоря - по типу меню "Пуск").

    Подскажите, пожалуйста, что я делаю не так!?


    • Изменено Hovanskiy 27 марта 2013 г. 8:56
    27 марта 2013 г. 8:51
  • Попробуйте так:

        <Page.Resources>
                <CollectionViewSource x:Name="ImagesSource"/>
        </Page.Resources>
    
        <GridView x:Name="grid" ItemsSource="{Binding Source={StaticResource ImagesSource}}">
            <GridView.ItemTemplate>
                <DataTemplate>
                    <Grid Height="130" Width="190" Margin="5">
                        <ToolTipService.ToolTip>
                            <Grid Background="{StaticResource ToolTipBackgroundThemeBrush}" Height="180" Width="460">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="10"/>
                                    <ColumnDefinition Width="210"/>
                                </Grid.ColumnDefinitions>
                                <Border BorderBrush="{StaticResource ToolTipBorderThemeBrush}" />
                                <StackPanel Grid.Column="0">
                                    <TextBlock Text="{Binding Title}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" Foreground="{StaticResource ToolTipForegroundThemeBrush}" FontSize="16" />
                                    <TextBlock Text="{Binding ImageType}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" Foreground="{StaticResource ToolTipForegroundThemeBrush}" />
                                    <TextBlock Text="{Binding ImageDate}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" Foreground="{StaticResource ToolTipForegroundThemeBrush}" />
                                    <TextBlock Text="{Binding Path}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" MaxHeight="80" Foreground="{StaticResource ToolTipForegroundThemeBrush}" />
                                </StackPanel>
                                <Image Source="{Binding Image}" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10" MaxWidth="190" MaxHeight="130"/>
                            </Grid>
                        </ToolTipService.ToolTip>
                        <Image Source="{Binding Image}" HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="190" MaxHeight="130" Stretch="Uniform"/>
                    </Grid>
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>
    

    И .cs файл страницы:

        public sealed partial class BlankPage : LayoutAwarePage
        {
            private GeneratorIncrementalLoadingClass<ImageItem> imageItems;
            private IReadOnlyList<StorageFile> fileList;
    
            public BlankPage()
            {
                this.InitializeComponent();
            }
    
            protected override async void LoadState(object navigationParameter, Dictionary<string, object> pageState)
            {
                var fp = new FolderPicker();
                fp.FileTypeFilter.Add(".jpg");
                fp.FileTypeFilter.Add(".jpeg");
                fp.FileTypeFilter.Add(".png");
                fp.FileTypeFilter.Add(".gif");
                fp.FileTypeFilter.Add(".bmp");
                fp.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
                fp.ViewMode = PickerViewMode.Thumbnail;
                var selectFolder = await fp.PickSingleFolderAsync();
                if (selectFolder != null)
                {
                    var queryOptions = new QueryOptions(CommonFileQuery.OrderByName, new[] { ".jpg", ".jpeg", ".png", ".gif", ".bmp" });
                    var storageFileQueryResult = selectFolder.CreateFileQueryWithOptions(queryOptions);
                    fileList = await storageFileQueryResult.GetFilesAsync();
                    imageItems = new GeneratorIncrementalLoadingClass<ImageItem>((uint)fileList.Count, i => new ImageItem(fileList.ElementAt(i)));
                    ImagesSource.Source = imageItems;
                }
            }
        }
    

    У меня никаких ошибок и все картинки грузятся.

    27 марта 2013 г. 19:02
  • :):):)

    А где взять ссылку на "LayoutAwarePage" ???

    :):):)

    28 марта 2013 г. 11:18
  • Создайте в проекте страницу Basic Page и автоматом добавится LayoutAwarePage.

    "Шаблон Basic Page создает класс, наследующий класс LayoutAwarePage, который добавляется в ваш проект в Common/LayoutAwarePage.cs или Common/LayoutAwarePage.vb. Класс LayoutAwarePage определяет методы GoBackи GoHome. "

    28 марта 2013 г. 11:30
  • Да, так работает. Подгружает элементы, только если прокрутить к концу списка. Но это не совсем то, что нужно.

    Мне необходимо заполнить список изображениями, с которыми я потом буду работать. Т.е. список должен быть фиксированный.

    Я, конечно, могу сделать так:

    1) гружу файлы из выбранной папки в переменную "список файлов" (например в List<StorageFile> fileList)

    2) затем я заполняю GridView элементами с помощью этого списка по мене необходимости (как в вашем примере)

    Но есть большое НО!

    Даже в вашем примере, если прокрутить список к концу - новые элементы создаются и добавляются в GridView (картинка в элементе загружается), а вот те картинки, которые уже не виды - остаются - как итог: память растет.

    Мне как раз хотелось избавиться от проблемы переполнения памяти.

    Объясню еще раз.

    1) выбрал папку с 1000 картинками

    2) в GridView на экране помещяется только 20 элементов ( т.е. грузиться только 20 картинок), и я вижу элементы с 1 по 20, но в каком-нить TextBlock я вижу что-то типа: "Изображений в списке: 1000 шт." - Все ОК!

    3.1) я прокручиваю GridView еще немного, и вижу элементы с 10 по 30 (10 новых картинок загрузилось) - Все ОК!

    3.2) в GridView элементы с 1 по 10 попрежнему висят в памяти (хотя на экране я их не вижу) - Все НЕ-ОК!

    Вот в пункте 3.2 весь мой геморрой!! Если я загружу 1000 картинок и прокручу список до последней - в памяти у меня окажутся все 1000 картинок, вместо всего лишь 20 реально видимых на экране!!!


    P.S.

    В принципе, пусть загружается сразу весь список, вся 1000 элементов - они почти ничего не весят, а в GridView я буду сразу видеть сколько их и видеть актуальную полосу прокрутки. Главная задача:

    1) чтобы картинки в элементы грузились когда они появятся на экране (в принципе, решено с помощью "protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)" и своего класса от GridView)

    2) чтобы картинки выгружались из памяти, когда элемент становится не видимым на экране (т.е. "image.source = null") ( попрежнему проблема!)

    • Изменено Hovanskiy 28 марта 2013 г. 11:53
    28 марта 2013 г. 11:44
  • Тогда в дополнение ко всему стоит ознакомиться со статьей в MSDN Использование виртуализации для списка или сетки
    28 марта 2013 г. 11:58
  • Тогда в дополнение ко всему стоит ознакомиться со статьей в MSDN Использование виртуализации для списка или сетки

    Хорошо, попробую изучить столь большую статью на английском, со своим знанием английского )))
    28 марта 2013 г. 12:03
  • я же вроде указал ссылку на русском... В общем если в строке запроса убрать en-US, то будет на русском! :)
    28 марта 2013 г. 12:08
  • Тогда в дополнение ко всему стоит ознакомиться со статьей в MSDN Использование виртуализации для списка или сетки

    Спасибо за статью! Нашел то что нужно:

    Виртуализация с произвольным доступом
    Виртуализация данных с произвольным доступом позволяет загружать полукомплект данных из любой области полного набора данных. Например, в случае привязки ListView к коллекции из 100 000 элементов и при выполнении пользователем прокрутки к середине данной коллекции ваше приложение может ограничиться загрузкой лишь элементов с 50 000 по 50 050. Если затем пользователь перейдет к концу списка, приложение загрузит элементы с 99 950 по 100 000. Индикатор прокрутки полосы прокрутки, или бегунок, всегда настроен на указание своего положения в полном наборе данных из 100 000 элементов.
    Для виртуализации данных с произвольным доступом необходимо использовать источник данных, применяющий INotifyCollectionChanged или IObservableVector.

    Что вы можете подсказать по этому поводу?
    28 марта 2013 г. 12:14
  • Интерфейс IObservableVector по сути заменяет INotifyCollectionChanged в WinRT. А в вашем варианте класс 

    publicabstractclass IncrementalLoadingBase: IList, ISupportIncrementalLoading, INotifyCollectionChanged 

    уже наследует от этого интерфейса. Т.е. динамическая загрузка у вас уже реализована.

    Попробуйте просто указать виртуальную панель для GridView:

          <GridView.ItemsPanel>
              <ItemsPanelTemplate>
                  <VirtualizingStackPanel Orientation="Horizontal" />
              </ItemsPanelTemplate>
          </GridView.ItemsPanel>

    Но помните, что "Виртуализация интерфейса пользователя для сгруппированных данных не поддерживается"!

      

    28 марта 2013 г. 12:34
  • Интерфейс IObservableVector по сути заменяет INotifyCollectionChanged в WinRT. А в вашем варианте класс 

    publicabstractclass IncrementalLoadingBase: IList, ISupportIncrementalLoading, INotifyCollectionChanged 

    уже наследует от этого интерфейса. Т.е. динамическая загрузка у вас уже реализована.

    Попробуйте просто указать виртуальную панель для GridView:

          <GridView.ItemsPanel>
              <ItemsPanelTemplate>
                  <VirtualizingStackPanel Orientation="Horizontal" />
              </ItemsPanelTemplate>
          </GridView.ItemsPanel>

    Но помните, что "Виртуализация интерфейса пользователя для сгруппированных данных не поддерживается"!

      

    К сожалению ни так ни с WrapGrid ничего нового.

    Если избавляться от ISupportIncrementalLoading - как можно сделать?

    28 марта 2013 г. 12:50
  • А как вы определили что ничего нового?

    Если избавляться, то просто уберите наследование от этого интерфейса и саму реализацию.


    28 марта 2013 г. 19:03
  • А как вы определили что ничего нового?

    Если избавляться, то просто уберите наследование от этого интерфейса и саму реализацию.


    1) При загрузке новых изображений память стабильно растет и даже спустя "1 сигарету" не уменьшается (понадеялся на системный "CG.Collect")

    2) Логично ), банальный вариант - но пробовал и все тоже (((

    В WPF с ListBox эту проблему решил (правда при стандартном виде) (а при ItemsPanel в WrapGrid - память тоже не освобождалась)

    Если хотите, могу скинуть вам похожий проект на WPF (загрузка элементов с Image в ListBox из выбранной папки) - у меня его переделать под WinRT не выходит (((

    28 марта 2013 г. 22:27
  • Ну давайте, посмотрю.
    29 марта 2013 г. 4:25
  • Ну вот, собственно, маленький примерчик:

    (SkyDrive) http://sdrv.ms/10fignq

    P.S.

    Для проверки работы я делал банально: запускал программу (F5) и следил за ней в диспетчере задач)))

    • Изменено Hovanskiy 29 марта 2013 г. 10:37
    29 марта 2013 г. 10:32
  • Провел эксперимент. Добавил VirtualizationStackPanel и высчитывал количество видимых элементов в списке:

        public sealed partial class BlankPage : LayoutAwarePage
        {
            private GeneratorIncrementalLoadingClass<ImageItem> imageItems;
            private IReadOnlyList<StorageFile> fileList;
    
            public BlankPage()
            {
                this.InitializeComponent();
            }
    
            protected override async void LoadState(object navigationParameter, Dictionary<string, object> pageState)
            {
                var fp = new FolderPicker();
                fp.FileTypeFilter.Add(".jpg");
                fp.FileTypeFilter.Add(".jpeg");
                fp.FileTypeFilter.Add(".png");
                fp.FileTypeFilter.Add(".gif");
                fp.FileTypeFilter.Add(".bmp");
                fp.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
                fp.ViewMode = PickerViewMode.Thumbnail;
                var selectFolder = await fp.PickSingleFolderAsync();
                if (selectFolder != null)
                {
                    var queryOptions = new QueryOptions(CommonFileQuery.OrderByName, new[] { ".jpg", ".jpeg", ".png", ".gif", ".bmp" });
                    var storageFileQueryResult = selectFolder.CreateFileQueryWithOptions(queryOptions);
                    fileList = await storageFileQueryResult.GetFilesAsync();
                    imageItems = new GeneratorIncrementalLoadingClass<ImageItem>((uint)fileList.Count, Generator);
                    ImagesSource.Source = imageItems;
                }
            }
    
            private ImageItem Generator(int i)
            {
                var count = GetVisualCount(grid);
                textBlock.Text = "Last index: " + i + ";    Visaul items: " + count;
                return new ImageItem(fileList.ElementAt(i));
            }
    
            private static int GetVisualCount(DependencyObject visual)
            {
                int visualCount = 1;
                int childCount = VisualTreeHelper.GetChildrenCount(visual);
    
                for (int i = 0; i < childCount; i++)
                {
                    DependencyObject childVisual = VisualTreeHelper.GetChild(visual, i);
                    visualCount += GetVisualCount(childVisual);
                }
    
                return visualCount;
            }
    
        }
    

    Жирным пометил изменения. Теперь XAML:

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <TextBlock x:Name="textBlock"/>
            <GridView x:Name="grid" ItemsSource="{Binding Source={StaticResource ImagesSource}}" Grid.Row="1" VerticalAlignment="Center">
                <GridView.ItemTemplate>
                    <DataTemplate>
                        <Grid Height="130" Width="190" Margin="5">
                            <ToolTipService.ToolTip>
                                <Grid Background="{StaticResource ToolTipBackgroundThemeBrush}" Height="180" Width="460">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*"/>
                                        <ColumnDefinition Width="10"/>
                                        <ColumnDefinition Width="210"/>
                                    </Grid.ColumnDefinitions>
                                    <Border BorderBrush="{StaticResource ToolTipBorderThemeBrush}" />
                                    <StackPanel Grid.Column="0">
                                        <TextBlock Text="{Binding Title}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" Foreground="{StaticResource ToolTipForegroundThemeBrush}" FontSize="16" />
                                        <TextBlock Text="{Binding ImageType}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" Foreground="{StaticResource ToolTipForegroundThemeBrush}" />
                                        <TextBlock Text="{Binding ImageDate}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" Foreground="{StaticResource ToolTipForegroundThemeBrush}" />
                                        <TextBlock Text="{Binding Path}" Margin="10,0,0,5" TextTrimming="WordEllipsis" TextWrapping="Wrap" MaxHeight="80" Foreground="{StaticResource ToolTipForegroundThemeBrush}" />
                                    </StackPanel>
                                    <Image Source="{Binding Image}" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10" MaxWidth="190" MaxHeight="130"/>
                                </Grid>
                            </ToolTipService.ToolTip>
                            <Image Source="{Binding Image}" HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="190" MaxHeight="130" Stretch="Uniform"/>
                        </Grid>
                    </DataTemplate>
                </GridView.ItemTemplate>
                <GridView.ItemsPanel>
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel Orientation="Horizontal" VirtualizingStackPanel.VirtualizationMode="Recycling"/>
                    </ItemsPanelTemplate>
                </GridView.ItemsPanel>
            </GridView>
        </Grid>
    

    В итоге количество видимых элементов в GridView получается меньше чем количество загруженных. Т.е. виртуализация работает. Аналогичный результат и для WrapGrid. И с диспетчером задач все вроде нормально... 

    29 марта 2013 г. 15:37
  • Спасибо, чуть попозже попробую...

    29 марта 2013 г. 17:06
  • Да, это почти то что нужно!!! Единственный момент: раз уж память не перезагружается - хотелось бы избавиться от "GeneratorIncrementalLoadingClass" и загружать весь список (реальная полоса прокрутки)

    Сделал так: "private ObservableCollection<ImageItem> imageItems;" - весь эффект пропал (((

    29 марта 2013 г. 17:24
  • Хотя, нет. Поторопился. При использовании вашей реализации, если прокрутить список (WrapGrid) до последнего элемента - память >500 Мб (((

    <ItemsPanelTemplate>
            <WrapGrid ItemHeight="130" ItemWidth="190" VirtualizingStackPanel.VirtualizationMode="Recycling"/>
    </ItemsPanelTemplate>

    "Last index 2156; Visual items 1673"

    29 марта 2013 г. 17:45
  • ну так количество видимых элементов меньше чем общее количество. Может проблема в другом?

    Попробуйте исправить ваш класс ImageItem следующим образом:

            public async void SetImage(StorageFile file)
            {
                BitmapImage imageBitmapImage;
                using (var imageThumbnail = await file.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.PicturesView))
                {
                    imageBitmapImage = new BitmapImage();
                    await imageBitmapImage.SetSourceAsync(imageThumbnail);
                }
                Image = imageBitmapImage;
            }
    29 марта 2013 г. 19:44
  • Все абсолютно так же, но:

    "Last index 2156; Visual items 1923"

    30 марта 2013 г. 6:45
  • Вообще, если следовать статье Использование виртуализации для списка или сетки, то все идет по "Виртуализация данных - Добавочная виртуализация", а я так понимаю нужно по "Виртуализация данных - Виртуализация с произвольным доступом". Это должно помочь. Но пока у меня ничего не выходит. (((

    30 марта 2013 г. 7:23
  • А что именно у вас не выходит?
    1 апреля 2013 г. 10:17
  • Уважаемый пользователь,

    Пожалуйста, не оставляйте Ваши темы без ответа или комментария по предложенным решениям.

    Спасибо

    3 апреля 2013 г. 11:41
  • Прошу меня извинить, но сейчас я очень занят машиной. Как только освобожусь - продолжу заниматься этим вопросом и все напишу.

    5 апреля 2013 г. 8:50