Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
392 views
in Technique[技术] by (71.8m points)

c# - WPF validation rule preventing decimal entry in textbox?

I have a WPF textbox defined in XAML like this:

<Window.Resources>        
    <Style x:Key="textBoxInError" TargetType="{x:Type 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>
</Window.Resources>

<TextBox x:Name="upperLeftCornerLatitudeTextBox" Style="{StaticResource textBoxInError}">
    <TextBox.Text>
        <Binding Path="UpperLeftCornerLatitude" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <local:LatitudeValidationRule ValidationStep="RawProposedValue"/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

As you can see, my textbox is bound to a decimal property on my business object called UpperLeftCornerLatitude which looks like this:

private decimal _upperLeftCornerLongitude;
public decimal UpperLeftCornerLatitude
{
    get { return _upperLeftCornerLongitude; }
    set
    {
        if (_upperLeftCornerLongitude == value)
        {
            return;
        }

        _upperLeftCornerLongitude = value;
        OnPropertyChanged(new PropertyChangedEventArgs("UpperLeftCornerLatitude"));
    }
}

My user will be entering a latitude value into this textbox and in order to validate that entry, I've created a validation rule that looks like this:

public class LatitudeValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        decimal latitude;

        if (decimal.TryParse(value.ToString(), out latitude))
        {
            if ((latitude < -90) || (latitude > 90))
            {
                return new ValidationResult(false, "Latitude values must be between -90.0 and 90.0.");
            }
        }
        else
        {
            return new ValidationResult(false, "Latitude values must be between -90.0 and 90.0.");
        }

        return new ValidationResult(true, null);
    }
}

My textbox initially starts off empty and I have a breakpoint set at the beginning of my validation rule. I enter 1 in the textbox and when my debugger breaks inside of the validation rule, I can see that value = "1". So far so good. Now I continue running and enter a decimal point in the textbox (so we should have "1." now). Again, the debugger breaks inside of the validation rule and, as expected, value = "1.". If I step through the validation rule code, I see that it passes the latitude value check and returns the following:

new ValidationRule(true, null);

However, as soon as the validation rule returns and I step into the next line of code, I find myself on the first line of my UpperLeftCornerLatitude property setter. Mousing over value here reveals that it's a value of "1" instead of "1." as I would expect. So naturally when I continue running my code, I end up back in the textbox staring at a value of "1" instead of "1.". If I remove all of the breakpoints, the effect is that I can't seem to enter a decimal point in the textbox. Is there something obvious that I'm missing here that's causing my setter to end up with a value of "1" even though I have entered "1." in the textbox? Thanks very much!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here are a few ways to fix this problem

A. Specify LostFocus (textbox default) for your binding

<Binding Path="UpperLeftCornerLatitude" Mode="TwoWay" UpdateSourceTrigger="LostFocus">
</Binding>

B. Specify a Delay for the binding that will allow for some time for you to type the decimal

<Binding Path="UpperLeftCornerLatitude" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" Delay="1000">
</Binding>

C. Change decimal to string and parse it yourself

D. Write a ValueConverter to override the default conversion process

class DecimalConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        ...
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        ...
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...