WPF Master-Details 视图与列表框和组合框与绑定

Posted

技术标签:

【中文标题】WPF Master-Details 视图与列表框和组合框与绑定【英文标题】:WPF Master-Details view with Listbox and Combobox with Binding 【发布时间】:2010-11-15 22:29:59 【问题描述】:

几天来,我一直在寻找问题的答案,但找不到解决方案。

问题是组合框用之前选择的用户更新了 User 类中的 Test 对象。

即你选择 user2 并且 user2 有 test2,然后你选择 user5 有 test5。现在如果你再次选择user2,它会显示它有test5。

这是一些代码。我有两个类用户和测试。每个都有两个 ObservableCollections。这就是我设置它们的方式:

public class User

    public string Name  get; set; 
    public int test  get; set; 
    public test userTest  get; set; 


public class test

    public int ID  get; set; 
    public String Name  get; set; 


public class ListOfTests:ObservableCollection<test>

    public ListOfTests()
    
        for (int i = 0; i < 4; i++)
        
            test newTest = new test();
            newTest.ID = i;
            newTest.Name = "Test " + i;
            Add(newTest);
        
    


public class ListOfUsers: ObservableCollection<User>

    public ListOfUsers()
    
        ListOfTests testlist = new ListOfTests();
        for (int i = 0; i < 10; i++)
        
            User newUser = new User();
            newUser.Name = "User " + i;
            newUser.ID = i;
            newUser.userTest = testlist[i];
            Add(newUser);
        
    

XAML 是:

<Window x:Class="ComboboxTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ComboboxTest"
Title="Window1" Height="300" Width="300">
<StackPanel x:Name="SP1">
    <StackPanel.Resources>
        <local:ListOfTests x:Key="ListOfTests" />
    </StackPanel.Resources>
    <ListBox ItemsSource="Binding" DisplayMemberPath="Name" IsSynchronizedWithCurrentItem="True"/>
    <TextBox Text="Binding Path=Name" Foreground="Black"  />
    <TextBox Text="Binding Path=userTest" />  
    <ComboBox SelectedItem="Binding Path=userTest" 
              SelectedValue="Binding Path=userTest.ID"
              ItemsSource="Binding Source=StaticResource ListOfTests" 
              DisplayMemberPath="Name" 
              SelectedValuePath="ID"

              Foreground="Black" />
</StackPanel>

现在,如果我将 SelectedItem 上的绑定更改为“Binding Path=userTest, Mode=OneWay”,那么它可以工作,但我无法手动更改它。

这是一个有趣的想法...如果我以 .Net 4.0 (VS2010) 为目标,那么它可以正常工作...

谁能帮我解决这个问题?

【问题讨论】:

【参考方案1】:

如果我理解您的问题,听起来 WPF 在属性值更改时不会收到通知。你可以通过实现INotifyPropertyChanged 接口来解决这个问题。例如,User 类看起来像这样:

public class User : INotifyPropertyChanged

    private string name = string.Empty;
    public string Name
    
        get  return this.name; 
        set
        
            this.name = value;
            this.OnPropertyChanged("Name");
        
    

    private int test = 0;
    public int Test
    
        get  return this.test; 
        set
        
            this.test = value;
            this.OnPropertyChanged("Test");
        
    

    private test userTest = null;
    public test UserTest
    
        get  return this.userTest; 
        set
        
            this.userTest = value;
            this.OnPropertyChanged("UserTest");
        
    

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propName)
    
        PropertyChangedEventHandler eh = this.PropertyChangd;
        if(null != eh)
        
            eh(this, new PropertyChangedEventArgs(propName));
        
    

您可能也应该为您的 test 课程做同样的事情。

WPF 将监视 PropertyChanged 事件何时触发,并根据需要更新任何受影响的绑定。这应该会导致 ComboBox 中的选定项目更改回 user2 的测试。

更新:好的,我想我已经成功了。我认为您在发布的内容中遗漏了部分代码(例如WindowDataContext 是什么),但这是我的工作:

我创建了一个名为ViewModel 的类,它设置为主WindowDataContext。这是它的代码:

