彩票走势图

logo DevExpress WinForm中文手册

导航和视图管理


立即下载DevExpress WinForms

本主题解释了如何在不同的应用程序视图之间实现导航,以及如何构建View-ViewModel关系。

标准导航服务

DevExpress MVVM框架包括许多,您可以利用它们来实现不同应用模块(视图)之间的导航。

使用任何MVVM服务包括三个主要步骤:

1.在视图中注册服务时可以全局注册(它可以从任何应用程序视图中获得)或本地注册(如果您打算仅从此模块中使用它)。

2.在ViewModel中声明一个属性来检索已注册服务的实例。

C#:

public class ViewLocator : IViewLocator {
object IViewLocator.Resolve(string name, params object[] parameters) {
object viewModel = paremeters.Length==3 ? parameters[0] : null;
object parameter = parameters.Length==3 ? parameters[1] : null;
object parentViewModel = (paremeters.Length==3) ? paremeters[2] : paremeters[0] ;
if(name == nameof(CustomersView))
return new CustomersView()
//...
return null;
}
}

3.调用ViewModel中Service实例的公共API。

例如,主应用程序的视图有MvvmContext组件,它将主应用程序的表单(视图)链接到“Form1ViewModel”ViewModel。

C#:

// View
mvvmContext1.ViewModelType = typeof(mvvmNavi.Form1ViewModel);

// ViewModel
[POCOViewModel()]
public class Form1ViewModel {
//...
}

VB.NET:

' View
mvvmContext1.ViewModelType = GetType(mvvmNavi.Form1ViewModel)

Private Sub InitializeBindings()
Dim fluent = mvvmContext1.OfType(Of Form1ViewModel)()
End Sub
End Class

' ViewModel
<POCOViewModel()>
Public Class Form1ViewModel
'...
End Class

该应用程序还有两个UserControl,每个都有自己的MvvmContext组件,UserControl的视图链接到它相应的ViewModel。

C#:

public partial class ViewA : UserControl {
MVVMContext mvvmContext;
public ViewA() {
mvvmContext = new MVVMContext();
mvvmContext.ContainerControl = this;
mvvmContext.ViewModelType = typeof(ViewAViewModel);
}
}

public class ViewAViewModel {
}

public partial class ViewB : UserControl {
MVVMContext mvvmContext;
public ViewB() {
mvvmContext = new MVVMContext();
mvvmContext.ContainerControl = this;
mvvmContext.ViewModelType = typeof(ViewBViewModel);
}
}

public class ViewBViewModel {
}

VB.NET:

Partial Public Class ViewA
Inherits UserControl

Private mvvmContext As MVVMContext
Public Sub New()
mvvmContext = New MVVMContext()
mvvmContext.ContainerControl = Me
mvvmContext.ViewModelType = GetType(ViewAViewModel)
End Sub
End Class

Public Class ViewAViewModel
End Class

Partial Public Class ViewB
Inherits UserControl

Private mvvmContext As MVVMContext
Public Sub New()
mvvmContext = New MVVMContext()
mvvmContext.ContainerControl = Me
mvvmContext.ViewModelType = GetType(ViewBViewModel)
End Sub
End Class

Public Class ViewBViewModel
End Class

提示:上面的代码初始化了MvvmContext组件,并设置了它们的ViewModelType属性,只是为了举例说明。在实际的应用程序中,建议在设计时将组件放在 Forms 和 UserControls上,并使用智能标签菜单来设置ViewModels。

下面的例子说明了如何根据您的任务选择和使用不同的DevExpress服务:

例1:DocumentManager选项卡

主应用程序表单(视图)有一个空的文档管理器,任务是将UserControls A和B显示为DocumentManager选项卡(文档)。

要管理DocumentManager文档,请使用并在主视图中注册它:

C#:

public Form1() {
InitializeComponent();
//. . .
var service = DocumentManagerService.Create(tabbedView1);
service.UseDeferredLoading = DevExpress.Utils.DefaultBoolean.True;
mvvmContext1.RegisterDefaultService(service);
}

VB.NET:

Public Sub Form1()
InitializeComponent()
'. . .
Dim service = DocumentManagerService.Create(tabbedView1)
service.UseDeferredLoading = DevExpress.Utils.DefaultBoolean.True
mvvmContext1.RegisterDefaultService(service)
End Sub

