نمونه کد: دارد
pdf:فعلا ندارد
مقدمه
یک نمونه برنامه ساده با استفاده از الگوی طراحی MVVM و Entity Framework در WPF برای کار با دیتابیس، که اعمال Select, Insert, Update و Delete را با استفاده از ساختار های الگوی MVVM انجام می دهد.
در صورتی که با الگوی MVVM آشنا نیستید مطالبی را که جناب آقای نصیری که در سایت شخصیشون در مورد MVVM قرار داده اند، مطالعه نمایید.
توضیحات
شمای کلی برنامه بصورت زیر است که نمایانگر لایه های مختلف MVVM می باشد.
لایه DataAccess:
شامل فایل دیتابیس است که یک جدول به نام Employee، دارای دو فیلد نام و ID می باشد. در این لایه می توان از انواع data source ها نظیر فایل های xml و ... نیز استفاده نمود.
البته توجه داشته باشید که چنین لایه ای در الگوی MVVM نامبرده نشده است اما برای شفافیت بیشتر کدنویسی معمولا برنامه نویسانی که با MVVM سر و کار دارند، چنین اقدامی می کنند!
لایه Model:
در این لایه باید مدلی از داده ای که در DataAccess وجود دارد، ایجاد کنیم. با استفاده از Entity Framework، یک Entity Data Model خواهیم داشت که به عنوان مدلی آماده از لایه DataAccess می توان از آن نام برد. زیرا کدهای مورد نیاز برای ایجاد کلاس های Model را بصورت خودکار تولید می کند.
[assembly: EdmSchemaAttribute()]
namespace MVVM_EntityFramework.Model
{
#region Contexts
/// <summary>
/// No Metadata Documentation available.
/// </summary>
public partial class EmployeeRepository : ObjectContext
{
#region Constructors
/// <summary>
/// Initializes a new EmployeeRepository object using the connection string found in the 'EmployeeRepository' section of the application configuration file.
/// </summary>
public EmployeeRepository() : base("name=EmployeeRepository", "EmployeeRepository")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
/// <summary>
/// Initialize a new EmployeeRepository object.
/// </summary>
public EmployeeRepository(string connectionString) : base(connectionString, "EmployeeRepository")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
/// <summary>
/// Initialize a new EmployeeRepository object.
/// </summary>
public EmployeeRepository(EntityConnection connection) : base(connection, "EmployeeRepository")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
#endregion
#region Partial Methods
partial void OnContextCreated();
#endregion
#region ObjectSet Properties
/// <summary>
/// No Metadata Documentation available.
/// </summary>
public ObjectSet<Employee> Employees
{
get
{
if ((_Employees == null))
{
_Employees = base.CreateObjectSet<Employee>("Employees");
}
return _Employees;
}
}
private ObjectSet<Employee> _Employees;
#endregion
#region AddTo Methods
/// <summary>
/// Deprecated Method for adding a new object to the Employees EntitySet. Consider using the .Add method of the associated ObjectSet<T> property instead.
/// </summary>
public void AddToEmployees(Employee employee)
{
base.AddObject("Employees", employee);
}
#endregion
}
#endregion
#region Entities
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmEntityTypeAttribute(NamespaceName="dbModel", Name="Employee")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class Employee : EntityObject
{
#region Factory Method
/// <summary>
/// Create a new Employee object.
/// </summary>
/// <param name="id">Initial value of the id property.</param>
public static Employee CreateEmployee(global::System.Int32 id)
{
Employee employee = new Employee();
employee.id = id;
return employee;
}
#endregion
#region Primitive Properties
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false , IsNullable=true)]
[DataMemberAttribute()]
public global::System.String Name
{
get
{
return _Name;
}
set
{
OnNameChanging(value);
ReportPropertyChanging("Name");
_Name = StructuralObject.SetValidValue(value, true);
ReportPropertyChanged("Name");
OnNameChanged();
}
}
private global::System.String _Name;
partial void OnNameChanging(global::System.String value);
partial void OnNameChanged();
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Int32 id
{
get
{
return _id;
}
set
{
if (_id != value)
{
OnidChanging(value);
ReportPropertyChanging("id");
_id = StructuralObject.SetValidValue(value);
ReportPropertyChanged("id");
OnidChanged();
}
}
}
private global::System.Int32 _id;
partial void OnidChanging(global::System.Int32 value);
partial void OnidChanged();
#endregion
}
#endregion
}
لایه View:
برای View یک user control به نام EmployeeListView داریم که دارای یک DataGrid برای نمایش اطلاعات مشتریان، دو TextBox برای ورود اطلاعات و سه Button برای اعمال Insert و Update و Delete است. برای این کار از Command استفاده شده است.
برای کد XAML آن به این شکل است: (به Binding ها دقت کنید)
<UserControl x:Class="MVVM_EntityFramework.View.EmployeeListVie w"
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="300">
<Grid>
<Grid.InputBindings>
<KeyBinding Key="I" Modifiers="Control" Command="{Binding InsertCommand}"/>
<KeyBinding Key="U" Modifiers="Control" Command="{Binding UpdateCommand}"/>
<KeyBinding Key="D" Modifiers="Control" Command="{Binding DeleteCommand}"/>
</Grid.InputBindings>
<StackPanel Margin="5" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<DataGrid ItemsSource="{Binding AllEmployees}" AutoGenerateColumns="False" Margin="5" AlternatingRowBackground="#FFD2CECE" Background="{x:Null}" HorizontalGridLinesBrush="#FFC6C6C6" VerticalGridLinesBrush="#FFC6C6C6" BorderBrush="#FFA3A3A3">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="ID">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding id}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<StackPanel Orientation="Horizontal" Margin="5" DataContext="{Binding NewEmployee}">
<TextBlock Text="Name:" Margin="5"/>
<TextBox Width="70" Margin="5" Text="{Binding Path=Name, Mode=OneWayToSource}"/>
<TextBlock Text="ID:" Margin="5"/>
<TextBox Width="70" Margin="5" Text="{Binding Path=id, Mode=OneWayToSource}"/>
</StackPanel>
<StackPanel Margin="5" Orientation="Horizontal">
<Button Content="Insert" Command="{Binding InsertCommand}" Margin="5" Width="50"/>
<Button Content="Update" Command="{Binding UpdateCommand}" Margin="5" Width="50"/>
<Button Content="Delete" Command="{Binding DeleteCommand}" Margin="5" Width="50"/>
</StackPanel>
<TextBlock Margin="10" Foreground="DarkGray" Text="Shortcut Keys: Insert = Ctrl+I , Update = Ctrl+U , Delete = Ctrl+D" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</UserControl>
لایه ViewModel:
بطور کلی برای هر View یک ViewModel باید باشد تا منطق آن را تولید نماید.
در اینجا ما دو View داریم، user control و main wondow.
کار main window نمایش View های دیگر در داخل خود است به همین دلیل جزو لایه View محسوب نمی شود. اما برای کنترل منطق آن باید یک ViewModel برای آن نیز داشته باشیم تا بتوانیم قابلیت نمایش View های مختلف در داخل آن را بوجود بیاوریم.
کلاس ViewModelBase را نیز برای داشتن یک چارجوب کلی برای تمام ViewModel ایجاد می کنیم و همه ViewModel های موجود از آن ارث خواهند برد.
ViewModelBase
namespace MVVM_EntityFramework.ViewModel
{
public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
{
protected ViewModelBase()
{
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
public void Dispose()
{
this.OnDispose();
}
protected virtual void OnDispose()
{
}
}
}
MainWindowViewModel
namespace MVVM_EntityFramework.ViewModel
{
public class MainWindowViewModel : ViewModelBase
{
readonly EmployeeRepository _employeeRepository;
ObservableCollection<ViewModelBase> _viewModels;
public MainWindowViewModel()
{
_employeeRepository = new EmployeeRepository();
EmployeeListViewModel viewModel = new EmployeeListViewModel(_employeeRepository);
this.ViewModels.Add(viewModel);
}
public ObservableCollection<ViewModelBase> ViewModels
{
get
{
if (_viewModels == null)
{
_viewModels = new ObservableCollection<ViewModelBase>();
}
return _viewModels;
}
}
}
}
EmployeeListViewModel
به تعاریف Command ها توجه نمایید.
namespace MVVM_EntityFramework.ViewModel
{
class EmployeeListViewModel : ViewModelBase
{
readonly EmployeeRepository _employeeRepository;
RelayCommand _insertCommand;
RelayCommand _updateCommand;
RelayCommand _deleteCommand;
public Employee NewEmployee { get; set; }
public ObservableCollection<Employee> AllEmployees
{
get;
private set;
}
public EmployeeListViewModel(EmployeeRepository employeeRepository)
{
if (NewEmployee == null)
NewEmployee = new Employee();
if (employeeRepository == null)
{
throw new ArgumentNullException("employeeRepository");
}
_employeeRepository = employeeRepository;
var tmp = from o in employeeRepository.Employees
select o;
this.AllEmployees = new ObservableCollection<Employee>(tmp.ToList());
}
protected override void OnDispose()
{
this.AllEmployees.Clear();
}
public ICommand InsertCommand
{
get
{
if (_insertCommand == null)
{
_insertCommand = new RelayCommand(param => this.InsertCommandExecute(), param => this.InsertCommandCanExecute);
}
return _insertCommand;
}
}
public ICommand UpdateCommand
{
get
{
if (_updateCommand == null)
{
_updateCommand = new RelayCommand(param => this.UpdateCommandExecute(), param => this.UpdateCommandCanExecute);
}
return _updateCommand;
}
}
public ICommand DeleteCommand
{
get
{
if (_deleteCommand == null)
{
_deleteCommand = new RelayCommand(param => this.DeleteCommandExecute(), param => this.DeleteCommandCanExecute);
}
return _deleteCommand;
}
}
void InsertCommandExecute()
{
Employee emp = Employee.CreateEmployee(NewEmployee.id);
emp.Name = NewEmployee.Name;
_employeeRepository.Employees.AddObject(emp);
_employeeRepository.SaveChanges();
var tmp = from o in _employeeRepository.Employees
select o;
this.AllEmployees = new ObservableCollection<Employee>(tmp.ToList());
OnPropertyChanged("AllEmployees");
}
bool InsertCommandCanExecute
{
get
{
var tmp = from o in _employeeRepository.Employees
where o.id == NewEmployee.id
select o;
if (tmp != null && tmp.Count() > 0)
{
return false;
}
return true;
}
}
void UpdateCommandExecute()
{
var tmp = from o in _employeeRepository.Employees
where o.id == NewEmployee.id
select o;
tmp.First().Name = NewEmployee.Name;
_employeeRepository.SaveChanges();
var tmp2 = from o in _employeeRepository.Employees
select o;
this.AllEmployees = new ObservableCollection<Employee>(tmp2.ToList());
OnPropertyChanged("AllEmployees");
}
bool UpdateCommandCanExecute
{
get
{
var tmp = from o in _employeeRepository.Employees
where o.id == NewEmployee.id
select o;
if (tmp != null && tmp.Count() > 0)
{
return true;
}
return false;
}
}
void DeleteCommandExecute()
{
var tmp = _employeeRepository.Employees.Single(o => o.id == NewEmployee.id);
_employeeRepository.Employees.DeleteObject(tmp);
_employeeRepository.SaveChanges();
var tmp2 = from o in _employeeRepository.Employees
select o;
this.AllEmployees = new ObservableCollection<Employee>(tmp2.ToList());
OnPropertyChanged("AllEmployees");
}
bool DeleteCommandCanExecute
{
get
{
var tmp = from o in _employeeRepository.Employees
where o.id == NewEmployee.id
select o;
if (tmp != null && tmp.Count() > 0)
{
return true;
}
return false;
}
}
}
}
نکته :
فایل App.cs را بصورت زیر تغییر می دهیم تا در زمان لود برنامه، محتوی main window را مشخص کنیم. main window قابلیت نمایش هر ViewModel ای را دارد و هر ViewModel دارای یک View است که در نهایت آن View دز main window نمایش داده می شود.
namespace MVVM_EntityFramework
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow window = new MainWindow();
var viewModel = new MainWindowViewModel();
window.DataContext = viewModel;
window.Show();
}
}
}
دقت کنید که در بخش XAML کلاس App قسمت مربوط به StaurtupUri را حذف کنید.
و در آخر، کد XAML مربوط به MainWindow به شکل زیر خواهد بود:
(به Resource های آن و Binding مربوط به ItemsSource دقت نمایید)
<Window x:Class="MVVM_EntityFramework.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MVVM_EntityFramework.ViewModel"
xmlns:vw="clr-namespace:MVVM_EntityFramework.View"
Title="MainWindow" Height="350" Width="525" Background="#FFBCB4B4">
<Window.Resources>
<DataTemplate DataType="{x:Type vm:EmployeeListViewModel}">
<vw:EmployeeListView />
</DataTemplate>
</Window.Resources>
<Grid Margin="4">
<Border BorderBrush="#FFA3A0A0" BorderThickness="1" CornerRadius="5">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFD6D2D2" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<ItemsControl ItemsSource="{Binding ViewModels}" Margin="4" />
</Border>
</Grid>
</Window>
نتیجه:
MainWindow را به گونه ای طراحی کردیم که قادر به نمایش دادن ViewModel های مختلف باشد. هر ViewModel نیز یک منطق است که داده ها را از Model (یا DataAccess) گرفته و با استفاده از یک View، در MainWindow نمایش می دهد. توجه داشته باشید که هیچ کدام از فایل های XAML، دارای کدنویسی در Code-behind خود نیستند (به جز کد های پیش فرض خودشان) و این امر سبب می شود به راحتی بتوانید View های خود را به دلخواه و بدون نگرانی از به هم ریختن کدها تغییر دهید. تنها کار لازم، اعمال Binding ها در کد XAML است!
برای پرسش، بحث و تبادل نظر به لینک زیر مراجعه کنید:
https://barnamenevis.org/showthread.php?t=196333







پاسخ با نقل قول