Лабораторный практикум 2
Лабораторная работа №6. Учет затрат
Задание
Создать приложение "Учет затрат" для Windows Phone 7 с возможностью добавления новых доходов и расходов и подсчетом итоговой суммы.
Освоение
- навигация между страницами
- основные элементы управления и разметки (Grid, StackPanel, Canvas, TextBox, TextBlock, ListBox, Button)
- события
- контекст ввода
- меню приложения
Описание
Создадим новый проект 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 поместим список доходов/затрат. Список будет иметь вид таблицы с 3 столбцами: название, актив/пассив (актив – доход, пассив – расход) и сумма. Итак, код элемента ContentPanel будет выглядеть следующим образом:
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" Canvas.ZIndex="0">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="название" FontSize="28" Margin="0,0,5,0" TextAlignment="Center" Grid.Row="0" Grid.Column="0" />
<TextBlock Text="а|п" FontSize="28" Margin="0,0,5,0" TextAlignment="Center" Grid.Row="0" Grid.Column="1" />
<TextBlock Text="сумма" FontSize="28" Margin="0,0,5,0" TextAlignment="Center" Grid.Row="0" Grid.Column="2" />
<ListBox Name="lstElements" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3">
<ListBoxItem>
<StackPanel Orientation="Horizontal" Margin="0,10,0,10">
<TextBlock Text="Элемент списка" FontSize="28" Width="300" />
<TextBlock Text="А" FontSize="28" Width="50" TextAlignment="Center" />
<TextBlock Text="3000" FontSize="28" Width="100" TextAlignment="Right" />
</StackPanel>
</ListBoxItem>
</ListBox>
</Grid>
Внутри элемент Grid сначала помещаем объявление строк и столбцов, а затем и сами элементы управления. Внутри элементов управления с помощью атрибутов Grid.Row и Grid.Column задаются индексы, соответственно, строки и столбца.
В первой строке поместим заголовки столбцов, во второй – сам список. С помощью атрибута Grid.ColumnSpan можно объединять столбцы. Элементы списка объединим с помощью StackPanel с горизонтальной ориентацией.
В конце страницы раскомментируем фрагмент кода, отвечающий за меню приложения. Добавим еще одну кнопку. Объявим обработчики нажатия на кнопки меню и добавим значки. Значки можно найти в папке 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.check.rest.png" Text="Подсчет"
Click="ApplicationBarMenuCount_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>
Перейдем к написанию кода.
Поскольку работать с локальной базой и файлами мы пока не умеем, для сохранения данных будем использовать статический класс MyDatabase. Создадим его. Он будет содержать список элементов типа MyElement, который нам также необходимо создать:
public struct MyElement
{
public string Element;
public bool Active;
public float Money;
}
Кроме того статический класс будет содержать функцию инициализации списка (несколько элементов зададим заранее в этой функции):
public static class MyDatabase
{
public static List<MyElement> Values;
public static void Init()
{
Values = new List<MyElement>();
MyElement tmp;
tmp.Element = "зарплата";
tmp.Active = true;
tmp.Money = 15000f;
Values.Add(tmp);
tmp.Element = "донор крови";
tmp.Active = true;
tmp.Money = 2000f;
Values.Add(tmp);
tmp.Element = "ресторан";
tmp.Active = false;
tmp.Money = 3500f;
Values.Add(tmp);
}
}
Теперь откроем файл MainPage.xaml.cs. В самый верх для обеспечения перехода между страницами добавим директиву:
using System.Windows.Navigation;
В класс MainPage добавим функцию формирования элементов ListBox страницы из списка класса MyDatabase. Для этого будем последовательно создавать экземпляры класса TextBlock, задавать свойства и помещать их в экземпляры класса StackPanel, которые, в свою очередь, будем помещать в ListBoxItem, а те уже – в список.
private void DrawList()
{
lstElements.Items.Clear();
for (int i = 0; i < MyDatabase.Values.Count; i++)
{
ListBoxItem lstitNew = new ListBoxItem();
StackPanel spNew = new StackPanel();
TextBlock tbElem = new TextBlock();
TextBlock tbAP = new TextBlock();
TextBlock tbMoney = new TextBlock();
tbElem.Text = MyDatabase.Values[i].Element;
tbElem.FontSize = 28;
tbElem.Width = 300;
tbAP.Text = MyDatabase.Values[i].Active ? "А" : "П";
tbAP.FontSize = 28;
tbAP.Width = 50;
tbAP.TextAlignment = TextAlignment.Center;
tbMoney.Text = MyDatabase.Values[i].Money.ToString();
tbMoney.FontSize = 28;
tbMoney.Width = 100;
tbMoney.TextAlignment = TextAlignment.Right;
spNew.Orientation = System.Windows.Controls.Orientation.Horizontal;
spNew.Margin = new Thickness(0, 10, 0, 10);
spNew.Children.Add(tbElem);
spNew.Children.Add(tbAP);
spNew.Children.Add(tbMoney);
lstitNew.Content = spNew;
lstElements.Items.Add(lstitNew);
}
}
В конструкторе класса MainPage проинициализируем список и "нарисуем" его:
// Constructor
public MainPage()
{
InitializeComponent();
MyDatabase.Init();
DrawList();
}
Переход на главную страницу может быть осуществлен только в самом начале при загрузке приложения и при переходе со страницы добавления доходов/затрат. Таким образом, при переходе список может быть изменен. Поэтому нам следует перерисовывать его. Для этого переопределим стандартный метод OnNavigatedTo():
// Вызывается при переходе на текущую страницу
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (null != MyDatabase.Values)
{
DrawList();
}
}
Теперь напишем обработчики нажатий на кнопки меню. При нажатии на кнопку "Подсчет" просто будем выводить в MessageBox-е сводку по доходам, расходам и общей сумме. При нажатии на кнопку "Добавить" просто будем осуществлять переход на страницу добавления доходов/расходов (PageAdd.xaml), которую создадим далее. При нажатии на кнопку "Удалить" будем удалять элемент с выделенных индексом из списка и перерисовывать его.
// Меню - Подсчет
private void ApplicationBarMenuCount_Click(object sender, EventArgs e)
{
float fActive = 0f;
float fPassive = 0f;
float fItog = 0f;
for (int i = 0; i < MyDatabase.Values.Count; i++)
{
if (MyDatabase.Values[i].Active)
{
fActive += MyDatabase.Values[i].Money;
}
else
{
fPassive += MyDatabase.Values[i].Money;
}
}
fItog = fActive - fPassive;
MessageBox.Show(
"Доходы: " + fActive.ToString() + "\n" +
"Расходы: " + fPassive.ToString() + "\n\n" +
"Итого: " + fItog.ToString()
);
}
// Меню - Добавить
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 = lstElements.SelectedIndex;
if (-1 != nIndex)
{
MyDatabase.Values.RemoveAt(nIndex);
DrawList();
}
else
{
MessageBox.Show("Элемент не выбран.");
}
}
Теперь добавим в проект приложения новую страницу – Windows Phone Portrait Page. Назовем ее PageAdd. В файле PageAdd.xaml определим разметку страницы. Она будет включать два текстовых поля (для названия дохода/расхода и суммы), один CheckBox (для определения расход это или доход) и две кнопки – "Принять" и "Отмена". Также не забудем про элементы 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 />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="320" />
</Grid.ColumnDefinitions>
<TextBlock Name="lblElem" 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="txtElem" Text="Элемент" Grid.Column="1" Grid.Row="0" />
<TextBox Name="txtMoney" Text="0" InputScope="Number" Grid.Column="1" Grid.Row="1" />
<CheckBox Name="checkActive" Content="Прибыль" Grid.Column="1" Grid.Row="2"
Click="checkActive_Click" />
</Grid>
<StackPanel Orientation="Horizontal">
<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>
</Grid>
Перейдем к коду страницы PageAdd.xaml.cs.
Точно так же в самый верх добавим директиву:
using System.Windows.Navigation;
При переходе на данную страницу будем очищать текстовые поля и снимать галочку с CheckBox-а:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
txtElem.Text = "";
txtMoney.Text = "";
checkActive.IsChecked = false;
txtElem.Focus();
}
При нажатии на CheckBox просто будем менять текст в текстовом блоке lblElem для удобства пользователя:
private void checkActive_Click(object sender, RoutedEventArgs e)
{
if (checkActive.IsChecked.Value)
lblElem.Text = "Доход:";
else
lblElem.Text = "Расход:";
}
При нажатии на кнопку "Отмена" будем переходить на предыдущую главную страницу, ничего не изменяя. При нажатии на кнопку "Принять" будем добавлять новый элемент в список MyDatabase.Values:
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 (txtElem.Text.Trim().Length > 0)
{
if (txtMoney.Text.Trim().Length > 0)
{
MyElement tmp;
tmp.Element = txtElem.Text;
tmp.Active = checkActive.IsChecked.Value;
tmp.Money = float.Parse(txtMoney.Text);
MyDatabase.Values.Add(tmp);
NavigationService.GoBack();
}
else
{
MessageBox.Show("Поле суммы не может быть пустым.");
txtMoney.Focus();
}
}
else
{
if(checkActive.IsChecked.Value)
MessageBox.Show("Поле дохода не может быть пустым.");
else
MessageBox.Show("Поле расхода не может быть пустым.");
txtElem.Focus();
}
}
Теперь можно скомпилировать приложение, запустить на эмуляторе или телефоне и проверить его функциональность.