MVVM(Model-View-ViewModel)模式是一种设计模式,用于简化用户界面的开发。它将应用程序的UI(视图)与业务逻辑(模型)分离,通过ViewModel层来实现数据的双向绑定。本文将通过一个简单的例子,介绍如何在WPF应用程序中实现MVVM模式。
DataContext和数据绑定
在MVVM模式中,DataContext是绑定数据的源,它定义了绑定操作的基础元素。数据绑定则是连接特定属性值和视觉控件的桥梁。例如,有一个WPF窗口,其中包含一个TextBox,希望在TextBox中显示窗口的标题,并能够通过修改TextBox的内容来修改窗口的标题。这就需要将TextBox的DataContext设置为窗口,并将Text属性绑定到窗口的Title属性上。以下是实现这一功能的XAML代码:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Name="MainWindow"
Title="MainWindow"
Height="111.194" Width="295.149">
<TextBox Name="TB1"
Text="{Binding Title, ElementName=MainWindow, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left"
Height="22"
VerticalAlignment="Top"
Width="248" />
</Window>
在这个例子中,为窗口指定了一个名称(MainWindow)。然后,将TextBox的Text属性绑定到MainWindow的Title属性上,以双向模式(TwoWay)进行绑定,并指定属性修改事件(PropertyChanged)作为触发更新操作的事件。运行这个示例,会发现窗口的标题会根据TextBox中输入的内容而改变。同样,如果修改代码来干预窗口的标题,也会看到TextBox内容的更新。
绑定数据模型
假设需要管理一个与外部类相关的绑定,这个类用于展示产品信息,包括产品代码和描述。以下是一个示例类,用于表示产品信息:
Public Class ItemData
Public _code As String
Public _des As String
Public Property Code As String
Get
Return _code
End Get
Set(value As String)
_code = value
End Set
End Property
Public Property Description As String
Get
Return _des
End Get
Set(value As String)
_des = value
End Set
End Property
Public Sub New(ByVal code As String, ByVal des As String)
_code = code
_des = des
End Sub
End Class
这个类可以定义新的产品和实体,并初始化其基本属性。但是,这个类不能直接用作DataContext,除非它在变量中被引用。这意味着,如果希望执行绑定操作,需要在代码后台进行操作,例如,可以在窗口的Loaded事件中进行操作:
Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs)
Dim item As New ItemData("PRDCODE01", "TEST PRODUCT")
TB1.DataContext = item
End Sub
对应的XAML代码如下:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Name="MainWindow"
Loaded="MainWindow_Loaded"
Title="MainWindow"
Height="111.194" Width="295.149">
<TextBox Name="TB1"
Text="{Binding Code}"
HorizontalAlignment="Left"
Height="22"
VerticalAlignment="Top"
Width="248" />
</Window>
在这个例子中,在TextBox的Text属性中使用了更简洁的语法。没有处理数据的双向性,只关注Code属性,而没有提到它必须从哪个元素(或DataContext)派生。这是因为DataContext是在代码端指定的,通过初始化ItemData类型的变量,然后将其作为TextBox的数据上下文。运行这个示例,会发现TextBox显示的值等于PR_CODE_01,即用来初始化ItemData的Code属性的字符串。
创建一个简单的ViewModel
ViewModel封装了一个模型,暴露了所有对视图有用的属性,以便访问底层数据。通常,它实现了INotifyPropertyChanged接口,该接口将作为事件使用,以跟踪特定属性的更改。在案例中,如果想要创建一个尽可能简单的ViewModel,可以编写如下的类:
Imports System.ComponentModel
Public Class ItemDataView
Implements INotifyPropertyChanged
Dim item As New ItemData("PR_CODE_01", "TEST PRODUCT")
Public Property Code As String
Get
Return item.Code
End Get
Set(value)
If Not (item.Code = value) Then
item.Code = value
NotifyPropertyChanged("Code")
End If
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(Optional ByVal propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
这个类初始化了一个ItemData,创建了它的一个新实例,并暴露了它的Code属性。同时,它允许修改该属性,并触发PropertyChanged事件,以通知发生的更改。
要使ItemDataView在整个MainWindow上下文中可见和可用,可以在窗口的XAML中将DataContext指定为全局,包含在其中的控件。TextBox的Binding属性将继续是Code,由ViewModel公开:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Name="MainWindow"
Title="MainWindow"
Height="111.194" Width="295.149">
<Window.DataContext>
<local:ItemDataView x:Name="MyItemView" />
</Window.DataContext>
<TextBox Name="TB1"
Text="{Binding Code}"
HorizontalAlignment="Left"
Height="22"
VerticalAlignment="Top"
Width="248" />
</Window>
或者,如果希望指定特定的TextBox DataContext,可以这样写:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Name="MainWindow"
Title="MainWindow"
Height="111.194" Width="295.149">
<TextBox Name="TB1"
DataContext="{Binding Source=ItemDataView}"
Text="{Binding Code}"
HorizontalAlignment="Left"
Height="22"
VerticalAlignment="Top"
Width="248" />
</Window>