在主ViewModel中,实现一个属性来检索注册服务的实例:

C#:

[POCOViewModel()]
public class Form1ViewModel {
protected IDocumentManagerService DocumentManagerService {
get { return this.GetService<IDocumentManagerService>(); }
}
}

VB.NET:

<POCOViewModel()>
Public Class Form1ViewModel
Protected ReadOnly Property DocumentManagerService() As IDocumentManagerService
Get
Return Me.GetService(Of IDocumentManagerService)()
End Get
End Property
End Class

DocumentManagerService.CreateDocument和DocumentManagerService.FindDocumentById方法允许您创建和定位文档,然后可以调用IDocument.Show方法来显示它们。

C#:

// main ViewModel
public void CreateDocument(object id, string documentType, string title) {
var document = DocumentManagerService.FindDocumentById(id);
if (document == null) {
document = DocumentManagerService.CreateDocument(
documentType, parameter: null, parentViewModel: this);
document.Id = id;
document.Title = title;
}
document.Show();
}

VB.NET:

' main ViewModel
Public Sub CreateDocument(ByVal id As Object, ByVal documentType As String, ByVal title As String)
Dim document = DocumentManagerService.FindDocumentById(id)
If document Is Nothing Then
document = DocumentManagerService.CreateDocument(documentType, parameter:= Nothing, parentViewModel:=Me)
document.Id = id
document.Title = title
End If
document.Show()
End Sub

这个核心方法可以在各种场景中使用。

  • 创建一个带有特定UserControl的新文档,并在应用程序启动时加载它:

C#:

// main ViewModel
readonly static object ViewA_ID = new object();
readonly static object ViewB_ID = new object();

public void CreateDocumentA() {
CreateDocument(ViewA_ID, "ViewA", "UserControl A");
}

public void CreateDocumentB() {
CreateDocument(ViewB_ID, "ViewB", "UserControl B");
}

// main View
var fluent = mvvmContext1.OfType<Form1ViewModel>();
fluent.WithEvent(this, "Load").EventToCommand(x => x.CreateDocumentA);

VB.NET:

' main ViewModel
Private ReadOnly Shared ViewA_ID As New Object()
Private ReadOnly Shared ViewB_ID As New Object()

Public Sub CreateDocumentA()
CreateDocument(ViewA_ID, "ViewA", "UserControl A")
End Sub

Public Sub CreateDocumentB()
CreateDocument(ViewB_ID, "ViewB", "UserControl B")
End Sub

' main View
Dim fluent = mvvmContext1.OfType(Of Form1ViewModel)()
fluent.WithEvent(Me, "Load").EventToCommand(Function(x) x.CreateDocumentA)

为每个UserControl创建一个文档,并在启动时加载所有这些文档。

C#:

// main ViewModel
public void CreateAllDocuments() {
CreateDocument(ViewA_ID, "ViewA", "UserControl A");
CreateDocument(ViewB_ID, "ViewB", "UserControl B");
}

// main View
var fluent = mvvmContext1.OfType<Form1ViewModel>();
fluent.WithEvent(this, "Load").EventToCommand(x => x.CreateAllDocuments);

VB.NET:

' main ViewModel
Public Sub CreateAllDocuments()
CreateDocument(ViewA_ID, "ViewA", "UserControl A")
CreateDocument(ViewB_ID, "ViewB", "UserControl B")
End Sub

' main View
Dim fluent = mvvmContext1.OfType(Of Form1ViewModel)()
fluent.WithEvent(Me, "Load").EventToCommand(Function(x) x.CreateAllDocuments)
  • 将UI元素(例如,Ribbon按钮)绑定到一个命令,该命令创建一个具有特定UserControl的新文档。

C#:

// main ViewModel
public void CreateDocument(object id, string documentType, string title) {
var document = DocumentManagerService.CreateDocument(
documentType, parameter: null, parentViewModel: this);
document.Id = id;
document.Title = title;
document.Show();
}

public void CreateDocumentA() {
CreateDocument(new object(), "ViewA", "UserControl A");
}

public void CreateDocumentB() {
CreateDocument(new object(), "ViewB", "UserControl B");
}

// main View
fluent.BindCommand(bbiCreateDocA, x => x.CreateDocumentA);
fluent.BindCommand(bbiCreateDocB, x => x.CreateDocumentB);

VB.NET:

' main ViewModel
Public Sub CreateDocument(ByVal id As Object, ByVal documentType As String, ByVal title As String)
Dim document = DocumentManagerService.CreateDocument(documentType, parameter:= Nothing, parentViewModel:=Me)
document.Id = id
document.Title = title
document.Show()
End Sub

Public Sub CreateDocumentA()
CreateDocument(New Object(), "ViewA", "UserControl A")
End Sub

Public Sub CreateDocumentB()
CreateDocument(New Object(), "ViewB", "UserControl B")
End Sub

' main View
fluent.BindCommand(bbiCreateDocA, Function(x) x.CreateDocumentA)
fluent.BindCommand(bbiCreateDocB, Function(x) x.CreateDocumentB)

示例2:导航框架

主表单(视图)有一个空的NavigationFrame组件,该组件可以存储多个页面,但一次只允许用户查看一个页面。要用页面填充该组件并实现导航,请使用NavigationService。

  • 全球服务注册:

C#:

// main View
var service = NavigationService.Create(navigationFrame1);
mvvmContext1.RegisterDefaultService(service);

VB.NET:

' main View
Dim service = NavigationService.Create(navigationFrame1)
mvvmContext1.RegisterDefaultService(service)
  • 检索Service实例的属性:

C#:

// main ViewModel
protected INavigationService NavigationService {
get { return this.GetService<INavigationService>(); }
}

VB.NET:

' main ViewModel
Protected ReadOnly Property NavigationService() As INavigationService
Get
Return Me.GetService(Of INavigationService)()
End Get
End Property
  • 导航:

C#:

// main View
var fluent = mvvmContext.OfType<RootViewModel>();
fluent.WithEvent(mainView, "Load")
.EventToCommand(x => x.OnLoad);

// main ViewModel

public void OnLoad() {
NavigationService.Navigate("ViewA", null, this);
}

VB.NET:

' main View
Private fluent = mvvmContext.OfType(Of RootViewModel)()
fluent.WithEvent(mainView, "Load").EventToCommand(Function(x) x.OnLoad)

' main ViewModel

public void OnLoad()
NavigationService.Navigate("ViewA", Nothing, Me)

Navigate方法可以接受参数作为它的第二个参数,这允许您在导航模块之间传递任何数据。DevExpress Demo Center示例演示了如何将先前活动模块的名称传递给当前选择的视图,注意在这个例子中,全局服务注册允许每个子ViewModel使用这个服务的API。

示例3:情态形式

在本例中,子视图在其他应用程序窗口上方显示为单独的表单。要做到这一点,请使用WindowedDocumentManagerService服务。

  • 本地注册:

C#:

// main View
var service = WindowedDocumentManagerService.Create(mainView);
service.DocumentShowMode = WindowedDocumentManagerService.FormShowMode.Dialog;
mvvmContext.RegisterService(service);

VB.NET:

' main View
Dim service = WindowedDocumentManagerService.Create(mainView)
service.DocumentShowMode = WindowedDocumentManagerService.FormShowMode.Dialog
mvvmContext.RegisterService(service)
  • 检索Service实例的属性:

C#:

// main ViewModel
protected IDocumentManagerService WindowedDocumentManagerService {
get { return this.GetService<IDocumentManagerService>(); }
}

VB.NET:

' main ViewModel
Protected ReadOnly Property WindowedDocumentManagerService() As IDocumentManagerService
Get
Return Me.GetService(Of IDocumentManagerService)()
End Get
End Property
  • 导航:

C#:

// main View
var fluent = mvvmContext.OfType<MainViewModel>();
fluent.BindCommand(showBtn, x => x.ShowAcceptDialog);

// main ViewModel
int id = 0;
public void ShowAcceptDialog() {
var viewModel = ViewModelSource.Create(() => new ViewAViewModel());
var document = WindowedDocumentManagerService.FindDocumentById(id);
if(document == null) {
document = WindowedDocumentManagerService.CreateDocument(string.Empty, viewModel: viewModel);
document.Id = id;
document.Title = "Accept Dialog";
}
document.Show();
}

