Поделиться через


Пример использования Windows Phone Silverlight для UWP: Bookstore2

В этом примере, которое основывается на информации, указанной в Bookstore1, начинается с приложения Windows Phone Silverlight, отображающего сгруппированные данные в LongListSelector. В модели представления каждый экземпляр класса Author представляет группу книг, написанных этим автором, и в LongListSelector мы можем просмотреть список книг, сгруппированных по автору, или уменьшить, чтобы просмотреть список авторов. Список переходов обеспечивает гораздо более быструю навигацию, чем прокрутка по списку книг. Мы рассмотрим этапы переноса приложения в приложение windows 10 универсальная платформа Windows (UWP).

Обратите внимание , что при открытии Bookstore2Universal_10 в Visual Studio отображается сообщение "Обязательное обновление Visual Studio", выполните действия по настройке целевой версии платформы в TargetPlatformVersion.

Скачиваемые файлы

Скачайте приложение Bookstore2WPSL8 Windows Phone Silverlight.

Скачайте приложение Bookstore2Universal_10 Windows 10.

Приложение Windows Phone Silverlight

На рисунке ниже показано, как выглядит приложение Bookstore2WPSL8 — приложение, которое мы собираемся перенести. Это вертикальный прокрутка LongListSelector книг, сгруппированных автором. Вы можете уменьшить масштаб списка переходов, а затем вернуться в любую группу. В этом приложении есть два основных элемента: модель представления, предоставляющая сгруппированные источники данных, и пользовательский интерфейс, который привязывается к этой модели представления. Как мы увидим, оба этих элемента легко переносятся из технологии Windows Phone Silverlight на универсальная платформа Windows (UWP).

Как выглядит bookstore2wpsl8

Перенос в проект Windows 10

Это быстрая задача для создания проекта в Visual Studio, копирования файлов в него из Bookstore2WPSL8 и включения скопированных файлов в новый проект. Начните с создания нового проекта пустого приложения (универсального приложения Windows). Назовите его Bookstore2Universal_10. Это файлы для копирования из Bookstore2WPSL8 в Bookstore2Universal_10.

  • Скопируйте папку, содержащую файлы PNG обложки книги (папка \ Assets\CoverImages). После копирования папки в Обозреватель решений убедитесь, что включен переключатель "Показать все файлы". Щелкните правой кнопкой мыши папку, скопированную и нажмите кнопку "Включить в проект". Эта команда означает, что мы имеем в виду" файлы или папки в проекте. Каждый раз при копировании файла или папки нажмите кнопку "Обновить" в Обозреватель решений, а затем включите файл или папку в проект. Нет необходимости делать это для файлов, которые вы заменяете в назначении.
  • Скопируйте папку, содержащую исходный файл модели представления (папка \ ViewModel).
  • Скопируйте MainPage.xaml и замените файл в назначении.

Мы можем сохранить App.xaml и App.xaml.cs, которые Visual Studio создали для нас в проекте Windows 10.

Измените исходный код и файлы разметки, которые вы только что скопировали и изменили все ссылки на пространство имен Bookstore2WPSL8 на Bookstore2Universal_10. Быстрый способ сделать это — использовать функцию "Заменить в файлах ". В императивном коде в исходном файле модели представления эти изменения переносятся.

  • Перейдите System.ComponentModel.DesignerProperties и DesignMode используйте команду "Разрешить" в ней. IsInDesignTool Удалите свойство и используйте IntelliSense для добавления правильного имени свойства: DesignModeEnabled
  • Используйте команду "Разрешить".ImageSource
  • Используйте команду "Разрешить".BitmapImage
  • Удаление using System.Windows.Media; и using System.Windows.Media.Imaging;.
  • Измените значение, возвращаемое свойством Bookstore2Universal_10.BookstoreViewModel.AppName , с "BOOKSTORE2WPSL8" на "BOOKSTORE2UNIVERSAL".
  • Как и для Bookstore1, обновите реализацию свойства BookSku.CoverImage (см. статью "Привязка изображения к модели представления").

