WPF多选ComboBox随机显示System.Data.DataRowView问题求助
WPF多选ComboBox点击复选框标签时搜索框显示System.Data.DataRowView异常
问题现象
我实现了一个包含复选框列表且支持文本搜索的WPF多选ComboBox,选择、取消选择、搜索、清除功能均正常。但测试时发现,连续选择选项时,若点击复选框关联的标签,搜索框会随机显示System.Data.DataRowView;仅点击复选框本身则无此问题。尝试在SelectionChanged和TextChanged事件中检测并清空该异常文本,但调试时发现cmbAppNameAddRemoveBranch.Text已为空,判断条件从未触发。
XAML代码
<ComboBox x:Name="cmbAppNameAddRemoveBranch" Style="{StaticResource md-2s-combo }" VerticalAlignment="top" HorizontalAlignment="Left" SelectedValuePath="Content" SelectionChanged="CmbAppNameAddRemoveBranch_SelectionChanged" TextBoxBase.TextChanged="CmbAppNameAddRemoveBranch_TextChanged" IsEditable="True" IsTextSearchEnabled="True" StaysOpenOnEdit="True" IsTextSearchCaseSensitive="False" Text=""> <ComboBox.ItemTemplate> <DataTemplate> <CheckBox Name="chkApp" Width="220" Checked="ChkApp_Checked" Unchecked="ChkApp_Unchecked" Content="{Binding APVALDESC}" IsChecked="{Binding Check_Status,Mode=TwoWay}" CommandParameter="{Binding APVALUE}"> </CheckBox> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> <Button x:Name="Show" Click="Show_Click"> Show selected</Button> <Button x:Name="Clear" Click="Clear_Click">Clear selected</Button>
后台C#代码
private void Page_Loaded(object sender, RoutedEventArgs e) { string lsErr = ""; cApplParameter cocAppl = new cApplParameter(Home.psProvider, Home.psconnstr, Home.psAppId1, Home.psSchema, Home.psOldSchema); cocAppl.GetApplParameters("VMS3", "APPL", "", ref ldtAllApplName, ref lsErr); ldtAllApplName.Columns.Add("Check_Status"); for(int lirow = 0; lirow <= ldtAllApplName.Rows.Count - 1; lirow++) { ldtAllApplName.Rows[lirow]["Check_Status"] = "false"; } //cmbAppNameAddRemoveBranch.DisplayMemberPath = "APVALDESC"; //cmbAppNameAddRemoveBranch.SelectedValuePath = "APVALUE"; cmbAppNameAddRemoveBranch.ItemsSource = ldtAllApplName.DefaultView; } private void CmbAppNameAddRemoveBranch_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (cmbAppNameAddRemoveBranch.Text == "System.Data.DataRowView") { cmbAppNameAddRemoveBranch.Text = ""; return; } } private void ChkApp_Checked(object sender, RoutedEventArgs e) { CheckBox clickedBox = (CheckBox)sender; for(int liRow = 0; liRow <= ldtAllApplName.Rows.Count - 1; liRow++) { if (clickedBox.Content == ldtAllApplName.Rows[liRow]["APVALDESC"].ToString().Trim()) { ldtAllApplName.Rows[liRow]["Check_Status"] = true; } } cmbAppNameAddRemoveBranch.ItemsSource = ldtAllApplName.DefaultView; } private void ChkApp_Unchecked(object sender, RoutedEventArgs e) { CheckBox clickedBox = (CheckBox)sender; for (int liRow = 0; liRow <= ldtAllApplName.Rows.Count - 1; liRow++) { if (clickedBox.Content == ldtAllApplName.Rows[liRow]["APVALDESC"].ToString().Trim()) { ldtAllApplName.Rows[liRow]["Check_Status"] = false; } } cmbAppNameAddRemoveBranch.ItemsSource = ldtAllApplName.DefaultView; } private void Show_Click(object sender, RoutedEventArgs e) { string lsMessage = ""; DataTable pdtDetails = new DataTable(); pdtDetails=ldtAllApplName.Select("Check_Status='true'").CopyToDataTable(); for(int lirow = 0; lirow <= pdtDetails.Rows.Count - 1; lirow++) { lsMessage = lsMessage + pdtDetails.Rows[lirow]["APVALDESC"].ToString().Trim() + "-" +pdtDetails.Rows[lirow]["APVALUE"].ToString().Trim()+ "\n"; } MessageBox.Show(lsMessage); } private void Clear_Click(object sender, RoutedEventArgs e) { for (int lirow = 0; lirow <= ldtAllApplName.Rows.Count - 1; lirow++) { ldtAllApplName.Rows[lirow]["Check_Status"] = "false"; } cmbAppNameAddRemoveBranch.ItemsSource = ldtAllApplName.DefaultView; MessageBox.Show("Cleared"); } private void CmbAppNameAddRemoveBranch_TextChanged(object sender, TextChangedEventArgs e) { try { if (cmbAppNameAddRemoveBranch.Text == "System.Data.DataRowView") { cmbAppNameAddRemoveBranch.Text = ""; return; } DataTable ldtDetail = new DataTable(); if (ldtAllApplName.Select("APVALDESC LIKE '%" + cmbAppNameAddRemoveBranch.Text.ToString().Trim() + "%'").Count() > 0) { cmbAppNameAddRemoveBranch.ItemsSource = ldtAllApplName.Select("APVALDESC LIKE '%" + cmbAppNameAddRemoveBranch.Text.ToString().Trim() + "%'").CopyToDataTable().DefaultView; cmbAppNameAddRemoveBranch.IsDropDownOpen = true; } } catch(Exception ex) { MessageBox.Show(ex.Message); } }
尝试过的无效代码
if (cmbAppNameAddRemoveBranch.Text == "System.Data.DataRowView") { cmbAppNameAddRemoveBranch.Text = ""; return; }
解决方案
1. 阻止点击复选框标签触发ComboBox选择(核心修复)
点击复选框标签时会触发ComboBox的Item选择逻辑,而DataRowView的默认ToString()结果就是System.Data.DataRowView。通过PreviewMouseDown事件拦截点击:
<!-- 在ComboBox标签中添加事件 --> <ComboBox ... PreviewMouseDown="CmbAppNameAddRemoveBranch_PreviewMouseDown">
后台添加事件处理:
private void CmbAppNameAddRemoveBranch_PreviewMouseDown(object sender, MouseButtonEventArgs e) { // 向上遍历视觉树,判断点击目标是否属于CheckBox var hitElement = e.OriginalSource as DependencyObject; while (hitElement != null && !(hitElement is CheckBox)) { hitElement = VisualTreeHelper.GetParent(hitElement); } if (hitElement is CheckBox) { // 阻止ComboBox处理点击事件,避免触发选择 e.Handled = true; } }
2. 设置DisplayMemberPath兜底
即使触发选择,让ComboBox知道要显示的字段,避免显示DataRowView的默认字符串:
// 在Page_Loaded方法中取消注释该行 cmbAppNameAddRemoveBranch.DisplayMemberPath = "APVALDESC";
3. 优化复选框状态更新逻辑
当前每次勾选都重新赋值ItemsSource会导致UI频繁刷新,改用DataView.Refresh()更新:
// 替换ChkApp_Checked和ChkApp_Unchecked中的ItemsSource赋值代码 (ldtAllApplName.DefaultView as DataView).Refresh();
4. 修复TextChanged中的SQL注入风险
原拼接字符串的查询方式存在注入风险,改用DataView.RowFilter处理:
private void CmbAppNameAddRemoveBranch_TextChanged(object sender, TextChangedEventArgs e) { try { string filterText = cmbAppNameAddRemoveBranch.Text.Trim(); if (!string.IsNullOrEmpty(filterText)) { DataView filteredView = new DataView(ldtAllApplName); // 转义单引号避免语法错误 filteredView.RowFilter = $"APVALDESC LIKE '%{filterText.Replace("'", "''")}%'"; cmbAppNameAddRemoveBranch.ItemsSource = filteredView; cmbAppNameAddRemoveBranch.IsDropDownOpen = true; } else { cmbAppNameAddRemoveBranch.ItemsSource = ldtAllApplName.DefaultView; } } catch(Exception ex) { MessageBox.Show(ex.Message); } }
内容的提问来源于stack exchange,提问作者krishna
相关产品推荐
相关产品推荐

