نحوه استفاده از کنترل CRUD در WPF

جمعه 19 شهریور 1395

در این مقاله ، به معرفی یک کنترل مفید در تکنولوژی WPF به نام WPF CrudControl و نحوه به کار گیری آن در این تکنولوژی خواهیم پرداخت. همچنین برای فهم بیشتر این کنترل، نحوه پیاده سازی آن در یک برنامه نشان داده شده است.

نحوه استفاده از کنترل CRUD در WPF

مقدمه

این مقاله، یک راهنمایی گام به گام برای استفاده از WPF CrudControl است که یک generic CRUD control است که بر اساس الگوی MVVM پیاده سازی شده است. این کنترل، هم بخش منطقی برنامه و هم بخش UI را در بر می گیرد و به این ترتیب ، کنترل کاملی بر اجزا و عملکرد شان به شما می دهد.

شرح مشکل    

مشکلی که در این مقاله می خواهیم به حل آن بپردازیم، نحوه توسعه ی یک کنترل است که تمامی عملیات CRUD  را برای ما انجام بدهد و همچنین دوباره قابل استفاده باشد و همچنین یک ماژول مدیریت داده ها نیز محسوب شود.

شروع به کار

نصب با استفاده از Nuget 

WPF CrudControl بر روی Nuget  در دسترس است ، شما می توانید آن را با استفاده از nuget manager نصب کنید و یا تکه کد زیر را در package manager console  وارد کرده و اجرا کنید.

PM> Install-Package WPFCRUDControl

 

Assembly های مربوط به WPF CrudControl

Dll های زیر را به پروژه خود اضافه کنید:

 

    GenericCodes.Core.dll

    GenericCodes.CRUD.WPF.dll         

 

آدرس های resource dictionary های زیر را به فایل App.xaml اضافه کنید:

<ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/GenericCodes.CRUD.WPF;component/Resources/CRUDResources.xaml"/>
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

و یا بسته WPF CrudControl را با استفاده از NuGet نصب کنید، با این کار، به صورت خودکار  Assembly های مربوطه اضافه خواهند شد و تغییرات لازم در فایل App.xaml به وجود خواهند آمد.

Supplier Lookup

حالا ما به صورت گام به گام یک Supplier Lookup (فرم جستجوی تهیه کنندگان) ایجاد خواهیم کرد. ما از پایگاه داده Northwind  در این مقاله استفاده خواهیم کرد.

گام اول

Entity Model :

مدل Supplier باید از کلاس Entity ارث بری کند.

شما باید برای همه ی Property های برنامه ، خاصیت property changed را در UI در نظر بگیرید، برای این کار باید از متد NotifyPropertyChanged() در کلاس Entity و یا Fody به روش زیر استفاده کنید.

[ImplementPropertyChanged]
    public partial class Supplier: Entity
    {
        public Supplier()
        {
            Products = new HashSet<Product>();
        }

        public int SupplierID { get; set; }

        [Required]
        [StringLength(40)]
        public string CompanyName { get; set; }

        [StringLength(24)]
        [Required]
        public string Phone { get; set; }
       ...
       ...
       ...
}

 

ایجاد Views  و  ViewModel های مربوط به Crud

در این گام، ما باید جستجوی Supplier ، اضافه کردن و ویرایش به صورت popup و لیست کردن views  و  viewModel ها را به روش زیر پیاده سازی کنیم:

1-جستجوی Supplier

ابتدا SupplierSearchView.xaml را ایجاد خواهیم کرد که فیلد های مربوط به جستجو در UI را تشکیل می دهد:

<UserControl x:Class="Northwind.Demo.Views.Suppliers.SuppliersSearch"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="700">
            <Grid>
              <Grid.ColumnDefinitions>
                 <ColumnDefinition Width="Auto"/>
                 <ColumnDefinition Width="Auto" />
                 <ColumnDefinition Width="Auto"/>
                 <ColumnDefinition Width="*" />
              </Grid.ColumnDefinitions>
              <Grid.RowDefinitions>
                 <RowDefinition/>
                 <RowDefinition/>
              </Grid.RowDefinitions>
              <TextBlock Grid.Column="0"  Grid.Row="0" Text="Company Name" VerticalAlignment="Center" 
                         Margin="2,2,16,4"  />
              <TextBox   Grid.Column="1"  Grid.Row="0" Width="220" HorizontalAlignment="Left" 
                         VerticalAlignment="Center"  Margin="2,2,5,4" 
                         Text="{Binding CompanyNameFilter, Mode=TwoWay}" />
              <TextBlock Grid.Column="2"  Grid.Row="0" Text="Phone" VerticalAlignment="Center" 
                         Margin="50,2,16,4" />
              <TextBox   Grid.Column="3"  Grid.Row="0" Width="220" HorizontalAlignment="Left"
                         VerticalAlignment="Center"  Margin="2,2,5,4" 
                         Text="{Binding PhoneFilter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
...
...
...

           </Grid>
 </UserControl>

سپس ما مطابق روش زیر SupplierSearchViewModel.cs را ایجاد خواهیم کرد. این کلاس باید از کلاس abstract ای به نام SearchCriteriaBase<Supplier> ارث بری کند و باید دو متد اصلی زیر در آن پیاده سازی شود:

  GetSearchCriteria(): در این متد، شما معیار های مربوط به جستجو را تعریف خواهید کرد. در این مثال، ما Supplier را بر اساس نام شرکت و شماره تماس فیلتر خواهیم کرد. اگر فیلدهای مربوط به جستجو خالی بودند، کنترل، همه رکوردها را نمایش خواهد داد ، در غیر این صورت رکوردهای متناسب با کلمات موجود در قسمت جستجو را بر خواهد گرداند.

 ResetSearchCriteria()  : در این متد، مقادیر پیش فرض مربوط به Property های جستجو را تعریف خواهید کرد:

public class SuppliersSearchViewModel : SearchCriteriaBase<Supplier>
    {
        public SuppliersSearchViewModel()
            : base()
        {

        }

        #region Public Properties

        private string _companyNameFilter = string.Empty;
        public string CompanyNameFilter
        {
            get { return _companyNameFilter; }
            set { Set(() => CompanyNameFilter, ref _companyNameFilter, value); }
        }

        private string _phoneFilter = string.Empty;
        public string PhoneFilter
        {
            get { return _phoneFilter; }
            set { Set(() => PhoneFilter, ref _phoneFilter, value); }
        }

        // other Properties 
        ...
        ...
        ...
        #endregion
        public override System.Linq.Expressions.Expression<Func<Supplier, bool>> GetSearchCriteria()
        {
            return (supplier => (string.IsNullOrEmpty(_companyNameFilter) ||
                                 supplier.CompanyName.ToLower().Contains(_companyNameFilter.ToLower())) &&
                                (string.IsNullOrEmpty(_phoneFilter) || 
                                 supplier.Phone.ToLower().Contains(_phoneFilter.ToLower())));
        }

        public override void ResetSearchCriteria()
        {
            CompanyNameFilter = string.Empty;
            PhoneFilter = string.Empty;
        }
    }

2- popup های مربوط به ویرایش و اضافه کردن یک مورد

ابتدا ما یک SupplierAddEditView.xaml ایجاد خواهیم کرد که UI مربوط به همه فیلد ها را تعریف خواهد کرد:

<UserControl x:Class="Northwind.Demo.Views.Suppliers.SuppliersAddEdit"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             Height="210" Width="500">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="30"/>
        </Grid.ColumnDefinitions>

        <TextBlock Grid.Row="0"
                           Grid.Column="0"
                           VerticalAlignment="Center"
                           Margin="5,0,16,4"
                           Text="Company Name"/>
        <TextBox Grid.Row="0"
                         Grid.Column="1"
                         VerticalAlignment="Center"
                         Margin="0,0,5,4"
                         Text="{Binding Entity.CompanyName}"/>

        <TextBlock Grid.Row="1"
                           Grid.Column="0"
                           VerticalAlignment="Center"
                           Margin="5,0,16,4"
                           Text="Phone"/>
        <TextBox  Grid.Row="1"
                           Grid.Column="1"
                           VerticalAlignment="Center"
                           Margin="0,0,5,4"
                           Text="{Binding Entity.Phone}"/>
..
..
..
    </Grid>
</UserControl>

توجه : همه ی فیلد های UI مانند زیر به Entity.PropertyName متصل خواهند شد:

<TextBox Text="{Binding Entity.CompanyName}"/>

سپس ما SupplierAddEditViewModel.cs را مطابق زیر ایجاد خواهیم کرد. این کلاس باید از abstract class ای به نام AddEditEntityBase<Supplier> ارث بری کند که عملیات مربوط به ذخیره سازی ، تنظیم دوباره و اعتبارسنجی را مدیریت خواهد کرد:

public class SuppliersAddEditViewModel : AddEditEntityBase<Supplier>
    {
        public SuppliersAddEditViewModel()
            : base()
        {

        }
    }

3-لیست کردن Supplier

ابتدا ما SuppliersUC.xaml را ایجاد خواهیم کرد. در این فایل ما عملیات زیر را انجام خواهیم داد:

