WPF Explorer-like MVVM ListBox-in-ListBox(主 - 详细信息)绑定表达式

时间:2011-12-18 13:41:25

标签: wpf xaml binding mvvm master-detail

我的情况的一行摘要:当我选择一个详细项目时,主人仍然未被选中,所以我的错误细节项目。

假设两个ViewModel MasterList和DetailList是ObservableCollection,并尝试制作类似Explorer的GUI。左侧面板具有主 - 详细信息列表框,右侧面板将显示所选详细信息项之一。没有必要在右侧面板中显示主信息。

在我的左侧面板上,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>

发生了问题的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绑定。

你能教我正确的约束表达吗?

2 个答案:

答案 0 :(得分:0)

你有没有尝试过:

<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是否实际上是级联的。

答案 1 :(得分:0)

您不应使用多个列表框,因为会有许多与多项选择相关的问题。例如,您可以选择第一个列表框,但选择第二个列表框中的项目。

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>