[Элементы управления](./t8_elements.md) | [Содержание](../readme.md#тема-8-оконные-приложения) | [Фильтрация данных](./wpf_filtering.md)

# Каркас приложения. Модель данных. Привязка данных. Табличный вывод.

## Каркас приложения.

На прошлых занятиях и лекциях мы разработали каркас для наших будущих приложений. Рассмотрим его ещё раз:

![](../img/08033.png)

* В левой колонке **(1)** у нас будет элемент **StackPanel**, объединяющий все три строки. В нём будут находится логотип компании и основное меню приложения. Эта колонка имеет фиксированную ширину.

* В верхней строке правой колонки **(2)** будет расположено меню для элементов фильтрации, поиска и т.д. Эта строка имеет высоту "Auto", т.е. будет зависеть от содержимого.

* В середине второй колонки **(3)** будет расположен основной список с данными: элементы **DataGrid** или **ListView**

* В нижней строке второй колонки **(4)** в перспективе будем располагать счётчики, пагинаторы и т.п. (высота тоже "auto")

Разметка выглядит так:

```xml
<Grid ShowGridLines="True">
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition />
        <RowDefinition Height="auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="200"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <!-- типа логотип компании -->
    <Image 
        Source="/Img/simon.png" 
        Grid.RowSpan="2"/>

    <StackPanel 
        Orientation="Vertical"
        Grid.RowSpan="3"
        VerticalAlignment="Bottom">
        <Button 
            x:Name="ExitButton"
            Content="Выход" 
            Click="ExitButton_Click"
            Height="50"/>
    </StackPanel>

    <WrapPanel
        Orientation="Horizontal"
        Grid.Column="1"
        MinHeight="50">
        <!-- минимальную высоту я тут поставил, чтобы верхнюю строку сетки было видно. В реальном приложении она не нужна -->
    </WrapPanel>
</Grid>
```

>Для того, чтобы картинки были видны в рантайме нужно их поместить а ресурсы:
>
>![](../img/image_resource.png)

## Модель данных

В следующем году мы будем получать данные из базы данных, а пока будем экспериментировать на "кошках". Заодно оформим получение данных в виде интерфейса.

>В заданиях WorldSkills есть требование логически выделять модели. Поэтому создадим в проекте папку **Model** (в контекстном меню *решения* выбрать *добавить - создать папку*)
>
>![](../img/08034.png)

1. Создадим классы **Cat (Кошка)** и **CatBreed (порода кошки)** (в контекстном меню папки *Model* выбрать *Добавить - класс*):

    ```catbreed.cs
    namespace WpfTemplate.Model
    {
        public class CatBreed
        {
            public string title { get; set; }
        }
    }    
    ```

    ```cat.cs
    namespace WpfTemplate.Model
    {
        public class Cat
        {
            public string name { get; set; }
            public int age{ get; set; }
            public string color { get; set; }
            public string photo { get; set; }
            // порода
            public CatBreed breed { get; set; }
        }
    }    
    ```

    >Студия может не добавить модификатор доступа классам - допишите **public**

1. Для работы с данными создадим интерфейс **IDataProvider** (поставщик данных) и класс **LocalDataProvider**, реализующий этот интерфейс (это уже не модель, а прочие классы, поэтому их создаём в каталоге `Classes`)

    В интерфейсе нам пока нужен только один метод: получение списка кошек

    ```cs
    namespace WpfTemplate.Classes
    {
        interface IDataProvider
        {
            IEnumerable<Cat> getCats();
        }
    }
    ```

    Метод *GetCats* класса **LocalDataProvider** возвращает список кошек, созданный программно

    ```cs
    namespace WpfTemplate.Classes
    {
        public class LocalDataProvider : IDataProvider
        {
            public IEnumerable<Cat> getCats()
            {
                return new Cat[]{
                    new Cat{
                        age=1,
                        breed = new CatBreed() { title = "Дворняжка" }, 
                        color="Белый", 
                        name="Ириска"},
                    new Cat{
                        age=2,
                        breed = new CatBreed() { title = "Шотландская вислоухая" }, 
                        color="Коричневый", 
                        name="Изи"},
                    new Cat{
                        age=3,
                        breed = new CatBreed() { title = "Сиамский" }, 
                        color="Цветной", 
                        name="Макс"}
                };
            }
        }
    }
    ```

1. В пространстве имен проекта создадим класс **Globals**, в котором объявим публичную статическую переменную *dataProvider* типа **IDataProvider**:

    ```cs
    namespace WpfTemplate.Classes
    {
        class Globals
        {
            public static IDataProvider dataProvider;
        }
    }
    ```

    Это нужно, чтобы поставщик данных был виден в любом месте проекта. 

1. В конструкторе главного окна (`MainWindow.xaml.cs`) присвоим глобальной переменной *dataProvider* экземпляр класса **LocalDataProvider** и сохраним список кошек в свойстве **catList**

    ```cs
    public IEnumerable<Cat> catList { get; set; }
   
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        Globals.dataProvider = new LocalDataProvider();
        catList = Globals.dataProvider.getCats();
    }

    private void ExitButton_Click(
        object sender, 
        RoutedEventArgs e)
    {
        Application.Current.Shutdown();
    }
    ```

## Привязка данных. Табличный вывод

Теперь, имея данные для отображения, мы можем разместить в разметке элемент **DataGrid** и "привязать" к нему данные:

```xml
<DataGrid
    Grid.Row="1"
    Grid.Column="1"
    CanUserAddRows="False"
    AutoGenerateColumns="False"
    ItemsSource="{Binding catList}"
>
    <DataGrid.Columns>
        <DataGridTextColumn
            Header="Кличка"
            Binding="{Binding name}"/>
        <DataGridTextColumn
            Header="Возраст"
            Binding="{Binding age}"/>
        <DataGridTextColumn
            Header="Цвет"
            Binding="{Binding color}"/>
        <DataGridTextColumn
            Header="Порода"
            Binding="{Binding breed.title}"/>
    </DataGrid.Columns>
</DataGrid>
```

* Атрибут `CanUserAddRows="False"` запрещает элементу **DataGrid** добавлять строки с таблицу

* Атриут `AutoGenerateColumns="False"` запрещает автоматическое формирование столбцов таблицы - мы вручную описываем те столбцы, которые хотим видеть.

* Атрибут `ItemsSource="{Binding catList}"` "привязывает" к таблице источник данных - наш список кошек. 

* В колонках таблицы мы уже "привязываемся" к свойствам класса источника данных

Приложение должно выглядеть примерно так:

![](../img/08035.png)

---

## Задание

Реализовать приложение из лекции и оформить отчет со скриншотами в репозиторий.

**Требования к репозиторию:**

- в репозитории должны быть исходные коды проекта (весь каталог проекта)
- в репозитории не должно быть бинарных файлов (каталоги `bin`, `obj` и `.vs` для C#) - используйте файл `.gitignore`
- в корне репозитория должен быть файл `readme.md` с отчётом о проекте
- в отчёте должны быть: 
    - заголовок (с названием конспекта или лабораторной)
    - блоки кода (не весь проект, а только наиболее важные куски, относящиеся к теме лекции/лабораторной) - скриншоты работающего приложения.

Например, ваш проект называется `SomeProject`, в таком случае структура файлов в репозитории должна быть примерно такая:

- `img` (каталог со скринами для отчета)
- `SomeProject` (каталог с проектом)
- `SomeProject.sln`
- `.gitignore`
- `readme.md`

[Элементы управления](./t8_elements.md) | [Содержание](../readme.md#тема-8-оконные-приложения) | [Фильтрация данных](./wpf_filtering.md)