В MainPage.xaml необходимы эти начальные изменения переноса.

  • Измените значение phone:PhoneApplicationPage Page (включая вхождения в синтаксисе элемента свойства).
  • phone Удалите объявления префикса пространства имен.shell
  • Измените "clr-namespace" на "using" в оставшемся объявлении префикса пространства имен.
  • Удалите SupportedOrientations="Portrait"и Orientation="Portrait"настройте портрет в манифесте пакета приложения в новом проекте.
  • Удалите shell:SystemTray.IsVisible="True".
  • Типы преобразователей элементов списка переходов (которые присутствуют в разметке в виде ресурсов) перемещены в пространство имен Windows.UI.Xaml.Controls.Primitives. Поэтому добавьте объявление префикса пространства имен Windows_UI_Xaml_Controls_Primitives и сопоставите его с Windows.UI.Xaml.Controls.Primitives. В ресурсах преобразователя элементов списка переходов измените префикс на phone: Windows_UI_Xaml_Controls_Primitives:.
  • Так же, как и для Bookstore1, замените все ссылки PhoneTextExtraLargeStyleна стиль TextBlock ссылкой SubtitleTextBlockStyleна , заменить на , SubtitleTextBlockStylePhoneTextSubtleStyle PhoneTextNormalStyle CaptionTextBlockStyleзаменить на , заменить на и заменить PhoneTextTitle1Style на .HeaderTextBlockStyle
  • Существует одно исключение.BookTemplate Стиль второго TextBlock должен ссылаться CaptionTextBlockStyle.
  • Удалите атрибут FontFamily из TextBlock внутри AuthorGroupHeaderTemplate и задайте для ссылки фон границы PhoneAccentBrush SystemControlBackgroundAccentBrush вместо ссылки.
  • Из-за изменений, связанных с пикселями представления, перейдите по разметке и умножьте любое фиксированное измерение размера (поля, ширина, высота и т. д.) на 0,8.

Замена longListSelector

Замена longListSelector элементом управления SemanticZoom займет несколько шагов, поэтому давайте начнем с этого. Модуль LongListSelector привязывается непосредственно к сгруппированому источнику данных, но семантикаZoom содержит элементы управления ListView или GridView, которые косвенно привязываются к данным через адаптер CollectionViewSource. CollectionViewSource должен присутствовать в разметке как ресурс, поэтому начнем с добавления разметки в MainPage.xaml внутри<Page.Resources>.

    <CollectionViewSource
        x:Name="AuthorHasACollectionOfBookSku"
        Source="{Binding Authors}"
        IsSourceGrouped="true"/>

Обратите внимание, что привязка в LongListSelector.ItemsSource становится значением CollectionViewSource.Source, а LongListSelector.IsGroupingEnabled становится CollectionViewSource.IsSourceGrouped. CollectionViewSource имеет имя (примечание: не ключ, как можно ожидать), чтобы мы могли привязать его к нему.

Затем замените phone:LongListSelector эту разметку, которая даст нам предварительную семантику Для работы.

    <SemanticZoom>
        <SemanticZoom.ZoomedInView>
            <ListView
                ItemsSource="{Binding Source={StaticResource AuthorHasACollectionOfBookSku}}"
                ItemTemplate="{StaticResource BookTemplate}">
                <ListView.GroupStyle>
                    <GroupStyle
                        HeaderTemplate="{StaticResource AuthorGroupHeaderTemplate}"
                        HidesIfEmpty="True"/>
                </ListView.GroupStyle>
            </ListView>
        </SemanticZoom.ZoomedInView>
        <SemanticZoom.ZoomedOutView>
            <ListView
                ItemsSource="{Binding CollectionGroups, Source={StaticResource AuthorHasACollectionOfBookSku}}"
                ItemTemplate="{StaticResource ZoomedOutAuthorTemplate}"/>
        </SemanticZoom.ZoomedOutView>
    </SemanticZoom>

Понятие LongListSelector для неструктурированного списка и режимов списка переходов отвечает в представлении SemanticZoom о увеличенном и увеличенном представлении соответственно. Масштабное представление — это свойство, и вы задаете это свойство экземпляру ListView. В этом случае масштабное представление также имеет значение ListView, и оба элемента управления ListView привязаны к нашему Объекту CollectionViewSource. В представлении с увеличенным увеличением используется тот же шаблон элемента, шаблон заголовка группы и параметр HideEmptyGroups (теперь с именем HidesIfEmpty) в качестве неструктурированного списка LongListSelector. Кроме того, в представлении увеличенного масштаба используется шаблон элемента, очень похожий на шаблон списка переходов LongListSelector (AuthorNameJumpListStyle). Кроме того, обратите внимание, что представление с увеличением масштаба привязывается к специальному свойству коллекции CollectionViewSource с именем CollectionGroups, которая является коллекцией, содержащей группы, а не элементы.

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

   <DataTemplate x:Key="ZoomedOutAuthorTemplate">
        <Border Margin="9.6,0.8" Background="{Binding Converter={StaticResource JumpListItemBackgroundConverter}}">
            <TextBlock Margin="9.6,0,9.6,4.8" Text="{Binding Group.Name}" Style="{StaticResource SubtitleTextBlockStyle}"
            Foreground="{Binding Converter={StaticResource JumpListItemForegroundConverter}}" VerticalAlignment="Bottom"/>
        </Border>
    </DataTemplate>

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

Теперь вы можете создать и запустить приложение. Вот как он выглядит на мобильном эмуляторе.

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

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

Начальная стилизация и шаблон

Чтобы выделить заголовки группы, измените AuthorGroupHeaderTemplate и задайте поле "0,0,0,9.6" на границе.

Чтобы выделить элементы книги удобно, измените BookTemplate и установите поле "9.6,0" на оба элемента TextBlock.

Чтобы выложить имя приложения и название страницы немного лучше, удалите верхнюю поля во втором TextBlock, задав значение "7.2,0,0,0".TitlePanel И на TitlePanel самом себе задайте для поля 0 значение (или любое значение выглядит хорошо для вас)

Измените LayoutRootфон "{ThemeResource ApplicationPageBackgroundThemeBrush}"на .

Адаптивный пользовательский интерфейс

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

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

