Click here to Skip to main content
15,883,883 members
Articles / Desktop Programming / WPF

WPF - Creating Observable Enumerators

Rate me:
Please Sign up or sign in to vote.
4.20/5 (7 votes)
22 Feb 2021CPOL2 min read 7.8K   7   8
Present enumerators in your WPF UI

Introduction

I was sitting around trying to recall WPF-related programming problems I had over the years, and remembered one involving creating observable collections of enumerator values. This happened to me long ago, and I can't find the code where I resolved the issue, but I do remember that it was specific to the enumerator involved, and that's actually a "bad thing" (TM). This article presents a debuggable generic approach, and should work for any enumerator you might decide is important. I won't pretend that this is the only or best way to approach the problem, but I can guarantee that it will work, and is certainly viable (in my eyes, at least). That being said, there are a couple of all-XAML solutions to the issue (and a simple Google search will reveal those methods), but I like the MVVM way of doing things and the debugging capabilities that provides.

This article will admittedly be pretty short, and it will have no screen shots or accompanying downloadable file because all of the source will be presented in a single <pre> block, and will be easily copy/pasteable into your own project.

UIdate!  (2021.02.23) - I posted this article which exercises the concept presented here - WPF - Dedicated Enumerator ListBox and ComboBox[^]

The Problem, and the Solution

In WPF, collections should be observable in order to work well with the UI, but enumerators aren't coducive to being in this manner. My solution is to write an extension method, and a free-standing static method that presents a given enumerator as an ObservableCollection. Without further ado (fuss, work, or delay, as opposed to the C# ADO framework), here's the code:

C#
using System;
using System.Collections.ObjectModel;

namespace ObjectExtensions
{
    public class EnumItem
    {
        public object Value          { get; set; }
        public string Name           { get; set; }
        public Type   EnumType       { get; set; }
        public Type   UnderlyingType { get; set; }
    }

    public static class ExtendEnum
    {
        /// <summary>
        /// Get a list of all items in the enumerator assiated with the one we called <br/>
        /// this method with. Example: DayOfWeek.Monday.AsObservableEnum will return <br />
        /// all items in the DayOfWeek enumerator.
        /// </summary>
        /// <param name="enumObj">An enumerator value, like DayOfWeek.Monday</param>
        /// <returns>ObservableCollection of items in the parent enumerator</returns>
        public static ObservableCollection<EnumItem> AsObservableEnum(this Enum enumObj)
        {
            // get our enumerator type, and call the plain static method
            Type enumType = enumObj.GetType();
            return AsObservableEnum(enumType);
        }

        /// <summary>
        /// Get a list of all items in the specified enumarator type.
        /// </summary>
        /// <param name="enumType">The enumerator type</param>
        /// <returns>ObservableCollection of items in the specified enumerator, or <br/>
        /// null is no enumerator was specified</returns>
        public static ObservableCollection<EnumItem>AsObservableEnum(Type enumType)
        {
            // set a predictable value
            ObservableCollection<EnumItem> list = null;
            // if the specified type is not nukll AND it is actually an enum type, 
            // we can create the collection
            if (enumType != null && enumType.IsEnum)
            {
                // discover the underlying type (int, long, byte, etc)
                Type underlyingType = Enum.GetUnderlyingType(enumType);
                // create the list
                list = new ObservableCollection<EnumItem>();
                // get each enum item and add it to the list
                foreach (Enum item in enumType.GetEnumValues())
                {
                    list.Add(new EnumItem()
                    { 
                        // the name that will probably be displayed in the UI component
                        Name           = item.ToString(), 
                        // the actual enum value (DayofWeek.Monday)
                        Value          = item, 
                        // the enum type
                        EnumType       = enumType,
                        // the underlying type (int, long, byte, etc)
                        UnderlyingType = underlyingType
                    });
                }
            }
            return list;
        }
    }
}

Using the code

Usage is typical in your standard WPF application. Since enumerators don't change their contents beyond their actual definition, it would be a good idea to somehow create the resulting observable enum collection as a static object (contained in a global static class or as a singleton object, or even a combination of both). Creating it once as a static object means you won't be spending time re-allocating the object over and over, which will prevent heap fragmentation and ultimately save CPU cycles. For simple testing, though, you can do the following in your window's code-behind:

C#
public class MainWindow : Window, INotifyPropertyChanged
{
    private ObservableCollection<EnumItem> enums;
    public ObservableCollection<EnumItem> Enums
    { 
        get { return this.enums; } 
        set { if (value != this.enums) { this.enums = value; this.NotifyPropertyChanged(); } } 
    }

    public MainWindow()
    {
        this.InitializeComponent();
        this.DataContext = this;

        this.Enums = DayOfWeek.Monday.AsObservableEnum();
        // or 
        //this.Enums = ExtendEnum.AsObservableEnum(typeof(DayOfWeek));
    }
}

And in your XAML, you might want to use a ListBox to allow selection of the presented enumerator:

XML
<ListBox ItemsSource="{Binding Path=Enums}" Height="120" Width="120" DisplayMemberPath="Name"/>

Once an enum value has been selected in your UI, you have complete access to the parent enum type for whatever purpose you might come up with.

Points of Interest

I maintain an assembly that contains nothing but extension methods, and this technique has been added to it. Each data type gets its own file, so for instance, I have classes/files called ExtendXMLToLinq, ExtendString, ExtendIEnumerable, etc. I recommend that if you do a lot of c# coding, that you start your own assembly. Believe me, your coding life will be easier if you start doing this now.

History

  • 2021.02.22 - Initial publication.
     

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Paddedwall Software
United States United States
I've been paid as a programmer since 1982 with experience in Pascal, and C++ (both self-taught), and began writing Windows programs in 1991 using Visual C++ and MFC. In the 2nd half of 2007, I started writing C# Windows Forms and ASP.Net applications, and have since done WPF, Silverlight, WCF, web services, and Windows services.

My weakest point is that my moments of clarity are too brief to hold a meaningful conversation that requires more than 30 seconds to complete. Thankfully, grunts of agreement are all that is required to conduct most discussions without committing to any particular belief system.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Pete O'Hanlon24-Feb-21 7:57
mvePete O'Hanlon24-Feb-21 7:57 
GeneralRe: My vote of 5 Pin
#realJSOP24-Feb-21 9:02
mve#realJSOP24-Feb-21 9:02 
QuestionUpcoming Update Pin
#realJSOP23-Feb-21 2:57
mve#realJSOP23-Feb-21 2:57 
GeneralMy vote of 5 Pin
Klaus Luedenscheidt22-Feb-21 18:30
Klaus Luedenscheidt22-Feb-21 18:30 
GeneralRe: My vote of 5 Pin
#realJSOP23-Feb-21 2:02
mve#realJSOP23-Feb-21 2:02 
GeneralMy vote of 5 Pin
tarco22-Feb-21 8:21
professionaltarco22-Feb-21 8:21 
QuestionIf You're Gonna Down-vote Pin
#realJSOP22-Feb-21 4:24
mve#realJSOP22-Feb-21 4:24 
AnswerRe: If You're Gonna Down-vote Pin
PureNsanity2-Jun-21 17:30
professionalPureNsanity2-Jun-21 17:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.