| Россия, г. Санкт-Петербург |
Лабораторный практикум 1
Лабораторная работа №4. Калькулятор
Задание
Создать приложение "Калькулятор" для Windows Phone 7 с тремя возможными режимами: обычным, инженерным и режимом для программистов (перевод из одной системы счисления в другую).
Освоение
- навигация между страницами
- основные элементы управления и разметки (Grid, StackPanel, TextBox, TextBlock, Button, RadioButton)
- события
- контекст ввода
- меню приложения
Описание
Создадим новый проект Silverlight for Windows Phone – Windows 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 :
Также определим обработчики событий для текстового поля – изменение текста и изменения выделения. Это необходимо для того, чтобы предотвратить ввод неверных данных. Описание действий данных обработчиков будет описано ниже.
Также добавим кнопки для операций "+", "-", "*", "/", смена знака на противоположный, остаток от деления, сброс и равно. Добавим обработчики нажатий на данные кнопки.
<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>
Теперь можно скомпилировать приложение, запустить на эмуляторе или телефоне и проверить его функциональность.
