1. 程式人生 > 其它 >DevExpress WPF入門指南 - 執行時生成的POCO檢視模型(三)

DevExpress WPF入門指南 - 執行時生成的POCO檢視模型(三)

POCO(Plain Old CLR Objects)檢視模型簡化並加快了開發過程。

POCO 檢視模型允許您:

  • 將可繫結屬性定義為簡單的自動實現的屬性。
  • 建立在執行時用作命令的方法。
  • 使屬性和方法實現特定於 MVVM 的介面。

這允許您建立乾淨、簡單、可維護和可測試的 MVVM 程式碼,POCO 檢視模型與任何 WPF 控制元件完全相容。

您可以使用在編譯時生成的檢視模型在編譯時為您的檢視模型生成樣板程式碼。

DevExpress WPF v21.2正式版下載

Services

DevExpress MVVM框架包括Services機制,下面的程式碼示例演示瞭如何訪問 Message Box 服務。

C#

using DevExpress.Mvvm.POCO;
...
public class LoginViewModel {
public IMessageBoxService MessageBoxService { get { return this.GetService<IMessageBoxService>(); } }
}
依賴注入

要將檢視繫結到檢視模型,請建立解析正確 ViewModel 型別的 MarkupExtension:

C#

public class DISource : MarkupExtension {
public static Func<Type, object, string, object> Resolver { get; set; }

public Type Type { get; set; }
public object Key { get; set; }
public string Name { get; set; }

public override object ProvideValue(IServiceProvider serviceProvider) => Resolver?.Invoke(Type, Key, Name);
}

在應用程式啟動時註冊解析器:

C#

protected override void OnStartup(StartupEventArgs e) {
base.OnStartup(e);
DISource.Resolver = Resolve;
}
object Resolve(Type type, object key, string name) {
if(type == null)
return null;
if(key != null)
return Container.ResolveKeyed(key, type);
if(name != null)
return Container.ResolveNamed(name, type);
return Container.Resolve(type);
}

通過以下方式在 XAML 中指定 DataContext:

XAML

DataContext="{common:DISource Type=common:MainViewModel}"

要在依賴注入容器中使用 POCO 檢視模型,請利用 ViewModelSource.GetPOCOType 方法註冊在執行時生成的 POCO 型別:

C#

container.RegisterType(typeof(IMainViewModel),
ViewModelSource.GetPOCOType(typeof(MainViewModel)));
檢視模型父子關係

POCO 檢視模型可以通過父子關係相互關聯。 這是通過 ISupportParentViewModel 介面實現的,該介面在您使用 ViewModelSource 類建立 POCO 物件時自動實現。 通過這個介面,子檢視模型可以訪問在主檢視模型中註冊的服務。

自動 IDataErrorInfo 實現

IDataErrorInfo 介面是 WPF 中資料驗證的標準機制,您可以使用此介面為每個單獨的屬性或整個物件定義驗證規則。 POCO 機制允許您基於定義的屬性或 Fluent API 自動實現IDataErrorInfo 介面。

要啟用此功能,請為您的檢視模型應用 POCOViewModel 屬性並將 POCOViewModel.ImplementIDataErrorInfo 引數設定為 True。

C#

//Attribute-based approach
[POCOViewModel(ImplementIDataErrorInfo = true)]
public class LoginViewModel {
[Required(ErrorMessage = "Please enter the user name.")]
public virtual string UserName { get; set; }
}

//Fluent API
[POCOViewModel(ImplementIDataErrorInfo = true)]
[MetadataType(typeof(LoginViewModel.Metadata))]
public class LoginViewModel {
public class Metadata : IMetadataProvider<LoginViewModel> {
void IMetadataProvider<LoginViewModel>.BuildMetadata(MetadataBuilder<LoginViewModel> builder) {
builder.Property(x => x.UserName).
Required(() => "Please enter the user name.");
}
}
public virtual string UserName { get; set; }
}

當 ViewModelSource 生成 View Model 的後代時,它會實現 IDataErrorInfo 介面,如下所示:

C#

public class LoginViewModel : IDataErrorInfo {
...
string IDataErrorInfo.Error {
get { return string.Empty; }
}
string IDataErrorInfo.this[string columnName] {
get { return IDataErrorInfoHelper.GetErrorText(this, columnName); }
}
}

IDataErrorInfoHelper類允許您根據指定的DataAnnotation 屬性或 Fluent API 獲取錯誤。

下面的程式碼示例演示瞭如何使用 POCO 機制來實現 IDataErrorInfo 介面。

MainView.xaml

<UserControl x:Class="Example.View.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
xmlns:ViewModel="clr-namespace:Example.ViewModel"
mc:Ignorable="d" d:DesignHeight="400" d:DesignWidth="400"
DataContext="{dxmvvm:ViewModelSource Type=ViewModel:MainViewModel}">
<UserControl.Resources>
<dxmvvm:BooleanNegationConverter x:Key="BooleanNegationConverter"/>
</UserControl.Resources>