Минимальная ширина окна в 548 epx подходит для этого варианта использования, так как это размер наименьшего устройства, на которое мы хотели бы отобразить широкий макет. Телефоны обычно меньше 548 epx, поэтому на маленьком устройстве, как это, мы будем оставаться в узком макете по умолчанию. На компьютере окно будет запускаться по умолчанию достаточно широко, чтобы активировать переключатель в широкое состояние, которое будет отображать 250x250 размер элементов. Оттуда вы сможете перетащить окно достаточно узким, чтобы отобразить не менее двух столбцов элементов 250x250. Любое более узкое, чем это, и триггер будет деактивирован, широкое визуальное состояние будет удалено, и узкий макет по умолчанию будет в действии.

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

  • Создайте копию элемента управления SemanticZoom в разметке и задайте x:Name="narrowSeZo" для копии. В исходном наборе, а x:Name="wideSeZo" также задайте Visibility="Collapsed" значение, чтобы широкое не отображалось по умолчанию.
  • В wideSeZoэтом случае измените значение ListViewна GridViewв представлении с увеличением и увеличенном представлении.
  • Создайте копию этих трех ресурсов AuthorGroupHeaderTemplateZoomedOutAuthorTemplateи BookTemplate добавьте слово Wide в ключи копий. Кроме того, обновите так wideSeZo , чтобы он ссылается на ключи этих новых ресурсов.
  • Замените содержимое AuthorGroupHeaderTemplateWide <TextBlock Style="{StaticResource SubheaderTextBlockStyle}" Text="{Binding Name}"/>на .
  • Замените содержимое ZoomedOutAuthorTemplateWide на:
    <Grid HorizontalAlignment="Left" Width="250" Height="250" >
        <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}"/>
        <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
          <TextBlock Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}"
              Style="{StaticResource SubtitleTextBlockStyle}"
            Height="80" Margin="15,0" Text="{Binding Group.Name}"/>
        </StackPanel>
    </Grid>
  • Замените содержимое BookTemplateWide на:
    <Grid HorizontalAlignment="Left" Width="250" Height="250">
        <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}"/>
        <Image Source="{Binding CoverImage}" Stretch="UniformToFill"/>
        <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
            <TextBlock Style="{StaticResource SubtitleTextBlockStyle}"
                Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}"
                TextWrapping="NoWrap" TextTrimming="CharacterEllipsis"
                Margin="12,0,24,0" Text="{Binding Title}"/>
            <TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="{Binding Author.Name}"
                Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" TextWrapping="NoWrap"
                TextTrimming="CharacterEllipsis" Margin="12,0,12,12"/>
        </StackPanel>
    </Grid>
  • Для широкого состояния группы в увеличенном представлении потребуют больше вертикального пространства для дыхания вокруг них. Создание и ссылка на шаблон панели элементов даст нам нужные результаты. Вот как выглядит разметка.
   <ItemsPanelTemplate x:Key="ZoomedInItemsPanelTemplate">
        <ItemsWrapGrid Orientation="Horizontal" GroupPadding="0,0,0,20"/>
    </ItemsPanelTemplate>
    ...

    <SemanticZoom x:Name="wideSeZo" ... >
        <SemanticZoom.ZoomedInView>
            <GridView
            ...
            ItemsPanel="{StaticResource ZoomedInItemsPanelTemplate}">
            ...
  • Наконец, добавьте соответствующую разметку диспетчера визуальных состояний в качестве первого дочернего LayoutRootэлемента.
    <Grid x:Name="LayoutRoot" ... >
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState x:Name="WideState">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="548"/>
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="wideSeZo.Visibility" Value="Visible"/>
                        <Setter Target="narrowSeZo.Visibility" Value="Collapsed"/>
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

    ...

Окончательный стилизация

Все, что остается, некоторые окончательные стили настройки.

  • В AuthorGroupHeaderTemplateполе "ТекстБлок"Foreground="White", чтобы он выглядел правильно при выполнении в семействе мобильных устройств.
  • Добавьте FontWeight="SemiBold" в TextBlock в обоих AuthorGroupHeaderTemplate и ZoomedOutAuthorTemplate.
  • В narrowSeZoколонтитуле групп и авторах в увеличенном представлении выровнены по левому краю вместо растяжения, поэтому давайте поработаем над этим. Мы создадим заголовокContainerStyle для масштабированного представления с параметром HorizontalContentAlignmentStretch. И мы создадим элемент ItemContainerStyle для увеличенного представления, содержащего то же средство setter. Вот как выглядит.
   <Style x:Key="AuthorGroupHeaderContainerStyle" TargetType="ListViewHeaderItem">
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
    </Style>

    <Style x:Key="ZoomedOutAuthorItemContainerStyle" TargetType="ListViewItem">
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
    </Style>

    ...

    <SemanticZoom x:Name="narrowSeZo" ... >
        <SemanticZoom.ZoomedInView>
            <ListView
            ...
                <ListView.GroupStyle>
                    <GroupStyle
                    ...
                    HeaderContainerStyle="{StaticResource AuthorGroupHeaderContainerStyle}"
                    ...
        <SemanticZoom.ZoomedOutView>
            <ListView
                ...
                ItemContainerStyle="{StaticResource ZoomedOutAuthorItemContainerStyle}"
                ...

Последняя последовательность операций стилизации оставляет приложение похожим на это.

перенесенное приложение windows 10, работающее на настольном устройстве, увеличенное представление, два размера окна

Перенесенное приложение Windows 10, работающее на классическом устройстве, увеличенное представление, два размера окна перенесенное приложение windows 10, работающее на настольном устройстве, увеличенное представление, два размера окна

Перенесенное приложение Windows 10, работающее на классическом устройстве, увеличенное представление, два размера окна

перенесенное приложение windows 10, работающее на мобильном устройстве, увеличенное представление

Перенесенное приложение Windows 10, работающее на мобильном устройстве, увеличенное представление

перенесенное приложение windows 10, работающее на мобильном устройстве, увеличенное представление

Перенесенное приложение Windows 10, работающее на мобильном устройстве, увеличенное представление

Сделать модель представления более гибкой

В этом разделе содержится пример объектов, которые открываются для нас благодаря перемещению приложения для использования UWP. Здесь мы объясним дополнительные действия, которые можно выполнить, чтобы сделать модель представления более гибкой при доступе через CollectionViewSource. Модель представления (исходный файл находится в ViewModel\BookstoreViewModel.cs), перенесенная из приложения Windows Phone Silverlight bookstore2WPSL8, содержит класс с именем Author, производный от List<T>, где T — BookSku. Это означает, что класс Author — это группа BookSku.

Когда мы привязываем CollectionViewSource.Source к авторам, единственное, что мы сообщаем, заключается в том, что каждый автор в авторах является группой чего-то. Мы оставим его в CollectionViewSource , чтобы определить, что Author — это, в данном случае, группа BookSku. Это работает: но это не гибко. Что, если мы хотим, чтобы автор был как группой BookSku, так и группой адресов, где жил автор? Автор не может быть обеими этими группами. Но у автора может быть любое количество групп. И это решение: используйте шаблон "имеет группу" вместо того, чтобы использовать шаблон "a-a-group", используемый в настоящее время. Это делается следующим образом:

  • Измените автор, чтобы он больше не был производным от списка<T>.
  • Добавьте это поле в
  • Добавление этого свойства в
  • И, конечно, мы можем повторить приведенные выше два шага, чтобы добавить столько групп в Author, сколько нам нужно.
  • Измените реализацию метода this.BookSkus.Add(bookSku);AddBookSku на .
  • Теперь, когда автор имеет по крайней мере одну группу, нам нужно связаться с CollectionViewSource , какой из этих групп он должен использовать. Для этого добавьте это свойство в CollectionViewSource: ItemsPath="BookSkus"

Эти изменения оставляют это приложение функционально неизменными, но теперь вы знаете, как можно расширить author, и CollectionViewSource, если вам нужно. Давайте внесите последнее изменение в Author, чтобы, если мы используем его без указания CollectionViewSource.ItemsPath, будет использоваться группа по умолчанию для выбора:

    public class Author : IEnumerable<BookSku>
    {
        ...

        public IEnumerator<BookSku> GetEnumerator()
        {
            return this.BookSkus.GetEnumerator();
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.BookSkus.GetEnumerator();
        }
    }

Теперь мы можем удалить, ItemsPath="BookSkus" если нам нравится, и приложение по-прежнему будет вести себя так же.

Заключение

Это исследование включало более амбициозный пользовательский интерфейс, чем предыдущий. Все средства и понятия Windows Phone Silverlight LongListSelector (и многое другое) были доступны для приложения UWP в виде Семантики, ListView, GridView и CollectionViewSource. Мы показали, как повторно использовать или копировать и редактировать императивный код и разметку в приложении UWP для достижения функциональных возможностей, пользовательского интерфейса и взаимодействия, адаптированных для удовлетворения самых узких и самых широких форм-факторов устройств Windows и всех размеров между ними.