Руководство: создание приложения базы данных клиентов

В этом руководстве создается простое приложение для управления списком клиентов. При этом в нем представлен ряд основных понятий для корпоративных приложений в UWP. Вы узнаете, как:

  • Реализуйте операции создания, чтения, обновления и удаления в локальной базе данных SQL.
  • Добавьте сетку данных для отображения и изменения данных клиентов в пользовательском интерфейсе.
  • Расположите элементы пользовательского интерфейса в базовом макете формы.

Отправной точкой для работы с этим руководством является одностраничное приложение с минимальными возможностями пользовательского интерфейса и функциональными возможностями, основанное на упрощенной версии примера приложения Базы данных заказов клиентов. Она написана на C# и XAML, и мы ожидаем, что у вас есть базовое знакомство с обоими этими языками.

Страница main рабочего приложения

Предварительные требования

После клонированного или скачав репозиторий, вы можете изменить проект, открыв CustomerDatabaseTutorial.sln в Visual Studio.

Примечание

Это руководство основано на примере базы данных заказов клиентов, который недавно был обновлен для использования WinUI и Windows App SDK. Пока этот учебник и код не будут обновлены, между двумя примерами будут различия.

Часть 1. Код интереса

Если вы запустите приложение сразу после его открытия, вы увидите несколько кнопок в верхней части пустого экрана. Хотя это приложение невидимо для вас, приложение уже включает локальную базу данных SQLite, подготовленную для нескольких тестовых клиентов. Здесь вы начнете с реализации элемента управления пользовательского интерфейса для отображения этих клиентов, а затем перейдете к добавлению операций в базе данных. Прежде чем начать, вот где вы будете работать.

Представления

CustomerListPage.xaml — это представление приложения, которое определяет пользовательский интерфейс для одной страницы в этом руководстве. Каждый раз, когда вам нужно добавить или изменить визуальный элемент в пользовательском интерфейсе, вы будете делать это в этом файле. В этом руководстве описано, как добавить следующие элементы:

  • RadDataGrid для отображения и редактирования клиентов.
  • StackPanel для задания начальных значений для нового клиента.

Модели представлений

ViewModels\CustomerListPageViewModel.cs — это расположение основной логики приложения. Каждое действие пользователя, выполняемое в представлении, будет передано в этот файл для обработки. В этом руководстве вы добавите новый код и реализуете следующие методы:

  • CreateNewCustomerAsync, который инициализирует новый объект CustomerViewModel.
  • DeleteNewCustomerAsync, который удаляет нового клиента перед его отображением в пользовательском интерфейсе.
  • DeleteAndUpdateAsync, который обрабатывает логику кнопки удаления.
  • GetCustomerListAsync, который получает список клиентов из базы данных.
  • SaveInitialChangesAsync, который добавляет сведения о клиенте в базу данных.
  • UpdateCustomersAsync, который обновляет пользовательский интерфейс, чтобы отразить все добавленные или удаленные клиенты.

CustomerViewModel — это оболочка для сведений о клиенте, которая отслеживает, была ли она недавно изменена. Вам не нужно ничего добавлять в этот класс, но часть кода, который вы добавите в другое место, будет ссылаться на него.

Дополнительные сведения о создании примера проверка в обзоре структуры приложения.

Часть 2. Добавление DataGrid

Прежде чем приступить к работе с данными клиентов, необходимо добавить элемент управления пользовательского интерфейса для отображения этих клиентов. Для этого мы будем использовать готовый сторонний элемент управления RadDataGrid . Пакет NuGet Telerik.UI.for.UniversalWindowsPlatform уже включен в этот проект. Давайте добавим сетку в проект.

  1. Откройте Файл Views\CustomerListPage.xaml из Обозреватель решений. Добавьте следующую строку кода в тег Page , чтобы объявить сопоставление с пространством имен Telerik, содержащим сетку данных.

        xmlns:telerikGrid="using:Telerik.UI.Xaml.Controls.Grid"
    
  2. Под панелью CommandBar в main RelativePanel представления добавьте элемент управления RadDataGrid с некоторыми базовыми параметрами конфигурации:

     <Grid
         x:Name="CustomerListRoot"
         Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
         <RelativePanel>
             <CommandBar
                 x:Name="mainCommandBar"
                 HorizontalAlignment="Stretch"
                 Background="AliceBlue">
                 <!--CommandBar content-->
            </CommandBar>
            <telerikGrid:RadDataGrid
                 x:Name="DataGrid"
                 BorderThickness="0"
                 ColumnDataOperationsMode="Flyout"
                 GridLinesVisibility="None"
                 GroupPanelPosition="Left"
                 RelativePanel.AlignLeftWithPanel="True"
                 RelativePanel.AlignRightWithPanel="True"
                 RelativePanel.Below="mainCommandBar" />
        </RelativePanel>
    </Grid>
    
  3. Вы добавили сетку данных, но для ее отображения требуются данные. Добавьте в него следующие строки кода:

    ItemsSource="{x:Bind ViewModel.Customers}"
    UserEditMode="Inline"
    

    Теперь, когда вы определили источник данных для отображения, RadDataGrid будет обрабатывать большую часть логики пользовательского интерфейса. Однако при запуске проекта вы по-прежнему не увидите никаких данных на экране. Это связано с тем, что ViewModel еще не загружает его.

Пустое приложение без клиентов

Часть 3. Чтение клиентов

При инициализации viewModels\CustomerListPageViewModel.cs вызывает метод GetCustomerListAsync . Этот метод должен получить тестовые данные клиента из базы данных SQLite, которая включена в учебник.

  1. В файле ViewModels\CustomerListPageViewModel.cs обновите метод GetCustomerListAsync , используя следующий код:

     public async Task GetCustomerListAsync()
     {
         var customers = await App.Repository.Customers.GetAsync(); 
         if (customers == null)
         {
             return;
         }
         await DispatcherHelper.ExecuteOnUIThreadAsync(() =>
         {
             Customers.Clear();
             foreach (var c in customers)
             {
                 Customers.Add(new CustomerViewModel(c));
             }
         });
     }
    

    Метод GetCustomerListAsync вызывается при загрузке ViewModel, но до этого шага он ничего не делал. Здесь мы добавили вызов метода GetAsync в Repository/SqlCustomerRepository. Это позволяет ему связаться с репозиторием, чтобы получить перечисляемую коллекцию объектов Customer. Затем он анализирует их в отдельные объекты, прежде чем добавлять их во внутреннюю коллекцию ObservableCollection , чтобы их можно было отображать и редактировать.

  2. Запустите приложение. Теперь вы увидите сетку данных со списком клиентов.

Начальный список клиентов

Часть 4. Изменение клиентов

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

  1. Сначала объявите, что файл ViewModels\CustomerListPageViewModel.cs реализует интерфейс INotifyPropertyChanged :

    public class CustomerListPageViewModel : INotifyPropertyChanged
    
  2. Затем в main теле класса добавьте следующие событие и метод:

    public event PropertyChangedEventHandler PropertyChanged;
    
    public void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    

    Метод OnPropertyChanged упрощает создание события PropertyChanged , необходимого для двусторонней привязки данных.

  3. Обновите метод задания для SelectedCustomer с помощью следующего вызова функции:

    public CustomerViewModel SelectedCustomer
    {
         get => _selectedCustomer;
         set
         {
            if (_selectedCustomer != value)
            {
                 _selectedCustomer = value;
                 OnPropertyChanged();
            }
         }
    }
    
  4. В файле Views\CustomerListPage.xaml добавьте свойство SelectedCustomer в сетку данных.

    SelectedItem="{x:Bind ViewModel.SelectedCustomer, Mode=TwoWay}"
    

    Это связывает выбор пользователя в сетке данных с соответствующим объектом Customer в коде программной части. Режим привязки TwoWay позволяет отражать изменения, внесенные в пользовательском интерфейсе, в этом объекте.

  5. Запустите приложение. Теперь вы можете видеть клиентов, отображаемых в сетке, и вносить изменения в базовые данные с помощью пользовательского интерфейса.

Изменение клиента в сетке данных

Часть 5. Обновление клиентов

Теперь, когда вы можете просматривать и изменять клиентов, необходимо иметь возможность отправлять изменения в базу данных и извлекать все обновления, внесенные другими пользователями.

  1. Вернитесь в viewModels\CustomerListPageViewModel.cs и перейдите к методу UpdateCustomersAsync . Обновите его с помощью этого кода, чтобы отправить изменения в базу данных и получить новые сведения:

    public async Task UpdateCustomersAsync()
    {
         foreach (var modifiedCustomer in Customers
             .Where(x => x.IsModified).Select(x => x.Model))
         {
             await App.Repository.Customers.UpsertAsync(modifiedCustomer);
         }
         await GetCustomerListAsync();
    }
    

    В этом коде используется свойство IsModifiedфайла ViewModels\CustomerViewModel.cs, которое автоматически обновляется при каждом изменении клиента. Это позволяет избежать ненужных вызовов и отправлять изменения только от обновленных клиентов в базу данных.

Часть 6. Создание нового клиента

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

