Самый простой способ привязать список флажков к списку отмеченных значений

У меня есть список AvailableItems, который я хочу отобразить в виде списка флажков, чтобы пользователи могли выбирать, какие элементы сгенерировать, которые затем сохраняются в другом списке под названием ItemsToGenerate (мои списки на самом деле являются списками строк).

Показать все доступные элементы с соответствующими флажками легко:

<ItemsControl ItemsSource="{Binding Path=AvailableItems}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <CheckBox Content="{Binding}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>    
</ItemsControl>

Но теперь мне нужно привязать каждое свойство Checkbox.IsChecked к тому, что элемент находится в списке ItemsToGenerate. Я думал сделать ListContainmentToBoolConverter следующим образом:

IsChecked="{Binding Path=ItemsToGenerate, 
            Converter={StaticResource ListContainmentToBoolConverter}}"

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

Любые идеи?

Ответ 1

Я нашел решение моей проблемы.

Я изменил свой ItemsControl на ListBox и добавил привязку между SelectedItems с моей коллекцией ItemsToGenerate, используя описанную технику здесь. В основном это позволяет мне синхронизировать любую пользовательскую коллекцию с ListBox.SelectedItems с помощью простого прикрепленного свойства.

<ListBox ItemsSource="{Binding AvailableItems}"
         Behaviors:MultiSelectorBehaviours.SynchronizedSelectedItems=
             "{Binding ItemsToGenerate}"
         SelectionMode="Multiple"
         Background="{x:Null}">  
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel />                    
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <CheckBox Content="{Binding}"
                      Margin="3"
                      IsChecked="{Binding RelativeSource=
                           {RelativeSource Mode=FindAncestor,
                            AncestorType={x:Type ListBoxItem}},
                           Path=IsSelected}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ListBox>

Я все еще могу отобразить это, как мне захотелось (список флажков), добавив шаблон данных, чтобы изменить каждый ListBoxItem на флажок и привязать каждый Checkbox.IsChecked к ListBoxItem.IsSelected.

У меня был такой шаблон во многих местах моего приложения, что это идеальное решение для меня, потому что теперь мне просто нужно указать одно прикрепленное свойство, а остальное все обрабатывается привязками данных, и я не нужен какой-либо дополнительный код.

Ответ 2

Я честно создаю список объектов, содержащих как строку, так и логическое значение, указывающее, проверено ли оно.

С небольшим Linq вы можете сгенерировать свой список объектов и привязать его к itemSource вместо привязки списка строк.

В конце концов, это будет проще, особенно если вам действительно нужно что-то обновить, если пользователю разрешено проверять/снимать флажки.

== update ==

в ответ на комментарий, я беру на себя это, потому что я не уверен, что понимаю, в чем проблема: если у нас есть полный список (AvailableItems) и список выбранных элементов (ItemsToGenerate):

public class ItemEntry
{
  public string Name { get; set; }
  public bool IsSelected {get; set; }
}

...

_Items = from item in AvailableItems
            select new ItemEntry() { 
                    Name = item, 
                    IsSelected = ItemsToGenerate.contains(item)
                  }).ToList();

Затем вы можете привязать свой список так, обнажив _Items как свойство с именем Items:

<ItemsControl ItemsSource="{Binding Path=Items}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <CheckBox Content="{Binding Name}" IsChecked="{Binding IsSelected}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>    
</ItemsControl>

Вы можете в более позднее время выбрать из _Items, где IsSelected является истинным, чтобы получить выбранные элементы, если вам нужно.

Кроме того, если ItemsToGenerate может стать большим, вы должны создать HashSet значений и использовать его в запросе, который должен сделать его быстрее, если потребуется.