在列表框 WPF 中绑定组合框

Bind combo box within listbox WPF

我是 WPF 的新手,我正在尝试填充列表框中的组合框控件

XAML :

        <Window.Resources>
    <DataTemplate x:Key="UserTemplate" >
      <StackPanel Orientation="Horizontal" >
        <ComboBox Name="rule" ItemsSource="{Binding}" DisplayMemberPath="DataContext.RuleType"  Width="85" Height="20"
          SelectedValuePath="DataContext.RuleType" SelectedValue="{Binding Path=DataContext.RuleType}"/>
        <TextBlock Text="{Binding Path= Name1}" Width="85" Margin="5,5,5,5"></TextBlock>
        <Button Content="Delete" Click="cmdDeleteUser_Clicked" Margin="5,5,5,5" />
        <Button Content="Add" Click="cmdAddUser_Clicked" Margin="5,5,5,5" />
      </StackPanel>
    </DataTemplate>

  </Window.Resources>

  <Grid>
    <ListBox Name="lbUsers" ItemsSource="{Binding }" ItemTemplate="{StaticResource UserTemplate}"/>
  </Grid> 

后面的代码:

        public ObservableCollection<User> Users;
        ObservableCollection<Listdata> listeddata;
        ObservableCollection<Records> Record;


        public MainWindow()
        {
            InitializeComponent();
            Users = new ObservableCollection<User>() {
                    new User() { Name = "", Age = "" },
                 };

            DataboundListbox.Records record = new Records();
            RuleType = record.record_Rule();
            lbUsers.DataContext = Users;

        }
        private string _Name;
        public string Name1
        {
            get { return _Name; }
            set
            {
                if (value != _Name)
                {
                    _Name = "John";
                    NotifyPropertyChanged("Name");
                }
            }
        }
        private List<string> _RuleType;
        public List<string> RuleType
        {
            get { return _RuleType; }
            set
            {
                if (value != _RuleType)
                {
                    _RuleType = value;
                    NotifyPropertyChanged("RuleType");
                }
            }
        }

        private void NotifyPropertyChanged(string info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        private void cmdDeleteUser_Clicked(object sender, RoutedEventArgs e)
        {
            Button cmd = (Button)sender;
            if (cmd.DataContext is User)
            {
                User deleteme = (User)cmd.DataContext;
                Users.Remove(deleteme);
            }
        }
        private void cmdAddUser_Clicked(object sender, RoutedEventArgs e)
        {
            Button cmd = (Button)sender;
            if (cmd.DataContext is User)
            {
                var addedUser = new User() { Name = "", Age = "" };
            Users.Add(addedUser);
            }
        }


        private List<string> _prp;
        public List<string> prp
        {
            get { return _prp; }
            set
            {
                if (value != _prp)
                {
                    _RuleType = value;
                    NotifyPropertyChanged("prp");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged; 

在我回答你的问题之前,有一些困惑需要澄清。

  1. 如果User已经有一个名为Name的成员那么Name1在父class中的作用是什么?

  2. 如果RuleType是一个列表,怎么设置成你的ComboBoxSelectedValue,不应该是ComboBox.itemsSource吗?如果应该,那么 属性 在哪里定义以保留 ComboBox.SelectedValue?

  3. UserTemplate里面怎么会有一个Add按钮? Delete 按钮没问题,但我认为 Add 属于 ListBox 之外。

如果我正确理解你的问题,那么这就是我能想到的解决方案。

Fisrt: User 需要像 SelectedRule 这样的 属性 来保持 Combobox.SelectedItem:

public class User : INotifyPropertyChanged
{

    // implementation of INotifyPropertyChanged

    string _name;
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
            NotifyPropertyChanged("Name");
        }
    }

    int _age;
    public int Age
    {
        get
        {
            return _age;
        }
        set
        {
            _age = value;
            NotifyPropertyChanged("Age");
        }
    }

    string _selectedRule;
    public string SelectedRule
    {
        get
        {
            return _selectedRule;
        }
        set
        {
            _selectedRule = value;
            NotifyPropertyChanged("SelectedRule");
        }
    }
}

第二个:你的DataTemplate应该像这样改变:

<Window.Resources>
    <DataTemplate x:Key="UserTemplate" >
        <StackPanel Orientation="Horizontal" >
            <ComboBox Name="rule" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=RuleType}" DisplayMemberPath="."  Width="85" Height="20"
      SelectedItem="{Binding SelectedRule}"/>
            <TextBlock Text="{Binding Path= Name}" Width="85" Margin="5,5,5,5"></TextBlock>
            <Button Content="Delete" Click="cmdDeleteUser_Clicked" Margin="5,5,5,5" />
        </StackPanel>
    </DataTemplate>
</Window.Resources>

最后 ListBox 部分变化如下:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <ListBox Grid.Row="0" Name="lbUsers" ItemsSource="{Binding}" ItemTemplate="{StaticResource UserTemplate}"/>
    <Button Grid.Row="1" Content="Add" Click="cmdAddUser_Clicked" Margin="5,5,5,5" />
</Grid>

如果你要像上面的代码一样带出 Add 按钮,那么你应该从 cmdAddUser_Clicked 方法中删除 if (cmd.DataContext is User)

问题:

主要问题出在这两行:

   {Binding Path=DataContext.RuleType}
   {Binding Path= Name1}
  1. 由于您已经声明了数据上下文,DataContext.RuleType 将导致编译器搜索 您的数据上下文。DataContext.RuleType 这显然不是你想要的。

    lbUsers.DataContext = Users;
    
  2. 您的数据上下文是 用户 class 的集合,不包含 Name1。因此 Binding Path=Name1 将 return "property not found" error

解决方案

在 WPF 中,强烈鼓励使用 MVVM(模型视图视图模型)模式。它的主要特点之一是将 GUI 逻辑与业务逻辑分开,使代码更清晰,更易于维护。

第 1 步:创建视图模型

  public class UserViewModel:INotifyPropertyChanged
 {
    private string name;
    private string age;
    private string rule;
    private List<string> ruleType;

    public String Name 
    {
        get { return name; }
        set { name = value; NotifyPropertyChanged("Name"); }
    }

    public String Age
    {
        get { return age; }
        set { age = value; NotifyPropertyChanged("Age"); }
    }

    public String Rule 
    {
        get { return rule; }
        set { rule = value; NotifyPropertyChanged("Rule"); }
    }
    public List<string> RuleType
    {
        get { return ruleType; }
        set { ruleType = value; NotifyPropertyChanged("RuleType"); }
    }

    public UserViewModel() 
    {
        name = "name";
        age = "";
        ruleType = new List<string>();
    }

    #region NotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    #endregion
 }

}

第 2 步:Link 视图模型的数据上下文

    public MainWindow()
    {
        InitializeComponent();
        Users = new ObservableCollection<UserViewModel>();
        //setup your data here
        //example:

        UserViewModel userViewModel = new UserViewModel();
        //populate your combobox here
        userViewModel.RuleType.Add("rule1")
        userViewModel.RuleType.Add("rule2");
        userViewModel.RuleType.Add("rule3");
        Users.Add(new UserViewModel());

        lbUsers.DataContext = Users ;

    }

第 3 步:更新您的 xaml

 <Window.Resources>
        <DataTemplate x:Key="UserTemplate" >
            <StackPanel Orientation="Horizontal" >
                <ComboBox Name="rule" ItemsSource="{Binding RuleType}"  Width="85" Height="20"
       SelectedValue="{Binding Rule}"/>
                <TextBlock Text="{Binding Path= Name}" Width="85" Margin="5,5,5,5"></TextBlock>
                <Button Content="Delete" Click="cmdDeleteUser_Clicked" Margin="5,5,5,5" />
                <Button Content="Add" Click="cmdAddUser_Clicked" Margin="5,5,5,5" />
            </StackPanel>
        </DataTemplate>

    </Window.Resources>

当我打字的时候,bahman 已经 post 相当详细 answer.So 我停在这里。如果您需要我的任何解释或解决方案,只需询问即可。

以后如果您怀疑绑定有任何错误,您可以搜索您的输出 window。 如果你看到你的输出 window 你可能会发现这个

System.Windows.Data Error: 40 : BindingExpression path error: 'DataContext' property not found on 'object' ''User' (HashCode=9080996)'. BindingExpression:Path=DataContext.RuleType; DataItem='User' (HashCode=9080996); target element is 'ComboBox' (Name=''); target property is 'SelectedValue' (type 'Object')
System.Windows.Data Error: 40 : BindingExpression path error: 'Name1' property not found on 'object' ''User' (HashCode=9080996)'. BindingExpression:Path=Name1; DataItem='User' (HashCode=9080996); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')