Россия, г. Санкт-Петербург |
Лабораторный практикум 2
Лабораторная работа №8. Веб-браузер
Задание
Создать простой веб-браузер для Windows Phone 7 с адресной строкой, историей посещений и сохранением истории в изолированном хранилище.
Освоение
- элемент управления WebBrowser
- изолированное хранилище
Описание
Создадим новый проект Silverlight for Windows Phone – Windows Phone Application.
Откроем файл разметки главной страницы MainPage.xaml. Добавим текстовое поле (для адресной строки), кнопку (при нажатии на нее будет происходить переход) и элемент WebBrowser. Обратите внимание, что браузер находится в пространстве имен "phone", т.о. не требуется подключение дополнительных библиотек. В меню добавим 3 кнопки для навигации по истории (вперед, назад и просмотр). При нажатии на кнопку просмотра истории будем переходить на другую страницу, где в списке будем выводить посещенные страницы.
Полностью разметка главной страницы примет следующий вид:
<phone:PhoneApplicationPage x:Class="Wp7IUSLab11.HistoryPage" 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="Portrait" Orientation="Portrait" mc:Ignorable="d" d:DesignHeight="696" d:DesignWidth="480" shell:SystemTray.IsVisible="True"> <!--LayoutRoot is the root grid where all page content is placed--> <Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--TitlePanel contains the name of the application and page title--> <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="ApplicationTitle" Text="ОБОЗРЕВАТЕЛЬ (история)" Style="{StaticResource PhoneTextNormalStyle}"/> <!--TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/--> </StackPanel> <!--ContentPanel - place additional content here--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <ListBox Name="lstHistory"> </ListBox> </Grid> </Grid> <!--Sample code showing usage of ApplicationBar--> <phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton IconUri="/Images/appbar.back.rest.png" Text="назад" Click="Menu1_Click" /> <shell:ApplicationBarIconButton IconUri="/Images/appbar.cancel.rest.png" Text="очистить" Click="Menu2_Click" /> <shell:ApplicationBarIconButton IconUri="/Images/appbar.next.rest.png" Text="перейти" Click="Menu3_Click" /> <!--shell:ApplicationBar.MenuItems> <shell:ApplicationBarMenuItem Text="MenuItem 1"/> <shell:ApplicationBarMenuItem Text="MenuItem 2"/> </shell:ApplicationBar.MenuItems--> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar> </phone:PhoneApplicationPage>
Разметка второй страницы будет состоять только из пустого списка (мы будем заполнять его динамически в коде страницы). Меню будет состоять из 3 кнопок (назад, перейти по выделенной ссылке и очистить историю).
<phone:PhoneApplicationPage x:Class="Wp7IUSLab11.HistoryPage" 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="Portrait" Orientation="Portrait" mc:Ignorable="d" d:DesignHeight="696" d:DesignWidth="480" shell:SystemTray.IsVisible="True"> <!--LayoutRoot is the root grid where all page content is placed--> <Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--TitlePanel contains the name of the application and page title--> <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="ApplicationTitle" Text="ОБОЗРЕВАТЕЛЬ (история)" Style="{StaticResource PhoneTextNormalStyle}"/> <TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/--> </StackPanel> <!--ContentPanel - place additional content here--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <ListBox Name="lstHistory" /> </Grid> </Grid> <!--Sample code showing usage of ApplicationBar--> <phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton IconUri="/Images/appbar.back.rest.png" Text="назад" Click="Menu1_Click" /> <shell:ApplicationBarIconButton IconUri="/Images/appbar.cancel.rest.png" Text="очистить" Click="Menu2_Click" /> <shell:ApplicationBarIconButton IconUri="/Images/appbar.next.rest.png" Text="перейти" Click="Menu3_Click" /> <!--shell:ApplicationBar.MenuItems> <shell:ApplicationBarMenuItem Text="MenuItem 1"/> <shell:ApplicationBarMenuItem Text="MenuItem 2"/> </shell:ApplicationBar.MenuItems--> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar> </phone:PhoneApplicationPage>
Для того чтобы упростить взаимодействие между страницами приложения создадим статический класс History, который будет содержать статический список ссылок, индекс текущего элемента списка и несколько функций:
public static class History { public static List<Uri> listHistory; public static int nCurHistoryPos; public static void Init() { listHistory = new List<Uri>(); nCurHistoryPos = 0; } public static string ListToString() { string strRes = ""; for (int i = 0; i < listHistory.Count; i++) { if (i != 0) { strRes += "|"; } strRes += listHistory[i].ToString(); } return strRes; } public static void StringToList(string strHist) { string[] strMas = strHist.Split( '|'); for (int i = 0; i < strMas.GetLength(0); i++) { listHistory.Add(new Uri(strMas[i])); } } }
Вернемся к файлу MainPage.xaml.cs. При нажатии на кнопку btnRun будем осуществлять загрузку в браузере страницы, адрес которой указан в текстовом поле. При этом в случае если ссылка не содержит "http://", то его обязательно будем добавлять:
private void btnRun_Click(object sender, RoutedEventArgs e) { try { //убираем пробелы и приводим к нижнему регистру txtAddress.Text = txtAddress.Text.Trim().ToLower(); //добавляем, если надо, "http://" if (txtAddress.Text.Length < 7) { txtAddress.Text = "http://" + txtAddress.Text; } else { if (!txtAddress.Text.Substring(0, 7).Equals("http://")) { txtAddress.Text = "http://" + txtAddress.Text; } } //переходим Uri url = new Uri(txtAddress.Text); wBrowser.Navigate(url); } catch (Exception ex) { MessageBox.Show(ex.Message); } }
Как только страница в браузере загружена, будем добавлять ее в историю. При этом, если история полна, будем удалять последний элемент:
private void wBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e) { //если мы движемся не по истории if (!bHistoryNavigate) { if (History.listHistory.Count > 0) { //в случае, если бродили по истории - подтираем ее до текущей позиции History.listHistory.RemoveRange(0, History.nCurHistoryPos); if (!History.listHistory[0].Equals(wBrowser.Source)) { AddToHistoryUri(); } } else { AddToHistoryUri(); } } else { //устанавливаем флаг назад в фолс bHistoryNavigate = false; } txtAddress.Text = wBrowser.Source.AbsoluteUri; }
При добавлении ссылки в историю будем сохранять всю историю в изолированном хранилище:
private void AddToHistoryUri() { if (History.listHistory.Count >= MAX_HISTORY_LEN) { History.listHistory.RemoveAt(History.listHistory.Count - 1); } History.listHistory.Insert(0, wBrowser.Source); string strHistoryText = History.ListToString(); SaveToIsolatedStorage(strHistoryText); }
Создадим константу для хранения имени файла, в который будем сохранять историю:
private const string strIStorageName = "Wp7IUSLab11.txt";
В конструкторе класса будем загружать историю из изолированного хранилища - файла (в случае, если файл существует):
// Constructor public MainPage() { InitializeComponent(); History.Init(); if (IsIsolatedStorageExist()) { string strText = LoadFromIsolatedStorage(); History.StringToList(strText); if (History.listHistory.Count > 0) { txtAddress.Text = "идет загрузка..."; wBrowser.Navigate(History.listHistory[0]); } } } private void SaveToIsolatedStorage(string histText) { IsolatedStorageFile fileStorage = IsolatedStorageFile.GetUserStoreForApplication(); IsolatedStorageFileStream fileStream = fileStorage.CreateFile(strIStorageName); StreamWriter sw = new StreamWriter(fileStream); sw.Write(histText); sw.Close(); fileStream.Close(); } private string LoadFromIsolatedStorage() { IsolatedStorageFile fileStorage = IsolatedStorageFile.GetUserStoreForApplication(); IsolatedStorageFileStream fileStream = fileStorage.OpenFile(strIStorageName, System.IO.FileMode.Open); StreamReader sr = new StreamReader(fileStream); string strRes = sr.ReadToEnd(); sr.Close(); fileStream.Close(); return strRes; } private bool IsIsolatedStorageExist() { IsolatedStorageFile sileStorage = IsolatedStorageFile.GetUserStoreForApplication(); return sileStorage.FileExists(strIStorageName); } private void RemoveIsolatedStorage() { if (IsIsolatedStorageExist()) { IsolatedStorageFile fileStorage = IsolatedStorageFile.GetUserStoreForApplication(); fileStorage.DeleteFile(strIStorageName); } }
Для работы с изолированным хранилищем в код необходимо добавить директивы:
using System.IO.IsolatedStorage; using System.IO;
При переходе на главную страницу будем загружать указанную страницу. В случае если история пуста, будем очищать изолированное хранилище:
protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); if (NavigationContext.QueryString.ContainsKey("hist")) { History.nCurHistoryPos = int.Parse(NavigationContext.QueryString["hist"].ToString()); bHistoryNavigate = true; wBrowser.Navigate(History.listHistory[History.nCurHistoryPos]); } else { if (0 == History.listHistory.Count) { RemoveIsolatedStorage(); } } }
Теперь можно скомпилировать приложение, запустить на эмуляторе или телефоне и проверить его функциональность.