среда, 29 июня 2011 г.

Разработка бизнес приложения на silverlight –Часть 4 (создание и работа с DataForm)



С выходом третьей версии Сильверлайта обновился и Silverlight Toolkit. Добавились новые элементы управления. Одним из них является DataForm. О нём и рассказывается в этой статье. Элемент управления DataForm позволяет делать практически всё тоже, что и DataGrid, только в отличие от последнего, DataForm нацелен на работу с одной записью данных, а не с набором. Хотя и позволяет производить навигацию по набору данных, добавлять, удалять, просматривать и редактировать записи. При этом в каждый момент времени отображается только одна запись данных. В статье речь идёт о том, как работать с формами данных (DataForm). Умелое использование этого контрола сэкономит вам массу времени при разработке формы для отображения, редактирования, добавления и удаления данных. DataForm умеет автоматически генерировать необходимые поля формы на основе открытых свойств вашего класса данных, а также контролировать вводимые значения по различным критериям. При этом вам не придется писать ни строчки кода логики, достаточно задать необходимые атрибуты и DataForm выполнит за вас всю рутинную работу.

Давайте перейдём к нашему бизнес приложению. Воспользуемся контролом DataForm для простого отображения информации о здании или этаже здания. Компонент DataForm не входит в состав стандартных библиотек Silverlight, а является частью дополнительных компонентов Silverlight Toolkit (если у вас еще не установлен Silverlight Toolkit, скачайте его по ссылке http://www.codeplex.com/Silverlight и выполните установку). Итак, сначала подключим к нашему проекту библиотеку System.Windows.Controls.Data.DataForm.Toolkit.dll в которой находится нужный нам контрол (для этого в панели Solution Explorer в текущем проекте EmailBook выбираем References->Add Reference и из открывшегося списка выбираем требуемую библиотеку).

Чтобы DataForm был виден в XAML, нужно подключить соответствующее пространство имен и назначить ему некоторое имя, например, toolkit:
xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
Теперь опишем сам DataForm.

        <appControls:CustomDataForm CurrentItem="{Binding ElementName=buildingDataGrid, Path=SelectedItem, Mode=TwoWay}"
                                            x:Name="dfBuilding"
                                            CommandButtonsVisibility="All"
                                            AutoEdit="False"
                                            AutoCommit="False"
                                            AutoGenerateFields="True"
                                            ItemsSource="{Binding ElementName=buildingDomainDataSource, Path=Data}"
                                            EditEnded="dfBuilding_EditEnded"
                                            DeletingItem="dfBuilding_DeletingItem"
                                            BeginningEdit="dfBuilding_BeginningEdit"
                                            Grid.Row="1" Grid.Column="1"/>

Это для отображения информации о здании.

        <appControls:CustomDataForm CurrentItem="{Binding ElementName=floorDataGrid, Path=SelectedItem, Mode=TwoWay}"
                                            x:Name="dfFloor"
                                            CommandButtonsVisibility="All"
                                            AutoEdit="False"
                                            AutoCommit="False"
                                            AutoGenerateFields="True"
                                            ItemsSource="{Binding ElementName=floorDomainDataSource, Path=Data}"
                                            EditEnded="dfFloor_EditEnded"
                                            ContentLoaded="dfFloor_ContentLoaded"
                                            DeletingItem="dfFloor_DeletingItem"
                                            BeginningEdit="dfFloor_BeginningEdit"
                                            EditEnding="dfFloor_EditEnding"
                                            Grid.Row="3" Grid.Column="1" />


А это для отображения информации об этаже здания. Как вы видите никакие поля модно не описывать в датаформе. Она сгенерирует их автоматически. По каким же данным она генерирует поля? Мы привязали датаформу к  выбранному элементу у DataGrid:
CurrentItem="{Binding ElementName=floorDataGrid, Path=SelectedItem, Mode=TwoWay}
А грид в свою очередь привязан к коллекции Building – ов. Таким образом, датаформа знает что отображать. Давайте теперь посмотрим на описание класса Building.

namespace BuildingApp.Web.DataAccess
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Data.Objects.DataClasses;
    using System.Linq;
    using System.ServiceModel.DomainServices.Hosting;
    using System.ServiceModel.DomainServices.Server;


    // The MetadataTypeAttribute identifies BuildingMetadata as the class
    // that carries additional metadata for the Building class.
    [MetadataTypeAttribute(typeof(Building.BuildingMetadata))]
    public partial class Building
    {

        internal sealed class BuildingMetadata
        {

            // Metadata classes are not meant to be instantiated.
            private BuildingMetadata()
            {
            }
            [Required]
            [StringLength(500)]
            [Display(Order = 1, Name = "Адрес")]
            public string Address { get; set; }

            [Display(Order = 3, Name = "Дата постройки")]
            public Nullable<DateTime> CreationDate { get; set; }
           
            [Display(AutoGenerateField = false)]
            public EntityCollection<Floor> Floor { get; set; }

            [Display(Order = 2, Name = "Этажность")]
            public Nullable<int> FloorCount { get; set; }
           
            [Display(AutoGenerateField = false)]
            public int ID { get; set; }
           
            [Display(Order = 7, Name = "Макс.арендная ставка (руб.)")]
            public Nullable<int> MaxRentalRate { get; set; }

            [Display(Order = 6, Name = "Мин.арендная ставка (руб.)")]
            public Nullable<int> MinRentalRate { get; set; }
           
            [Required]
            [StringLength(200)]
            [Display(Order = 0, Name = "Название")]
            public string Name { get; set; }
           
            [Display(Order = 5, Name = "Общее кол-во помещений")]
            public Nullable<int> Room { get; set; }

            [Display(Order = 8, Name = "Кол-во арендаторов")]
            public Nullable<int> TenantsCount { get; set; }

            [Display(Order = 4, Name = "Общая площадь (кв.м)")]
            public Nullable<int> TotalArea { get; set; }
        }
    }

    // The MetadataTypeAttribute identifies FloorMetadata as the class
    // that carries additional metadata for the Floor class.
    [MetadataTypeAttribute(typeof(Floor.FloorMetadata))]
    public partial class Floor
    {

        internal sealed class FloorMetadata
        {

            // Metadata classes are not meant to be instantiated.
            private FloorMetadata()
            {
            }
            [Display(Order = 1, Name = "Общая площадь (кв.м)")]
            public Nullable<int> Area { get; set; }
          
            [Display(AutoGenerateField = false)]
            public Building Building { get; set; }
           
            [Display(AutoGenerateField = false)]
            public int BuildingID { get; set; }

            [Required]
            [Editable(false, AllowInitialValue=true)]
            [Display(Order = 0, Name = "Номер этажа")]
            public int Number { get; set; }

            [Display(Order = 2, Name = "Кол-во арендаторов")]
            public Nullable<int> TenantsCount { get; set; }
        }
    }
}



