Статьи

Создание клиента Imgur для Windows Phone — часть 3 — просмотр сведений об изображении

Если вы пропустили предыдущие части серии, вы можете прочитать их здесь:

Сегодня я расскажу о способе просмотра деталей изображения, а также всего изображения. В предыдущих статьях я показал вам, как можно поэкспериментировать с «ленивой загрузкой». Тем не менее,  виртуализация пользовательского интерфейса  уже выполняется автоматически в ListBox, поэтому нам не нужно беспокоиться об этом сейчас. Нам также не нужно беспокоиться о  виртуализации данных , поскольку начальный набор данных относительно невелик.

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

Кроме того, помните, что если вы используете пользовательскую ItemsPanel (например, с  WrapPanel ), как я делал в предыдущем примере, вам нужно быть очень осторожным с элементами, которые вы загружаете одновременно, так как цепочка автоматической виртуализации будет быть сломанным.

В этом примере я изменяю шаблон DataTemplate для прямой привязки к коллекции DeserializedHomeImages . Я также отображаю название изображения, когда оно отображается на главной странице:

<DataTemplate>
    <Grid Height="240" Width="480">
        <Image Stretch="UniformToFill" 
               Source="{Binding Link}"></Image>
        <Grid Height="80" VerticalAlignment="Bottom">
            <Grid.Background>
                <SolidColorBrush Color="Black" Opacity=".7"></SolidColorBrush>
            </Grid.Background>
            <TextBlock VerticalAlignment="Center" Style="{StaticResource PhoneTextTitle2Style}" Text="{Binding Title}"></TextBlock>
        </Grid>
    </Grid>
</DataTemplate>

Это приводит к чему-то вроде этого:

Нужно ли вести счетчики диагностики? Да. Поскольку вы работаете с различными изображениями, которые привязаны к списку, вы увидите, как легко отслеживать снижение производительности и нужно ли настраивать количество изображений, которые должны быть связаны и отображаться одновременно.

Давайте теперь поговорим о том, как можно просматривать детали изображения, а также само изображение. Imgur удаляет все метаданные EXIF ​​с изображения, поэтому мы не можем сосредоточиться на этом. Вместо этого мы можем получить:

  • дата и время загрузки изображения
  • аккаунт, который его создал
  • сколько пропускной способности было использовано изображением
  • количество голосов
  • количество отрицательных голосов
  • общий счет

Создайте новую страницу XAML в своем проекте и назовите ее
ImageDetails . Это будет контейнер, который будет отображать детали, которые нам нужны. Некоторые из метаданных будут отображаться в простом виде, другие будут скрыты, если пользователь явно не попросит об этом Так как я собираюсь передать выбранное изображение на новую страницу? Создавая модель, я могу связываться с.

Перейдите в класс MainPageViewModel и добавьте новое свойство — CurrentImage :

private ImgurImage _currentImage;
public ImgurImage CurrentImage
{
    get
    {
        return _currentImage;
    }
    set
    {
        if (_currentImage != value)
        {
            _currentImage = value;
            NotifyPropertyChanged("CurrentImage");
        }
    }
}

Для самого ListBox мне нужно определить, когда выбран элемент — это можно сделать с помощью обработчика события SelectionChanged :

private void mainList_SelectionChanged_1(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
    if (mainList.SelectedItem != null)
    {
        MainPageViewModel.Instance.CurrentImage = (ImgurImage)mainList.SelectedItem;
        mainList.SelectedItem = null;
        NavigationService.Navigate(new System.Uri("/ImageDetails.xaml", System.UriKind.Relative));
    }
}

Идея проста — когда ListBox привязан к коллекции пользовательских моделей, вы можете привести выбранный элемент в качестве экземпляра этой модели — поэтому я могу легко получить экземпляр ImgurImage вместо того, чтобы ссылаться на него по имени или другому уникальному идентификатор. 

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

Вот макет по умолчанию, который я создал для начинающего средства просмотра деталей:

<phone:PhoneApplicationPage
    x:Class="Imagine.ImageDetails"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
    mc:Ignorable="d"
    shell:SystemTray.IsVisible="False"
    DataContext="{Binding Path=Instance.CurrentImage, Source={StaticResource MainPageViewModel}}">

    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
        <Grid x:Name="ContentPanel" Grid.Row="0">
            <StackPanel Margin="0,0,0,24">
                <TextBlock Style="{StaticResource PhoneTextTitle3Style}" TextWrapping="Wrap" Text="{Binding Title}"></TextBlock>
                <TextBlock Style="{StaticResource PhoneTextSmallStyle}" TextWrapping="Wrap" Text="{Binding DateTime, Converter={StaticResource CompositeToDateConverter}}"></TextBlock>
                <StackPanel Margin="8,0,0,0" Orientation="Horizontal">
                    <Image VerticalAlignment="Center" Source="{Binding Converter={StaticResource ThemeDetector}, ConverterParameter=score}" Height="48"></Image>
                    <TextBlock VerticalAlignment="Center" Text="{Binding Score}"></TextBlock>

                    <Image Margin="12,0,0,0" VerticalAlignment="Center" Source="{Binding Converter={StaticResource ThemeDetector}, ConverterParameter=user}" Height="48"></Image>
                    <TextBlock VerticalAlignment="Center" Text="{Binding AccountUrl, Converter={StaticResource UserNameChecker}}"></TextBlock>
                </StackPanel>
            </StackPanel>
        </Grid>

        <ScrollViewer VerticalAlignment="Top" x:Name="scvImage" 
                      HorizontalScrollBarVisibility="Disabled" 
                      Grid.Row="1">
            <Image 
                x:Name="imgPreview" 
                Stretch="Uniform" 
                Source="{Binding Link}"></Image>
        </ScrollViewer>
    </Grid>
</phone:PhoneApplicationPage>

Есть несколько вещей, которые я должен упомянуть здесь. Прежде всего, взгляните на DataContext, который установлен для страницы. Поскольку мне нужно только текущее выбранное изображение, я связываю его со свойством CurrentImage в viewmodel главной страницы ( MainPageViewModel.cs ).

Во-вторых, обратите внимание, что два изображения внутри заголовка связаны не с прямым URI, а с конвертером. Это происходит по причине — если пользователь переключает тему со светлой на темную и наоборот, мне нужно сохранить две версии одного и того же изображения. Чтобы дать вам лучшее представление, взгляните на два скриншота ниже:

На этом этапе преобразователь, отвечающий за определение правильной темы, должен просто выбрать другое местоположение изображения в зависимости от двух факторов — переданного ему параметра, который будет определять тип изображения, и текущей темы. Реализация выглядит так:

using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media.Imaging;

namespace Imagine.Core
{
    public class ThemeDetector : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var theme = Application.Current.Resources["PhoneDarkThemeVisibility"];
            var visibility = (Visibility)theme;

            switch ((string)parameter)
            {
                case "score":
                    {
                        if (visibility == Visibility.Visible)
                            return new BitmapImage(new Uri("/Images/appbar.gauge.75.png", UriKind.Relative));
                        else
                            return new BitmapImage(new Uri("/Images/appbar.gauge.75.light.png", UriKind.Relative));
                    }
                case "user":
                    {
                        if (visibility == Visibility.Visible)
                            return new BitmapImage(new Uri("/Images/appbar.people.png", UriKind.Relative));
                        else
                            return new BitmapImage(new Uri("/Images/appbar.people.light.png", UriKind.Relative));
                    }
                default:
                    return null;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

Он опирается на хорошо известную  уловку ресурса DarkThemeVisibility . Вы можете легко изменить это, чтобы получить любое изображение в самом приложении, а также внешние.

Далее идет дата. Свойство DateTime по умолчанию в классе ImgurImage — это  отметка времени UNIX    C # не имеет класса по умолчанию для обработки этого, но, зная, как он генерируется, мы можем легко реализовать конвертер, который даст необходимую дату в стандартном формате:

using System;
using System.Windows.Data;

namespace Imagine.Core
{
    public class CompositeToDateConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            DateTime current = new DateTime(1970, 1, 1).AddSeconds(System.Convert.ToDouble(value));
            return current;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

При текущей компоновке XAML для страницы она будет правильно отображена (и изменен размер изображения) как в альбомном, так и в портретном режиме. Элемент управления изображением размещен внутри ScrollViewer, чтобы гарантировать, что непропорционально большие изображения (например, высота имеет гораздо более высокое значение, чем ширина), доступны для просмотра.

Вы можете скачать текущий исходный код  здесь .