Search code examples
c#wpfxamlmvvmenums

Putting Enum Description in an IEnumerable and ComboBox


I am still getting used to MVVM so if there is a better way to do this let me know. I do have to stick to a certain way of doing things so everything at the company I work at stays somewhat standard.I am trying to populate a combobox with the descriptions from my Enum class. I think this is an easy task, but I am not sure how to go about it. Any help is appreciated. If you need more code or information, please let me know. Code is below.

I have my Descriptions in a class called Enum.cs:

[TypeConverter(typeof(EnumToStringUsingDescription))]    
    public enum ArTypes
    {
        [Description("(All)")]
        arAll = 0,
        [Description("Adjustment")]
        [EnumInformation("Adjustment", true, 1)]
        arAdjustment = 1,
        [Description("Payment")]
        [EnumInformation("Payment", true, 2)]
        arPayment = 3,
        [Description("Deposit Receipt")]
        [EnumInformation("Deposit Receipt", true, 3)]
        arDepositReceipt = 5,
        [Description("Deposit Applied")]
        [EnumInformation("Deposit Applied", true, 4)]
        arDepositApplied = 7,
        [Description("Bad Debt Transfer")]
        [EnumInformation("Bad Debt Transfer", true, 5)]
        arBadDebtTransfer = 9,
        [Description("Bad Debt Writeoff")]
        [EnumInformation("Bad Debt Writeoff", true, 6)]
        arBadDebtWriteoff = 11,
        [Description("Bad Debt Recovery")]
        [EnumInformation("Bad Debt Recovery", true, 7)]
        arBadDebtRecovery = 13,
        [Description("Charge")]
        [EnumInformation("Charge", true, 8)]
        arCharge = 15,
        [Description("Immediate Case Receipt")]
        [EnumInformation("Immediate Cash Receipt", true, 9)]
        arImmediateCashReceipt = 17,
        [Description("Over Payment")]
        [EnumInformation("Over Payment", true, 10)]
        arOverPayment = 19,
        [Description("Balance Forward")]
        [EnumInformation("Balance Forward", true, 11)]
        arBalanceForward = 21
    }

In my viewmodel I have the code to fill my combobox:

public IEnumerable<ArTypes> GetAllArTypesData()
{
    try
    {
        // I believe the logic for setting the IEnumerable would go here
    }
    catch (Exception ex)
    {
        LogException(ex);
    }

    return null;
}

To populate the combobox I am running this:

void PopulateComboLists()
{
    CycleList = new ObservableCollection<Cycle>();
    ServiceTypeList = new ObservableCollection<ServiceType>();
    RateList = new ObservableCollection<Rate>();
    CustomerTypeList = new ObservableCollection<CustomerType>();
    ArCodeList = new ObservableCollection<Arcode>();
    ArTypeList = new ObservableCollection<ArTypes>();

    CycleList.Add(new Cycle() { CycleID = -1, CycleDescription = "(All)" });
    ServiceTypeList.Add(new ServiceType() { ServiceTypeID = -1, ServiceDescription = "(All)" });
    CustomerTypeList.Add(new CustomerType() { CustomerTypeID = -1, Description = "(All)" });
    ArCodeList.Add(new Arcode() { ArcodeID = -1, Description = "(All)" });

    foreach (var item in cycles)
    {
        CycleList.Add(item);
    }

    foreach (var item in serviceTypes)
    {
        ServiceTypeList.Add(item);
    }

    foreach (var item in rates)
    {
        RateList.Add(item);
    }

    foreach (var item in custTypes)
    {
        CustomerTypeList.Add(item);
    }

    foreach (var item in arCodes)
    {
        ArCodeList.Add(item);
    }

    foreach (var item in arTypes)
    {
        ArTypeList.Add(item);
    }
}

Here is where the data is loaded onto the UI:

protected override void LoadDataStart()
{
    // Insert IsBusy and any other logic needed before database calls //
    this.IsBusy = true;
    this.BusyMessage = "Generating Widget...";

    try
    {
        // Create a method for each database call - start a new task for each //
        var taskCTR = Task.Factory.StartNew(() => GetAllCustomerTypeReportsData()).ContinueWith(results => GetAllCustomerTypeReportsDataContinue(results.Result));
        var taskST = Task.Factory.StartNew(() => GetAllServiceTypesData()).ContinueWith(results => GetAllServiceTypesDataContinue(results.Result));
        var taskRL = Task.Factory.StartNew(() => GetAllRatesData()).ContinueWith(results => GetAllRatesDataContinue(results.Result));
        var taskCL = Task.Factory.StartNew(() => GetAllCyclesData()).ContinueWith(results => GetAllCyclesDataContinue(results.Result));
        var taskCT = Task.Factory.StartNew(() => GetAllCustomerTypesData()).ContinueWith(results => GetAllCustomerTypesDataContinue(results.Result));
        var taskAC = Task.Factory.StartNew(() => GetAllArCodesData()).ContinueWith(results => GetAllArCodesDataContinue(results.Result));
        var taskAT = Task.Factory.StartNew(() => GetAllArTypesData()).ContinueWith(results => GetAllArTypesDataContinue(results.Result));

        Task[] allTasks = new Task[7] { taskCTR, taskST, taskRL, taskCL, taskCT, taskAC, taskAT };

        Task.Factory.ContinueWhenAll(allTasks, loadDataContinue =>
                {
                    Action executeContinue = () =>
                    {
                        PopulateComboLists();

                        if (CanLoad)
                        {
                            LoadDataContinue();
                            IsBusy = false;
                        }
                        else
                            ShowOptionsView();
                    };
                    this.UIDispatcher.Invoke(executeContinue);
                });
    }
    catch (Exception ex)
    {
        LogException(ex);
    }

    // Base class will call LoadDataContinue() once the database calls are complete //
}

XAML:

<Label VerticalAlignment="Center" Margin="5,0,0,0" Content="ArType: " Grid.Row="5" Grid.Column="0"></Label>
        <telerik:RadComboBox ItemsSource="{Binding ArTypeList}"
                             DisplayMemberPath="Key"
                             SelectedValuePath="Value"
                             HorizontalAlignment="Left" Width="190"
                             SelectedValue="{Binding Path=SelectedArType, Mode=TwoWay, ValidatesOnDataErrors=True}" 
                             TabIndex="5"  Grid.Row="5" VerticalAlignment="Center" Grid.Column="1"
                             Style="{StaticResource RadComboBoxStyle}" />

Solution

  • If you're asking how to collect the values from the DescriptionAttribute on each enum instance, you can do it something like this:

    ObservableCollection descriptions = new ObservableCollection();
    foreach (object instance in Enum.GetValues(typeof(ArTypes)))
    {
        FieldInfo fieldInfo = typeof(ArTypes).GetField(instance.ToString());
        object[] customAttributes = 
            fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
        if (customAttributes != null && customAttributes.Length > 0)
            descriptions.Add(((DescriptionAttribute)customAttributes[0]).Description);
    }
    

    There could be an error in there somewhere as I can't test this out at the moment, so please let me know if there is.