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

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

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

Лабораторная работа №4. Калькулятор

Задание

Создать приложение "Калькулятор" для Windows Phone 7 с тремя возможными режимами: обычным, инженерным и режимом для программистов (перевод из одной системы счисления в другую).

Освоение

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

Описание

Создадим новый проект 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 дополним таблицу Grid. Пусть в ней будет 4 колонки (для кнопок) и 3 строки (для текстового поля и двух рядок кнопок). Определим это в специальных тегах Grid.RowDefinitions и Grid.ColumnDefinitions:

      <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="120" />
                <RowDefinition Height="90" />
                <RowDefinition Height="90" />
            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>

		…

        </Grid>

Для позиционирования элементов управления внутри таблицы используются атрибуты Grid.Row и Grid.Column. Они хранят индексы строки и столбца родительской таблицы.

Элементы управления будем описывать на месте многоточия. Сначала опишем блок элементов для ввода/вывода значений: два TextBlock для введенного аргумента и операции и TextBox для ввода. Выравнивание в этих элементах (атрибут TextAlignment) будет по правому краю. Также определим отступы с помощью атрибута Margin. Для текстового поля определим контекст ввода – какого типа данные можно вводить. Установим атрибут InputScope="Number". При этом при активации данного текстового поля клавиатура будет принимать следующий вид Рис. 2.5 :

Клавиатура для ввода значений

Рис. 2.5. Клавиатура для ввода значений

Также определим обработчики событий для текстового поля – изменение текста и изменения выделения. Это необходимо для того, чтобы предотвратить ввод неверных данных. Описание действий данных обработчиков будет описано ниже.

Также добавим кнопки для операций "+", "-", "*", "/", смена знака на противоположный, остаток от деления, сброс и равно. Добавим обработчики нажатий на данные кнопки.

      <StackPanel Grid.ColumnSpan="4" Grid.Row="0" Grid.Column="0">
                <TextBlock Name="lblArg1" Text="" TextAlignment="Right" Height="30" Margin="15,0,15,0" />
                <TextBlock Name="lblArgOp" Text="" TextAlignment="Right" Height="30" Margin="15,0,15,0" />
                <TextBox Name="txtEnter" Text="" TextAlignment="Right" VerticalAlignment="Bottom" InputScope="Number" 
                Height="70" TextChanged="txtEnter_TextChanged" SelectionChanged="txtEnter_SelectionChanged" />
            </StackPanel>

            <Button Content="+" Height="100" Width="100" Grid.Row="1" 
            Grid.Column="0" Click="btnAdd_Click" />
            <Button Content="-" Height="100" Width="100" Grid.Row="1" 
            Grid.Column="1" Click="btnSub_Click" />
            <Button Content="*" Height="100" Width="100" Grid.Row="1" 
            Grid.Column="2" Click="btnMul_Click" />
            <Button Content="/" Height="100" Width="100" Grid.Row="1" 
            Grid.Column="3" Click="btnDiv_Click" />

            <Button Content="±" Height="100" Width="100" Grid.Row="2" 
            Grid.Column="0" Click="btnInv_Click" />
            <Button Content="%" Height="100" Width="100" Grid.Row="2" 
            Grid.Column="1" Click="btnMod_Click" />
            <Button Content="C" Height="100" Width="100" Grid.Row="2" 
            Grid.Column="2" Click="btnRes_Click" />
            <Button Content="=" Height="100" Width="100" Grid.Row="2" 
            Grid.Column="3" Click="btnEnter_Click" />

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

  <!--Sample code showing usage of ApplicationBar-->
    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" 
            Text="Инженерный" Click="Menu1_Click" />
            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" 
            Text="Программист" Click="Menu2_Click" />
            
            <!--shell:ApplicationBar.MenuItems>
                <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
                <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
            </shell:ApplicationBar.MenuItems-->
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>
 

Перейдем к написанию кода для данной страницы.

Для начала в классе напишем перечисление для существующих операций и определим две переменные для хранения уже введенного первого операнда и операции:

enum Operation
  {
   ADD,
   SUP,
   MUL,
   DIV,
   MOD
  }

    private double arg1;
    private Operation oper;

В конструкторе проинициализируем первый операнд нулем:

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

            arg1 = 0d;
        }

Напишем функцию, которая будет считать результат. В случае деления она также будет проверять второй операнд на неравенство 0.

   private double Count(double arg2)
        {
            double result = 0d;

            switch (oper)
            {
                case Operation.ADD:
                    result = arg1 + arg2;
                    break;
                case Operation.SUP:
                    result = arg1 - arg2;
                    break;
                case Operation.MUL:
                    result = arg1 * arg2;
                    break;
                case Operation.DIV:
                    if (arg2 != 0)
                    {
                        result = arg1 / arg2;
                    }
                    else
                    {
                        MessageBox.Show("Делить на ноль нельзя.");
                        result = double.MaxValue;
                    }
                    break;
                case Operation.MOD:
                    result = arg1 % arg2;
                    break;
                default:
                    break;
            }

            return result;
        }

