0

私はWPFアプリケーションを開発しています。私のページの 1 つに、3 つの ComboBoxes を含む DockPanel があります。これら 3 つの ComboBox は相互に依存しています。そのため、DockPanel にバインド グループがあり、そのバインド グループの検証ルールがあります。

DockPanel がフォーカスを失ったときに、これら 3 つの ComboBoxes を検証したいと思います。ただし、DockPanel の LostFocus イベントは、ユーザーがその中の子 ComboBox の 1 つをクリックすると発生します。

子コントロールにフォーカスがある場合、親コントロールにもフォーカスがあると思いましたが、そうではないようです。

DockPanel がフォーカスを失ったときにバインディング グループで検証を実行するソリューションを探しています...その子の 1 つではないコントロールにフォーカスが失われます。

編集:

そこで、問題を説明するために、できる限り単純なアプリケーションを作成しました。

Fertilizer を構成する 3 つの整数を含む「FertilizerCombination」クラスがあります。これらはアプリケーション内で一意である必要があります。現在、使用できない組み合わせは 10-10-10 のみです。これは、クラスでハードコードされた値です。

Public Class FertilizerCombination
  Private _nitrogen As Integer
  Private _phosphorous As Integer
  Private _potassium As Integer

<System.ComponentModel.DataAnnotations.Range(1, 20)> _
Public Property Nitrogen As Integer
    Get
        Return _nitrogen
    End Get
    Set(value As Integer)
        System.ComponentModel.DataAnnotations.Validator.ValidateProperty(value, NitrogenValidationContext)
        _nitrogen = value
    End Set
End Property

<System.ComponentModel.DataAnnotations.Range(1, 20)> _
Public Property Phosphorous As Integer
    Get
        Return _phosphorous
    End Get
    Set(value As Integer)
        System.ComponentModel.DataAnnotations.Validator.ValidateProperty(value, PhosphorousValidationContext)
        _phosphorous = value
    End Set
End Property

<System.ComponentModel.DataAnnotations.Range(1, 20)> _
Public Property Potassium As Integer
    Get
        Return _potassium
    End Get
    Set(value As Integer)
        System.ComponentModel.DataAnnotations.Validator.ValidateProperty(value, PotassiumValidationContext)
        _potassium = value
    End Set
End Property


Public Sub New()
End Sub
Public Sub New(ByVal nitrogen As Integer, ByVal phosphorous As Integer, ByVal potassium As Integer)
    Me.Nitrogen = nitrogen
    Me.Phosphorous = phosphorous
    Me.Potassium = potassium
End Sub


Public Shared Function FertilizerCombinationAvailable(ByVal nitrogen As Integer, ByVal phosphorous As Integer, ByVal potassium As Integer) As Boolean
    'Checking against combinations already used'
    If nitrogen = "10" And phosphorous = "10" And potassium = "10" Then
        'Combination has already been used'
        Return False
    End If
    'Combination was not used yet'
    Return True
End Function

Public Sub SetFertilizerCombination(ByVal nitrogen As Integer, ByVal phosphorous As Integer, ByVal potassium As Integer)
    System.ComponentModel.DataAnnotations.Validator.ValidateProperty(nitrogen, NitrogenValidationContext)
    System.ComponentModel.DataAnnotations.Validator.ValidateProperty(phosphorous, PhosphorousValidationContext)
    System.ComponentModel.DataAnnotations.Validator.ValidateProperty(potassium, PotassiumValidationContext)
    If FertilizerCombination.FertilizerCombinationAvailable(nitrogen, phosphorous, potassium) = False Then
        Throw New ArgumentException("This fertilizer combination has already been used")
    End If
    Me.Nitrogen = nitrogen
    Me.Phosphorous = phosphorous
    Me.Potassium = potassium
End Sub

Private NitrogenValidationContext = New System.ComponentModel.DataAnnotations.ValidationContext(Me, Nothing, Nothing) With {.MemberName = "Nitrogen"}
Private PhosphorousValidationContext = New System.ComponentModel.DataAnnotations.ValidationContext(Me, Nothing, Nothing) With {.MemberName = "Phosphorous"}
Private PotassiumValidationContext = New System.ComponentModel.DataAnnotations.ValidationContext(Me, Nothing, Nothing) With {.MemberName = "Potassium"}
End Class

Fertilizer Combination Class の Validation Rule を作成しました。

Public Class FertilizerCombinationValidationRule
Inherits ValidationRule

