Архив

Архив раздела ‘Silverlight’

45 дней с Windows Phone 7. День #19. Элемент управления Map.

<< День #18. элемент управления WebBrowser.День #20. Уведомления (Push Notifications) >>

Это девятнадцатая статья серии «45 дней с Windows Phone 7».

Вчера мы говорили про элемент управления «WebBrowser», с помощью которого можно отображать не только web сайты, но и произвольно сгенерированный HTML. Сегодня мы рассмотрим более специфический элемент управления, который позволяет показывать карту в Windows Phone 7 Silverlight приложениях. Название данного элемента управления весьма логично, он называется «Map» – карта.

Использование элемента управления «Map».

Перетащите элемент управления Map из Toolbox на страницу Silverlight приложения. При этом подключится соответствующее XML пространство имён:

xmlns:map="clr-namespace:Microsoft.Phone.Controls.Maps;
assembly=Microsoft.Phone.Controls.Maps"

После того как я задал позиционирование и размеры для карты, XAML код элемента управления выглядит следующим образом:

<map:Map Height="607" HorizontalAlignment="Left"
Name="myMap" VerticalAlignment="Top" Width="456" />

Запустим приложение и посмотрим на карту.

Посмотрев на иллюстрацию выше, Вы, наверное, хотите спросить меня про белую надпись посередине карты, с текстом: «Invalid Credentials. Sign up for a developer account.». Если вкратце, нам нужен ключ API для работы с картой. Далее в статье я расскажу, как такой ключ получить (абсолютно бесплатно, надо заметить), а также мы рассмотрим множество других аспектов работы с картой.

Регистрация аккаунта разработчика и получение ключа для работы с картой.

Регистрация аккаунта разработчика и получение ключа для работы с картой.

Итак, зайдите на портал Bing Maps и зарегистрируйтесь. Поле регистрации создайте новый ключ API в разделе «Create or view keys». Форма создания нового ключа представлена на иллюстрации ниже.

В качестве «Application URL» можете ввести адрес Вашего web сайта. После заполнения формы Вы получите ключ, похожий на данный:

AsWlUnHbvLgHlLHaRhTZLslewv1QIdGppxOqyL-6He2jxyHvLAjutrcntemUih-w9

(Нет, это не мой ключ. Я заменил достаточно много знаков. Но показанный ключ похож на тот, что Вы получите).

Задаём Credentials Provider.

После того, как Вы получите ключ API, данный ключ надо задать в приложении. Если в Вашем приложении только одна карта, просто напишите ключ в свойстве «CredentialsProvider» элемента управления «Map».

<map:Map CredentialsProvider=
"AsWlUnHbvLgHlLHaRhTZLslewv1QIdGppxOqyL-6He2jxyHvLAjutrcntemUih-w9"/>

Но, если карт в приложении несколько, лучше использовать другой подход: определить ключ API один раз в файле App.xaml, а потом с помощью механизма связывания данных задать ключ для каждой карты. Далее приведу код, добавляемый в файл App.xaml при использовании данного подхода:

<Application.Resources>
<map:ApplicationIdCredentialsProvider ApplicationId=
"AsWlUnHbvLgHlLHaRhTZLslewv1QIdGppxOqyL-6He2jxyHvLAjutrcntemUih-w9"
x:Key="BingMapsAPIKey"/>
</Application.Resources>

Теперь свойство «CredentialsProvider» у элемента управления Map измениться:

<map:Map CredentialsProvider="{StaticResource BingMapsAPIKey}">

После задания «CredentialsProvider» запустите приложение. Белая надпись посреди карты должна исчезнуть.

Добавляем точки(Pushpin) на карту.

Мы можем добавить пользовательские точки на карту как с помощью C# кода, так и в XAML разметке. Для добавления точки требуется определить местоположение, а также содержимое, являющееся, по сути, подписью. Добавим точку в XAML разметке:

<map:Map CredentialsProvider=
"AsWlUnHEvLgHlLHaRqTZLslewv1QIdGppxOqyL-7He2jxyHvLAjutrcntemUih-w9">
<map:Pushpin Location="40.1449, -82.9754" FontSize="30" 
Background="Orange" Content="1" />
</map:Map>