Напишем обработчики нажатий на кнопки. Одинаковый функционал вынесем в отдельную функцию Operation_Click().

       private void Operation_Click()
        {
            //если мы вводим первый аргумент
            if (0d == arg1)
            {
                lblArg1.Text = txtEnter.Text;
                txtEnter.Text = "";

                arg1 = double.Parse(lblArg1.Text);
            }
            else
            {
                //если первый аргумент уже есть

                if (txtEnter.Text.Length > 0)
                {
                    double arg2 = double.Parse(txtEnter.Text);

                    if ((Operation.DIV == oper) && (0 == arg2))
                    {
                        MessageBox.Show("Делить на ноль нельзя.");
                        txtEnter.Text = "";
                    }
                    else
                    {
                        arg1 = Count(arg2);
                        lblArg1.Text = arg1.ToString();
                        txtEnter.Text = "";
                    }
                }
            }

            txtEnter.Focus();
        }

        private void btnAdd_Click(object sender, RoutedEventArgs e)
        {
            Operation_Click();

            lblArgOp.Text = "+";
            oper = Operation.ADD;
        }

        private void btnSub_Click(object sender, RoutedEventArgs e)
        {
            Operation_Click();

            lblArgOp.Text = "-";
            oper = Operation.SUP;
        }

        private void btnMul_Click(object sender, RoutedEventArgs e)
        {
            Operation_Click();

            lblArgOp.Text = "*";
            oper = Operation.MUL;
        }

        private void btnDiv_Click(object sender, RoutedEventArgs e)
        {
            Operation_Click();

            lblArgOp.Text = "/";
            oper = Operation.DIV;
        }

        private void btnInv_Click(object sender, RoutedEventArgs e)
        {
            if (txtEnter.Text.Length > 0)
            {
                txtEnter.Text = (- double.Parse(txtEnter.Text)).ToString();
            }
            txtEnter.Focus();
        }

        private void btnMod_Click(object sender, RoutedEventArgs e)
        {
            Operation_Click();

            lblArgOp.Text = "%";
            oper = Operation.MOD;
        }

        private void btnRes_Click(object sender, RoutedEventArgs e)
        {
            arg1 = 0d;
            lblArg1.Text = "";
            lblArgOp.Text = "";
            txtEnter.Text = "";
            txtEnter.Focus();
        }

        private void btnEnter_Click(object sender, RoutedEventArgs e)
        {
            if ((lblArg1.Text.Length > 0) && (lblArgOp.Text.Length > 0) && (txtEnter.Text.Length > 0))
            {
                double arg2 = double.Parse(txtEnter.Text);

                if ((Operation.DIV == oper) && (0 == arg2))
                {
                    MessageBox.Show("Делить на ноль нельзя.");
                    txtEnter.Text = "";
                }
                else
                {
                    arg1 = Count(arg2);
                    lblArg1.Text = arg1.ToString();
                    lblArgOp.Text = "";
                    txtEnter.Text = "";
                }
            }

            txtEnter.Focus();
        }

Напишем обработчики событий для текстового поля. Функция txtEnter_TextChanged() будет ограничивать введенные данные 16 символами. Функция txtEnter_SelectionChanged() будет устанавливать курсор в конец текстового поля.

      private void txtEnter_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (txtEnter.Text.Length > 16)
            {
                txtEnter.Text = txtEnter.Text.Substring(0, 16);

                //установить кусор в конец
                txtEnter.Select(16, 0);
            }
        }

        private void txtEnter_SelectionChanged(object sender, RoutedEventArgs e)
        {
            //если курсор стоит не в самом конце - поставить его туда
            if (txtEnter.SelectionStart != txtEnter.Text.Length)
                txtEnter.Select(txtEnter.Text.Length, 0);
        }

Для обеспечения перехода между страницами добавим в самый верх директиву:

 using System.Windows.Navigation;     

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

      private void Menu1_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("/PageEngineer.xaml?data=" +
                    Uri.EscapeDataString(txtEnter.Text),
                    UriKind.Relative));
        }

        private void Menu2_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("/PageProgrammer.xaml?data=" +
                    Uri.EscapeDataString(txtEnter.Text),
                    UriKind.Relative));
        }

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

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

            if (NavigationContext.QueryString.ContainsKey("data"))
            {
                txtEnter.Text = NavigationContext.QueryString["data"].ToString();
                txtEnter.Focus();
            }
        }

Добавим в проект еще 2 страницы: PageEngineer.xaml и PageProgrammer.xaml. Разметка в них будет выглядеть аналогичным образом. Разве что, на странице PageProgrammer.xaml вместо кнопок поместим RadioButton для систем счисления dec, bin и hex.

PageEngineer.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 - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="120" />
                <RowDefinition Height="90" />
                <RowDefinition Height="90" />
            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>

            <StackPanel Grid.ColumnSpan="4" Grid.Row="0" Grid.Column="0">
                <TextBlock Name="lblArg1" Text="" TextAlignment="Right"
                 Height="30" Margin="15,0,15,0" />
                <TextBlock Name="lblArgOp" Text="" TextAlignment="Right" 
                Height="30" Margin="15,0,15,0" />
                <TextBox Name="txtEnter" Text="" TextAlignment="Right" 
                VerticalAlignment="Bottom" InputScope="Number" 
                Height="70" TextChanged="txtEnter_TextChanged" 
                SelectionChanged="txtEnter_SelectionChanged" />
            </StackPanel>

            <Button Content="^" Height="100" Width="100" Grid.Row="1" 
            Grid.Column="0" Click="btnDegree_Click" />
            <Button Content="v" Height="100" Width="100" Grid.Row="1" 
            Grid.Column="1" Click="btnRoot_Click" />
            <Button Content="ln" Height="100" Width="100" Grid.Row="1" 
            Grid.Column="2" Click="btnLn_Click" />
            <Button Content="log" Height="100" Width="100" Grid.Row="1" 
            Grid.Column="3" Click="btnLog_Click" />

            <Button Content="sin" Height="100" Width="100" Grid.Row="2" 
            Grid.Column="0" Click="btnSin_Click" />
            <Button Content="cos" Height="100" Width="100" Grid.Row="2"
             Grid.Column="1" Click="btnCos_Click" />
            <Button Content="C" Height="100" Width="100" Grid.Row="2" 
            Grid.Column="2" Click="btnRes_Click" />
            <Button Content="=" Height="100" Width="100" Grid.Row="2" 
            Grid.Column="3" Click="btnEnter_Click" />
        </Grid>
    </Grid>

    <!--Sample code showing usage of ApplicationBar-->
    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" 
            Text="Обычный" Click="Menu1_Click" />
            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" 
            Text="Программист" Click="Menu2_Click" />

            <!--shell:ApplicationBar.MenuItems>
                <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
                <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
            </shell:ApplicationBar.MenuItems-->
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>

PageProgrammer.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 - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="80" />
                <RowDefinition Height="70" />
                <RowDefinition Height="70" />
            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>

            <!--StackPanel>
                <TextBlock Name="lblArg1" Text="" TextAlignment="Right" 
                Height="30" Margin="15,0,15,0" />
                <TextBlock Name="lblArgOp" Text="" TextAlignment="Right" 
                Height="30" Margin="15,0,15,0" />
                <TextBox Name="txtEnter" Text="" TextAlignment="Right" 
                VerticalAlignment="Bottom" InputScope="Number" 
                Height="70" TextChanged="txtEnter_TextChanged" 
                SelectionChanged="txtEnter_SelectionChanged" />
            </StackPanel-->

            <TextBox Name="txtEnter" Text="" Grid.ColumnSpan="2" Grid.Row="0" Grid.Column="0" TextAlignment="Right" 
            VerticalAlignment="Bottom" InputScope="Number" Height="70" 
            TextChanged="txtEnter_TextChanged" SelectionChanged="txtEnter_SelectionChanged" />

            <RadioButton Name="radioDec"  GroupName="a1" HorizontalAlignment="Center" Content="Dec" Grid.Row="1"
             Grid.Column="0" IsChecked="True" Checked="radioDec_Checked" />
            <RadioButton Name="radioBin" GroupName="a1" HorizontalAlignment="Center" Content="Bin" Grid.Row="2" 
            Grid.Column="0" Checked="radioBin_Checked" />
            <RadioButton Name="radioHex" GroupName="a1" HorizontalAlignment="Center" Content="Hex" Grid.Row="1" 
            Grid.Column="1" FlowDirection="RightToLeft" Checked="radioHex_Checked" />
            <Button Content="C" Height="70" Width="150" Grid.Row="2" Grid.Column="2" Click="btnRes_Click" />
        </Grid>
    </Grid>

    <!--Sample code showing usage of ApplicationBar-->
    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png"
            Text="Обычный" Click="Menu1_Click" />
            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" 
            Text="Инженерный" Click="Menu2_Click" />

            <!--shell:ApplicationBar.MenuItems>
                <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
                <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
            </shell:ApplicationBar.MenuItems-->
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>

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

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