Опубликован: 11.01.2013 | Доступ: свободный | Студентов: 623 / 124 | Длительность: 12:06:00
Лекция 2:

Лабораторный практикум 1

< Лекция 1 || Лекция 2: 12345 || Лекция 3 >

Лабораторная работа №5. База паролей

Задание

Создать приложение "База паролей" для Windows Phone 7, которое позволяло бы хранить пароли для различных ресурсов и дополнять базу новыми паролями.

Освоение

  • навигация между страницами
  • основные элементы управления и разметки (Grid, StackPanel, Canvas, TextBox, TextBlock, PasswordBox, ListBox, Button, Border)
  • события
  • меню приложения

Описание

Создадим новый проект Silverlight for Windows PhoneWindows Phone Application.

Приложение будет состоять из двух окон:

  • главное окно со списком ресурсов
  • окно создания нового ресурса (с паролем)

Откроем файл разметки главной страницы MainPage.xaml. В самом верху страницы разметки изменим выводимое название приложения, например, на "БАЗА ПАРОЛЕЙ". Заголовок страницы удалим или закомментируем с целью экономии места:

    <!--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 поместим список ресурсов. При выделении ресурса и нажатии на кнопку меню будем запрашивать код подтверждения пользователя (с целью дополнительной защиты данных), после чего будем выводить пароль.

Для запроса код подтверждения пользователя добавим на страницу разметки специальные поля, которые будем отображать (свойство Visibility устанавливать в "Visible") при нажатии на кнопку меню и скрывать в противном случае (свойство Visibility устанавливать в "Collapsed"). Таким образом, создадим подобие MessageBox-а. Аналогичным образом будем выводить пароль для указанного ресурса.

Итак, код ContentPanel станицы MainPage.xaml будем выглядеть примерно так:

    <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" Canvas.ZIndex="0">
            <Canvas>
                <ListBox Name="lstSources" Canvas.Left="0" Canvas.Top="0" SelectionChanged="lstSources_SelectionChanged">
                    <ListBoxItem>
                        <TextBlock Text="Элемент списка" FontSize="28" Margin="0,10,0,10" Width="450" />
                    </ListBoxItem>
                </ListBox>

                <Border Name="msgCode" Visibility="Collapsed" BorderThickness="4" BorderBrush="White"  
                Canvas.Left="0" Canvas.Top="75" Canvas.ZIndex="2">
                    <StackPanel Width="442" Background="Black">
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="Введите код подтверждения:" FontSize="26" Width="372" Margin="10,10,10,10" />
                            <TextBlock Text="X" TextAlignment="Right" FontSize="26" Width="30" 
                            Margin="10,10,10,10" MouseEnter="lblCodeX_MouseEnter" />
                        </StackPanel>
                        <!--TextBox Name="txtPassword" Text="" InputScope="Password" /-->
                        <PasswordBox Name="pwdCodeBox" Password="" />
                        <!--StackPanel Orientation="Horizontal"-->
                            <Button Name="btnCodeApply" Content="Принять" Margin="0,0,5,0" Click="btnCodeApply_Click" />
                            <!--Button Name="btnCodeCancel" Content="Отмена" Width="220" Margin="5,0,0,0" />
                        </StackPanel-->
                    </StackPanel>
                </Border>

                <Border Name="msgPass" Visibility="Collapsed" BorderThickness="4" BorderBrush="White" 
                Canvas.Left="0" Canvas.Top="75" Canvas.ZIndex="1">
                    <StackPanel Width="442" Background="Black">
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="Пароль:" FontSize="26" Width="372" Margin="10,10,10,10" />
                            <TextBlock Text="X" TextAlignment="Right" FontSize="26" Width="30" Margin="10,10,10,10" 
                            MouseEnter="lblPassX_MouseEnter" />
                        </StackPanel>
                        <TextBox Name="txtPass" IsReadOnly="True" />
                        <Button Name="btnPassShow" Content="Показать" Click="btnPassShow_Click" />
                    </StackPanel>
                </Border>
            </Canvas>
        </Grid>

В конце страницы раскомментируем фрагмент кода, отвечающий за меню приложения. Добавим еще одну кнопку. Объявим обработчики нажатия на кнопки меню и добавим значки. Значки можно найти в папке C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.0\Icons (лучше всего брать из папки dark) или нарисовать самостоятельно. Добавим в проект папку "Images" и поместим в нее необходимые изображения. Для каждого из изображений в свойствах назначим полю Build Action значение Content (если этого не сделать, ApplicationBar не сможет найти изображения).

   <!--Sample code showing usage of ApplicationBar-->
    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
            <shell:ApplicationBarIconButton IconUri="Images/appbar.feature.search.rest.png" Text="Просмотр" 
            Click="ApplicationBarMenuShow_Click" />
            <shell:ApplicationBarIconButton IconUri="Images/appbar.add.rest.png" Text="Добавить" 
            Click="ApplicationBarMenuAdd_Click" />
            <shell:ApplicationBarIconButton IconUri="Images/appbar.cancel.rest.png" Text="Удалить"
            Click="ApplicationBarMenuRemove_Click" />
            
            <!--shell:ApplicationBar.MenuItems>
                <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
                <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
            </shell:ApplicationBar.MenuItems-->
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>