VB.NET:

' main View
Dim fluent = mvvmContext.OfType(Of MainViewModel)()
fluent.BindCommand(showBtn, Function(x) x.ShowAcceptDialog)

' main ViewModel
Private id As Integer = 0
Public Sub ShowAcceptDialog()
Dim viewModel = ViewModelSource.Create(Function() New ViewAViewModel())
Dim document = WindowedDocumentManagerService.FindDocumentById(id)
If document Is Nothing Then
document = WindowedDocumentManagerService.CreateDocument(String.Empty, viewModel:= viewModel)
document.Id = id
document.Title = "Accept Dialog"
End If
document.Show()
End Sub
  • 结束情态形式:

C#:

public class ChildViewModel : IDocumentContent {
public void Close() {
// Closes the document.
DocumentOwner?.Close(this);
}
public IDocumentOwner DocumentOwner { get; set; }
public object Title { get; set; }
void IDocumentContent.OnClose(CancelEventArgs e) {
/* Do something */
}
void IDocumentContent.OnDestroy() {
/* Do something */
}
}

VB.NET:

Public Class ChildViewModel
Implements IDocumentContent

Public Sub Close()
' Closes the document.
DocumentOwner?.Close(Me)
End Sub
Public Property DocumentOwner() As IDocumentOwner
Public Property Title() As Object
Private Sub IDocumentContent_OnClose(ByVal e As CancelEventArgs) Implements IDocumentContent.OnClose
' Do something
End Sub
Private Sub IDocumentContent_OnDestroy() Implements IDocumentContent.OnDestroy
' Do something
End Sub
End Class

ViewType属性

如果您遵循命名约定(“ModuleX”视图的ViewModel被称为“ModuleXViewModel”),并且视图/ViewModel位于相同的命名空间中,则上述示例中显示的MVVM服务的默认使用就足够了,否则框架将无法定位与给定ViewModule相关的视图。要解决这个问题,需要用ViewType属性修饰Views,来显式地设置View-ViewModel关系。

C#:

[DevExpress.Utils.MVVM.UI.ViewType("AccountCollectionView")]
public partial class AccountsView {
// ...
}

[DevExpress.Utils.MVVM.UI.ViewType("CategoryCollectionView")]
public partial class CategoriesView {
// ...
}

[DevExpress.Utils.MVVM.UI.ViewType("TransactionCollectionView")]
public partial class TransactionsView {
// ...
}

VB.NET:

<DevExpress.Utils.MVVM.UI.ViewType("AccountCollectionView")>
Partial Public Class AccountsView
' ...
End Class

<DevExpress.Utils.MVVM.UI.ViewType("CategoryCollectionView")>
Partial Public Class CategoriesView
' ...
End Class

<DevExpress.Utils.MVVM.UI.ViewType("TransactionCollectionView")>
Partial Public Class TransactionsView
' ...
End Class

单独程序集中的视图

当视图位于单独的程序集中或具有自定义构造函数时,ViewType属性是不够的。在这些情况下,请使用以下方法之一:

IViewService

将导航服务实例转换为DevExpress.Utils.MVVM.UI.IViewService接口。

C#:

var service = DevExpress.Utils.MVVM.Services.DocumentManagerService.Create(tabbedView1);
var viewService = service as DevExpress.Utils.MVVM.UI.IViewService;
mvvmContext1.RegisterService(service);

VB.NET:

Dim service = DevExpress.Utils.MVVM.Services.DocumentManagerService.Create(tabbedView1)
Dim viewService = TryCast(service, DevExpress.Utils.MVVM.UI.IViewService)
mvvmContext1.RegisterService(service)

之后,处理QueryView事件并根据所需的视图类型动态分配视图。

C#:

viewService.QueryView += (s, e) =>
{
if(e.ViewType == "View1")
e.Result = new Views.View1();
//...
};

VB.NET:

AddHandler viewService.QueryView, Sub(s, e)
If e.ViewType = "View1" Then
e.Result = New Views.View1()
End If
'...
End Sub

要指定需要哪种视图类型,您需要在导航ViewModel中实现相应的逻辑。例如,下面的代码将所有可用的视图枚举为Modules集合中的项。

C#:

