WPF区分编码SelectionChanged和鼠标SelectionChanged

3
我有一个政治家式的问题(对我而言)。 在我的WPF应用程序中,我有一个ListBox,在ItemTemplate中有一个ComboBox。当用户选择ComboBoxItem时,我必须对ListBox的ItemsSource进行一些复杂的操作,然后显示更改后的数据。问题是,如果我处理ComboBox控件的“SelectionChanged”事件,每次修改comboboxItems的源类都会进入处理事件的方法,这将生成错误结果。简而言之,我必须以某种方式区分由代码生成的SelectionChanged和由用户手动使用鼠标生成的SelectionChanged。 我尝试了很多方法,但没有一种可行的方法:-(
我认为最好的解决方案是处理组合框项容器样式的ContentPresenter的“GotFocus”或“MouseUp”事件,或者是处理组合框的ItemsPanel的相同事件(“GotFocus”和“MouseUp”),但我处理的方法没有捕获事件(在调试中光标根本没有进入该方法)。
我不能使用布尔值来停止“SelectionChanged”方法,直到“第一轮”完成,因为ComboBoxItems的源类的更改发生在方法全部执行之后。
Combos的默认值并不总是第一个(这太容易了:-),也不总是相同的。每次用户选择一个Combo的项目时,其他Combos的默认值都必须更改。
你能帮我吗? Pileggi
' XAML
<Style x:Key="modComboCriteriEventParts" TargetType="{x:Type ComboBox}">
    <EventSetter Event="Selector.SelectionChanged" Handler="cb_SelectionChanged"/>
</Style>

<DataTemplate x:Key="modLBoxCriteriParts">
    <ComboBox Style = "{StaticResource modComboCriteriEventParts}"
        ItemsSource = "{Binding CriteriItemList}"
        ItemContainerStyle = "{DynamicResource modComboContainerParts}"
        SelectedIndex = "{Binding valueSelected}" ... />
</DataTemplate>

<ListBox x:Name="lbCriteri" IsSynchronizedWithCurrentItem="True"
    ItemsSource = "{Binding CriteriList, Source={StaticResource P_CriteriDataSource}}"
    ItemTemplate = "{DynamicResource modLBoxCriteriParts}"
    ... />


' Code Behind
Private Sub cb_SelectionChanged(ByVal sender As System.Object, ByVal e As SelectionChangedEventArgs)
    Dim ri as New RicambiCriteriList() As ObservableCollection(Of P_CriteriItem)

    ' some complex operations with ri ...

    be = BindingOperations.GetBindingExpression(Me.lbCriteri, ListBox.ItemsSourceProperty)
    Dim allCriteri As P_Criteri = DirectCast(be.DataItem, P_Criteri)
    allCriteri.AddData (ri)

    e.Handled = True
End Sub


' Source-Class
Public Class P_Criteri

    Private _CriteriList As New ObservableCollection(Of P_CriteriItem)

    Public ReadOnly Property CriteriList() As ObservableCollection(Of P_CriteriItem)
        Get
            CriteriList = _CriteriList
        End Get
    End Property

    Public Sub AddData(ByVal CriteriListPass As ObservableCollection(Of P_CriteriItem))
        _CriteriList.Clear()
        For Each a As P_CriteriItem In CriteriListPass
            _CriteriList.Add(a)
        Next
    End Sub
End Class

Public Class P_CriteriItem
    Implements INotifyPropertyChanged

    Public Sub New(ByVal criterioPass As String, ByVal CriteriItemListPass As ObservableCollection(Of P_CriteriItemValore), _
        ByVal widthCriteriValuesPass As Double)

        Me._criterio = criterioPass
        Me._CriteriItemList = CriteriItemListPass
        Me._widthCriteriValues = widthCriteriValuesPass
    End Sub

    Private _criterio As String = ""
    Private _CriteriItemList As New ObservableCollection(Of P_CriteriItemValore)

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Public Property criterio() As String
        Get
            Return Me._criterio
        End Get
        Set(ByVal value As String)
            If Not Object.Equals(Me._criterio, value) Then
                Me._criterio = value
                Me.OnPropertyChanged ("criterio")
            End If
        End Set
    End Property

    Public Property CriteriItemList() As ObservableCollection(Of P_CriteriItemValore)
        Get
            Return Me._CriteriItemList
        End Get
        Set(ByVal value As ObservableCollection(Of P_CriteriItemValore))
            If Not Object.Equals(Me._CriteriItemList, value) Then
                Me._CriteriItemList = value
                Me.OnPropertyChanged ("CriteriItemList")
            End If
        End Set
    End Property

    Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String)
        Dim handler As PropertyChangedEventHandler = Me.PropertyChangedEvent
        If handler IsNot Nothing Then
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
        End If
    End Sub
End Class

Public Class P_CriteriItemValore
    Implements INotifyPropertyChanged

    Public Sub New(ByVal criterioValorePass As String)
        Me._criterioValore = criterioValorePass
    End Sub

    Private _criterioValore As String = Nothing

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Public Property criterioValore() As String
        Get
            Return Me._criterioValore
        End Get
        Set(ByVal value As String)
            If Not Object.Equals(Me._criterioValore, value) Then
                Me._criterioValore = value
                Me.OnPropertyChanged ("criterioValore")
            End If
        End Set
    End Property

    Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String)
        Dim handler As PropertyChangedEventHandler = Me.PropertyChangedEvent
        If handler IsNot Nothing Then
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
        End If
    End Sub
End Class
1个回答

4
首先,我认为最好在项目容器本身上处理事件,而不是在项目内的内容呈现器上处理。现在我想起来了,这可能就是为什么你看不到这些事件的原因。容器可能会吞噬选中事件。
但无论如何,如果您无法捕获MouseDown / GotFocus事件,则可以使用PreviewMouseDown / PreviewGotFocus事件。以防万一您不确定这些事件的含义,您应该阅读有关WPF事件路由架构和事件冒泡和隧道的相关知识。

2
非常感谢!!正如您所说,事件MouseUp是无法捕获的,取而代之的是PreviewMouseUp。我喜欢这个论坛。 - lamarmora
曾遇到类似问题。我想使用连接到EmailTableA的ListView网格项来显示电子邮件信息(收件人、抄送、主题、消息)。单击该项将填充下拉列表,其中包含其标题及基于来自EmailTableA的该项的那些其他文本框。如果我接下来单击下拉列表以更改标题,则它会根据该电子邮件标题从另一个EmailTableB中获取信息,而不是网格使用的不同EmailTableA。问题在于,如果我单击ListView项,而不是从EmailTableA中显示该项,它将使用下拉列表的SelectedChanged事件,并从EmailTableB中显示该项。 - vapcguy
我按照lamarmora的建议解决了问题 - 我将下拉列表的事件从“SelectedChanged”更改为“PreviewMouseUp”。然后,我的ListView从“EmailTableA”填充数据,不再在我的下拉列表上运行事件,并且不再在没有直接从鼠标点击调用的情况下获取“EmailTableB”数据。如果我点击下拉列表以提取不同的电子邮件,则会从“EmailTableB”中获取它,正如所期望的那样。 - vapcguy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接