Аналогичный результат можно получить и с помощью C# кода:

Pushpin pushpin = new Pushpin();
Location location = new Location();
location.Latitude = 40.1449;
location.Longitude = -82.9754;
pushpin.Location = location;
pushpin.Background = new SolidColorBrush(Colors.Orange);
pushpin.Content = "1";
pushpin.FontSize = 30;
MapControl.Children.Add(pushpin);

На иллюстрации ниже показано, как только что добавленная нами точка выглядит на карте.

Если Вам интересно как преобразовать какой-либо адрес в значение широты/долготы, прочтите соответствующую статью. Данная статья описывает работу с картами в десктопном Silverlight, но актуальна и для работы с картами на телефоне.

Добавляем на карту произвольные фигуры.

Полезным классом при работе с картой является «MapPolygon». MapPolygon позволяет задать список координат, по которым на карте рисуется фигура. Данная фигура позиционируется в нужное место карты независимо от прокрутки и изменения масштаба карты. Координаты являются значениями широты/долготы, что позволяет обозначить на карте страны, регионы, места для парковки, да и вообще все, что может потребоваться обозначить в Вашем приложении. Далее я добавляю простую фигуру на карту:

XAML

<map:MapPolygon Fill="Purple"
Stroke="White" Opacity=".7"
Locations="40.1449,-82.9754 40.1449,-12.9754 10.1449,-82.9754" />

C#

MapPolygon mapPolygon = new MapPolygon();
mapPolygon.Fill = new SolidColorBrush(Colors.Purple);
mapPolygon.Stroke = new SolidColorBrush(Colors.White);
mapPolygon.Opacity = .7;
LocationCollection locations = new LocationCollection();
Location location = new Location();
location.Latitude = 40.1449;
location.Longitude = -82.9754;
Location location1 = new Location();
location1.Latitude = 40.1449;
location1.Longitude = -12.9754;
Location location2 = new Location();
location1.Latitude = 10.1449;
location1.Longitude = -82.9754;
locations.Add(location);
locations.Add(location1);
locations.Add(location2);
mapPolygon.Locations = locations;
MapControl.Children.Add(mapPolygon);

Теперь на карте у нас есть точка и фигура. Как это выглядит показано на иллюстрации ниже.

Если Вам интересны другие возможности по работе с картой, обратите внимание на Bing Maps Silverlight Control Interactive SDK. Там вы найдёте много полезной информации.

Пример кода.

Пример кода показывает, как добавить точку и фигуру на карту в XAML разметке и C# коде. Естественно применять два способа одновременно не нужно, выберете один и используйте его.

45 дней с Windows Phone 7. День #18. Элемент управления WebBrowser.

<< День #17. Элемент управления Pivot.День #19. Элемент управления Map >>

День #24. WebBrowser. Часть 2. Локальный контент.

Это восемнадцатая статья серии «45 дней с Windows Phone 7».

Что может делать элемент управления «WebBrowser»?

Очевидно, что элемент управления WebBrowser служит для просмотра Web страниц. Но полноценным браузером данный элемент управления не является, так как у него нет адресной строки, вкладок, различных диалогов и.т.д. Можно представить данный элемент управления как некий аналог «iframe» из HTML мира. Кроме того, элемент управления WebBrowser уже имеет встроенную поддержку мультитач жестов. Вам для этого делать ничего не требуется.

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

Вообще говоря, добавить WebBrowser в Silverlight Windows Phone 7 приложение просто. Надо только перетащить данный элемент управления из Toolbox:

<phone:WebBrowser x:Name="wbMain"/>

При этом будет подключено соответствующее XML пространство имён:

xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"

Для того чтобы в браузере отобразить страницу, достаточно вызвать один метод:

wbMain.Navigate(new Uri("http://pugachev.info"));

Я также заменил заголовок страницы Silverlight приложения и название самого приложения. Результат показан на иллюстрации ниже.

Важно отметить, что элемент управления WebBrowser в Windows Phone 7, в отличие от своего аналога в десктопном Silverlight не нуждается в HtmlBrush, так как большинство графических преобразований можно сделать над самим браузером. Например, повернём WebBrowser на 45 градусов по оси Y:

<phone:WebBrowser x:Name="wbMain">
    <phone:WebBrowser.Projection>
        <PlaneProjection RotationY="-45"/>
    </phone:WebBrowser.Projection>
</phone:WebBrowser>

Вот как это будет выглядеть:

При этом элемент управления WebBrowser полностью интерактивен и продолжает работать как обычно. Во внебраузерном Silverlight 4 приложении мы такое бы сделать не смогли.

Загрузка локального контента.

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

Давайте теперь загрузим в браузере какую-либо страницу. Загрузка страниц будет происходить в том числе с помощью классов из XNA Framework, поэтому добавим в раздел «References» проекта нашего приложения библиотеку «Microsoft.Xna.Framework.dll». Классы из XNA часто помогают упростить разработку не только игр, но и обычных Silverlight Windows Phone 7 приложений.

Итак, в начале мы прочитаем нужную страницу с помощью StreamReader, а после этого вызовем у WebBrowser метод «NavigateToString»:

StreamReader reader = new StreamReader(TitleContainer.OpenStream("html/wp7wiki.html"));
wbMain.NavigateToString(reader.ReadToEnd());

Класс «TitleContainer» находится в пространстве имён «Microsoft.Xna.Framework». Если Вы хотите узнать больше про «Microsoft.Xna.Framework», прочитайте соответствующую статью на MSDN.

Кроме загрузки уже существующей страницы, мы можем генерировать HTML прямо в коде приложения. Далее приведу пример того, как это можно сделать:

string html = "<h1 style=\"color:red;\">Привет!</h1>" +
"<a href=\"http://pugachev.info\">Блог Сергея Пугачёва</a>";
wbMain.NavigateToString(ConvertExtendedAscii(html));

Функция «ConvertExtendedAscii» используется для устранения возможных проблем с кодировкой.

public static string ConvertExtendedAscii(string html)
{
    var retVal = "";
    var s = html.ToCharArray();

    foreach (char c in s)
    {
        if (Convert.ToInt32(c) > 127)
            retVal += "&#" + Convert.ToInt32(c) + ";";
        else
            retVal += c;
    }

    return retVal;
}

JavaScript по умолчанию отключён.

Если на Ваших страницах есть JavaScript, то просто так он работать не будет. Его поддержку надо включить явно. Сделать это очень просто, достаточно задать значение свойства «IsScriptEnabled»:

<phone:WebBrowser x:Name="wbMain" IsScriptEnabled="True"/>

Взаимодействие со страницами, загруженными в WebBrowser.

При взаимодействии со страницей, загруженной в WebBrowser мы можем вызывать из C# кода JavaScript функции, а из JavaScript уведомлять Silverlight приложение. Вызвать JavaScript функцию из C# можно с помощью «.InvokeScript()». В данном примере мы вызываем функцию «functionName» с параметром «parametr1» и получаем возвращаемое значение:

string returnValue = (string)wbMain.InvokeScript("functionName", "parametr1");

Для оповещения Silverlight приложения из JavaScript в Silverlight приложении надо подписаться на событие «ScriptNotify» элемента управления браузера. В обработчике события можно получить параметр, переданный из JavaScript:

void wbMain_ScriptNotify(object sender, NotifyEventArgs e)
{
    var value = e.Value;
}

В JavaScript для вызова оповещения надо сделать следующее:

window.external.Notify("value");

В данном примере мы передаём Silverlight приложению параметр со значением «value».

Так как взаимодействовать с JavaScript не имеет смысла до загрузки страницы, мы можем отслеживать процесс загрузки с помощью двух событий элемента управления «WebBrowser»: «Navigating» и «Navigated». Например, во время загрузки, чтобы не смущать пользователя белым экраном, можно показывать ProgressBar:

<ProgressBar Foreground="Orange" x:Name="ProgBar"
Visibility="Collapsed" IsIndeterminate="True" Height="4"
HorizontalAlignment="Left" Margin="10,66,0,0"
VerticalAlignment="Top" Width="460" />

void Browser_Navigating(object sender, NavigatingEventArgs e)
{
    ProgBar.Visibility = Visibility.Visible;
}

void Browser_Navigated(object sender, NavigationEventArgs e)
{
    ProgBar.Visibility = Visibility.Collapsed;
}

Соответственно, когда страница загружается, ProgressBar будет виден, а после загрузки страницы он будет скрыт.

Пример кода.

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

45 дней с Windows Phone 7. День #17. Элемент управления Pivot.

<< День #16. Элемент управления Panorama.День #18. Элемент управления WebBrowser >>

Это семнадцатая статья серии «45 дней с Windows Phone 7».

Вчера мы говорили о том, для чего служит и как используется элемент управления «Panorama». Сегодня мы рассмотрим похожий, но всё-таки другой элемент управления, который часто путают с «Panorama» и который называется «Pivot».

Элемент управления «Pivot».

Pivot представляет собой элемент управления для создания своеобразных табов. Данный сценарий применяется в окне настроек телефона для переключения между настройками самого телефона и настройками приложений. Рассмотрим другое приложение на телефоне. Например, в календаре можно переключиться из режима «day» в режим «agenda». Данные режимы позволяют посмотреть на одни и те же данные как бы с различных сторон. Это также частый сценарий применения элемента управления «Pivot». Можно сказать даже основной сценарий. Далее приведены иллюстрации того, как всё это выглядит.

Когда следует использовать элемент управления «Panorama», а когда «Pivot».

Tim Heuer написал прекрасную статью о том, когда что стоит использовать. Вы также можете посмотреть видео на Channel 9.

Мои советы достаточно просты:

  • Используйте элемент управления Panorama для Вашего приложения, кода оно представляется хабом (некой длинной лентой, которую пользователю будет интересно прокучивать), и нет необходимости в панели приложения.
  • Используйте элемент управления Pivot, когда Вам надо показать одни и те же или связанные данные с различных сторон. Как бы расплывчато это не звучало.

Использование элемента управления «Pivot».

Если Вы не читали вчерашнюю статью про элемент управления Panorama, прочтите её. Там описывалось, как добавить Panorama и Pivot на Toolbox в Visual Studio. Вообще, использование Pivot очень похоже на использование Panorama и далее мы рассмотрим использование Pivot на конкретных примерах. В сегодняшнем примере мы будем применять связывание данных (Data Binding) и шаблоны данных (Data Templates) для отображения списка имён в приложении, используемом для подбора имени ребёнка. В трёх вкладках элемента управления Pivot можно будет посмотреть на имена (данные) с различных сторон: имена для мальчиков и девочек, мужские и женские имена по отдельности соответственно. Создадим три вкладки:

<controls:Pivot Title="BABY NAMES">
    <controls:PivotItem Header="boys">

    </controls:PivotItem>
    <controls:PivotItem Header="girls">

    </controls:PivotItem>
    <controls:PivotItem Header="either">

    </controls:PivotItem>
</controls:Pivot>

Но пустые вкладки мало чем могут помочь, давайте двигаться дальше.

Добавляем данные.

На каждую вкладку мы добавим элемент управления ListBox, у которого будет задан шаблон элемента (ItemTemplate). Далее приведён полный получившийся XAML код:

<controls:Pivot Title="BABY NAMES">
    <controls:PivotItem Header="boys">
        <ListBox x:Name="boyList" Margin="0,0,-12,0">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Margin="0,0,0,17" >
                        <TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </controls:PivotItem>
    <controls:PivotItem Header="girls">
        <ListBox x:Name="girlList" Margin="0,0,-12,0">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Margin="0,0,0,17" >
                        <TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </controls:PivotItem>
    <controls:PivotItem Header="either">
        <ListBox x:Name="allList" Margin="0,0,-12,0">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Margin="0,0,0,17" >
                        <TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </controls:PivotItem>
</controls:Pivot>

