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

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

< Лекция 2 || Лекция 3: 12345 || Лекция 4 >
Аннотация: Лабораторные работы: учет затрат, список задач, веб-браузер, игра, определенные местоположения.

Лабораторная работа №6. Учет затрат

Задание

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

Освоение

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

Описание

Создадим новый проект 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 поместим список доходов/затрат. Список будет иметь вид таблицы с 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();
            }
        }

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

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