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

大兔子大兔子 提交于 2020-01-24 12:15:23

问题


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.


回答1:


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



回答2:


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>



回答3:


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


来源:https://stackoverflow.com/questions/10310595/wpf-binding-enum-list-or-similar-to-list-of-checkboxes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!