简体   繁体   中英

ComboBox SelectedValuePath Validation not working

I am trying to implement data validation in XAML / WPF for the very first time. I have read several tutorials and there seems to be a variety of ways to do it. I have settled on one approach and it is not working.

First, this is ideally how I would like the error displayed ( above the ComboBox and to the right of the Label ). If that is possible ...

验证错误的位置

I created a ValidationRule :

using System;
using System.Globalization;
using System.Windows.Controls;

namespace OCLMEditor.ValidationRules
{
    class StudyPointValidationRule : ValidationRule
    {
        public bool BibleReading { get; set; }

        public StudyPointValidationRule()
        {
            BibleReading = false;
        }

        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            if(value == null)
                return new ValidationResult(false, "The study point has not been set.");

            int iStudy = (int)value;
            if(BibleReading)
            {
                if(iStudy > 17)
                    return new ValidationResult(false, "Maximum study point for bible readings is 17.");
            }
            else
            {
                if(iStudy == 7 || iStudy > 51)
                    return new ValidationResult(false, "Study points 7, 52 and 53 are not permitted for student items.");
            }

            return new ValidationResult(true, null);
        }
    }
}

Then, I added this to my Window class in XAML:

xmlns:ValidationRules="clr-namespace:OCLMEditor.ValidationRules":

In the Window.Resources :

<ControlTemplate x:Key="StudyPointValidationTemplate">
    <DockPanel>
        <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
        <AdornedElementPlaceholder/>
    </DockPanel>
</ControlTemplate>

Then, for my first ComboBox where I want this validation applied I changed it from:

<ComboBox DataContext="{Binding DataContext, ElementName=oclmEditor}"
          ItemsSource="{Binding ReadingStudyPointsList}"
          ItemContainerStyle="{StaticResource StudyPointComboBoxStyle}"
          ItemTemplate="{StaticResource StudyPointComboItem}"
          Validation.ErrorTemplate="{StaticResource StudyPointValidationTemplate}"
          Tag="{Binding Meeting.BibleReadingMainName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
          SelectedValue="{Binding Meeting.BibleReadingMainStudyPoint, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
          SelectedValuePath="Number"/>

To:

<ComboBox DataContext="{Binding DataContext, ElementName=oclmEditor}"
          ItemsSource="{Binding ReadingStudyPointsList}"
          ItemContainerStyle="{StaticResource StudyPointComboBoxStyle}"
          ItemTemplate="{StaticResource StudyPointComboItem}"
          Validation.ErrorTemplate="{StaticResource StudyPointValidationTemplate}"
          Tag="{Binding Meeting.BibleReadingMainName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
          SelectedValue="{Binding Meeting.BibleReadingMainStudyPoint, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
          >
    <ComboBox.SelectedValuePath>
        <Binding Path="Number">
            <Binding.ValidationRules>
                <ValidationRules:StudyPointValidationRule BibleReading="True"/>
            </Binding.ValidationRules>
        </Binding> 
    </ComboBox.SelectedValuePath>
</ComboBox>

I get this error:

System.Windows.Data Error: 40 : BindingExpression path error: 'Number' property not found on 'object' ''OCLMEditorViewModel' (HashCode=17586541)'. BindingExpression:Path=Number; DataItem='OCLMEditorViewModel' (HashCode=17586541); target element is 'ComboBox' (Name=''); target property is 'SelectedValuePath' (type 'String')

I don't understand why because I was able to use "Number" previously.

Update

I have extended my Model Data classes to throw an exception if the value is too high. For example:

[XmlIgnore]
public int BibleReadingMainStudyPoint
{
    get { return _TFGW.BibleReadingItem.Main.StudyPoint; }
    set
    {
        if (value > 17)
            throw new ArgumentOutOfRangeException("Maximum Bible reading study point is 17.");

        _TFGW.BibleReadingItem.Main.StudyPoint = value;
        MarkDirty();
        OnPropertyChanged("BibleReadingMainStudyPoint");
    }
}

I definately get the exception thrown:

Parameter name: Maximum Bible reading study point is 17. at OCLMEditor.Data.MeetingInfo.Meeting.set_BibleReadingMainStudyPoint(Int32 value) in D:\\My Programs\\OCLMEditor\\OCLMEditor\\Data\\MeetingInfo\\Meeting.cs:line 234 at OCLMEditor.OCLMEditorViewModel.set_SelectedStudentItem(Student value) in D:\\My Programs\\OCLMEditor\\OCLMEditor\\ViewModels\\OCLMEditorViewModel.cs:line 184'

And I changed the XAMl since the ValidationRule does not apply in this instance:

<Binding.ValidationRules>
    <ExceptionValidationRule/>
</Binding.ValidationRules>

So I was expecting it to now show the error visually. No nothing on screen.

The resources:

<Window.Resources>
    <ValueConverters:StudyPointWorkingOn x:Key="StudyPointWorkingOn" />

    <Style x:Key="StudyPointComboBoxStyle" TargetType="ComboBoxItem">
        <Setter Property="Tag" Value="{Binding Number}" />
        <Style.Triggers>
            <DataTrigger Value="True">
                <DataTrigger.Binding>
                    <MultiBinding Converter="{StaticResource StudyPointWorkingOn}">
                        <Binding RelativeSource="{RelativeSource Self}" Path="Tag"/>
                        <Binding Path="DataContext" ElementName="oclmEditor" UpdateSourceTrigger="PropertyChanged" />
                        <Binding Path="Tag" RelativeSource="{RelativeSource AncestorType=ComboBox}"/>
                    </MultiBinding>
                </DataTrigger.Binding>
                <Setter Property="Background" Value="Red"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>

    <DataTemplate x:Key="StudyPointComboItem">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding Number}"/>
            <TextBlock Text=" - "/>
            <TextBlock Text="{Binding Title}"/>
        </StackPanel>
    </DataTemplate>

    <ControlTemplate x:Key="StudyPointValidationTemplate">
        <DockPanel>
            <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
            <AdornedElementPlaceholder/>
        </DockPanel>
    </ControlTemplate>

</Window.Resources>

Update 2

I can just make out that there is a validation symbol on teh screen. But it has not reduced the width of the combo so it can be seen.

SelectedValuePath is a string . It tells the ComboBox which value of the selected data item to assign to the SelectedValue property. There's nothing to validate about that; that's not the property that tells you what the user selected. It shouldn't change when the selection changes, because Number is always the property you want to use for SelectedValue (unless you change the type of the items in the ComboBox -- but that's not what you're doing).

The property you want to bind with validation is SelectedValue , which you've got as an attribute in your question.

<ComboBox
    ...other properties...
    SelectedValuePath="Number"
    >
    <ComboBox.SelectedValue>
        <Binding 
            Path="Meeting.BibleReadingMainStudyPoint"
            Mode="TwoWay"
            UpdateSourceTrigger="PropertyChanged"
            >
            <Binding.ValidationRules>
                <ValidationRules:StudyPointValidationRule BibleReading="True"/>
            </Binding.ValidationRules>
        </Binding> 
    </ComboBox.SelectedValue>
    <ComboBox.Style>
        <Style 
            TargetType="ComboBox" 
            BasedOn="{StaticResource {x:Type ComboBox}}"
            >
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter 
                        Property="ToolTip"
                        Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </ComboBox.Style>
</ComboBox>

You should do something like this if you want to achieve the validation:

<ComboBox SelectedValuePath="Number" DataContext="{Binding DataContext, ElementName=oclmEditor}"
          ItemsSource="{Binding ReadingStudyPointsList}"
          ItemContainerStyle="{StaticResource StudyPointComboBoxStyle}"
          ItemTemplate="{StaticResource StudyPointComboItem}"
          Validation.ErrorTemplate="{StaticResource StudyPointValidationTemplate}"
          Tag="{Binding Meeting.BibleReadingMainName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
          SelectedValue="{Binding Meeting.BibleReadingMainStudyPoint, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
          >
    <ComboBox.SelectedValue>
                <Binding Path="Meeting.BibleReadingMainName"  UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
                    <Binding.ValidationRules>
                                   <ValidationRules:StudyPointValidationRule BibleReading="True"/>
                    </Binding.ValidationRules>
                </Binding>
            </ComboBox.SelectedValue>
</ComboBox>

Use the SelectedValuePath just for tell which property use to get the SelectedValue from the SelectedItem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM