WPF绑定ListBox主/细节

5
我可以使用XmlDataSource让它工作,但是我不能用自己的类。我只想将列表框绑定到我的集合实例,然后将文本框链接到列表框,以便我可以编辑人的姓名(双向)。我故意保持这个尽可能简单,希望有人能填补空白。
XAML:
<Window x:Class="WpfListTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfListTest"
    Title="Window1" Height="300" Width="600">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="160"/>
            <ColumnDefinition Width="3"/>
            <ColumnDefinition Width="1*"/>
        </Grid.ColumnDefinitions>
        <DockPanel Grid.Column="0">
            <ListBox />
        </DockPanel>
        <DockPanel Grid.Column="2">
            <StackPanel>
                <Label>Name</Label>
                <TextBox />
            </StackPanel>
        </DockPanel>
    </Grid>
</Window>

C#代码后台:

namespace WpfListTest
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public People MyPeeps = new People();

        public Window1()
        {
            InitializeComponent();

            MyPeeps.Add(new Person("Fred"));
            MyPeeps.Add(new Person("Jack"));
            MyPeeps.Add(new Person("Jill"));
        }
    }

    public class Person
    {
        public string Name { get; set; }

        public Person(string newName)
        {
            Name = newName;
        }
    }

    public class People : List<Person>
    {
    }
}

所有网上的例子似乎都有一个有效的静态类返回代码定义的数据(比如return new Person("blah blah")),而不是我自己的集合实例 - 在这种情况下是MyPeeps。或者我没有说出正确的搜索咒语。
也许有一天我会突然理解这个绑定的东西,但现在它让我困惑了。任何帮助都将不胜感激。

许多事情。与其用不靠谱的代码混淆问题,我选择从零开始,保持简单。实际上,我已经成功地通过一个简单的testListBox.DataContext = MyPeeps语句使列表框包含了项目,但现在我无法将文本框同步。前进一步,后退两步。 - GeoffM
2个回答

15

正确的方法是使用MVVM模式并创建一个ViewModel,如下所示:

public class MainWindowViewModel : INotifyPropertyChanged
{
    private People _myPeeps;
    private Person _selectedPerson;

    public event PropertyChangedEventHandler PropertyChanged;

    public People MyPeeps
    {
        get { return _myPeeps; }
        set
        {
            if (_myPeeps == value)
            {
                return;
            }
            _myPeeps = value;
            RaisePropertyChanged("MyPeeps");
        }
    }

    public Person SelectedPerson
    {
        get { return _selectedPerson; }
        set
        {
            if (_selectedPerson == value)
            {
                return;
            }
            _selectedPerson = value;
            RaisePropertyChanged("SelectedPerson");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

可以在您的视图代码后台初始化它,如下所示:

public partial class MainWindow : Window
{
    private readonly MainWindowViewModel _viewModel;

    public MainWindow()
    {
        _viewModel = new MainWindowViewModel();
        _viewModel.MyPeeps = new People();
        _viewModel.MyPeeps.Add(new Person("Fred"));
        _viewModel.MyPeeps.Add(new Person("Jack"));
        _viewModel.MyPeeps.Add(new Person("Jill"));
        DataContext = _viewModel;

        InitializeComponent();
    }
}

然后像这样绑定数据:

<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Height="350"
        Width="525">
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="160" />
      <ColumnDefinition Width="3" />
      <ColumnDefinition Width="1*" />
    </Grid.ColumnDefinitions>
    <DockPanel Grid.Column="0">
      <ListBox SelectedItem="{Binding SelectedPerson}"
               DisplayMemberPath="Name"
               ItemsSource="{Binding MyPeeps}" />
    </DockPanel>
    <DockPanel Grid.Column="2">
      <StackPanel>
        <Label>Name</Label>
        <TextBox Text="{Binding SelectedPerson.Name}" />
      </StackPanel>
    </DockPanel>
  </Grid>
</Window>

绑定将按如下方式工作:

窗口本身的DataContext设置为ViewModel实例。因为ListBox和TextBox没有指定任何DataContext,它们从Window中继承DataContext。如果没有其他指定,对象上的绑定始终相对于DataContext起作用。这意味着TextBox绑定寻找其DataContext(即MainWindowViewModel)中的SelectedPerson属性,并寻找该SelectedPerson中的Name属性。

此示例的基本机制如下: ViewModel上的SelectedPerson属性始终与ListBoxSelectedItem同步,并且TextBoxText属性始终与SelectedPersonName属性同步。


完美,解释得非常好。谢谢。有时候最简单的解决方案并不是最明显的! - GeoffM
非常有教育意义且直截了当,不像互联网上那些过于复杂的例子。我很惊讶这个答案只有一个投票。谢谢。 - Konrad Morawski

0
尝试让你的People类继承自ObservableCollection<Person>

我已经让列表框本身工作,但是与文本框不同步。更改People类很困难(在我想要使用它的项目中,而不仅仅是这里),因为它是自动生成的。然而,我尝试过了,但没有任何不同!也许如果文本框也能正常工作,它会有所不同。 - GeoffM
这个页面的顶部? - GeoffM
可能是我漏看了什么,但我找不到绑定在哪里。 - Miklós Balogh
没错!这就是我的问题!我该如何绑定?我尝试了很多方法。 - GeoffM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接