WPF C#动态按钮删除所属StackPanel仅最后一个生效问题
WPF动态生成控件删除失效问题
我正在开发一款联系人应用,编辑联系人时会打开对应界面:界面截图
初始实现代码
加载待编辑联系人数据的代码如下:
private void LoadContactToEdit() { FirstNameTextBox.Text = contactToEdit.FirstName; LastNameTextBox.Text = contactToEdit.LastName; string[] phoneNumbers = contactToEdit.PhoneNumbers.Split('|'); int i = 0; foreach(string phoneNumber in phoneNumbers) { StackPanel sp = new StackPanel(); NameScope.SetNameScope(this, new NameScope()); sp.Name = $"PNStackPanel{i}"; RegisterName(sp.Name, sp); sp.Orientation = Orientation.Horizontal; sp.HorizontalAlignment = HorizontalAlignment.Center; TextBox txtbox = new TextBox(); txtbox.Name = $"PNTextBox{i}"; txtbox.Text = phoneNumber; txtbox.Width = 200; txtbox.Background = Brushes.White; txtbox.Margin = new Thickness(0, 10, 0, 0); sp.Children.Add(txtbox); if (i > 0) { Button btn = new Button(); btn.Name = $"PNButton{i}"; btn.Content = btn.Name; btn.Width = 100; btn.Click += DeletePhoneNumberTextBox; btn.Margin = new Thickness(0, 10, 0, 0); sp.Children.Add(btn); } PhoneNumberStackPanel.Children.Add(sp); i++; } string[] emailAddresses = contactToEdit.EmailAddresses.Split('|'); i = 0; foreach(string emailAddress in emailAddresses) { StackPanel sp = new StackPanel(); sp.Name = $"EAStackPanel{i}"; sp.Orientation = Orientation.Horizontal; sp.HorizontalAlignment = HorizontalAlignment.Center; TextBox txtbox = new TextBox(); txtbox.Name = $"EATextBox{i}"; txtbox.Text = emailAddress; txtbox.Width = 200; txtbox.Background = Brushes.White; txtbox.Margin = new Thickness(0, 10, 0, 0); sp.Children.Add(txtbox); if (i > 0) { Button btn = new Button(); btn.Name = $"EAButton{i}"; btn.Content = btn.Name; btn.Width = 100; //btn.Click += DeleteEmailAddressTextBox; btn.Margin = new Thickness(0, 10, 0, 0); sp.Children.Add(btn); } EmailAddressStackPanel.Children.Add(sp); i++; } }
最初实现的电话号码条目删除逻辑如下,目标是点击动态生成的按钮时,删除按钮所在的StackPanel容器:
private void DeletePhoneNumberTextBox(object sender, RoutedEventArgs e){ string s = (sender as Button).Name; char c = s[s.Length - 1]; string x = $"PNStackPanel{c}"; StackPanel sp = PhoneNumberStackPanel.FindName(x) as StackPanel; PhoneNumberStackPanel.Children.Remove(sp); }
遇到的问题
实际运行时只有最后一个生成的按钮可以成功删除对应的StackPanel,最初尝试改用ItemsControl替代StackPanel实现相关逻辑时遇到不少困惑,没定位到原有代码的问题。
编辑补充:经过查阅相关资料,已将StackPanel替换为ItemsControl实现,删除子项的操作便捷了很多。
问题根因
原有代码只有最后一个删除按钮生效的核心原因有两个:
- 循环生成电话号码条目的代码里,每次迭代都会调用
NameScope.SetNameScope(this, new NameScope()),这行代码会把当前窗口的命名范围重置为全新的空实例,之前注册过的所有控件名称都会被清空。循环全部执行完成后,只有最后一次迭代注册的控件名能被FindName检索到,其余按钮点击时查找对应StackPanel都会返回null,自然无法完成删除。 - 靠控件名称拼接末尾数字索引查找目标容器的写法本身健壮性极差,一旦出现删除条目、插入条目的场景,索引序号就会混乱,后续很容易出现删错条目、找不到控件的问题。
实现建议
换成ItemsControl的思路是完全正确的,这也是WPF做动态列表的标准实现方式,不需要手动操作UI元素、注册控件名:
- 用
ObservableCollection<string>存储电话号码、邮箱地址这类动态集合,集合发生增删时UI会自动同步更新 - 给ItemsControl配置ItemTemplate,定义好每一行的输入框、删除按钮布局,直接把ItemsSource绑定到对应集合即可
- 删除按钮的触发逻辑里,直接取按钮的
DataContext就是当前行绑定的条目数据,直接从ObservableCollection里移除对应项就完成删除,完全不需要做视觉树查找、控件名匹配的操作。
内容的提问来源于stack exchange,提问作者Kostas Soulios
相关产品推荐
相关产品推荐

