WPF-подобное MVVM выражение привязки ListBox-in-ListBox (master-detail) - PullRequest
1 голос
/ 18 декабря 2011

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

Предположим, что два ViewModel MasterList и DetailList являются ObservableCollection, и попробуйте создать похожий на Explorer графический интерфейс. Левая панель имеет списки основных деталей, а на правой панели отображается один из выбранных элементов подробностей. нет необходимости показывать основную информацию на правой панели.

На моей левой панели элементы управления ListBox кодируются, как показано ниже.

<ListBox x:Name="listBoxMaster" ItemsSource="{Binding Path=MasterList}" SelectionMode="Extended"
            IsSynchronizedWithCurrentItem="True">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <ListBox x:Name="listBoxDetail" ItemsSource="{Binding Path=DetailList}" IsSynchronizedWithCurrentItem="True" />
        </DataTemplate>
</ListBox.ItemTemplate>

Обратите внимание, что я установил IsSynchronizedWithCurrentItem = "True" для обоих элементов управления ListBox.

и на правой панели информация о выбранном элементе детали будет отображаться с привязкой, как показано ниже. Давайте упростим класс элемента детализации с именем DetailItemClass, который имеет свойства Name и Number.

Пробная версия 1

<WrapPanel Name="wrapPanel1" DataContext="{Binding ElementName=listBoxMaster, Path=SelectedItem}">
    <StackPanel DataContext="{Binding Path="DetailList/">
        <TextBox Text="{Binding Path=Name}" />
        <TextBox Text="{Binding Path=Number}" />
    </StackPanel>
</WrapPanel>

Ошибка weired GUI - случайная. если у нас есть.

  • Master1 имеет детали 1,2,3.
  • Master2 имеет детали 4,5,6.

Когда я выбираю M1-D2, это работает. после этого, когда я выбираю деталь M2-D4, все работает. НО, после этого, когда я снова выбираю M1-D2, он НЕ работает! предварительно выбранный элемент может не обновлять свой мастер для выбора. Более того, иногда выбор M2-D5 дает M1-D2 на правой панели. weired.

Пробная версия 2

<WrapPanel Name="wrapPanel1" DataContext="{Binding Path=MasterList/DetailList/}">
    <StackPanel>
        <TextBox Text="{Binding Path=Name}" />
        <TextBox Text="{Binding Path=Number}" />
    </StackPanel>
</WrapPanel>

Просто не работает. Хотя я установил для IsSynchronizedWithCurrentItem значение true, но не удалось. Не знаю почему.

Пробная версия 3

Я просто избавляюсь от привязок DataContext XAML и использую триггер события при выделении кода, как показано ниже.

    private void listBoxMaster_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems.Count == 1)
        {
            if (e.AddedItems[0] is DetailItemClass)
            {
                var element = (DetailItemClass)e.AddedItems[0];
                wrapPanel1.DataContext = element;
            }
        }
    }

Работает очень хорошо, но не использует XAML Binding.

Не могли бы вы научить меня правильному обязательному выражению?

Ответы [ 2 ]

0 голосов
/ 18 декабря 2011

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

multiple list boxes fail

Замените внешний список на класс ItemsControl, чтобы он не получал выделение и не фокусировался.

Также добавьтеединственное свойство для корневой модели, которое содержит один выбранный элемент для всех списков.Я воспроизвел вашу иерархию, используя очень простые модели, и в этом примере это свойство называется SelectedDetailItem:

public class MasterListViewModel : INotifyPropertyChanged
{
    public ObservableCollection<DetailListViewModel> MasterList { get; set; }

    private ItemViewModel _selectedDetailItem;

    public ItemViewModel SelectedDetailItem
    {
        get { return _selectedDetailItem; }
        set
        {
            _selectedDetailItem = value;
            OnPropertyChanged("SelectedDetailItem");
        }
    }
}

public class DetailListViewModel
{
    public ObservableCollection<ItemViewModel> DetailList { get; set; }
}

public class ItemViewModel
{
    public string Title { get; set; }
}

И привяжите это свойство к обоим элементам управления:

<ItemsControl x:Name="listBoxMaster" ItemsSource="{Binding Path=MasterList}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ListBox x:Name="listBoxDetail" ItemsSource="{Binding Path=DetailList}" DisplayMemberPath="Title" 
                        SelectedItem="{Binding DataContext.SelectedDetailItem, Mode=TwoWay, ElementName=listBoxMaster}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

<WrapPanel Name="wrapPanel1" DataContext="{Binding SelectedDetailItem}" Grid.Column="1">
    <TextBox Text="{Binding Path=Title}" />
</WrapPanel>
0 голосов
/ 18 декабря 2011

Вы пробовали:

<WrapPanel Name="wrapPanel1" DataContext="{Binding ElementName=listBoxMaster, Path=SelectedItem}">
    <StackPanel>
        <TextBox Text="{Binding Path=DetailList.Name}" />
        <TextBox Text="{Binding Path=DetailList.Number}" />
    </StackPanel>
</WrapPanel>

или

<WrapPanel Name="wrapPanel1">
    <StackPanel>
        <TextBox Text="{Binding ElementName=listBoxMaster, Path=SelectedItem.DetailList.Name}" />
        <TextBox Text="{Binding ElementName=listBoxMaster, Path=SelectedItem.DetailList.Number}" />
    </StackPanel>
</WrapPanel>

Они предназначены для проверки того, действительно ли DataContext является каскадным.

...