Tomas Grosup
Tomas Grosup

Reputation: 6514

WPF binding Enum list (or similar) to list of checkboxes

I would like to bind a checkbox list to a collection of enum values in WPF. The enum is not [Flags].

Context: It is for filtering a datagrid, in which each item has a instance of my enum. It doesn't necessarily need to bind to an List, a fixed size collection of would work as well.

Upvotes: 3

Views: 6403

Answers (4)

Al Banna Techno logy
Al Banna Techno logy

Reputation: 359

What @Dummy01 says is correct, but I think most of the people will need to get the result as

Dictionary<int,string>

Not

Dictionary<string,int>

So The Solution should be

using System;
using System.Collections.Generic;
using System.Linq;

namespace Sample
{
    class Sample
    {
        public static IDictionary<String, Int32> ConvertEnumToDictionary<K>()
        {
            if (typeof(K).BaseType != typeof(Enum))
            {
                throw new InvalidCastException();
            }
            return Enum.GetValues(typeof(K)).Cast<Int32>().ToDictionary(i => i, i => Enum.GetName(typeof(K), i));
        }
    }
}

So now you will get the KeyValuePair<Int32,string>

This will be Very Useful if you work with enum as FLAGS

[Flags]
public enum LogSystemLogType
{
   All = 1 << 1,
   CreateDatabase = 1 << 2,
   CreateContract = 1 << 3,
}

Upvotes: -1

Nick
Nick

Reputation: 4766

And here's how to do it without any code behind, or DataObjectProviders defined in your view.

Step 1: Create a new class that can store the value of your ListBoxItem, and a property for whether or not it is selected.

It needs to implement INotifyPropetyChanged in order to support two-way binding for whether it is selected or not. I'll make it generic so only needs to be defined once and can be reused for any type. If you already have a base class that implements INotifyPropertyChanged you can just inherit from that and ignore the first few lines of this class.

public class SelectableItem<T> : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
  {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }

  public SelectableItem(T val)
  {
    Value = val;
    _isSelected = false;
  }

  public T Value { get; private set; }

  private bool _isSelected;
  public bool IsSelected
  {
    get => _isSelected;
    set
    {
      if (_isSelected == value) return;
      _isSelected = value;
      OnPropertyChanged();
    }
  }
}

Step 2: Add a collection of SelectableItem property to your ViewModel/DataContext. Since your list of items is a static list of available enum values, there's no need to implement it as an ObservableCollection or anything fancy like that. The ItemsSource only needs a 1 time binding.

public class ViewModel : ViewModelBase
{
  public ViewModel()
  {
    AvailableItems = typeof(TestEnum).GetEnumValues().Cast<TestEnum>().Select((e) => new SelectableItem<TestEnum>(e)).ToList();
  }

  public IEnumerable<SelectableItem<TestEnum>> AvailableItems { get; private set; }
}

Step 3: Use an ItemContainerStyle to allow binding to the IsSelected property of your ListBoxItems:

<ListBox ItemsSource="{Binding AvailableItems, Mode=OneTime}" SelectionMode="Extended">
  <ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
      <Setter Property="Content" Value="{Binding Value}"/>
      <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
    </Style>
  </ListBox.ItemContainerStyle>
</ListBox>

To get the selected items, just iterate through the AvailableItems property and find the items that have IsSelected = true. You can even add a readonly property to do this for you:

public IEnumerable<TestEnum> SelectedItems
{
  get => AvailableItems.Where((o) => o.IsSelected).Select((o) => o.Value).ToList();
}

Upvotes: 1

Botz3000
Botz3000

Reputation: 39610

Assuming you want to bind to all possible values of your enum, you can do it with an ObjectDataProvider. Declare this in your resources (Window.Resources or App.Resources etc.):

    <ObjectDataProvider x:Key="enumValues" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
        <ObjectDataProvider.MethodParameters>
            <x:Type TypeName="local:TestEnum"/>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>

This basically represents a call to Enum.GetValues(typeof(TestEnum)) and exposes it as a data source. Note: You need to declare the namespaces sys and local before, where sys is clr-namespace:System;assembly=mscorlib and local is the namespace of your enum.

Once you have that, you can use that ObjectDataProvider as a Binding source just like anything else, for example:

<ListBox ItemsSource="{Binding Source={StaticResource enumValues}}"/>

The non-declarative way of doing this is just assigning that in code:

someListBox.ItemsSource = Enum.GetValues(typeof(TestEnum));

For binding the selected items, unfortunately the SelectedItems property cannot be set from Xaml, but you can use the SelectionChanged event:

<ListBox Name="lb" ItemsSource="{Binding Source={StaticResource enumValues}}" SelectionMode="Multiple" SelectionChanged="lb_SelectionChanged"></ListBox>

and then set the property on your ViewModel (or whatever you use) in the event:

private void lb_SelectionChanged(object sender, SelectionChangedEventArgs e) {
    viewModel.SelectedValues = lb.SelectedItems.OfType<TestEnum>().ToList();
}

Upvotes: 6

Dummy01
Dummy01

Reputation: 1995

Does this one fits you? It converts any Enum to a Dictionary, so that you can have access to the internal ints of your Enum and also to their names (for display).

using System;
using System.Collections.Generic;
using System.Linq;

namespace Sample
{
    class Sample
    {
        public static IDictionary<String, Int32> ConvertEnumToDictionary<K>()
        {
            if (typeof(K).BaseType != typeof(Enum))
            {
                throw new InvalidCastException();
            }
            return Enum.GetValues(typeof(K)).Cast<Int32>().ToDictionary(currentItem => Enum.GetName(typeof(K), currentItem));
        }
    }
}

EDIT:

You can you use the IDictionary properties Keys and Values which are of type ICollection to do the bindings you want.

myListBox.ItemsSource = myEnumDictionary.Keys;

or of course you can do it directly in XAML.

<ListBox ItemsSource="{Binding myEnumDictionary.Keys}"></ListBox>

Upvotes: 2

Related Questions