Обновление кода программной части

  1. Добавьте новое частное поле и открытое свойство в файл ViewModels\CustomerListPageViewModel.cs. Он будет использоваться для управления видимости панели.

    private bool _addingNewCustomer = false;
    
    public bool AddingNewCustomer
    {
         get => _addingNewCustomer;
         set
         {
            if (_addingNewCustomer != value)
            {
                 _addingNewCustomer = value;
                 OnPropertyChanged();
            }
         }
    }
    
  2. Добавьте новое открытое свойство в ViewModel, обратное значению AddNewCustomer. Он будет использоваться для отключения обычных кнопок панели команд, когда панель видна.

    public bool EnableCommandBar => !AddingNewCustomer;
    

    Теперь вам потребуется способ отображения сворачиваемой панели и создания клиента для редактирования в ней.

  3. Добавьте новое частное свойство и открытое свойство в ViewModel, чтобы содержать только что созданного клиента.

    private CustomerViewModel _newCustomer;
    
    public CustomerViewModel NewCustomer
    {
        get => _newCustomer;
        set
        {
            if (_newCustomer != value)
            {
                _newCustomer = value;
                OnPropertyChanged();
            }
        }
    }
    
  4. Обновите метод CreateNewCustomerAsync , чтобы создать нового клиента, добавить его в репозиторий и задать в качестве выбранного клиента:

    public async Task CreateNewCustomerAsync()
    {
        CustomerViewModel newCustomer = new CustomerViewModel(new Models.Customer());
        NewCustomer = newCustomer;
        await App.Repository.Customers.UpsertAsync(NewCustomer.Model);
        AddingNewCustomer = true;
    }
    
  5. Обновите метод SaveInitialChangesAsync , чтобы добавить в репозиторий только что созданного клиента, обновить пользовательский интерфейс и закрыть панель.

    public async Task SaveInitialChangesAsync()
    {
        await App.Repository.Customers.UpsertAsync(NewCustomer.Model);
        await UpdateCustomersAsync();
        AddingNewCustomer = false;
    }
    
  6. Добавьте следующую строку кода в качестве последней строки в метод задания для AddNewCustomer:

    OnPropertyChanged(nameof(EnableCommandBar));
    

    Это обеспечит автоматическое обновление EnableCommandBar при каждом изменении AddNewCustomer .

Обновление пользовательского интерфейса

  1. Вернитесь к Views\CustomerListPage.xaml и добавьте StackPanel со следующими свойствами между commandBar и сеткой данных:

     <StackPanel
         x:Name="newCustomerPanel"
         Orientation="Horizontal"
         x:Load="{x:Bind ViewModel.AddingNewCustomer, Mode=OneWay}"
         RelativePanel.Below="mainCommandBar">
     </StackPanel>
    

    Атрибут x:Load гарантирует, что эта панель отображается только при добавлении нового клиента.

  2. Внесите следующее изменение в положение сетки данных, чтобы убедиться, что она перемещается вниз при появлении новой панели:

    RelativePanel.Below="newCustomerPanel"
    
  3. Обновите панель стека, используя четыре элемента управления TextBox . Они привязываются к отдельным свойствам нового клиента и позволяют изменять его значения, прежде чем добавлять их в сетку данных.

    <StackPanel
         x:Name="newCustomerPanel"
         Orientation="Horizontal"
         x:Load="{x:Bind ViewModel.AddingNewCustomer, Mode=OneWay}"
         RelativePanel.Below="mainCommandBar">
         <TextBox
             Header="First name"
             PlaceholderText="First"
             Margin="8,8,16,8"
             MinWidth="120"
             Text="{x:Bind ViewModel.NewCustomer.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
         <TextBox
             Header="Last name"
             PlaceholderText="Last"
             Margin="0,8,16,8"
             MinWidth="120"
             Text="{x:Bind ViewModel.NewCustomer.LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
         <TextBox
             Header="Address"
             PlaceholderText="1234 Address St, Redmond WA 00000"
             Margin="0,8,16,8"
             MinWidth="280"
             Text="{x:Bind ViewModel.NewCustomer.Address, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
         <TextBox
             Header="Company"
             PlaceholderText="Company"
             Margin="0,8,16,8"
             MinWidth="120"
             Text="{x:Bind ViewModel.NewCustomer.Company, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </StackPanel>
    
  4. Добавьте простую кнопку на панель нового стека, чтобы сохранить только что созданного клиента:

    <StackPanel>
        <!--Text boxes from step 3-->
        <AppBarButton
            x:Name="SaveNewCustomer"
            Click="{x:Bind ViewModel.SaveInitialChangesAsync}"
            Icon="Save"/>
    </StackPanel>
    
  5. Обновите commandBar, чтобы обычные кнопки создания, удаления и обновления были отключены при отображении панели стека:

    <CommandBar
        x:Name="mainCommandBar"
        HorizontalAlignment="Stretch"
        IsEnabled="{x:Bind ViewModel.EnableCommandBar, Mode=OneWay}"
        Background="AliceBlue">
        <!--App bar buttons-->
    </CommandBar>
    
  6. Запустите приложение. Теперь вы можете создать клиента и ввести его данные на панели стека.

Создание нового клиента

Часть 7. Удаление клиента

Удаление клиента — это последняя базовая операция, которую необходимо реализовать. При удалении клиента, выбранного в сетке данных, необходимо немедленно вызвать UpdateCustomersAsync , чтобы обновить пользовательский интерфейс. Однако вам не нужно вызывать этот метод, если вы удаляете только что созданного клиента.

  1. Перейдите в файл ViewModels\CustomerListPageViewModel.cs и обновите метод DeleteAndUpdateAsync :

    public async void DeleteAndUpdateAsync()
    {
        if (SelectedCustomer != null)
        {
            await App.Repository.Customers.DeleteAsync(_selectedCustomer.Model.Id);
        }
        await UpdateCustomersAsync();
    }
    
  2. В файле Views\CustomerListPage.xaml обновите панель стека для добавления нового клиента, чтобы она содержала вторую кнопку:

     <StackPanel>
         <!--Text boxes for adding a new customer-->
         <AppBarButton
             x:Name="DeleteNewCustomer"
             Click="{x:Bind ViewModel.DeleteNewCustomerAsync}"
             Icon="Cancel"/>
         <AppBarButton
             x:Name="SaveNewCustomer"
             Click="{x:Bind ViewModel.SaveInitialChangesAsync}"
             Icon="Save"/>
     </StackPanel>
    
  3. В файле ViewModels\CustomerListPageViewModel.cs обновите метод DeleteNewCustomerAsync , чтобы удалить нового клиента:

    public async Task DeleteNewCustomerAsync()
    {
        if (NewCustomer != null)
        {
            await App.Repository.Customers.DeleteAsync(_newCustomer.Model.Id);
            AddingNewCustomer = false;
        }
    }
    
  4. Запустите приложение. Теперь вы можете удалять клиентов в сетке данных или на панели стека.

Удаление нового клиента

Заключение

Поздравляем! После всего этого ваше приложение теперь имеет полный спектр операций с локальной базой данных. Вы можете создавать, читать, обновлять и удалять клиентов в пользовательском интерфейсе. Эти изменения сохраняются в базе данных и будут сохраняться при различных запусках приложения.

Теперь, когда все готово, рассмотрим следующее:

Или если вы готовы к вызову, вы можете продолжить...

Дальнейшие действия. Подключение к удаленной базе данных

Мы предоставили пошаговое руководство по реализации этих вызовов к локальной базе данных SQLite. Но что делать, если вместо этого вы хотите использовать удаленную базу данных?

Если вы хотите попробовать, вам потребуется собственная учетная запись Azure Active Directory (AAD) и возможность размещения собственного источника данных.

Вам потребуется добавить проверку подлинности, функции для обработки вызовов REST, а затем создать удаленную базу данных для взаимодействия. В полном примере базы данных заказов клиентов есть код, на который можно ссылаться для каждой необходимой операции.

Параметры и конфигурация

Действия, необходимые для подключения к собственной удаленной базе данных, описаны в файле сведений примера. Вам понадобится выполнить следующие процедуры.

  • Укажите идентификатор клиента учетной записи Azure в Файле Constants.cs.
  • Укажите URL-адрес удаленной базы данных в Файле Constants.cs.
  • Укажите строку подключения для базы данных к Файлу Constants.cs.
  • Свяжите приложение с Microsoft Store.
  • Скопируйте проект службы в приложение и разверните его в Azure.

Проверка подлинности

Вам потребуется создать кнопку для запуска последовательности проверки подлинности, а также всплывающее окно или отдельную страницу для сбора сведений о пользователе. После создания необходимо предоставить код, который запрашивает сведения о пользователе и использует их для получения маркера доступа. Пример базы данных заказов клиентов заключает вызовы Microsoft Graph в библиотеку WebAccountManager для получения маркера и обработки проверки подлинности в учетной записи AAD.

Вызовы REST

Вам не нужно изменять код, добавленный в этом руководстве, для реализации вызовов REST. Вместо этого вам потребуется выполнить следующие действия.

  • Создайте новые реализации интерфейсов ICustomerRepository и ITutorialRepository , реализуя тот же набор функций с помощью REST вместо SQLite. Вам потребуется сериализовать и десериализовать JSON и при необходимости можно заключить вызовы REST в отдельный класс HttpHelper . Подробные сведения см. в полном примере .
  • В Файле App.xaml.cs создайте новую функцию для инициализации репозитория REST и вызовите ее вместо SqliteDatabase при инициализации приложения. Опять же, ознакомьтесь с полным примером.

После выполнения всех трех этих действий вы сможете пройти проверку подлинности в учетной записи AAD с помощью приложения. Вызовы REST к удаленной базе данных заменяют локальные вызовы SQLite, но взаимодействие с пользователем должно быть таким же. Если вы чувствуете себя еще более амбициозным, вы можете добавить страницу параметров, чтобы позволить пользователю динамически переключаться между ними.