تعریف فضای نام crud

استفاده از یک استایل پیش فرض و یا سفارشی سازی استایل

نمایش و یا عدم نمایش برخی ویژگی ها مانند اضافه کردن یک مورد، جستجو و یا حذف

تعریف کردن ستون های مربوط به مرتب سازی با استفاده از مجموعه SortingProperties

تعریف لیستی از ستون ها با استفاده از مجموعه ی using Columns و ColumnType که مقدار پیش فرض آن TextColumn است.

تعریف View DataContext

<UserControl x:Class="Northwind.Demo.Views.Suppliers.SuppliersUC"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:crud="https://genericcodes.com/crud"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="1000"
             DataContext="{Binding Suppliers,Source={StaticResource Locator}}">
    <Grid>
        <Border Style="{DynamicResource BorderStyle}">
            <ScrollViewer Margin="5,10,0,10">
                <StackPanel MinWidth="200">
                    <crud:GenericCrudControl 
                          x:Name="SupplierCrudControl"
                          EnableSortingPaging="{Binding EnableSortingPaging,Mode=TwoWay}"
                          DataGridStyle="{StaticResource CRUDDataGridStyle}"
                          DataGridColumnHeaderStyle="{StaticResource customDataGridColHeader}"
                          DataGridRowHeaderStyle="{StaticResource customDataGridRowHeader}"
                          DataGridRowStyle="{StaticResource DataGridRowStyle}"
                          DataGridCellStyle="{StaticResource DataGridCellStyle}"
                          SearchGroupBoxStyle="{StaticResource SupplierSearchGroupBoxStyle}"
                          SearchButtonStyle="{StaticResource CRUDSearchButtonStyle}"
                          SortingByComboBoxStyle="{StaticResource SortingByComboBoxStyle}"
                          SortingByLabelStyle="{StaticResource SortingByLabelStyle}"
                          SortingTypeComboBoxStyle="{StaticResource SortingTypeComboBoxStyle}"
                          SortingTypeLabelStyle="{StaticResource SortingTypeLabelStyle}"
                          ResetButtonStyle="{StaticResource CRUDResetButtonStyle}"
                          AddButtonStyle="{StaticResource CRUDAddButtonStyle}"
                          DeleteButtonStyle="{StaticResource CRUDDeleteButtonStyle}"
                          PagerTotalRecordLabelStyle="{StaticResource TotalRecordLabelStyle}"
                          PagerPageSizeLabelStyle="{StaticResource PageSizeLabelStyle}"
                          PagerFirstPageBtnStyle="{StaticResource PagerFirstPageButtonStyle}"
                          PagerLastPageBtnStyle="{StaticResource PagerLastPageButtonStyle}"
                          PagerNextPageBtnStyle="{StaticResource PagerNextPageButtonStyle}"
                          PagerPreviousPageBtnStyle="{StaticResource PagerPreviousPageButtonStyle}"
                          PagerCurrentPageTextBoxStyle="{StaticResource PagerCurrentPageTextBoxStyle}"
                          PagerPageSizeComboBoxStyle="{StaticResource PagerPageSizeComboBoxStyle}">
                        <crud:GenericCrudControl.SortingProperties>
                            <crud:SortingProperty DisplayName="Company Name" PropertyPath="CompanyName"/>
                            <crud:SortingProperty DisplayName="City" PropertyPath="City"/>
                            <crud:SortingProperty DisplayName="Country" PropertyPath="Country"/>
                        </crud:GenericCrudControl.SortingProperties>
                        <crud:GenericCrudControl.Columns>
                            <crud:CustomDataGridColumn Header="Company Name" BindingExpression="CompanyName"/>
                            <crud:CustomDataGridColumn Header="Country" BindingExpression="Country" Width="*"/>
                            <crud:CustomDataGridColumn Header="Phone" ColumnType="TemplateColumn">
                              <crud:CustomDataGridColumn.DataGridColumnTemplate>
                                  <DataTemplate>
                                      <TextBlock Text="{Binding Phone}"></TextBlock>
                                  </DataTemplate>
                              </crud:CustomDataGridColumn.DataGridColumnTemplate>
                            </crud:CustomDataGridColumn>
                        </crud:GenericCrudControl.Columns>
                    </crud:GenericCrudControl>
                </StackPanel>
            </ScrollViewer>
        </Border>
    </Grid>
</UserControl>