Public Overrides Function Validate(ByVal value As Object, ByVal cultureInfo As System.Globalization.CultureInfo) As System.Windows.Controls.ValidationResult
    Dim bg As BindingGroup = TryCast(value, BindingGroup)

    If bg IsNot Nothing AndAlso bg.Items.Count > 0 Then
        'The BindingGroup Items property contains the original object(s)'
        Dim fertilizerCombo As FertilizerCombination = TryCast(bg.Items(0), FertilizerCombination)

        'Using the BindingGroups GetValue method to retrieve the user provided values'
        Dim proposedNitrogen As Integer
        Dim proposedPhosphorous As Integer
        Dim proposedPotassium As Integer
        Try
            proposedNitrogen = bg.GetValue(fertilizerCombo, "Nitrogen")
            proposedPhosphorous = bg.GetValue(fertilizerCombo, "Phosphorous")
            proposedPotassium = bg.GetValue(fertilizerCombo, "Potassium")
            If FertilizerCombination.FertilizerCombinationAvailable(proposedNitrogen, proposedPhosphorous, proposedPotassium) = False Then
                Return New ValidationResult(False, "This fertializer combination has already been used")
            End If
        Catch noValue As System.Windows.Data.ValueUnavailableException
            'a binding was not properly bound yet'
        End Try

    End If
    Return New ValidationResult(True, Nothing)
End Function

End Class

FertilizerCombination プロパティを持つ「PlantSolution」クラスがあります。XAML で簡単にバインドできるように、そのクラスの VM もあります。

Public Class PlantSolutionVM
  Public Property PlantSolution As PlantSolution
  Public Sub New()
    PlantSolution = New PlantSolution("Produce Blooms", "Using this fertilizer will help your plant produce flowers!", 10, 10, 20)
End Sub
End Class

Public Class PlantSolution

Private _name As String
Private _description As String
Private _fertilizer As FertilizerCombination

<System.ComponentModel.DataAnnotations.Required()>
Public Property Name As String
    Get
        Return _name
    End Get
    Set(value As String)
        System.ComponentModel.DataAnnotations.Validator.ValidateProperty(value, NameValidationContext)
        _name = value
    End Set
End Property
<System.ComponentModel.DataAnnotations.Required()>
Public Property Description As String
    Get
        Return _description
    End Get
    Set(value As String)
        System.ComponentModel.DataAnnotations.Validator.ValidateProperty(value, DescriptionValidationContext)
        _description = value
    End Set
End Property
Public ReadOnly Property Fertilizer As FertilizerCombination
    Get
        Return _fertilizer
    End Get
End Property
Public Sub New(name As String, description As String, nitrogen As Integer, phosphorous As Integer, potassium As Integer)
    _fertilizer = New FertilizerCombination(nitrogen, phosphorous, potassium)
    Me.Name = name
    Me.Description = description
End Sub

Private NameValidationContext = New System.ComponentModel.DataAnnotations.ValidationContext(Me, Nothing, Nothing) With {.MemberName = "Name"}
Private DescriptionValidationContext = New System.ComponentModel.DataAnnotations.ValidationContext(Me, Nothing, Nothing) With {.MemberName = "Description"}
End Class

これが、「FunWithFocus」というウィンドウの XAML です。

    <Grid>
    <Grid.Resources>
        <x:Array x:Key="CombinationOptions" Type="sys:Int32" xmlns:sys="clr-namespace:System;assembly=mscorlib">
            <sys:Int32>0</sys:Int32>
            <sys:Int32>1</sys:Int32>
            <sys:Int32>2</sys:Int32>
            <sys:Int32>3</sys:Int32>
            <sys:Int32>4</sys:Int32>
            <sys:Int32>5</sys:Int32>
            <sys:Int32>6</sys:Int32>
            <sys:Int32>7</sys:Int32>
            <sys:Int32>8</sys:Int32>
            <sys:Int32>9</sys:Int32>
            <sys:Int32>10</sys:Int32>
            <sys:Int32>11</sys:Int32>
            <sys:Int32>12</sys:Int32>
            <sys:Int32>13</sys:Int32>
            <sys:Int32>14</sys:Int32>
            <sys:Int32>15</sys:Int32>
            <sys:Int32>16</sys:Int32>
            <sys:Int32>17</sys:Int32>
            <sys:Int32>18</sys:Int32>
            <sys:Int32>19</sys:Int32>
            <sys:Int32>20</sys:Int32>
        </x:Array>
        <local:PlantSolutionVM x:Key="PlantSolutionVM" />
        <Style TargetType="DockPanel">
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="TextBox">
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Grid.Resources>
    <Grid DataContext="{Binding Source={StaticResource PlantSolutionVM}, Path=PlantSolution}" Margin="50">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <TextBlock Text="Solution Name:" Grid.Row="0" Grid.Column="0" Margin="5"/>
        <TextBox x:Name="ItemName" Text="{Binding Name, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Grid.Row="0" Grid.Column="1" VerticalAlignment="Top" Margin="5"/>

        <TextBlock Text="Description of Problem:" Grid.Row="1" Grid.Column="0" Margin="5"/>
        <TextBox x:Name="ItemDescription" Text="{Binding Description, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Grid.Row="1" Grid.Column="1" Margin="5"/>

        <TextBlock Text="Recommended Fertilizer:" Grid.Row="2" Grid.Column="0" Margin="5"/>
        <DockPanel x:Name="FertilizerCombinationContainer" DataContext="{Binding Fertilizer}" Grid.Row="2" Grid.Column="1" Margin="5" HorizontalAlignment="Left" VerticalAlignment="Top">
            <DockPanel.BindingGroup>
                <BindingGroup NotifyOnValidationError="True">
                    <BindingGroup.ValidationRules>
                        <local:FertilizerCombinationValidationRule />
                    </BindingGroup.ValidationRules>
                </BindingGroup>
            </DockPanel.BindingGroup>
            <ComboBox x:Name="NitrogenValue" HorizontalAlignment="Left" VerticalAlignment="Top"
                      ItemsSource="{StaticResource CombinationOptions}"
                      SelectedItem="{Binding Nitrogen}"/>
            <ComboBox x:Name="PhosphorousValue" HorizontalAlignment="Left"  VerticalAlignment="Top"
                      ItemsSource="{StaticResource CombinationOptions}"
                      SelectedItem="{Binding Phosphorous}"/>
            <ComboBox x:Name="PotatssiumValue" HorizontalAlignment="Left"  VerticalAlignment="Top"
                      ItemsSource="{StaticResource CombinationOptions}"
                      SelectedItem="{Binding Potassium}"/>
        </DockPanel>

        <Button x:Name="SaveIt" Content="Save" Grid.Row="3" Grid.Column="1"/>
    </Grid>
</Grid>

このページのコード ビハインドは次のとおりです。

Class FunWithFocus

Private Sub FertilizerCombinationContainer_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles FertilizerCombinationContainer.Loaded
    'FertilizerCombinationContainer.BindingGroup.CancelEdit()'
    'FertilizerCombinationContainer.BindingGroup.BeginEdit()'
End Sub

Private Sub FertilizerCombinationContainer_LostFocus(sender As Object, e As System.Windows.RoutedEventArgs) Handles FertilizerCombinationContainer.LostFocus
    'This will get fired if the user drops down one of the comboboxes'
End Sub

'This is how I am currently handling the lost focus...but it doesn't fire if the user clicks somewhere that doesn't take keyboard focus'
'Private Sub FertilizerCombinationContainer_IsKeyboardFocusWithinChanged(sender As Object, e As System.Windows.DependencyPropertyChangedEventArgs) Handles FertilizerCombinationContainer.IsKeyboardFocusWithinChanged'
'    If FertilizerCombinationContainer.IsKeyboardFocusWithin = False Then'
'        FertilizerCombinationContainer.BindingGroup.ValidateWithoutUpdate()'
'        If Validation.GetErrors(FertilizerCombinationContainer).Count = 0 Then'
'            FertilizerCombinationContainer.BindingGroup.CommitEdit()'

'            Dim bg As BindingGroup = FertilizerCombinationContainer.BindingGroup'
'            Dim fertilizerCombo As FertilizerCombination = bg.Items(0)'
'            Dim proposedNitrogen As Integer'
'            Dim proposedPhosphorous As Integer'
'            Dim proposedPotassium As Integer'
'            Try'
'                proposedNitrogen = bg.GetValue(fertilizerCombo, "Nitrogen")'
'                proposedPhosphorous = bg.GetValue(fertilizerCombo, "Phosphorous")'
'                proposedPotassium = bg.GetValue(fertilizerCombo, "Potassium")'
'                ''there was a change: set it'
'                fertilizerCombo.SetFertilizerCombination(proposedNitrogen, proposedPhosphorous, proposedPotassium)'
'            Catch noValue As System.Windows.Data.ValueUnavailableException'
'                ''a binding was not properly bound yet'
'            End Try'
'        End If'
'    End If'

'End Sub'



End Class

FertilizerCombinationContainer の LostFocus イベントを処理するメソッドにブレーク ポイントを配置すると、FertilizerContainer 要素の子であっても、ComboBox の 1 つを選択すると発生することがわかります。

ありがとうございました、

-フリニー

4

0 に答える 0