Перейдем к написанию кода. Откроем файл MainPage.xaml.cs. В самый верх для обеспечения перехода между страницами добавим директиву:

  using System.Windows.Navigation; 

В класс MainPage добавим константу и 2 переменные:

     private const string m_Code = "qwe";
        private bool m_bHidePass = true;
        private string m_strSelected = "";

Константа будет определять код подтверждения пользователя для просмотра паролей. Таким образом просмотр паролей будет доступен только если верно ввести код "qwe". Переменные пригодятся при выполнении кода приложения.

Поскольку работать с локальной базой и файлами мы пока не умеем, для сохранения данных будем использовать статический класс MyDatabase. Создадим его. Он будет содержать список элементов типа MyElement, который нам также необходимо создать:

     public struct MyElement
    {
        public string Source;
        public string Password;
    }

Кроме того статический класс будет содержать функции инициализации списка (несколько ресурсов зададим заранее в этой функции), вывода пароля для заданного ресурса и сокрытие пароля (осуществляет замену символов на "*" для дополнительной защиты):

     public static class MyDatabase
    {
        public static List<MyElement>
 Values;

        public static void Init()
        {
            Values = new List<MyElement>
();

            MyElement tmp;
            tmp.Source = "компьютер";
            tmp.Password = "4";

            Values.Add(tmp);

            tmp.Source = "почта";
            tmp.Password = "qwerty";

            Values.Add(tmp);

            tmp.Source = "аська";
            tmp.Password = "12345";

            Values.Add(tmp);
        }

        public static string GetPassword(string src)
        {
            string strRes = "";

            for (int i = 0; i < Values.Count; i++)
            {
                if (Values[i].Source.Equals(src))
                {
                    strRes = Values[i].Password;
                    break;
                }
            }

            return strRes;
        }

        public static string HidePassword(string pwd)
        {
            string strRes = "";

            for (int i = 0; i < pwd.Length; i++)
            {
                strRes += "*";
            }

            return strRes;
        }
    }

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

    private void lblCodeX_MouseEnter(object sender, MouseEventArgs e)
        {
            msgCode.Visibility = System.Windows.Visibility.Collapsed;
            pwdCodeBox.Password = "";
            lstSources.IsEnabled = true;
            ApplicationBar.IsVisible = true;
        }

        private void lblPassX_MouseEnter(object sender, MouseEventArgs e)
        {
            msgPass.Visibility = System.Windows.Visibility.Collapsed;
            txtPass.Text = "";
            lstSources.IsEnabled = true;
            ApplicationBar.IsVisible = true;
        }

Напишем обработчики нажатия на кнопки у созданных нами окон:

            private void btnPassShow_Click(object sender, RoutedEventArgs e)
        {
            if (m_bHidePass)
            {
                //показать
                if (!m_strSelected.Equals(""))
                {
                    txtPass.Text = MyDatabase.GetPassword(m_strSelected);
                    btnPassShow.Content = "Скрыть";
                }
                else
                {
                    MessageBox.Show("Элемент не выбран.");
                }
            }
            else
            {
                //скрыть
                txtPass.Text = MyDatabase.HidePassword(txtPass.Text);
                btnPassShow.Content = "Показать";
            }

            m_bHidePass = !m_bHidePass;
        }

        private void btnCodeApply_Click(object sender, RoutedEventArgs e)
        {
            if (pwdCodeBox.Password.Equals(m_Code))
            {
                msgCode.Visibility = System.Windows.Visibility.Collapsed;
                pwdCodeBox.Password = "";
                msgPass.Visibility = System.Windows.Visibility.Visible;

                txtPass.Text = MyDatabase.HidePassword(MyDatabase.GetPassword(m_strSelected));
                m_bHidePass = true;
            }
            else
            {
                MessageBox.Show("Код неверный.");
                pwdCodeBox.Password = "";
                pwdCodeBox.Focus();
            }
        }

При выделении какого-нибудь элемента списка будем менять значение переменной m_strSelected :

    private void lstSources_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (-1 != lstSources.SelectedIndex)
                m_strSelected = ((TextBlock)((ListBoxItem)lstSources.SelectedItem).Content).Text;
        }