public class MyNavigationViewModel {
protected IDocumentManagerService DocumentManagerService {
get { return this.GetService<IDocumentManagerService>(); }
}
//Lists all available view types
public string[] Modules {
get { return new string[] { "View1", "View2", "View3" }; }
}
//Bind this command to required UI elements to create and display a document
public void Show(string moduleName) {
var document = DocumentManagerService.CreateDocument(moduleName, null, this);
if(document != null) {
document.Title = moduleName;
document.Show();}
}
}

VB.NET:

Public Class MyNavigationViewModel
Protected ReadOnly Property DocumentManagerService() As IDocumentManagerService
Get
Return Me.GetService(Of IDocumentManagerService)()
End Get
End Property
'Lists all available view types
Public ReadOnly Property Modules() As String()
Get
Return New String() { "View1", "View2", "View3" }
End Get
End Property
'Bind this command to required UI elements to create and display a document
Public Sub Show(ByVal moduleName As String)
Dim document = DocumentManagerService.CreateDocument(moduleName, Nothing, Me)
If document IsNot Nothing Then
document.Title = moduleName
document.Show()
End If
End Sub
End Class
控制APIs

您可以使用导航服务管理的单个视图控件的API。例如,如果视图应该显示为DocumentManager选项卡,便处理BaseView.QueryControl事件来填充文档,View类型存储Document.ControlName属性值。

C#:

var service = DevExpress.Utils.MVVM.Services.DocumentManagerService.Create(tabbedView1);
mvvmContext1.RegisterService(service);

tabbedView1.QueryControl += (s, e) =>
{
if(e.Document.ControlName == "View 2")
e.Control = new Views.View2();
//...
};

VB.NET:

Dim service = DevExpress.Utils.MVVM.Services.DocumentManagerService.Create(tabbedView1)
mvvmContext1.RegisterService(service)

AddHandler tabbedView1.QueryControl, Sub(s, e)
If e.Document.ControlName = "View 2" Then
e.Control = New Views.View2()
End If
'...
End Sub
IViewLocator

所有DevExpress导航服务都使用DevExpress.Utils.MVVM.UI.IViewLocator服务来查找和管理所需的视图,您可以创建此服务的自定义实现并注册它(本地或全局)来更改它与应用程序视图的工作方式。请参阅本文了解如何实现和注册自定义服务:services。

视图和视图模型生存期

处置视图也处置MvvmContext和ViewModel,您既可以实现IDisposable.Dispose方法,也可以将命令绑定到视图的HandleDestroyed事件,以便在ViewModel被处置时执行操作。

C#:

// ViewModel
public ViewModel() {
// Registers a new connection to the messenger.
Messenger.Default.Register(...);
}
public void OnCreate() {
// Captures UI-bound services.
EnsureDispatcherService();
}
public void OnDestroy() {
// Destroys a connection to the messanger.
Messenger.Default.Unregister(...);
}
IDispatcherService dispatcher;
IDispatcherService EnsureDispatcherService() {
return dispatcher ?? (dispatcher = this.GetRequiredService<IDispatcherService>());
}

// View (UserControl/Form)
fluent.WithEvent(this, nameof(HandleCreated)).EventToCommand(x => x.OnCreate);
fluent.WithEvent(this, nameof(HandleDestroyed)).EventToCommand(x => x.OnDestroy);

VB.NET:

Public Sub New()
' Registers a new connection to the messenger.
Messenger.Default.Register(...)
End Sub
Public Sub OnCreate()
' Captures UI-bound services.
EnsureDispatcherService()
End Sub
Public Sub OnDestroy()
' Destroys a connection to the messanger.
Messenger.Default.Unregister(...)
End Sub
Private dispatcher As IDispatcherService
Private Function EnsureDispatcherService() As IDispatcherService
If dispatcher IsNot Nothing Then
Return dispatcher
Else
dispatcher = Me.GetRequiredService(Of IDispatcherService)()
Return dispatcher
End If
End Function

' View (UserControl/Form)
fluent.WithEvent(Me, nameof(HandleCreated)).EventToCommand(Function(x) x.OnCreate)
fluent.WithEvent(Me, nameof(HandleDestroyed)).EventToCommand(Function(x) x.OnDestroy)
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP