Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Building WPF Applications with Self-Tracking Entity Generator and Visual Studio 2012 - Enum Support

0.00/5 (No votes)
10 Oct 2012 1  
This article describes how to use the enum type with Self-Tracking Entity Generator and Visual Studio 2012.
  • Download source code from here
  • Please visit this project site for the latest releases and source code.

Contents

Introduction

In this article, we will focus on how to use the enum type with the Self-Tracking Entity Generator for WPF/Silverlight. An enum is a special value type that lets us specify a group of named numeric constants. Adding enum support is one of the new features introduced by Entity Framework 5, which allows us to have enum properties in our entity classes. In order to use this new feature, we need to set the target framework to .NET Framework 4.5 inside Visual Studio 2012 projects.

Adding Enum in the Entity Designer

To demonstrate how the enum type is used in WPF applications, let us go through an example of adding a new enum type to our demo application SchoolSample. Let's say, when we create a new student record, we want to store the information of whether that student is a full-time or part-time student. To achieve that, we first need to modify our sample database and add a new field called Status in the Person table as follows:

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
)

After the sample database is updated, we open the entity designer of SchoolModel.edmx. Right click the designer surface and choose "Update Model from Database...". After the entity model is updated, we should be able to see a new property called Status inside the Person entity as shown below.

This Status property is initially set as Int32 type, because that is what we have defined in the database. To change its type to enum, we need to create a new enum type next.

Adding a New Enum Type

To add a new enum type, we need to open the Model Browser window. Next, look for the "Enum Types" node and right click to choose "Add New Enum Type...".

This opens the "Add Enum Type" popup window, and we can define our new StatusEnum type as follows:

After adding the StatusEnum type through the Model Browser window, we can change the type of the Status property in the conceptual model. From the entity designer, select the Status property of the Person entity, and then from the Properties window, change the Type dropdown and choose SCHOOLModel.StatusEnum.

After this is done, we can now save all changes to SchoolModel.edmx. Our next step is to re-generate entity classes on both the client (project SchoolSample.Data) and server side (project SchoolSample.Data.Wcf). This is done by selecting the entity design surface, from the Properties window, choose "STE Settings" as shown below:

After the "STE Settings" dialog box appears, click the "Update" button and it will generate all the newly added enum types and their helper classes:

Auto-generated Classes

The auto-generated classes include all enum types defined through the Model Browser window and an enum helper class called EnumCollection<T>. Following is the StatusEnum type we just added, and this file is generated by SchoolModel.tt:

The helper class EnumCollection<T> is generated by SchoolModel.SteClient.tt, and it is only available on the client side (project SchoolSample.Data). We will discuss its main functionality next:

The EnumCollection Class

In WPF applications, we commonly use a ComboBox to bind to an enum property, and the EnumCollection<T> class can help us create a collection that is used as the ItemsSource of the ComboBox. The main features of this class are as follows:

  • Creates a collection that contains every possible enum value of T where each element's Value property stores the enum value and DisplayName property keeps its matching display name.
  • Optionally, adds to the collection one element whose Value property stores null and DisplayName property contains string.Empty.
  • Supports localization by retrieving the DisplayName property from a resource file.
  • Provides the flexibility to manually add or remove elements of the collection.

The EnumItem Class

For the collection created by EnumCollection<T>, its element is of type EnumItem. From the source code below, we can see that this class contains three properties: DisplayName, ResourceEntryKey, and Value. The Value property stores the actual enum value, and the DisplayName property stores its matching display name. If the ResourceEntryKey is not empty, it contains the resource entry key where the DisplayName property retrieves its actual display name. The value of the ResourceEntryKey takes the format of the enum type name, followed by an underscore and the enum value name. For example, the ResourceEntryKey values for StatusEnum type are: StatusEnum_FullTime and 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;
}

The EnumCollection Class Constructor

The constructor of class EnumCollection<T> takes two optional parameters. The first one is a Boolean field, if it is set to True, the collection contains a null selection as its first element. And, the second parameter is the ResourceManager of a resource file where the DisplayName property retrieves its value. If this second parameter is left as null, ResourceEntryKey is always set as an empty string, and the DisplayName contains the string format of the actual enum value.

/// <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
                });
        }
    }
}

The Refresh() Method

The Refresh() method is created to support localization and it refreshes the DisplayName property for every item where its matching ResourceEntryKey is not empty. We normally call this method after the current culture has changed.

/// <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;
    }
}

The Items Property

And finally, we expose the Items property which returns the EnumItem collection created by the class constructor. This is done because we want the flexibility to add or remove any element of the EnumCollection<T>.

public Collection<EnumItem> Items
{
    get { return enumItems; }
}

This concludes our discussion of the EnumCollection<T> class. Next, we will see how to use this helper class in our sample ViewModel and View classes.

Using Enum in ViewModel and View Classes

First, in class StudentPageViewModel, we define a property called StudentStatusCollection of class EnumCollection<StatusEnum>.

public EnumCollection<StatusEnum> StudentStatusCollection { get; private set; }

Next, in the class constructor, we initialize StudentStatusCollection and pass in SchoolModelResource as its resource file.

#region "Constructor"

[ImportingConstructor]
public StudentPageViewModel(ISchoolModel schoolModel)
{
    ......

    // set enum type
    StudentStatusCollection = new EnumCollection<StatusEnum>(false, SchoolModelResource.ResourceManager);

    ......
}

#endregion "Constructor"

Whenever the student page is loaded, we call StudentStatusCollection.Refresh() in case there is a culture change.

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();
}

And, after defining and initializing the StudentStatusCollection property in the ViewModel class, we can bind it to the ComboBox inside StudentPage.xaml as follows:

Lastly, we need to modify the SchoolModelResource.resx file with all the new DisplayName values of the Status enum type:

When we run the sample application, we should be able to see something similar to the following screenshot:

Wrapping Up

We have finished discussing how to use the enum type with the Self-Tracking Entity Generator for WPF/Silverlight. First, we covered how to add a new enum type through the Entity Designer. Then, we talked about the main functionality of the new helper class EnumCollection<T>. After that, we discussed how to use the enum in our ViewModel and View classes.

I hope you find this article useful, and please rate and/or leave feedback below. Thank you!

History

  • October, 2012 - Initial release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here