ViewModel и привязка для динамически добавляемых комбинированных списков - PullRequest
1 голос
/ 28 июля 2010

Я не нашел решения для этого, но я думаю, что это должно быть выполнимо.

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

  • Item1 CanInclude: Item4, Item5
  • Item2 CanInclude: Item3, Item4
  • Item3 CanInclude: Item2
  • Item4 CanInclude: Item1
  • Item5 CanInclude: Item2, Item3

Начальный элемент выбран в другом месте.

Поэтому, если начальный элемент - Item1, я хочу иметь комбинированный список с Item4 и Item5.Если я выберу Item5 в этом поле со списком и нажму кнопку «+», я хочу получить новый Box с Item2, Item3 (из последнего флажка) и Item4 (из начального элемента) и так далее, пока не появится другой элемент, который можетбыть выбран или пользователь нажимает «ОК».

Я думал о простой коллекции в viewmodel, где [0] содержит начальный элемент, [1] выбранный элемент из 1. comboBox и так далее.Но я не знаю, как мне динамически добавлять comboBox или позволить comboBox создать элемент [n] в коллекции выбранных элементов.Также я не могу придумать способ включить все элементы свойств CanInclude уже выбранных элементов в новый флажок.

Буду очень признателен, если у кого-нибудь возникнет идея.

РЕДАКТИРОВАТЬ: Просто для пояснения я хочу что-то вроде этого (псевдокод включен, так как вы не можете сделать {Binding} + {Binding}, но я думаю, что вы поняли):

<ComboBox ItemsSource="{Binding Path=SelectableItems}" SelectedItem="{Binding Path=SelectedItem1}" />
<ComboBox ItemsSource="{Binding Path=SelectedItem1.CanInclude}" SelectedItem="{Binding Path=SelectedItem2}"/>
<ComboBox ItemsSource="{Binding Path=SelectedItem1.CanInclude} + {Binding Path=SelectedItem2.CanInclude} - {Binding Path=SelectedItem1} - {Binding Path=SelectedItem2}" SelectedItem="{Binding Path=SelectedItem3}"/>

Но я хочу, чтобы это работало для не фиксированного количества записей.

1 Ответ

1 голос
/ 28 июля 2010

Вы можете создать шаблон для ListBox, чтобы каждый элемент представлял собой комбинированный список.Таким образом, вы можете привязать источник данных ListBox к ObservableCollection ViewModels, которые представляют элементы.Что-то вроде:

public class TopLevelViewModel
{
    private List<ItemViewModel> _allItems;

    public ObservableCollection<ItemViewModel> CurrentlySelectedItems { get; set; }

    public TopLevelViewModel()
    {
         DefineAllItems()
         SelectFirstItem()
    }

    private void DefineAllItems()
    {
        ItemViewModel item1 = new ItemViewModel { Name = "Item1" }
        item1.SelectedItemChanged += HandleItemViewModelSelectedItemChanged;

        ItemViewModel item2 = new ItemViewModel { Name = "Item2" }
        item2.SelectedItemChanged += HandleItemViewModelSelectedItemChanged;

        ItemViewModel item3 = new ItemViewModel { Name = "Item3" }
        item3.SelectedItemChanged += HandleItemViewModelSelectedItemChanged;

        item1.CanInclude = new ObservableCollection<ItemViewModel>
        {
              item2, item3
        }

        item2.CanInclude = new ObservableCollection<ItemViewModel>
        {
              item3
        }

        _allItems = new List<ItemViewModel>
        {
              item1, item2, item3
        }            
    }

    private void SelectFirstItem()
    {
         //Add item1 as the first combobox
         CurrentlySelectedItems.Add(_allItems[0]);
    }

    private void HandleItemViewModelSelectedItemChanged(object sender, EventArgs e)
    {
        ItemViewModel parent = (ItemViewModel)sender;

        //Find the view model whose item has changed in the CurrentlySelectedItems
        int indexOfParent = CurrentlySelectedItems.IndexOf(parent);

        //Remove all itemviewmodels below that item
        CurrentlySelectedItems.RemoveRange(
                   indexOfParent+1,
                   CurrentlySelectedItems.Count-(indexofParent+1))

        //Add the selected item into the list
        CurrentlySelectedItems.Add(parent.SelectedItem);            
    }
}

public class ItemViewModel
{
    public string Name { get; set; }
    public ObservableCollection<ItemViewModel> CanInclude { get; set; }
    public ItemViewModel SelectedItem { get; set; }

    public event EventHandler SelectedItemChanged;
}

Затем свяжите ListBox с CurrentSelectedItems и ComboBox внутри шаблона с CanInclude:

<ListBox ItemsSource="{Binding CurrentlySelectedItems}">
   <ListBox.ItemTemplate>
       <DataTemplate>
          <ComboBox ItemsSource="{Binding CanInclude}"
                    DisplayMemberPath="{Binding Title}"
                    SelectedItem="{Binding SelectedItem}"/>
       </DataTemplate>
   </ListBox.ItemTemplate>
</ListBox>

Отредактировано, чтобы добавить решение, которое лучше подходитпроблема:

Xaml:

<ListBox ItemsSource="{Binding PreviouslySelectedItems}"/>
<ComboBox ItemsSource="{Binding CanInclude}"
          DisplayMemberPath="{Binding Title}"
          SelectedItem="{Binding SelectedItem}"/>
<Button Content="Add"
        Command="{Binding AddCommand}"/>

ViewModel:

public class TopLevelViewModel
{
      public ObservableCollection<ItemViewModel> PreviouslySelectedItems { get; private set; }
      public ObservableCollection<ItemViewModel> CanInclude { get; private set; }
      public ItemViewModel SelectedItem { get; set; }

      //Set up all the items as above, and pre-populate the first item
      //and the initial CanInclude options.

      //When the Add button is clicked
      public void ExecuteAdd()
      {
           //Add the currently selected item to the Listbox
           PreviouslySelectedItems.Add(SelectedItem)

           //Rebuild the CanInclude list
           CanInclude.Clear();

           var newCanInclude =
               PreviouslySelectedItems.SelectMany(x => x.CanInclude)
                                      .Where(x => !PreviouslySelectedItems.Contains(x))

           CanInclude.AddRange(newCanInclude);
      }
}
...