views:

422

answers:

2

I'm trying to create a TextBox that only allows positive integer input, and that should display the input with culture-specific formatting automatically (in this case en-US, so it should use the ',' sign as a separator for larger numbers). So:

  • An entry of 1000 should show as '1,000' in the TextBox
  • An entry of 1,000 should show as such, but be interpreted correctly...

At present, no such automatic formatting is made in the first case, and the second case triggers an error from the ValidationRule I've implemented to check that the input is correct (which uses a TryParse to check a valid number has been entered).

I'm embarrassingly new at thinking in terms of globalization and internationalization here, so I'm wondering if there is som culture-related magic I can work to separate the displayed formatting, from the actual data and make the formatting automated, while being entered?

This is the xaml for the TextBox from the code-behind:

<TextBox 
                Foreground="{StaticResource WindowForegroundBrush}" 
                Background="{StaticResource EntryFieldBackgroundBrush}" 
                TextWrapping="NoWrap" 
                MaxLines="1" 
                MaxLength="100" 
                Margin="{StaticResource EntryFormTextBoxMargins}" 
                VerticalAlignment="Stretch" 
                RenderTransformOrigin="0.5,0.5" 
                HorizontalAlignment="Stretch" 
                VerticalContentAlignment="Center" 
                MinWidth="300" 
                MinHeight="30" 
                x:Name="PopTxtBox" 
                MaxWidth="300" 
                TextChanged="PopTxtChange">
                <Binding 
                    Path="locationPopulation"                                                 
                    Source="{StaticResource locDT}" 
                    UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <c:PopValidationRule />
                    </Binding.ValidationRules>
                </Binding>

And here's the ValidationRule I've written:

   public class PopulationValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            UInt64 popValue;

            if (value == null)
            {
                return new ValidationResult(true, null);
            }
            else if (!UInt64.TryParse((string)value, NumberStyles.AllowThousands, null, out popValue))
            {
                return new ValidationResult(false, "Value must be a valid number.");
            }

            return new ValidationResult(true, null);
        }
    }

As an aside, I'd like the TextBox to be able to display as empty - right now, the TextBox displays '0' on load, and leaving the TextBox empty triggers a validation error (even though I allow it in the ValidationRule). As far as I can gather, when I bind the TextBox to have a numeric value, it's not allowed a null value. Is there some way to handle that as well?

A: 

You may find this useful, paying attention to the CultureInfo class

This article contains a good explanation..

Liz Albin
Thanks for the pointers!
Ryuching
A: 

Well, I got it working to my satisfaction.

  • Apparently, I had to specify that the Binding applied specifically to the TextBox.Text-element (why I hadn't done so from the start I am unsure). After that, the StringFormat worked as advertised.

  • I added a rather simplistic event handler for the PreviewLostKeyboardFocus-event, which removes group separators as the value we are looking for should be an unformatted ulong - display-formatting is then applied by our StringFormat converter, thus the value expected should not already have any such formatting (and fails Validation if it does). The event also removes any potential leading zeros for good measure.

I suppose the above auto-corrections might be done in a more pretty manner, with some regexp-voodoo, but since I'm not a houngan of that particular brand, this is what I came up with.

Thanks guys for the comments and ideas!

The code-behind for my TextBox:

 <TextBox 
                Foreground="{StaticResource WindowForegroundBrush}" 
                Background="{StaticResource EntryFieldBackgroundBrush}" 
                TextWrapping="NoWrap" 
                MaxLines="1" 
                MaxLength="100" 
                Margin="{StaticResource EntryFormTextBoxMargins}" 
                VerticalAlignment="Stretch" 
                RenderTransformOrigin="0.5,0.5" 
                HorizontalAlignment="Stretch" 
                VerticalContentAlignment="Center" 
                MinWidth="300" 
                MinHeight="30" 
                x:Name="PopTxtBox" 
                MaxWidth="300" PreviewLostKeyboardFocus="PopTxtBoxLeavePreview"                                         
                >
                <TextBox.Text>
                    <Binding 
                        Path="locationPopulation"                                                 
                        Source="{StaticResource locDT}"                        
                        UpdateSourceTrigger="LostFocus"
                        StringFormat="{}{0:#,#;;}">
                        <Binding.ValidationRules>
                            <dataTemplates:PopValidationRule />
                        </Binding.ValidationRules>
                    </Binding>
                </TextBox.Text>

            </TextBox>

The ValidationRule:

public class PopValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            ulong popVal;

            if (!ulong.TryParse((string)value, out popVal) && !(value == null))
            {
                return new ValidationResult(false, "ValidationResult: Not a number.");
            }

            return new ValidationResult(true, null);
        }
    }

PreviewLostKeyboard Event:

private void PopTxtBoxLeavePreview(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
    {
        TextBox senderBox = (TextBox)sender;

        //remove leading 0:s.
        senderBox.Text = senderBox.Text.TrimStart('0');
        //remove spaces as group separator
        senderBox.Text = senderBox.Text.Replace(" ", "");
        //remove commas as group separator
        senderBox.Text = senderBox.Text.Replace(",", "");
        //remove singlequotes as group separator
        senderBox.Text = senderBox.Text.Replace("'", "");            
    }
}
Ryuching