Теперь воспользуемся связыванием данных и определим то, что будет отображаться. Сделаем это в C# коде. Сейчас я не буду подробно рассказывать про связывание данных, так как на эту тему уже написано достаточно. Знание данной темы необходимо любому Silverlight/WPF разработчику, поэтому я предполагаю, что со связыванием данных Вы знакомы.

namespace Day17_PivotControl
{
    public partial class MainPage : PhoneApplicationPage
    {
        BabyName[] names = new BabyName[10] {new BabyName("Steve", 1, 0),
					new BabyName("Jennifer", 2, 0),
					new BabyName("Alex", 1, 2),
					new BabyName("Casey", 1, 2),
					new BabyName("Quinn", 1, 2),
					new BabyName("Anthony", 1, 0),
					new BabyName("Sarah", 2, 0),
					new BabyName("Parker", 2, 1),
					new BabyName("Jessica", 2, 0),
					new BabyName("Jeff", 1, 0)};

        // Constructor
        public MainPage()
        {
            InitializeComponent();
            boyList.ItemsSource = from n in names
                                  where (n.Gender1 == 1 || n.Gender2 == 1)
                                  orderby n.Name
                                  select new BabyName(n.Name, n.Gender1, n.Gender2);

            girlList.ItemsSource = from n in names
                                   where (n.Gender1 == 2 || n.Gender2 == 2)
                                   orderby n.Name
                                   select new BabyName(n.Name, n.Gender1, n.Gender2);

            allList.ItemsSource = from n in names
                                  orderby n.Name
                                  select new BabyName(n.Name, n.Gender1, n.Gender2);
        }
    }
}

Скачать исходный код класса «BabyName» можно в разделе «Пример кода».

В данном примере для каждой вкладки мы определяем своё представление начальных данных. В этом конкретном случае посредством фильтрации.

Хочется отметить, что в 99.99% не стоит использовать в приложении больше семи (7) вкладок/секций для элементов управления Pivot и Panorama. В первую очередь, потому, что пользователи больше просто не запомнят. А если они это не запомнят, как они будут это использовать? Семь плюс-минус два – это хорошо известный предел кратковременной памяти человека. В основном, поэтому телефонные номера редко бывают длиннее семи цифр. Вообще говоря, для большинства приложений не стоит делать даже больше пяти вкладок/секций.

Пример кода.

В данной статье приведён простой пример использования элемента управления Pivot в Silverlight приложениях для Windows Phone 7. Скачайте пример и попробуйте элемент управления в действии.

45 дней с Windows Phone 7. День #16. Элемент управления Panorama.

<< День #15. Изолированное хранилище.День #17. Элемент управления Pivot >>

Это шестнадцатая статья серии «45 дней с Windows Phone 7».

Вчера мы говорили про изолированное хранилище, и то, как приложения могут сохранять данные на телефоне c Windows Phone 7. Сегодня мы рассмотрим намного менее системную тему, а именно работу с элементом управления «Panorama» (Панорама :) ).

Элемент управления Panorama.

Если Вы смотрели презентации, посвящённые Windows Phone 7, Вы точно видели, как работает элемент управления Panorama. Он применяется очень часто, взять хотя бы те же хабы.

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

Другой пример, но без наведённого телефона:

Отлично, теперь мы знаем, что Panorama является крутым элементом управления. А значит, теперь поговорим, как использовать его в своих приложениях.

Проект приложения «Panorama Application».

В предыдущих 15 статьях серии каждый проект начинался с самого простого шаблона «Windows Phone Application». Для приложений, использующих панораму, Вы можете применить шаблон проекта «Windows Phone Panorama Application». Конечно, если Вам этого хочется. Панорама представляет собой просто элемент управления, поэтому её можно использовать в любом проекте Windows Phone 7 приложения, и не важно, какой шаблон использовался.

Если Вы забыли, какие проекты доступны, вот их список:

Важно отметить, что шаблон «Windows Phone Panorama Application» использует для построения интерфейса паттерн проектирования MVVM (Model-View-ViewModel) , что даёт нам хороший пример при разработке приложений.

