Лабораторный практикум 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(); } }
Теперь можно скомпилировать приложение, запустить на эмуляторе или телефоне и проверить его функциональность.