Напишем основные функции работы со списком: снятие выделения с элементов и вырисовка (добавление элементов списка из статического класса MyDatabase в список файла разметки ListBox с именем lstSources ).

    private void DeselectList()
        {
            //снять выделение с элементов
            lstSources.SelectedIndex = -1;
            m_strSelected = "";
        }

        private void DrawList()
        {
            lstSources.Items.Clear();

            for (int i = 0; i < MyDatabase.Values.Count; i++)
            {
                ListBoxItem lstitNew = new ListBoxItem();
                TextBlock txtSource = new TextBlock();

                txtSource.Text = MyDatabase.Values[i].Source;
                txtSource.FontSize = 28;
                txtSource.Width = 450;
                txtSource.Margin = new Thickness(0, 10, 0, 10);

                lstitNew.Content = txtSource;

                lstSources.Items.Add(lstitNew);
            }

            lstSources.SelectedIndex = lstSources.Items.Count - 1;
        }

Изменим конструктор класса MainPage:

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

            MyDatabase.Init();
            DrawList();
        }

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

    // Меню - Просмотр
        private void ApplicationBarMenuShow_Click(object sender, EventArgs e)
        {
            if (!m_strSelected.Equals(""))
            {
                lstSources.IsEnabled = false;
                ApplicationBar.IsVisible = false;
                msgCode.Visibility = System.Windows.Visibility.Visible;
                pwdCodeBox.Focus();
            }
            else
            {
                MessageBox.Show("Элемент не выбран.");
            }
        }

        // Меню - Добавить
        private void ApplicationBarMenuAdd_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("/PageAdd.xaml", UriKind.Relative));
        }

        // Меню - Удалить
        private void ApplicationBarMenuRemove_Click(object sender, EventArgs e)
        {
            int nIndex = lstSources.SelectedIndex;

            if (-1 != nIndex)
            {
                MyDatabase.Values.RemoveAt(nIndex);
                DrawList();
                DeselectList();
            }
            else
            {
                MessageBox.Show("Элемент не выбран.");
            }
        }

При переходе на текущую страницу будем просто перерисовывать список. Для этого переопределим стандартный метод OnNavigatedTo():

    protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            if (null != MyDatabase.Values)
            {
                DrawList();
            }
        }

С главной страницей закончено. Теперь добавим в проект приложения новую страницу – Windows Phone Portrait Page. Назовем ее PageAdd. В файле PageAdd.xaml определим разметку страницы. Она будет включать два текстовых поля – для названия ресурса и пароля – и две кнопки – "Принять" и "Отмена". Также не забудем про элементы TextBlock с пояснительными подписями к текстовым полям

    <!--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">
            <StackPanel Margin="5,5,5,5">
                <Grid ShowGridLines="False" Margin="0,20,0,0">
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>

                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="120" />
                        <ColumnDefinition Width="320" />
                    </Grid.ColumnDefinitions>

                    <TextBlock Text="Источник:" Grid.Column="0" Grid.Row="0" 
                    VerticalAlignment="Center" FontSize="24" />
                    <TextBlock Text="Пароль:" Grid.Column="0" Grid.Row="1" VerticalAlignment="Center" FontSize="24" />
                    <TextBox Name="txtSource" Text="Источник" Grid.Column="1" Grid.Row="0" />
                    <PasswordBox Name="pwdAdd" Password="Пароль" Grid.Column="1" Grid.Row="1" />
                </Grid>

                <StackPanel Orientation="Horizontal" Margin="0,20,0,0">
                    <Button Name="btnApply" Height="90" Width="220" Grid.Column="0" Grid.Row="0" 
                    Content="Принять" Click="btnApply_Click" />
                    <Button Name="btnCancel" Height="90" Width="220" Grid.Column="1" Grid.Row="0" 
                    Content="Отмена" Click="btnCancel_Click" />
                </StackPanel>
            </StackPanel>
        </Grid>

В коде страницы PageAdd.xaml.cs определим поведение:

   public partial class PageAdd : PhoneApplicationPage
    {
        public PageAdd()
        {
            InitializeComponent();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            txtSource.Text = "";
            pwdAdd.Password = "";

            txtSource.Focus();
        }

        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            MessageBoxResult result = MessageBox.Show("Изменения будут потеряны. Продолжить?", 
"Отмена", MessageBoxButton.OKCancel);
            if (result == MessageBoxResult.OK)
            {
                NavigationService.GoBack();
            }
        }

        private void btnApply_Click(object sender, RoutedEventArgs e)
        {
            if (txtSource.Text.Trim().Length > 0)
            {
                if (pwdAdd.Password.Trim().Length > 0)
                {
                    MyElement tmp;
                    tmp.Source = txtSource.Text;
                    tmp.Password = pwdAdd.Password;
                    MyDatabase.Values.Add(tmp);

                    NavigationService.GoBack();
                }
                else
                {
                    MessageBox.Show("Поле пароля не может быть пустым.");
                    pwdAdd.Focus();
                }
            }
            else
            {
                MessageBox.Show("Поле источника не может быть пустым.");
                txtSource.Focus();
            }
        }
    }

Теперь можно скомпилировать приложение, запустить на эмуляторе или телефоне и проверить его функциональность.

< Лекция 1 || Лекция 2: 12345 || Лекция 3 >