class ViewModel : INotifyPropertyChanged

    public ViewModel()
    
        for(int i = 0; i < 4; i++)
        
            this.tests.Add(new Test()
            
                ID = i,
                Name = "Test " + i.ToString(),
            );
        

        for(int i = 0; i < 4; i++)
        
            this.users.Add(new User()
            
                Name = "User " + i.ToString(),
                ID = i,
                UserTest = this.tests[i],
            );
        
    

    private ObservableCollection<User> users = new ObservableCollection<User>();
    public IEnumerable<User> Users
    
        get  return this.users; 
    

    private ObservableCollection<Test> tests = new ObservableCollection<Test>();
    public IEnumerable<Test> Tests
    
        get  return this.tests; 
    

    private User currentUser = null;
    public User CurrentUser
    
        get  return this.currentUser; 
        set
        
            this.currentUser = value;
            this.OnPropertyChanged("CurrentUser");
        
    

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propName)
    
        var eh = this.PropertyChanged;
        if(null != eh)
        
            eh(this, new PropertyChangedEventArgs(propName));
        
    

我将这两个列表的创建移到了代码中。我在您的示例中注意到的一件事是ListOfTests 的一个实例用作ComboBoxItemsSource,而另一个实例用于构建ListOfUsers。我不确定这是否是问题的一部分,但最好只列出一份测试。

主要Window 的 XAML 如下:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <ListBox ItemsSource="Binding Path=Users"
                 SelectedItem="Binding Path=CurrentUser"
                 DisplayMemberPath="Name"
                 IsSynchronizedWithCurrentItem="True">
        </ListBox>
        <TextBox Text="Binding Path=CurrentUser.Name" />
        <TextBox Text="Binding Path=CurrentUser.UserTest.Name" />
        <ComboBox ItemsSource="Binding Path=Tests"
                  SelectedItem="Binding Path=CurrentUser.UserTest"
                  DisplayMemberPath="Name" />
    </StackPanel>
</Window>

让事情顺利进行的关键是CurrentUser 属性。它绑定到ListBox.SelectedItem,而ComboBox.SelectedItem 绑定到CurrentUser.UserTest。这将更改ComboBox 中的选择以代表ListBox 中选择的用户的测试。

我使用 Visual Studio 2008 SP1 完成了这一切,因此希望它也适用于您。如果您在执行此操作时遇到任何问题,请告诉我,我会看看我能做些什么。

【讨论】:

嗨安迪,我已经按照你的建议实施了 INotifyPropertyChanged,但不幸的是我仍然遇到同样的问题。我在cid-eddcda42d46afe81.skydrive.live.com/self.aspx/Public%20Dev/… 发布了一个解决方案文件,注意用户编号和测试编号应该相同。随机点击几个用户,然后检查你点击的用户,如果用户和测试仍然排队。 我无法打开您的 zip 文件 - 我收到一条错误消息,指出它无效。无论如何,作为您的 DataContext 并具有 userTest 属性的对象是否也实现了 INotifyPropertyChanged? 嗨,安迪,是的……我有它: public class User : INotifyPropertyChanged private string name; public string Name ... ...私有测试userTest;公共测试用户测试 获取 返回用户测试; 设置 用户测试 = 值; OnPropertyChanged("UserTest"); 哇,这真的很奇怪 - 看起来它应该可以工作。如果今晚或明天有时间,我会尝试用你的代码构建一个示例,看看我是否能解决任何问题。 是的,这很奇怪。我认为这可能是.Net 3.5 SP1 中的一个错误。正如我之前所说,如果我以 .NET 4.0 为目标,它可以正常工作...【参考方案2】:

安迪,

这是我现在拥有的代码中更易读的摘录。

public class User : INotifyPropertyChanged

    private string name;
    public string Name 
    
        get
        
            return name;
        
        set
        
            name = value;
            OnPropertyChanged("Name");
        
    

    private int iD;
    public int ID 
    
        get
        
            return iD;
        
        set
        
            iD = value;
            OnPropertyChanged("ID");
        
    

    private test userTest;
    public test UserTest 
    
        get
        
            return userTest;
        
        set
        
            userTest = value;
            OnPropertyChanged("UserTest");
        
    


    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propName)
    
        PropertyChangedEventHandler eh = this.PropertyChanged;
        if (null != eh)
        
            eh(this, new PropertyChangedEventArgs(propName));
        
    

看起来比 cmets 更好。

问候 角

【讨论】:

以上是关于WPF Master-Details 视图与列表框和组合框与绑定的主要内容,如果未能解决你的问题,请参考以下文章

WPF 列表框 + 绑定 + IDataErrorInfo =?

WPF 组合框和数据绑定到其他类

2022-03-23 WPF面试题 ListBox 与 ListView - 如何选择以及何时进行数据绑定?

2022-03-23 WPF面试题 ListBox 与 ListView - 如何选择以及何时进行数据绑定?

WPF 非同质视图列表

WPF MVVM 文本框文本绑定与 changedText 事件