彩票走势图

界面控件DevExpress WPF入门级指南:运行时生成的POCO视图模型(三)

翻译|使用教程|编辑:龚雪|2022-05-07 10:15:20.120|阅读 122 次

概述:本文主要介绍DevExpress MVVM架构下生成的POCO视图模型,欢迎下载官方正式版体验!

# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>

相关链接:

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 实现

接口是 WPF 中数据验证的标准机制,您可以使用此接口为每个单独的属性或整个对象定义验证规则。 POCO 机制允许您基于定义的属性或 Fluent API 自动实现接口。

要启用此功能,请为您的视图模型应用 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); }
}
}

类允许您根据指定的 属性或 Fluent API 获取错误。

下面的代码示例演示了如何使用 POCO 机制来实现 IDataErrorInfo 接口。

MainView.xaml

<UserControl x:Class="Example.View.MainView"
xmlns="//schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="//schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="//schemas.microsoft.com/expression/blend/2008"
xmlns:mc="//schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dx="//schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxe="//schemas.devexpress.com/winfx/2008/xaml/editors"
xmlns:dxmvvm="//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      欢迎一起进群讨论

慧都图表线上公开课火热报名中

标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@pclwef.cn

文章转载自:慧都网

为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP