使用自跟踪实体生成器和 Visual Studio 2012 构建 WPF 应用程序 - 枚举支持
本文介绍如何将枚举类型与 Self-Tracking Entity Generator 和 Visual Studio 2012 一起使用。
目录
引言
在本文中,我们将重点介绍如何在 WPF/Silverlight 的 Self-Tracking Entity Generator 中使用枚举类型。枚举是一种特殊的值类型,它允许我们指定一组命名的数字常量。添加枚举支持是 Entity Framework 5 引入的新功能之一,它允许我们在实体类中使用枚举属性。为了使用此新功能,我们需要在 Visual Studio 2012 项目中将目标框架设置为 .NET Framework 4.5。
在实体设计器中添加枚举
为了演示如何在 WPF 应用程序中使用枚举类型,让我们通过一个在演示应用程序 SchoolSample 中添加新枚举类型的示例。假设我们创建新的学生记录时,想要存储该学生是全职还是兼职学生的信息。要实现此目的,我们首先需要修改示例数据库,并在 Person 表中添加一个名为 Status
的新字段,如下所示:
CREATE TABLE [dbo].[Person](
[PersonId] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
[EnrollmentDate] [datetime] NULL,
[HireDate] [datetime] NULL,
[Salary] [decimal](18, 2) NULL,
[Status] [int] NOT NULL,
[Role] [varchar](50) NOT NULL,
[Version] [timestamp] NOT NULL
)
更新示例数据库后,我们打开 SchoolModel.edmx 的实体设计器。右键单击设计器表面,然后选择“从数据库更新模型...”。更新实体模型后,我们应该能在 Person
实体中看到一个名为 Status
的新属性,如下所示:
此 Status
属性最初设置为 Int32
类型,因为这是我们在数据库中定义的。要将其类型更改为枚举,我们需要接下来创建一个新的枚举类型。
添加新的枚举类型
要添加新的枚举类型,我们需要打开 Model Browser 窗口。然后,找到“Enum Types”节点,右键单击并选择“Add New Enum Type...” 。
这将打开“Add Enum Type”弹出窗口,我们可以按如下方式定义我们的新 StatusEnum
类型:
通过 Model Browser 窗口添加 StatusEnum
类型后,我们就可以在概念模型中更改 Person
实体的 Status
属性的类型了。在实体设计器中,选择 Person
实体的 Status
属性,然后在 Properties 窗口中,更改 Type 下拉菜单,选择 SCHOOLModel.StatusEnum
。
完成后,我们就可以保存对 SchoolModel.edmx 的所有更改。下一步是重新生成客户端(项目 SchoolSample.Data)和服务器端(项目 SchoolSample.Data.Wcf)的实体类。这可以通过选择实体设计表面,从 Properties 窗口选择“STE Settings”来完成,如下所示:
出现“STE Settings”对话框后,单击“Update”按钮,它将生成所有新添加的枚举类型及其辅助类。
自动生成的类
自动生成的类包括通过 Model Browser 窗口定义的所有枚举类型以及一个名为 EnumCollection<T>
的枚举辅助类。以下是我们刚刚添加的 StatusEnum
类型,此文件由 SchoolModel.tt 生成:
辅助类 EnumCollection<T>
由 SchoolModel.SteClient.tt 生成,它仅在客户端(项目 SchoolSample.Data)可用。我们将在下一节讨论它的主要功能。
EnumCollection 类
在 WPF 应用程序中,我们通常使用 ComboBox 来绑定到枚举属性,而 EnumCollection<T>
类可以帮助我们创建一个用作 ComboBox 的 ItemsSource
的集合。此类主要功能如下:
- 创建一个包含
T
的所有可能枚举值的集合,其中每个元素的Value
属性存储枚举值,DisplayName
属性保留其对应的显示名称。 - 可选地,在集合中添加一个元素,其
Value
属性存储 null,DisplayName
属性包含string.Empty
。 - 通过从资源文件中检索
DisplayName
属性来支持本地化。 - 提供手动添加或删除集合元素的灵活性。
EnumItem 类
对于由 EnumCollection<T>
创建的集合,其元素类型为 EnumItem
。从下面的源代码可以看出,此类包含三个属性:DisplayName
、ResourceEntryKey
和 Value
。Value
属性存储实际的枚举值,DisplayName
属性存储其对应的显示名称。如果 ResourceEntryKey
不为空,它将包含 DisplayName
属性检索其实际显示名称的资源条目键。ResourceEntryKey
的值格式为枚举类型名称,后跟一个下划线和枚举值名称。例如,StatusEnum
类型的 ResourceEntryKey
值是:StatusEnum_FullTime
和 StatusEnum_PartTime
。
public class EnumItem : INotifyPropertyChanged
{
public string DisplayName
{
get { return _displayName; }
set
{
_displayName = value;
OnPropertyChanged("DisplayName");
}
}
private string _displayName;
public string ResourceEntryKey { get; set; }
public T? Value
{
get { return _value; }
set
{
_value = value;
OnPropertyChanged("Value");
}
}
private T? _value;
private void OnPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
EnumCollection 类的构造函数
EnumCollection<T>
类的构造函数接受两个可选参数。第一个是一个布尔字段,如果设置为 True,则集合的第一个元素包含一个 null 选择。第二个参数是资源文件的 ResourceManager
,DisplayName
属性从中检索其值。如果第二个参数留空(null),则 ResourceEntryKey
始终设置为空字符串,DisplayName
包含实际枚举值的字符串格式。
/// <summary>
/// Constructor to initialize EnumCollection
/// </summary>
/// <param name="firstAsNull">True to add the first row as null</param>
/// <param name="resourceManager">ResourceManager where DisplayName gets its value</param>
public EnumCollection(bool firstAsNull = false, ResourceManager resourceManager = null)
{
enumItems = new Collection<EnumItem>();
this.resourceManager = resourceManager;
if (firstAsNull)
enumItems.Add(new EnumItem
{
DisplayName = string.Empty,
ResourceEntryKey = string.Empty,
Value = null
});
var type = typeof (T);
foreach (var enumValue in (from field in type.GetFields(BindingFlags.Public | BindingFlags.Static)
where field.IsLiteral
select (T) field.GetValue(null)))
{
if (resourceManager != null)
{
var resourceEntryKey = string.Format("{0}_{1}", typeof (T).Name, enumValue);
var displayName = resourceManager.GetString(resourceEntryKey);
if (displayName != null)
{
enumItems.Add(new EnumItem
{
DisplayName = displayName,
ResourceEntryKey = resourceEntryKey,
Value = enumValue
});
}
else
{
enumItems.Add(new EnumItem
{
DisplayName = string.Format("{0}_{1}", typeof (T).Name, enumValue),
ResourceEntryKey = string.Empty,
Value = enumValue
});
}
}
else
{
enumItems.Add(new EnumItem
{
DisplayName = string.Format("{0}", enumValue),
ResourceEntryKey = string.Empty,
Value = enumValue
});
}
}
}
Refresh() 方法
创建 Refresh()
方法是为了支持本地化,它会刷新每个 ResourceEntryKey
不为空的项的 DisplayName
属性。我们通常在当前区域性更改后调用此方法。
/// <summary>
/// Refreshes the DisplayName property for every item
/// where the ResourceEntryKey property is not empty
/// </summary>
public void Refresh()
{
if (resourceManager == null) return;
foreach (var item in enumItems.Where(n => !string.IsNullOrEmpty(n.ResourceEntryKey)))
{
var displayName = resourceManager.GetString(item.ResourceEntryKey);
if (displayName != null) item.DisplayName = displayName;
}
}
Items 属性
最后,我们公开 Items
属性,该属性返回类构造函数创建的 EnumItem
集合。这样做是为了方便添加或删除 EnumCollection<T>
的任何元素。
public Collection<EnumItem> Items
{
get { return enumItems; }
}
关于 EnumCollection<T>
类的讨论到此结束。接下来,我们将看到如何在示例 ViewModel 和 View 类中使用这个辅助类。
在 ViewModel 和 View 类中使用枚举
首先,在 StudentPageViewModel
类中,我们定义一个名为 StudentStatusCollection
的属性,其类型为 EnumCollection<StatusEnum>
。
public EnumCollection<StatusEnum> StudentStatusCollection { get; private set; }
接下来,在类构造函数中,我们初始化 StudentStatusCollection
并将 SchoolModelResource
作为其资源文件传递。
#region "Constructor"
[ImportingConstructor]
public StudentPageViewModel(ISchoolModel schoolModel)
{
......
// set enum type
StudentStatusCollection = new EnumCollection<StatusEnum>(false, SchoolModelResource.ResourceManager);
......
}
#endregion "Constructor"
每当加载学生页面时,我们都会调用 StudentStatusCollection.Refresh()
,以防区域性发生更改。
private void OnPageLoadedCommand()
{
// refresh the StudentStatusCollection
StudentStatusCollection.Refresh();
// synchronize _schoolModel.StudentsList with local cache
if (_schoolModel != null && _allStudentsCache != null)
_schoolModel.StudentsList = _allStudentsCache;
// synchronize _schoolModel.CurrentStudent with local cache
if (_schoolModel != null && _currentStudentCache != null)
_schoolModel.CurrentStudent = _currentStudentCache;
// if CurrentStudentHasErrors, call TryValidate() to push the new
// error messages into the ValidationSummary
if (CurrentStudentHasErrors && CurrentStudent != null)
CurrentStudent.TryValidate();
}
在 ViewModel 类中定义和初始化 StudentStatusCollection
属性后,我们可以将其绑定到 StudentPage.xaml 中的 ComboBox,如下所示:
最后,我们需要修改 SchoolModelResource.resx 文件,包含 Status
枚举类型的所有新 DisplayName
值。
当我们运行示例应用程序时,应该会看到类似如下的屏幕截图:
总结
我们已经完成了关于如何在 WPF/Silverlight 的 Self-Tracking Entity Generator 中使用枚举类型的讨论。首先,我们介绍了如何通过实体设计器添加新的枚举类型。然后,我们讨论了新的辅助类 EnumCollection<T>
的主要功能。之后,我们讨论了如何在 ViewModel 和 View 类中使用枚举。
希望您觉得本文有用,请在下方评分和/或留下反馈。谢谢!
历史
- 2012 年 10 月 - 初始发布。