<Grid>
<StackPanel Orientation="Vertical" Margin="10" dxe:ValidationService.IsValidationContainer="True" x:Name="validationContainer">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" Margin="0,0,4,6">
<TextBlock Text="Name" Margin="6,2,0,2"/>
<dxe:TextEdit NullText="First" EditValue="{Binding FirstName, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="4,0,0,6" Grid.Column="1">
<TextBlock Text=" " Margin="6,2,0,2"/>
<dxe:TextEdit NullText="Last" EditValue="{Binding LastName, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>
<StackPanel Orientation="Vertical" Margin="0,0,0,6">
<TextBlock Text="Email" Margin="6,2,0,2"/>
<dxe:TextEdit EditValue="{Binding Email, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="0,0,0,6">
<TextBlock Text="Password" Margin="6,2,0,2"/>
<dxe:PasswordBoxEdit EditValue="{Binding Password, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="0,0,0,6">
<TextBlock Text="Confirm Password" Margin="6,2,0,2"/>
<dxe:PasswordBoxEdit EditValue="{Binding ConfirmPassword, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>

<Button VerticalAlignment="Top" Content="Sign Up" Width="150" HorizontalAlignment="Right" Margin="0,10"
IsEnabled="{Binding Path=(dxe:ValidationService.HasValidationError), ElementName=validationContainer, Converter={StaticResource BooleanNegationConverter}}"/>
</StackPanel>
</Grid>
</UserControl>

MainViewModel.cs

using DevExpress.Mvvm;
using DevExpress.Mvvm.DataAnnotations;
using System.Windows.Media;

namespace Example.ViewModel {
[POCOViewModel(ImplementIDataErrorInfo = true)]
public class MainViewModel : ViewModelBase {
static PropertyMetadataBuilder<MainViewModel, string> AddPasswordCheck(PropertyMetadataBuilder<MainViewModel, string> builder) {
return builder.MatchesInstanceRule((name, vm) => vm.Password == vm.ConfirmPassword, () => "The passwords don't match.")
.MinLength(8, () => "The password must be at least 8 characters long.")
.MaxLength(20, () => "The password must not exceed the length of 20.");
}
public static void BuildMetadata(MetadataBuilder<MainViewModel> builder) {
builder.Property(x => x.FirstName)
.Required(() => "Please enter the first name.");
builder.Property(x => x.LastName)
.Required(() => "Please enter the last name.");
builder.Property(x => x.Email)
.EmailAddressDataType(() => "Please enter a correct email address.");
AddPasswordCheck(builder.Property(x => x.Password))
.Required(() => "Please enter the password.");
AddPasswordCheck(builder.Property(x => x.ConfirmPassword))
.Required(() => "Please confirm the password.");
}
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string Email { get; set; }
public virtual string Password { get; set; }
public virtual string ConfirmPassword { get; set; }
public void OnPasswordChanged() {
this.RaisePropertyChanged(() => ConfirmPassword);
}
public void OnConfirmPasswordChanged() {
this.RaisePropertyChanged(() => Password);
}
}
}

MainViewModel.vb

Imports DevExpress.Mvvm
Imports DevExpress.Mvvm.DataAnnotations
Imports System.Windows.Media

Namespace Example.ViewModel
<POCOViewModel(ImplementIDataErrorInfo := True)> _
Public Class MainViewModel
Inherits ViewModelBase

Private Shared Function AddPasswordCheck(ByVal builder As PropertyMetadataBuilder(Of MainViewModel, String)) As PropertyMetadataBuilder(Of MainViewModel, String)
Return builder.MatchesInstanceRule(Function(name, vm) vm.Password = vm.ConfirmPassword, Function() "The passwords don't match.").MinLength(8, Function() "The password must be at least 8 characters long.").MaxLength(20, Function() "The password must not exceed the length of 20.")
End Function
Public Shared Sub BuildMetadata(ByVal builder As MetadataBuilder(Of MainViewModel))
builder.Property(Function(x) x.FirstName).Required(Function() "Please enter the first name.")
builder.Property(Function(x) x.LastName).Required(Function() "Please enter the last name.")
builder.Property(Function(x) x.Email).EmailAddressDataType(Function() "Please enter a correct email address.")
AddPasswordCheck(builder.Property(Function(x) x.Password)).Required(Function() "Please enter the password.")
AddPasswordCheck(builder.Property(Function(x) x.ConfirmPassword)).Required(Function() "Please confirm the password.")
End Sub
Public Overridable Property FirstName() As String
Public Overridable Property LastName() As String
Public Overridable Property Email() As String
Public Overridable Property Password() As String
Public Overridable Property ConfirmPassword() As String
Public Sub OnPasswordChanged()
Me.RaisePropertyChanged(Function() ConfirmPassword)
End Sub
Public Sub OnConfirmPasswordChanged()
Me.RaisePropertyChanged(Function() Password)
End Sub
End Class
End Namespace

DevExpress WPF | 下載試用

DevExpress WPF擁有120+個控制元件和庫,將幫助您交付滿足甚至超出企業需求的高效能業務應用程式。通過DevExpress WPF能建立有著強大互動功能的XAML基礎應用程式,這些應用程式專注於當代客戶的需求和構建未來新一代支援觸控的解決方案。 無論是Office辦公軟體的衍伸產品,還是以資料為中心的商業智慧產品,都能通過DevExpress WPF控制元件來實現。


DevExpress技術交流群6:600715373      歡迎一起進群討論

更多DevExpress線上公開課、中文教程資訊請上中文網獲取