سپس ما کلاس SuppliersViewModel.cs را ایجاد خواهیم کرد. این کلاس باید از abstarct class ای به نام GenericCrudBase<Supplier> ارث بری کند. constructor  این کلاس نیاز به شی های SuppliersSearchViewModel  و SuppliersAddEditViewModel دارد. این کار همه عملیات مربوط به CRUD را برای ما انجام خواهد داد. و همچنین شرایط خاص مربوط به اضافه کردن و حذف رکوردها و محدودیت های آن ها نیز در این فایل لحاظ می شود.

public class SuppliersViewModel : GenericCrudBase<Supplier>
{
    public SuppliersViewModel(SuppliersSearchViewModel supplierSearch,ISuppliersService supplierService,
                              SuppliersAddEditViewModel suppliersAddEdit)
        : base(supplierSearch, suppliersAddEdit)
    {           
        PostDataRetrievalDelegate = (list) =>
        {
            supplierService.UpdateCanSelect(list);
        };
    }
}

سپس ما یک resource dictionary به نام SuppliersTemplates.xaml ایجاد خواهیم کرد و دو عدد DataTemplate برای تعریف نحوه نمایش SupplierSearch  و  SupplierAddEdit قرار خواهیم داد. کد زیر را برای این کار قرار می دهیم :

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:supplierViewModels="clr-namespace:Northwind.Demo.ViewModel.Suppliers"
                    xmlns:supplierViews="clr-namespace:Northwind.Demo.Views.Suppliers">
    	
    <DataTemplate DataType="{x:Type supplierViewModels:SuppliersSearchViewModel}">
        <supplierViews:SuppliersSearch/>
    </DataTemplate>

    <DataTemplate DataType="{x:Type supplierViewModels:SuppliersAddEditViewModel}">
        <supplierViews:SuppliersAddEdit/>
    </DataTemplate>

</ResourceDictionary>

سپس شما باید آدرس این resource dictionary را در فایل App.xaml اضافه کنید:

<ResourceDictionary> 
 <ResourceDictionary.MergedDictionaries> 
   <ResourceDictionary Source="/GenericCodes.CRUD.WPF;component/Resources/CRUDResources.xaml"/>
   <ResourceDictionary Source="/Resources/SuppliersTemplates.xaml"/>
 </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

گام سوم :

IoC Container Registration  :

در این گام، شما باید بخش های DbContext, IUnitofWork, IRepository<T>, IDialogService  و  ViewModels را در درون container خودتان اضافه کنید. در برنامه ای که ما ساخته ایم، از Unity container استفاده کرده ایم :

public class ViewModelLocator
   {
       static ViewModelLocator()
       {

           IUnityContainer container = new UnityContainer();

           #region register DataContext & UnitOfWork
           Database.SetInitializer<NorthwindContext>
                                   (new System.Data.Entity.NullDatabaseInitializer<NorthwindContext>());

           container.RegisterType<ApplicationDbContext, NorthwindContext>(new PerThreadLifetimeManager());
           container.RegisterType<IUnitOfWork, UnitOfWork>();

           #endregion

           #region Register Repositories
           container.RegisterType<IRepository<Supplier>, Repository<Supplier>>();
           container.RegisterType<IRepository<Product>, Repository<Product>>();
           container.RegisterType<IRepository<Category>, Repository<Category>>();
           #endregion

           #region Register App Services
           container.RegisterType<IDialogService, DialogService>();
           #endregion

           #region Register Business Services
           container.RegisterType<ISuppliersService, SuppliersService>();
           container.RegisterType<IProductService, ProductService>();
           //container.RegisterType<IService<Category>, Service<Category>>();
           #endregion

           #region Register ViewModels

           container.RegisterType<MainViewModel>();

           container.RegisterType<SuppliersViewModel>();
           container.RegisterType<SuppliersSearchViewModel>();
           container.RegisterType<SuppliersAddEditViewModel>();

           container.RegisterType<ProductsListViewModel>();
           container.RegisterType<ProductsSearchViewModel>();
           container.RegisterType<ProductAddEditViewModel>();

           #endregion

           ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));
       }

       public SuppliersViewModel  Suppliers
       {
           get
           {
               return ServiceLocator.Current.GetInstance<SuppliersViewModel>();
           }
       }
   }

توجه : App DBContext شما باید از ApplicationDbContext ارث بری کند و همچنین DbContext باید به همراه یک LifetimeManager ایجاد شود .

container.RegisterType<ApplicationDbContext, NorthwindContext>(new PerThreadLifetimeManager());
public partial class NorthwindContext : ApplicationDbContext

    {

        public NorthwindContext()

            : base("name=NorthwindContext")

        {

        }

       public virtual DbSet<Supplier> Suppliers { get; set; }

..

..

..

}
فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان
  • WPF
  • 3k بازدید
  • 2 تشکر

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید