65.9K
CodeProject 正在变化。 阅读更多。
Home

使用自跟踪实体生成器和 Visual Studio 2012 构建 WPF 应用程序 - 枚举支持

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (5投票s)

2012年10月10日

CPOL

6分钟阅读

viewsIcon

17978

本文介绍如何将枚举类型与 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。从下面的源代码可以看出,此类包含三个属性:DisplayNameResourceEntryKeyValueValue 属性存储实际的枚举值,DisplayName 属性存储其对应的显示名称。如果 ResourceEntryKey 不为空,它将包含 DisplayName 属性检索其实际显示名称的资源条目键。ResourceEntryKey 的值格式为枚举类型名称,后跟一个下划线和枚举值名称。例如,StatusEnum 类型的 ResourceEntryKey 值是:StatusEnum_FullTimeStatusEnum_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 选择。第二个参数是资源文件的 ResourceManagerDisplayName 属性从中检索其值。如果第二个参数留空(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 月 - 初始发布。
© . All rights reserved.