TwoWay Binding для Dictionary<Enum, bool?>

312
29 апреля 2017, 20:16

Есть несколько checkbox'ов с тремя состояниями: включено, исключено, не установлено.

Эти checkbox'ы представляют из себя фильтры. В ViewModel они реализованы в виде словаря, где ключ это определённый фильтр (checkbox), а значение это его состояние.

Ключ у меня это тип перечисления. Каждая категория фильтров это отдельный словарь со своим перечислимым типом в ключе.

Например, вот Статус:

public enum TitleStatus { anons, ongoing, latest, released }
...
Dictionary<TitleStatus, bool?> TitleStatusFilter { get; set; }

Как словарь привязать словарь к checkbox'ам? Когда я пытаюсь сделать что-то вроде

<!-- xmlns:lib="clr-namespace:ShikiApiLib;assembly=ShikiApiLib" -->
<CheckBox IsChecked="{Binding Path=TitleStatus[ShikiApiLib.TitleStatus.released]}" IsThreeState="True">Вышедшее</CheckBox>
<CheckBox IsChecked="{Binding Path=TitleStatus[lib:TitleStatus.released]}" IsThreeState="True">Вышедшее</CheckBox>
<CheckBox IsChecked="{Binding TitleStatus[ShikiApiLib.TitleStatus.released]}" IsThreeState="True">Вышедшее</CheckBox>
<CheckBox IsChecked="{Binding TitleStatus[lib:TitleStatus.released]}" IsThreeState="True">Вышедшее</CheckBox>

Ни один из способов не работает, ибо TitleStatus всё время null, независимо от положения checkbox'ов =/

Answer 1

Непосредственно на ваш вопрос: будет работать такой простой XAML:

<CheckBox IsThreeState="True" IsChecked="{Binding TitleStatus[released]}">Released</CheckBox>
<CheckBox IsThreeState="True" IsChecked="{Binding TitleStatus[ongoing]}">Ongoing</CheckBox>
<CheckBox IsThreeState="True" IsChecked="{Binding TitleStatus[anons]}">Announced</CheckBox>

внутри StackPanel показывает следующее:

Дело в том, что для enum-значений есть конвертер, который конвертирует из строк в enum. И он в некоторых случаях (когда framework знает ожидаемый тип) применяется.

Но это скорее всего не будет работать как вам нужно: словари не имплементируют INotifyPropertyChanged, а значит, изменённые значения не будут подхватываться. То есть если в другой части программы будет изменено значение в словаре, ваш UI этого «не заметит».

На VM-уровне заведите просто ObservableCollection классов, которые имплементируют INotifyPropertyChanged и содержат и ключ, и значение как свойства.

Получится что-то такое:

class FilterItemVM : VM
{
    public FilterItemVM(TitleStatus status) { Status = status; }
    public TitleStatus Status { get; }
    bool? v;
    public bool? Value
    {
        get { return v; }
        set { Set(ref v, value); }
    }
}

(базовый класс VM можно взять, например, тут)

Ваш XAML будет, например, таким:

<ItemsControl ItemsSource="{Binding TitleStatus}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <CheckBox
                IsThreeState="True"
                Content="{Binding Status}"
                IsChecked="{Binding Value}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Результат:

Обратите внимание, что здесь у вас здесь имена берутся из названий enum-элементов, так что для «красивых» имён может понадобиться дополнительный конвертер.

Обновление: Поскольку список у вас в реальности неизменный, то ObservableCollection по идее не нужна. Для этого случая подойдёт комбинированный вариант с INotifyPropertyChanged:

class FilterItemValueVM : VM
{
    bool? v;
    public bool? Value
    {
        get { return v; }
        set { Set(ref v, value); }
    }
}
TitleStatus = new Dictionary<TitleStatus, FilterItemValueVM>
{
    [TitleStatus.released] = new FilterItemValueVM() { Value = false },
    [TitleStatus.ongoing] = new FilterItemValueVM() { Value = null },
    [TitleStatus.anons] = new FilterItemValueVM() { Value = true }
}
<StackPanel>
    <CheckBox IsThreeState="True" IsChecked="{Binding TitleStatus[released].Value}">
        Released
    </CheckBox>
    <CheckBox IsThreeState="True" IsChecked="{Binding TitleStatus[ongoing].Value}">
        Ongoing
    </CheckBox>
    <CheckBox IsThreeState="True" IsChecked="{Binding TitleStatus[anons].Value}">
        Announced
    </CheckBox>
</StackPanel>

Результат выглядит так же, как и на первой картинке.

READ ALSO
Имя таблицы через переменную в запросе Mysql

Имя таблицы через переменную в запросе Mysql

Есть множество однотипных таблиц, различных только по именам и даннымКоличество столбцов и их названия идентичны

370
C# Вложенность при парсинге скобок

C# Вложенность при парсинге скобок

Необходимо написать программу, которая будет получать на вход строку и выводить эту же строку, с учётом расставленных скобок и их вложенности

295
BitmapImage Не удается декодировать изображение

BitmapImage Не удается декодировать изображение

Сохраняю изображение в массив байтов, перед этим произвел конвертацию изображения в JPEG

265
Удаление элемента из списка при условии C#

Удаление элемента из списка при условии C#

У меня есть некий список, в котором находятся обьектыК примеру есть тигр, рыба,

349