Wpf 组合框作为DataGridTemplateColumn SelectedValue未更新绑定属性

Wpf 组合框作为DataGridTemplateColumn SelectedValue未更新绑定属性,wpf,vb.net,xaml,Wpf,Vb.net,Xaml,当DataGrid弹出到DataGridTemplateColumn中时,我无法实现此CustComboBox(重命名为MultiColumnComboBox): 这里有一个指向SampleApp的链接,我把它放在一起,展示了我当前的实现和问题: 在本例中,我有两个对象: ConnectionPair有两个字符串属性“Connection”和“EQConnection” ToolObj有一个字符串属性“ConnectionT” SampleApp MainWindow.vb: Pr

当DataGrid弹出到DataGridTemplateColumn中时,我无法实现此CustComboBox(重命名为MultiColumnComboBox):

这里有一个指向SampleApp的链接,我把它放在一起,展示了我当前的实现和问题:

在本例中,我有两个对象:

  • ConnectionPair有两个字符串属性“Connection”和“EQConnection”
  • ToolObj有一个字符串属性“ConnectionT”
SampleApp MainWindow.vb:

    Private _ToolObjCollection As New ObservableCollection(Of ToolObj)
    Public Property ToolObjCollection As ObservableCollection(Of ToolObj)
        Get
            Return _ToolObjCollection
        End Get
        Set(value As ObservableCollection(Of ToolObj))
            _ToolObjCollection = value
        End Set
    End Property

    Private _ConnectionPairCollection As New ObservableCollection(Of ConnectionPair)
    Public Property ConnectionPairCollection As ObservableCollection(Of ConnectionPair)
        Get
            Return _ConnectionPairCollection
        End Get
        Set(value As ObservableCollection(Of ConnectionPair))
            _ConnectionPairCollection = value
        End Set
    End Property

    Private _TestTool As New ToolObj("conn2")
        Public Property TestTool As ToolObj
        Get
            Return _TestTool
        End Get
        Set(value As ToolObj)
            _TestTool = value
        End Set
    End Property

    Sub New()
        InitializeComponent()

        _ToolObjCollection.Add(New ToolObj("conn1"))
        _ToolObjCollection.Add(New ToolObj("conn2"))

        _ConnectionPairCollection.Add(New ConnectionPair("conn1", "eqconn1"))
        _ConnectionPairCollection.Add(New ConnectionPair("conn2", "eqconn2"))

        DataContext = Me

    End Sub
我希望将组合弹出窗口的ItemsSource绑定到ConnectionPairCollection,并希望选择更新我的ToolObj上的Connection属性

当使用DataGridTemplateColumn之外的MultiColumnComboBox执行此操作时,一切似乎都正常工作。实现如下所示:

<local:MultiColumnComboBox
    DisplayMemberPath="Connection"
    ItemsSource="{Binding ConnectionPairCollection}"
    SelectedValue="{Binding TestTool.ConnectionT}"
    SelectedValuePath="Connection">
    <DataGridTextColumn
        Header="Connection"
        Binding="{Binding Connection}" />
    <DataGridTextColumn
        Header="EQ Connection"
        Binding="{Binding EQConnection}" />
</local:MultiColumnComboBox>
    <DataGrid
        Margin="0"
        Grid.Row="1"
        ItemsSource="{Binding ToolObjCollection}"
        Tag="{Binding ConnectionPairCollection}">
        <DataGrid.Columns>
            <DataGridTemplateColumn
                Header="TemplatedMultiColumnCombo">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <local:MultiColumnComboBox
                            DisplayMemberPath="Connection"
                            ItemsSource="{Binding DataContext.ConnectionPairCollection, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
                            SelectedValue="{Binding ConnectionT}"
                            SelectedValuePath="Connection">
                            <DataGridTextColumn
                                Header="Connection"
                                Binding="{Binding Connection}" />
                            <DataGridTextColumn
                                Header="EQ Connection"
                                Binding="{Binding EQConnection}" />
                        </local:MultiColumnComboBox>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>

当我将MultiColumnComboBox放在DataGrid的DataGridTemplateColumn中,并将其ItemsSource绑定到ToolObjCollection时,绑定只能以一种方式工作。它正确地从我的ToolObj接收SelectedValue并在弹出窗口中注册正确的选择,但更改组合框的选择不会更新ToolObj的值。实现如下所示:

<local:MultiColumnComboBox
    DisplayMemberPath="Connection"
    ItemsSource="{Binding ConnectionPairCollection}"
    SelectedValue="{Binding TestTool.ConnectionT}"
    SelectedValuePath="Connection">
    <DataGridTextColumn
        Header="Connection"
        Binding="{Binding Connection}" />
    <DataGridTextColumn
        Header="EQ Connection"
        Binding="{Binding EQConnection}" />
</local:MultiColumnComboBox>
    <DataGrid
        Margin="0"
        Grid.Row="1"
        ItemsSource="{Binding ToolObjCollection}"
        Tag="{Binding ConnectionPairCollection}">
        <DataGrid.Columns>
            <DataGridTemplateColumn
                Header="TemplatedMultiColumnCombo">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <local:MultiColumnComboBox
                            DisplayMemberPath="Connection"
                            ItemsSource="{Binding DataContext.ConnectionPairCollection, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
                            SelectedValue="{Binding ConnectionT}"
                            SelectedValuePath="Connection">
                            <DataGridTextColumn
                                Header="Connection"
                                Binding="{Binding Connection}" />
                            <DataGridTextColumn
                                Header="EQ Connection"
                                Binding="{Binding EQConnection}" />
                        </local:MultiColumnComboBox>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>

我已经尝试将SelectedValue的绑定模式显式设置为TwoWay,但它没有任何作用(正如预期的那样,因为在MultiColumnCombo的第一个实现中不需要显式设置TwoWay)

有什么我遗漏的吗?或者可能需要添加一些编码结构到对象类或MainWindow codebehind

作为参考,我的CustComboBox移植版本:

MultiColumnComboBox.vb

' Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
'
' Step 1a) Using this custom control in a XAML file that exists in the current project.
' Add this XmlNamespace attribute to the root element of the markup file where it is 
' to be used:
'
'     xmlns:MyNamespace="clr-namespace:WinSurvWPF"
'
'
' Step 1b) Using this custom control in a XAML file that exists in a different project.
' Add this XmlNamespace attribute to the root element of the markup file where it is 
' to be used:
'
'     xmlns:MyNamespace="clr-namespace:WinSurvWPF;assembly=WinSurvWPF"
'
' You will also need to add a project reference from the project where the XAML file lives
' to this project and Rebuild to avoid compilation errors:
'
'     Right click on the target project in the Solution Explorer and
'     "Add Reference"->"Projects"->[Browse to and select this project]
'
'
' Step 2)
' Go ahead and use your control in the XAML file. Note that Intellisense in the
' XML editor does not currently work on custom controls and its child elements.
'
'     <MyNamespace:MultiColumnComboBox/>
'

Imports System.ComponentModel
Imports System.Windows.Markup
Imports System.Collections.ObjectModel
Imports System.Windows.Controls.Primitives
Imports System.Runtime.CompilerServices

''' <summary>
''' 
''' </summary>
<DefaultProperty("Columns")>
<ContentProperty("Columns")>
Public Class MultiColumnComboBox
    Inherits ComboBox
    Implements INotifyPropertyChanged
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub


    Const partPopupDataGrid As String = "PART_PopupDataGrid"
    'Columns of DataGrid
    Private _Columns As ObservableCollection(Of DataGridTextColumn)
    'Attached DataGrid control
    Private PopupDataGrid As DataGrid

    Shared Sub New()
        DefaultStyleKeyProperty.OverrideMetadata(GetType(MultiColumnComboBox), New FrameworkPropertyMetadata(GetType(MultiColumnComboBox)))
    End Sub

    'The property is default and Content property for CustComboBox
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
    Public ReadOnly Property Columns() As ObservableCollection(Of DataGridTextColumn)
        Get
            If Me._Columns Is Nothing Then
                Me._Columns = New ObservableCollection(Of DataGridTextColumn)()
            End If
            Return Me._Columns
        End Get
    End Property

    'Apply theme and attach columns to DataGrid popupo control
    Public Overrides Sub OnApplyTemplate()
        If PopupDataGrid Is Nothing Then

            PopupDataGrid = TryCast(Me.Template.FindName(partPopupDataGrid, Me), DataGrid)
            If PopupDataGrid IsNot Nothing AndAlso _Columns IsNot Nothing Then
                'Add columns to DataGrid columns
                For i As Integer = 0 To _Columns.Count - 1
                    PopupDataGrid.Columns.Add(_Columns(i))
                Next

                'Add event handler for DataGrid popup
                PopupDataGrid.AddHandler(PreviewMouseDownEvent, New MouseButtonEventHandler(AddressOf PopupDataGrid_MouseDown))

                PopupDataGrid.AddHandler(SelectionChangedEvent, New SelectionChangedEventHandler(AddressOf PopupDataGrid_SelectionChanged))
            End If
        End If
        'Call base class method
        MyBase.OnApplyTemplate()
    End Sub

    'Synchronize selection between Combo and DataGrid popup
    Private Sub PopupDataGrid_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)
        'When open in Blend prevent raising exception 
        If Not DesignerProperties.GetIsInDesignMode(Me) Then
            Dim dg As DataGrid = TryCast(sender, DataGrid)
            If dg IsNot Nothing Then
                Me.SelectedItem = dg.SelectedItem
                Me.SelectedValue = dg.SelectedValue
                Me.SelectedIndex = dg.SelectedIndex

                Me.SelectedValuePath = dg.SelectedValuePath
            End If
        End If
    End Sub

    'Event for DataGrid popup MouseDown
    Private Sub PopupDataGrid_MouseDown(sender As Object, e As MouseButtonEventArgs)
        Dim dg As DataGrid = TryCast(sender, DataGrid)
        If dg IsNot Nothing Then
            Dim dep As DependencyObject = DirectCast(e.OriginalSource, DependencyObject)

            ' iteratively traverse the visual tree and stop when dep is one of ..
            While (dep IsNot Nothing) AndAlso Not (TypeOf dep Is DataGridCell) AndAlso Not (TypeOf dep Is DataGridColumnHeader)
                dep = VisualTreeHelper.GetParent(dep)
            End While

            If dep Is Nothing Then
                Return
            End If

            ' do something
            If TypeOf dep Is DataGridColumnHeader Then
            End If
            'When user clicks to DataGrid cell, popup have to be closed
            If TypeOf dep Is DataGridCell Then
                Me.IsDropDownOpen = False
            End If
        End If
    End Sub

    'When selection changed in combobox (pressing  arrow key down or up) must be synchronized with opened DataGrid popup
    Protected Overrides Sub OnSelectionChanged(e As SelectionChangedEventArgs)
        MyBase.OnSelectionChanged(e)
        If PopupDataGrid Is Nothing Then
            Return
        End If

        If Not DesignerProperties.GetIsInDesignMode(Me) Then
            If IsDropDownOpen Then
                PopupDataGrid.SelectedItem = Me.SelectedItem
                If PopupDataGrid.SelectedItem IsNot Nothing Then
                    PopupDataGrid.ScrollIntoView(PopupDataGrid.SelectedItem)
                End If
            End If
        End If
    End Sub
    Protected Overrides Sub OnDropDownOpened(e As EventArgs)
        PopupDataGrid.SelectedItem = Me.SelectedItem

        MyBase.OnDropDownOpened(e)

        If PopupDataGrid.SelectedItem IsNot Nothing Then
            PopupDataGrid.ScrollIntoView(PopupDataGrid.SelectedItem)
        End If

    End Sub
End Class
”按照步骤1a或1b,然后按照步骤2在XAML文件中使用此自定义控件。
'
'步骤1a)在当前项目中存在的XAML文件中使用此自定义控件。
'将此XmlNamespace属性添加到标记文件的根元素
“将要使用:
'
'xmlns:MyNamespace=“clr namespace:WinSurvWPF”
'
'
'步骤1b)在不同项目中存在的XAML文件中使用此自定义控件。
'将此XmlNamespace属性添加到标记文件的根元素
“将要使用:
'
'xmlns:MyNamespace=“clr命名空间:WinSurvWPF;assembly=WinSurvWPF”
'
'您还需要从XAML文件所在的项目中添加项目引用
'并重新生成以避免编译错误:
'
'右键单击解决方案资源管理器中的目标项目,然后单击
““添加引用”->“项目”->[浏览并选择此项目]
'
'
"第二步)
'继续并使用XAML文件中的控件。请注意,在
'XML编辑器当前不适用于自定义控件及其子元素。
'
'     
'
导入System.ComponentModel
导入System.Windows.Markup
导入System.Collections.ObjectModel
导入System.Windows.Controls.Primitives
导入System.Runtime.CompilerServices
''' 
''' 
''' 
公共类多列组合框
继承组合框
实现INotifyPropertyChanged
公共事件PropertyChanged为PropertyChangedEventHandler实现INotifyPropertyChanged.PropertyChanged
私有子NotifyPropertyChanged(可选的ByVal propertyName作为字符串=无)
RaiseEvent PropertyChanged(Me,新PropertyChangedEventArgs(propertyName))
端接头
Const partPopUpdateAgrid As String=“PART\u popUpdateAgrid”
'数据网格的列
私有列作为可观察集合(DataGridTextColumn的)
'附加数据网格控件
作为DataGrid的私有PopUpdateAgrid
共享子新()
DefaultStyleKeyProperty.OverrideMetadata(GetType(MultiColumnComboBox),New FrameworkPropertyMetadata(GetType(MultiColumnComboBox)))
端接头
'该属性是CustComboBox的默认属性和内容属性
公共只读属性列()作为(DataGridTextColumn的)ObservableCollection
得到
如果是我,那就什么都不是了
Me.\u Columns=新的ObservableCollection(DataGridTextColumn的)()
如果结束
还给我
结束
端属性
'应用主题并将列附加到DataGrid popupo控件
应用程序模板()上的公共覆盖子对象
如果PopUpdateAgrid什么都不是
PopUpdateAgrid=TryCast(Me.Template.FindName(partPopUpdateAgrid,Me),DataGrid)
如果PopUpdateAgrid不是Nothing,并且Also_列也不是Nothing,那么
'将列添加到DataGrid列
对于i作为整数=0到_Columns.Count-1
添加(_Columns(i))
下一个
'为DataGrid弹出窗口添加事件处理程序
AddHandler(PreviewMouseDownEvent,新的MouseButtonEventHandler(PopUpdateAgrid\u MouseDown的地址))
AddHandler(SelectionChangedEvent、New SelectionChangedEventHandler(PopUpdateAgrid\u SelectionChanged的地址))
如果结束
如果结束
'调用基类方法
MyBase.OnApplyTemplate()
端接头
'在组合框和DataGrid弹出窗口之间同步选择
私有子PopUpdateAgrid\u SelectionChanged(发件人作为对象,e作为SelectionChangedEventArgs)
'在混合中打开时,防止引发异常
如果不是DesignerProperties.GetIsInDesignMode(Me),则
Dim dg As DataGrid=TryCast(发送方,DataGrid)
如果dg不是什么,那么
Me.SelectedItem=dg.SelectedItem
Me.SelectedValue=dg.SelectedValue
Me.SelectedIndex=dg.SelectedIndex
Me.SelectedValuePath=dg.SelectedValuePath
如果结束
如果结束
端接头
'数据网格弹出式鼠标向下移动的事件
私人潜水艇
Private Sub PopupDataGrid_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)
    If Not DesignerProperties.GetIsInDesignMode(Me) Then
        Dim dg As DataGrid = TryCast(sender, DataGrid)
        If dg IsNot Nothing Then
            Me.SelectedItem = dg.SelectedItem
            Me.SelectedValue = dg.SelectedValue
            Me.SelectedIndex = dg.SelectedIndex
            Me.SelectedValuePath = dg.SelectedValuePath

            If BindingPath IsNot Nothing Then
                CallByName(Me.DataContext, BindingPath, CallType.Set, Me.SelectedValue)
            End If
        End If
    End If
End Sub
    <DataGrid
        Margin="0"
        Grid.Row="1"
        ItemsSource="{Binding ToolObjCollection}"
        Tag="{Binding ConnectionPairCollection}">
        <DataGrid.Resources>
            <local:BindingProxy
                x:Key="proxy"
                Data="{Binding}" />
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTemplateColumn
                Header="TemplatedMultiColumnCombo">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <local:MultiColumnComboBox
                            DisplayMemberPath="Connection"
                            ItemsSource="{Binding Data.ConnectionPairCollection, Source={StaticResource proxy}}"
                            BindingPath="ConnectionT"
                            SelectedValue="{Binding ConnectionT}"
                            SelectedValuePath="Connection">
                            <DataGridTextColumn
                                Header="Connection"
                                Binding="{Binding Connection}" />
                            <DataGridTextColumn
                                Header="EQ Connection"
                                Binding="{Binding EQConnection}" />
                        </local:MultiColumnComboBox>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>