【问题标题】:WPF Datagrid Showing Second DB Table As ComboBox OptionsWPF 数据网格将第二个数据库表显示为组合框选项
【发布时间】:2018-10-25 13:06:26
【问题描述】:

我在我的数据库中添加了第二个表,其中包含我希望在新列中的数据网格的每一行的组合框中看到的不同选项。每个选项都有一个与之关联的键,它与我的主表中的一列匹配,该值是从 0 到 8 的值。我有 2 个问题,其中一个我已经解决了:

  1. 加载网格时,我希望组合框列显示与每一行的 [Status] 键对应的 [Description]。这是有效的。

  2. 我希望用户选择 [Description] 单元格并使用下拉菜单打开所有可能的 [Description] 选项,以便他可以更改项目的状态。

例如,如果某个组件处于暂停状态,用户需要在网格中选择该组件并在状态列的下拉菜单中选择“暂停”。

状态正确显示,但下拉列表中未填充 CNCComponentStatus 表中的 8 个选项。我敢打赌,我没有在 SqlDataAdapter 中正确地用第二个表填充数据集。那或者我没有正确使用 SelectedItem/ItemSource/SelectedValuePath。

这是网格的截图 这是我的数据结构

这些是状态选项:

这是数据网格的 xaml(仅相关部分):

<DataGrid Name="dataGrid1" ItemsSource="{Binding Path=test_3DimensionalDB}" AutoGenerateColumns="False">
<DataGridTemplateColumn x:Name="StatusColumn" Header="Status" Width="*" IsReadOnly="False">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock x:Name="cboStatus" Text="{Binding Path=Description, Mode=TwoWay}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ComboBox x:Name="StatusCombo" IsEditable="True" SelectedValuePath="{Binding Status}" DisplayMemberPath="{Binding Description}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

这是获取数据的方法:

 Private Sub SelectQuery(ByVal company As String, ByVal status As String, ByVal component As String)

    Dim com As String = "SELECT tmfCNCComponent_threed.[CNCComponentKey]
    ,tmfCNCComponent_threed.[CompanyID]
    ,tmfCNCComponent_threed.[JobNumber]
    ,tmfCNCComponent_threed.[LogNumber]
    ,tmfCNCComponent_threed.[Customer]
    ,tmfCNCComponent_threed.[DueDate]
    ,tmfCNCComponent_threed.[JobLeader]
    ,tmfCNCComponent_threed.[CADProgrammer]
    ,tmfCNCComponent_threed.[Salesperson]
    ,tmfCNCComponent_threed.[CNCProgrammer]
    ,tmfCNCComponent_threed.[ComponentDescription]
    ,tmfCNCComponent_threed.[ComponentFilePath]
    ,tmfCNCComponent_threed.[Material]
    ,tmfCNCComponent_threed.[ComponentSizeX]
    ,tmfCNCComponent_threed.[ComponentSizeY]
    ,tmfCNCComponent_threed.[ComponentSizeZ]
    ,tmfCNCComponent_threed.[QuantityShown]
    ,tmfCNCComponent_threed.[QuantityMirror]
    ,tmfCNCComponent_threed.[UpdateTime]
    ,tmfCNCComponent_threed.[Status]
    ,tmfCNCComponent_threed.[ProgStarted]
    ,tmfCNCComponentStatus_threed.[Description]
    FROM [test_3DimensionalDB].[dbo].[tmfCNCComponent_threed]
    INNER JOIN tmfCNCComponentStatus_threed
    ON tmfCNCComponent_threed.Status = tmfCNCComponentStatus_threed.CNCComponentStatusKey 
    WHERE [ComponentDescription] " & component & " 'trode%' AND [CompanyID]='" & company & "' AND [Status]" & status & "ORDER BY [UpdateTime] DESC"

    Dim Adpt As New SqlDataAdapter(com, con)
    con.Open()
    Dim ds As New DataSet()
    Adpt.Fill(ds, "dbo.tmfCNCComponent_threed")

    dataGrid1.ItemsSource = ds.Tables("dbo.tmfCNCComponent_threed").DefaultView

    con.Close()

    RowCount()
    searchBox.Clear()

End Sub

感谢您的宝贵时间,非常抱歉。

编辑: 这是我尝试填充组合框的方式,因为我没有正确绑定。虽然我不太明白如何只显示一列而不是 DefaultView。另外,“StatusCombo”由于其保护级别而无法访问?

    Dim com2 As String = "SELECT * FROM tmfCNCComponentStatus_threed"
    Dim AdptStatus As New SqlDataAdapter(com2, con)
    AdptStatus.Fill(ds, "dbo.tmfCNCComponentStatus_threed")
    StatusCombo.ItemsSource = ds.Tables("dbo.tmfCNCComponentStatus_threed").DefaultView

【问题讨论】:

  • 你能展示你用来填充 ComboBox 的代码吗?
  • 我没有,我希望如果我在 xaml 中正确使用数据绑定,我将不需要任何代码。我尝试制作另一个数据适配器并再次填充数据集,但没有奏效
  • 请告诉我您为填充组合框所做的一切。绝对任何地方。我不介意它是不是你不认为是“代码”的东西。
  • 我在帖子底部添加了一个编辑,显示了我如何尝试在代码中填充组合框,但据我了解,我应该在 xaml 中进行数据绑定
  • 好的,所以您有多个StatusCombo,因为它位于一个模板中,该模板为表中的每一行创建了一个实例。因此,其名称的范围仅限于创建它的 DataTemplate。那是没得商量的。但这没关系;明智的是,无论如何您都不想在代码中执行此操作——您想使用数据绑定。关键问题:您有视图模型吗?

标签: c# sql wpf data-binding datagrid


【解决方案1】:

我认为组合框最终应该是这样的。不需要 x:Name 属性。我怀疑是否需要 IsEditable 。

<DataGridTemplateColumn.CellEditingTemplate>
    <DataTemplate>
        <ComboBox 
            IsEditable="True" 
            SelectedValuePath="Status" 
            DisplayMemberPath="Description" 
            SelectedValue="{Binding Status}"
            />
    </DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>

SelectedValuePathDisplayMemberPath 是字符串。它们是填充ComboBox 的数据行中的列的名称(如果使用常规C# 类实例填充它,它们将是项目类的属性名称)。您试图将这些属性绑定到您没有的视图模型的属性,但即使您确实有一个,它也不会有这些属性。但是一开始,绑定是什么以及它的作用的概念可能会很奇怪。

我差点告诉你read the documentation on SelectedValuePath,但是文档已经修改,现在基本上没有意义了。 DisplayMemberPath is better

第二:你应该有一个视图模型。缺少视图模型使一切变得更加困难。但是您没有视图模型。

您需要做的是从数据库中获取您想要的内容,并将其放入组合框可以绑定到的某个集合中。

缺少一个视图模型来充当该集合的自然归宿,我们将通过在窗口的资源中创建一个 CollectionViewSource 来实现。

<Window
    x:Class="blah blah"
    ...stuff...
    >
    <Window.Resources>
        <CollectionViewSource
            x:Key="StatusItems"
            />
    </Window.Resources>

我们将使用它来填充组合框,如下所示:

<ComboBox 
    x:Name="StatusCombo" 
    SelectedValuePath="Status" 
    DisplayMemberPath="Description" 
    ItemsSource="{Binding Source={StaticResource StatusItems}}"
    />

但是我们的 CollectionViewSource 是空的。所以我们必须以某种方式填充它。每当您进行数据库查询时,我们都会这样做。

Dim statusCVS As CollectionViewSource = FindResource("StatusItems")

Dim com2 As String = "SELECT * FROM tmfCNCComponentStatus_threed"
Dim AdptStatus As New SqlDataAdapter(com2, con)
AdptStatus.Fill(ds, "dbo.tmfCNCComponentStatus_threed")

Dim statusRows = ds.Tables("dbo.tmfCNCComponentStatus_threed").Rows
Dim statuses As New List(Of Object)

For Each row As DataRow In statusRows
    statuses.Add(New With {
            .Status = CInt(row("CNCComponentStatusKey")),
            .Description = CStr(row("Description"))
        })
Next

statusCVS.Source = statuses

如果可行的话,这会比循环更好:

Dim statuses = From row In ds.Tables("dbo.tmfCNCComponentStatus_threed")
               .Rows.Cast(Of DataRow)
               Select New With {
                    .Status = CInt(row("CNCComponentStatusKey")),
                    .Description = CStr(row("Description"))
                }

statusCVS.Source = statuses

好的,现在我们有了这个部分:

    SelectedValue="{Binding Status}"

我猜test_3DimensionalDB(您的DataGrid 是否实际填充?)必须有一些列是组合中状态ID 值的外键,我猜它可能被称为Status

所以我们想要发生的是:假设你有两个项目:

item 0:
    Status = 1
    Description = Dog
item 1:
    Status = 2
    Description = Cat

DisplayMemberPath="Description" 表示项目 0 将显示为“Dog”,即其 Description 属性的值。出于同样的原因,项目 1 将显示为“猫”。

SelectedValuePath="Status" 表示当绑定将 SelectedValue 设置为 2 时,组合框将查看其项目集合以查找具有等于 2Status 属性的项目,并选择该项目。如果我们设置SelectedValuePath="Fred",它将查找一个名为Fred 的属性等于2 的项目。

同样,如果用户自己更改了选择,则相反:假设用户选择项目 0,因此 ComboBox 会查询自己的 SelectedValuePath,查看“状态”,并获取 Status 属性值(如果有的话)所选择的项目。对于第 0 项,它是 1。然后组合框会将1 的值分配给它自己的SelectedValue 属性。

SelectedValue 上的绑定随后将收到SelectedValue 已更改的通知,它将获取新值并更新它绑定到的数据库行列(在本例中也称为“状态”)。

Binding 是一个固定在周围并做事的对象。这不是“任务”的花哨词。

SelectedValue="{Binding Status}"

【讨论】:

  • 感谢您的信息。我已经调整了所有内容以符合您上面的说明,并且在启动时出现异常,说明“System.InvalidOperationException:连接不支持 MultipleActiveResultSets”。在我进行这些更改之前填充的数据网格,它只是没有使用描述选项填充组合框。
  • 好的,然后您应该使用具有StatusDescription 属性的普通类or an anonymous type 的集合填充该组合。
  • 我正试图弄清楚如何做到这一点,但在 Visual Basic 中做任何事情对我来说都是一种折磨。
  • 我认为更简单的方法是去掉第二个表,将 [Status] 作为字符串。并对组合框的选项进行硬编码。然后当用户选择一个值时,将行的状态更新为组合框选择的字符串值。
  • 就是这样!非常感谢 Ed,不仅为您提供答案,还为您完整地解释了解决方案!我现在唯一要弄清楚的是为什么当我更改组合框值时数据没有更新
猜你喜欢
  • 2018-06-18
  • 2013-03-12
  • 1970-01-01
  • 2012-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-26
  • 2011-07-10
相关资源
最近更新 更多