Здесь как раз и указаны атрибуты, которые использует датаформа. С помощью них можно изменить некоторые параметры отображения данных в форме. Например, определить порядок отображения полей в форме (в Silverlight приложении все поля просто будут отсортированы по алфавиту (не очень жизненный сценарий), изменить заголовки полей, указать допустимые значения или обязательность поля (для того чтобы датаформа могла провалидировать поле).

Далее хочу обратить внимание, что я установила  AutoEdit="False" и AutoCommit="False"и написала свои обработчики на эти события в cs файле, которые сразу же сохраняют введёные данные в базу и ещё описывают специфическую для нашего приложения логику, такую например как вычисление номера этажа или скрытие ненужных кнопок. Ознакомится подробнее можно ниже.

        private void dfBuilding_EditEnded(object sender, DataFormEditEndedEventArgs e)
        {
            dfBuilding.CommitEdit();
            buildingDomainDataSource.SubmitChanges();
            dfBuilding.CommandButtonsVisibility = DataFormCommandButtonsVisibility.All;
        }
       
        private void dfBuilding_DeletingItem(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (MessageBox.Show("Удалить выбранное здание?", "Предупреждение", MessageBoxButton.OKCancel) == MessageBoxResult.Cancel)
            {
                e.Cancel = true;
                return;
            }
            buildingDomainDataSource.DataView.Remove(dfBuilding.CurrentItem);
            buildingDomainDataSource.SubmitChanges();

            e.Cancel = true;
        }

        private void dfBuilding_BeginningEdit(object sender, System.ComponentModel.CancelEventArgs e)
        {
            dfBuilding.CommandButtonsVisibility = DataFormCommandButtonsVisibility.Navigation | DataFormCommandButtonsVisibility.Cancel | DataFormCommandButtonsVisibility.Edit | DataFormCommandButtonsVisibility.Commit;
        }

        private void dfFloor_EditEnded(object sender, DataFormEditEndedEventArgs e)
        {
            dfFloor.CommitEdit();
            floorDomainDataSource.SubmitChanges();
            dfFloor.CommandButtonsVisibility = DataFormCommandButtonsVisibility.All;
        }

        private void dfFloor_ContentLoaded(object sender, DataFormContentLoadEventArgs e)
        {
            //Вычисление и установка номера этажа по умолчанию (при добавлении нового этажа)
            if (e.Mode == DataFormMode.AddNew)
            {
                var floor = (dfFloor.CurrentItem as Floor);
                floor.BuildingID = (buildingDataGrid.SelectedItem as Building).ID;

                floor.Number = dfFloor.ItemsSource
                    .OfType<Floor>()
                    .Where(s => s != floor)
                    .Select(s => s.Number)
                    .OrderByDescending(i => i)
                    .FirstOrDefault() + 1;
            }
        }

        private void dfFloor_DeletingItem(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (MessageBox.Show("Удалить выбранный этаж?", "Предупреждение", MessageBoxButton.OKCancel) == MessageBoxResult.Cancel)
            {
                e.Cancel = true;
                return;
            }
            floorDomainDataSource.DataView.Remove(dfFloor.CurrentItem);
            floorDomainDataSource.SubmitChanges();
            e.Cancel = true;
        }

        private void dfFloor_EditEnding(object sender, DataFormEditEndingEventArgs e)
        {
            if (e.EditAction == DataFormEditAction.Cancel)
            {
                return;
            }
            var currentFloor = dfFloor.CurrentItem as Floor;
            bool isNewFloor = currentFloor.EntityState == System.ServiceModel.DomainServices.Client.EntityState.New;
            if(!isNewFloor)
            {
                return;
            }

            foreach (Floor floor in dfFloor.ItemsSource)
            {
                if (floor != currentFloor && floor.Number == currentFloor.Number)
                {
                    MessageBox.Show("Этаж с таким номером уже существует.");
                    e.Cancel = true;
                    return;
                }
            }
        }

        private void dfFloor_BeginningEdit(object sender, System.ComponentModel.CancelEventArgs e)
        {
            dfFloor.CommandButtonsVisibility = DataFormCommandButtonsVisibility.Navigation | DataFormCommandButtonsVisibility.Cancel | DataFormCommandButtonsVisibility.Edit | DataFormCommandButtonsVisibility.Commit;
        }

.
Наконец, то всё завершено, и мы можем полюбоваться результатами нашего труда. Вот что получилось у меня:

4 комментария:

  1. Добрый день, есть возможность получить исходный проект?

    ОтветитьУдалить
    Ответы
    1. Да конечно, вот ссылка на исходный проект
      http://narod.ru/disk/42386563001.8cbf19160f5a965eb06ad95346fa0899/BuildingApp.rar.html

      Удалить
  2. Пере залейте пожалуйста ссылку на исходник!(((((

    ОтветитьУдалить
  3. Пере залейте пожалуйста ссылку на исходник!((((( ОЧЕНЬ НАДО!

    ОтветитьУдалить