Но для лучшего понимания работы с элементом управления, давайте теперь посмотрим, как с нуля добавить панораму на страницу (шаблон «Windows Phone Panorama Application» мы использовать НЕ будем).

Добавляем Panorama на Toolbox

Панорама не отображается в списке стандартных элементов управления в Toolbox. Поэтому, для добавления панорамы можно выполнить ряд действий руками (например, определить XML пространство имён в XAML коде), а можно один раз добавить панораму на Toolbox, чтобы потом её достаточно было просто перетащить нужную страницу.

Для этого в контекстном меню вкладки «Windows Phone controls» на Toolbox выберите пункт «Choose Items…»

В открывшимся диалоге на вкладке «Windows Phone Components» отметьте элемент управления Panorama (завтра мы будем говорить про элемент управления Pivot, так что сейчас можете добавить и его).

После нажатия кнопки «OK» нужные нам элементы управления появятся на Toolbox.

Добавляем элемент управления Panorama на страницу.

После того, как мы добавили панораму на Tooolbox, сделав, тем самым, себе жизнь легче, пришло время использовать данный элемент управления. Удалите всё содержимое страницы, на которую будет добавляться панорама (внутри тега «Grid» с именем «LayoutRoot»).

Перетащите панораму на страницу. После этого Вы увидите в XAML коде нечто похожее:

<controls:Panorama />

Не густо. Также можно заметить, что на странице добавлено XML пространство имён:

xmlns:controls=
"clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"

Теперь у нас есть абсолютный минимум кода и никакого пользовательского интерфейса. Давайте добавим этот самый пользовательский интерфейс. Далее приведена иллюстрация того, как выглядят разные части элемента управления Panorama.

Установка фона и заголовка.

Одной из классных «фич» панорамы является возможность иметь одно большое изображение в фоне, которое прокручивается медленнее, чем остальной контент. Для реальных приложений найдите классное изображение, которое отражает суть Вашего приложения. Далее я подобрал фоновое изображение для приложения, которое будет использоваться, чтобы скоротать время во время ожидания еды в ресторане (данное здание, это Corner Grill в Bowling Green).

Для установки данного изображения как фона панорамы, я вначале добавил изображение в проект, а потом для свойства «Background» панорамы задал кисть «ImageBrush», которая использует наше изображение. Кроме того я задал прозрачность (Opacity) для кисти равной 0.5. Это сделано для того, чтобы белый текст лучше читался на данном весьма светлом изображении.

<controls:Panorama Title="waiter">
    <controls:Panorama.Background>
        <ImageBrush ImageSource="PanoramaBackground.jpg" Opacity=".5" />
    </controls:Panorama.Background>
</controls:Panorama>

Кроме того я задал заголовок для панорамы – «waiter» (тот, кто ожидает кого-л. или что-л.).
Вот как теперь выглядит приложение:

Замечательно! У нас есть фон. Теперь пора добавить то, ради чего приложение, собственно, и создаётся – контент.

Создаём PanoramaItem (секцию панорамы).

Сейчас наше приложение работает не очень хорошо. Оно не прокручивается и не показывает ничего кроме фона и заголовка. Добавляя PanoramaItem мы создаём индивидуальные секции панорамы, а уже в PanoramaItem можно добавить XAML разметку, представляющую конкретную секцию.

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

Далее я добавлю три секции и задам заголовок для каждой из них. Скриншоты каждой секции представлены ниже после кода.

<controls:Panorama Title="waiter">
    <controls:Panorama.Background>
        <ImageBrush ImageSource="PanoramaBackground.jpg" Opacity=".5" />
    </controls:Panorama.Background>
    <controls:PanoramaItem Header="learn">

    </controls:PanoramaItem>
    <controls:PanoramaItem Header="play">

    </controls:PanoramaItem>
    <controls:PanoramaItem Header="all">

    </controls:PanoramaItem>
</controls:Panorama>

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

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

Добавляем контент в PanoramaItem.

Конечно, не обязательно, но я бы рекомендовал начать создание секции с добавления ListBox`а. Это даст Вам возможность прокручивать элементы по вертикали. Есть много способов размещать элементы в секции, но ListBox часто оказывается наиболее удобным (кроме того использовать связывание данных в случае ListBox`а очень просто. Прочитайте, что об этом пишет Scott Guthrie).

В моём примере будут элементы, описывающие пять игр, которые, пользователь может запустить, чтобы скоротать время (естественно сейчас самих игр нет, но Вы можете их написать, чтобы потом продавать получившееся приложение в Marketplace). Я создал некоторый XAML код для описания каждой игры и поместил его в ListBox. Далее приведен код получившейся секции «play» и скриншот того, как данная секция выглядит в эмуляторе.

<controls:PanoramaItem Header="play">
    <ListBox Margin="0,0,-12,0">
        <StackPanel Orientation="Horizontal" Margin="0,0,0,17">
            <Image Height="100" Width="100" Source="icons/tictactoe.png" Margin="12,0,9,0"/>
            <StackPanel Width="311">
                <TextBlock Text="tic tac toe"  TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                <TextBlock Text="the classic two player game" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
            </StackPanel>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0,0,0,17">
            <Image Height="100" Width="100" Source="icons/numbers.png" Margin="12,0,9,0"/>
            <StackPanel Width="311">
                <TextBlock Text="numbers"  TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                <TextBlock Text="learn your digits from 1 - 20" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
            </StackPanel>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0,0,0,17">
            <Image Height="100" Width="100" Source="icons/wordsearch.png" Margin="12,0,9,0"/>
            <StackPanel Width="311">
                <TextBlock Text="word search"  TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                <TextBlock Text="find as many words as you can" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
            </StackPanel>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0,0,0,17">
            <Image Height="100" Width="100" Source="icons/animals.png" Margin="12,0,9,0"/>
            <StackPanel Width="311">
                <TextBlock Text="animals"  TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                <TextBlock Text="hear and learn your favorites" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
            </StackPanel>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0,0,0,17">
            <Image Height="100" Width="100" Source="icons/alphabet.png" Margin="12,0,9,0"/>
            <StackPanel Width="311">
                <TextBlock Text="alphabet"  TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                <TextBlock Text="learn your letters" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
            </StackPanel>
        </StackPanel>
    </ListBox>
</controls:PanoramaItem>

Пример кода.

Пример кода показывает в работе всё, о чём мы говорили в данной статье. Скачайте его и попробуйте панораму в действии!

45 дней с Windows Phone 7. День #15. Изолированное хранилище.

<< День #14. Захоранивание.День #16. Элемент управления Panorama >>

Это пятнадцатая статья серии «45 дней с Windows Phone 7».

Вчера мы говорили о многозадачности в Windows Phone 7, и о том, как сохранять данные приложения при его деактивации. Сегодня мы поговорим про сохранение данных на телефоне, подходящее для любых ситаций. Для этого мы будем использовать механизм изолированного хранилища (Isolated Storage).

Что такое изолированное хранилище?

Изолированное хранилище не новая концепция для .NET вообще и Silverlight в частности. В Silverlight оно поддерживается, начиная со второй версии. Это основной способ для Silverlight приложений сохранить данные на компьютере, а теперь и на телефоне пользователя. Хранилище называется изолированным, так как каждое приложение имеет доступ к своему собственному хранилищу, изолированному от хранилищ других приложений. Здесь и далее речь идёт об изолированном хранилище применительно к телефону. В .NET и Silverlight политики доступа к изолированному хранилищу более разнообразны. Кроме того, важным отличием от Silverlight на компьютере, является то, что в случае телефона у нас нет квоты на размер данных, записываемых в изолированное хранилище. Всё ограничивается только памятью телефона.

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

Настройки или файлы.

Есть два способа хранить данные на телефоне. Первый способ – словарь ключ-значение. Данный способ использует класс «IsolatedStorageSettings». Второй способ – создание файлов и папок. При этом используется класс «IsolatedStorageFile». Иллюстрация ниже вкратце показывает оба этих способа.

Далее мы рассмотрим каждый из них подробнее.

IsolatedStorageSettings

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

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

