You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 13:16:59