【问题标题】:WPF Master-Details view with Listbox and Combobox with BindingWPF Master-Details 视图与列表框和组合框与绑定
【发布时间】: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) 为目标,那么它可以正常工作...

谁能帮我解决这个问题?

【问题讨论】:

    标签: wpf data-binding combobox listbox


    【解决方案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 更好。

    问候 角

    【讨论】:

      猜你喜欢
      • 2016-11-26
      • 1970-01-01
      • 1970-01-01
      • 2014-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多