Реальные изменения не записываются во flash память телефона, пока мы не вызовем метод «Save()» объекта класса «IsolatedStorageSettings». До этого все изменения происходят только в оперативной памяти. Рекомендуется не вызывать метод «Save()» слишком часто, так как это может негативно сказаться на производительности.

using System.IO.IsolatedStorage;
using System.Windows;
using Microsoft.Phone.Controls;

namespace WindowsPhoneApplication3
{
    public partial class MainPage : PhoneApplicationPage
    {
        readonly IsolatedStorageSettings _settings = 
        IsolatedStorageSettings.ApplicationSettings;

        // Constructor
        public MainPage()
        {
            InitializeComponent();
            InitializeSettings();
        }

        private void InitializeSettings()
        {
            if (_settings.Contains("emailFlag"))
            {
                EmailFlag.IsChecked = (bool)_settings["emailFlag"];
            }
            else
            {
                _settings.Add("emailFlag", false);
                _settings.Save();
            }
        }

        private void EmailFlag_Unchecked(object sender, RoutedEventArgs e)
        {
            _settings["emailFlag"] = false;
            _settings.Save();
        }

        private void EmailFlag_Checked(object sender, RoutedEventArgs e)
        {
            _settings["emailFlag"] = true;
            _settings.Save();
        }
    }
}

Как можно видеть всё достаточно просто, но есть несколько вещей, которые следует запомнить.

  • Попытка получения значения по несуществующему ключу всегда будет вызывать «KeyNotFoundException». Перед получением значения, проверьте существование ключа.
  • В данном примере я сохранял булевское значение, но Вы можете сохранять объект любого серилизуемого типа.
  • Помните, что при получении объекта выполняется явное приведение типов. Это прекрасное место для возникновения ошибок.
  • Присвоение значения при отсутствии такового является эквивалентом вызова метода «settings.Add()». В предыдущем примере вызов «settings.Add()» не является обязательным, а используется только для задания значения по умолчанию.

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

IsolatedStorageFile

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

Да, кода больше, чем в примере с IsolatedStorageSettings, но в данном случае он также достаточно прост. Код включает комментарии, так что Вы можете более детально понять что происходит.

private void SaveButton_Click(object sender, RoutedEventArgs e)
{
	//Obtain a virtual store for application
	IsolatedStorageFile fileStorage = 
           IsolatedStorageFile.GetUserStoreForApplication();

	//Create new subdirectory
	fileStorage.CreateDirectory("textFiles");

	//Create a new StreamWriter, 
           //to write the file to the specified location.
	StreamWriter fileWriter = 
           new StreamWriter(new IsolatedStorageFileStream(
           "textFiles\\newText.txt", FileMode.OpenOrCreate, fileStorage));
	//Write the contents of our TextBox to the file.
	fileWriter.WriteLine(writeText.Text);
	//Close the StreamWriter.
	fileWriter.Close();
}

private void GetButton_Click(object sender, RoutedEventArgs e)
{
	//Obtain a virtual store for application
	IsolatedStorageFile fileStorage = 
           IsolatedStorageFile.GetUserStoreForApplication();
	//Create a new StreamReader
	StreamReader fileReader = null;

	try
	{
		//Read the file from the specified location.
		fileReader = new StreamReader(
                      new IsolatedStorageFileStream(
                      "textFiles\\newText.txt", FileMode.Open, fileStorage));
		//Read the contents of the file (the only line we created).
		string textFile = fileReader.ReadLine();

		//Write the contents of the file to the TextBlock on the page.
		viewText.Text = textFile;
		fileReader.Close();
	}
	catch
	{
		//If they click the view button first, 
                    //we need to handle the fact that 
                    //the file hasn't been created yet.
		viewText.Text = "Need to create directory and the file first.";
	}
}

Ну что же, у нас есть два простых механизма сохранения данных: IsolatedStorageSettings и IsolatedStorageFile. Выбирайте то, что больше подходит Вам для каждой конкретной ситуации.

Пример кода.

Код включает пример работы с обоими механизмами сохранения данных.