1

It seems that WPF has some validation rule which is turned on by default. When I enter non numeric text into the bound textbox and tab out it, a read border shows around it. What is going on here? I Have set ValidatesOnExceptions to false, where is the validation rule coming from? I am using version 4.5.2 of the .Net framework.

Here is my XAML

<Window x:Class="WpfApplication2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApplication2"
            mc:Ignorable="d"
            Title="MainWindow" Height="159.206" Width="193.953">
        <Grid>
            <TextBox x:Name="textBox" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" TextWrapping="Wrap" 
                     Text="{Binding Foo, ValidatesOnExceptions=False, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" 
                     Width="91" Margin="10,10,0,0"/>
            <TextBox x:Name="textBox1" HorizontalAlignment="Left" Height="23" Margin="10,48,0,0" 
                     TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="91"/>
        </Grid>
</Window>

Here is the code behind

namespace WpfApplication2
{
    public partial class MainWindow : Window
    {
        public int Foo { get; set; } = 42;
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}
Shane
  • 2,271
  • 3
  • 27
  • 55

3 Answers3

2

You can never set an int property to anything else than a valid int value.

This "validation", or rather type-safety feature of the programming language, cannot be turned off.

You can however get rid of or customize the the default error template if you want to. Just set the Validation.ErrorTemplate property to an emoty ControlTemplate:

<TextBox x:Name="textBox" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" TextWrapping="Wrap" 
        Text="{Binding Foo, ValidatesOnExceptions=False, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" 
        Width="91" Margin="10,10,0,0">
    <Validation.ErrorTemplate>
        <ControlTemplate />
    </Validation.ErrorTemplate>
</TextBox>
mm8
  • 163,881
  • 10
  • 57
  • 88
  • Thanks very much. What if have a int? and I want blank text to map to null, this doesn't seem to work by default. Do I have to write a custom converter? – Shane Jul 04 '17 at 11:02
  • Probably. You need to convert the empty string to null. String.Empty is not a valid int? value. – mm8 Jul 04 '17 at 11:03
0

On app startup, you can add this call to FrameworkCompatibiltyPreferences to disable all textboxes from messing with your input:

class App
{
    [STAThread]
    public static void Main(params string[] args)
    {     
        System.Windows.FrameworkCompatibilityPreferences.KeepTextBoxDisplaySynchronizedWithTextProperty = false;
        Application app = new Application();
        app.Run(new Window1());
    }
}

If you're using data binding, it appears that the textboxes will still protect against invalid input. A validation error will be shown (default being the red border) and the view model property will not be set with the invalid value. But now you can input whatever string you want. Makes entering decimal values bound to float or double properties MUCH easier.

Andrew
  • 342
  • 3
  • 12
0

Expanding on mm8's excellent answer, for anyone looking to do this for a nullable int, you can do as follows;

XAML

<Window.Resources>
  <ResourceDictionary>
    <converters:NullableIntToStringConverter x:Key="NullableIntConverter" />
  </ResourceDictionary>
</Window.Resources>
<!-- ...snip ... -->
<TextBox Grid.Column="1" HorizontalAlignment="Stretch" Margin="5"              
         Text="{Binding Path=SearchRackNumber,Mode=TwoWay,Converter={StaticResource NullableIntConverter},ValidatesOnExceptions=False}"
         PreviewTextInput="TxtRackNumber_OnPreviewTextInput"
         PreviewKeyDown="TxtRackNumber_OnPreviewKeyDown">
  <Validation.ErrorTemplate>
    <ControlTemplate />
  </Validation.ErrorTemplate>
</TextBox>

Code Behind

The events here prevent the user from typing non-numeric characters. In my case, I only cared about positive integer values. Two events are needed to handle most text, and separately, spaces (see here for why).

private void TxtRackNumber_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
    var numericRegEx = new Regex("^[0-9]{0,10}$");
    e.Handled = !numericRegex.IsMatch(e.Text);
}

private void TxtRackNumber_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
    // a separate event is needed to handle the space as it doesn't trigger the
    // OnPreviewTextInput event. This is by WPF design to handle IME languages
    // (ones that take more than one key press to enter a character, e.g., Chinese).
    e.Handled = e.Key == Key.Space;
}

Converter

using System;
using System.Globalization;
using System.Windows.Data;

namespace YourApp
{
    internal class NullableIntToStringConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null) return null;
            return value.ToString();
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || string.IsNullOrWhiteSpace(value.ToString())) return null;

            var stringValue = value.ToString();
            if (!int.TryParse(stringValue, out int parsed)) return null;

            return parsed;
        }
    }
}
DiskJunky
  • 4,750
  • 3
  • 37
  • 66