Home > front end >  WPF How to change the entered character when clicking on the textbox?
WPF How to change the entered character when clicking on the textbox?

Time:06-09

I have a code from WinForms that, when pressing a key on a textbox, checks which character is entered and if it is a dot, then it changes to a comma, and if it is something else, then it is checked whether it is a digit or not

private void textbox_KeyPress(object sender, KeyPressEventArgs e)
    {
        char ch = e.KeyChar;
        if (ch == '.')
        {
            e.KeyChar = ',';
        }
        if ((!Char.IsDigit(ch)) && (ch != ',') && (ch != '.') && (ch != 8))
        {
            e.Handled = true;
        }
    }

How do I implement such code on WPF

Now the code is like this:

<TextBox 
        PreviewTextInput="NumberValidationTextBox"
        />

private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
    {
        Regex regex = new Regex("[^0-9,.] ");
        e.Handled = regex.IsMatch(e.Text);
    }

In the TextBox, you can now enter numbers, a dot and a comma, it remains only to change the dot to a comma, as an option to use the textchanged event, but since I need this event for another code where the dots will already be changed to commas, then using this event is not an option.

CodePudding user response:

As a result, this code works:

private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
    {
        Regex regex = new Regex("[^0-9,.] ");
        e.Handled = regex.IsMatch(e.Text);
        Regex regex1 = new Regex(",");
        if (regex1.IsMatch(e.Text))
        {
            e.Handled = true;
            tb1.Text  = ".";
            tb1.CaretIndex = tb1.Text.Length;
        }
    }

CodePudding user response:

Perhaps you are able to cast the sender as a TextBox and then set the text that way? Like:

    private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
    {
            Regex regex = new Regex("[^0-9,.] ");

            e.Handled = regex.IsMatch(e.Text);


            if (sender is TextBox textbox)
            {


                var currentText = textbox.Text;

                textbox.Text = currentText.Replace('.', ',');
            }
    }

It's difficult to figure out what your intent is here.

You may just want to subscribe to the TextChanged event on your text entry and modify the information as it comes in. Within that method you should be able to update the text to your liking, and confirm that the text is valid.

See Microsoft's documentation here.

Here is an example of how you might do it:

<TextBox TextChanged="Entry_TextChanged"/>
private void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
        //textBox.TextChanged -= Entry_TextChanged;
        //textBox.Text = textBox.Text.Replace(".",".");
        //textBox.TextChanged  = Entry_TextChanged;
}

Further research shows Microsoft has documentation on Binding Validation, which I believe will suit your case. You can read more about it here.

Here's an example of how to implement a validation rule in WPF, where the XAML shows the instantiation of the ValidationRule object, and the C# shows how that ValidationRule was implemented:

<TextBox Name="textBox1" Width="50" FontSize="15"
         Validation.ErrorTemplate="{StaticResource validationTemplate}"
         Style="{StaticResource textBoxInError}"
         Grid.Row="1" Grid.Column="1" Margin="2">
  <TextBox.Text>
    <Binding Path="Age" Source="{StaticResource ods}"
             UpdateSourceTrigger="PropertyChanged" >
      <Binding.ValidationRules>
        <c:AgeRangeRule Min="21" Max="130"/>
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>
public class AgeRangeRule : ValidationRule
{
    public int Min { get; set; }
    public int Max { get; set; }

    public AgeRangeRule()
    {
    }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        int age = 0;

        try
        {
            if (((string)value).Length > 0)
                age = Int32.Parse((String)value);
        }
        catch (Exception e)
        {
            return new ValidationResult(false, $"Illegal characters or {e.Message}");
        }

        if ((age < Min) || (age > Max))
        {
            return new ValidationResult(false,
              $"Please enter an age in the range: {Min}-{Max}.");
        }
        return ValidationResult.ValidResult;
    }
}

If that doesn't work, then this answer may be helpful: How do I get a TextBox to only accept numeric input in WPF?

Otherwise, this can also be done by implementing some form of DataTrigger.

A DataTrigger "Represents a trigger that applies property values or performs actions when the bound data meets a specified condition."

See Microsoft's documentation here.

An example pulled from their page shows how you can change the foreground color of a ListBoxItem conditionally, based on the text property of a Place object:

<Window.Resources>
  <c:Places x:Key="PlacesData"/>

  <Style TargetType="ListBoxItem">
    <Style.Triggers>
      <DataTrigger Binding="{Binding Path=State}" Value="WA">
        <Setter Property="Foreground" Value="Red" />
      </DataTrigger>    
      <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
          <Condition Binding="{Binding Path=Name}" Value="Portland" />
          <Condition Binding="{Binding Path=State}" Value="OR" />
        </MultiDataTrigger.Conditions>
        <Setter Property="Background" Value="Cyan" />
      </MultiDataTrigger>
    </Style.Triggers>
  </Style>

  <DataTemplate DataType="{x:Type c:Place}">
    <Canvas Width="160" Height="20">
      <TextBlock FontSize="12"
             Width="130" Canvas.Left="0" Text="{Binding Path=Name}"/>
      <TextBlock FontSize="12" Width="30"
                 Canvas.Left="130" Text="{Binding Path=State}"/>
    </Canvas>
  </DataTemplate>
</Window.Resources>

<StackPanel>
  <TextBlock FontSize="18" Margin="5" FontWeight="Bold"
    HorizontalAlignment="Center">Data Trigger Sample</TextBlock>
  <ListBox Width="180" HorizontalAlignment="Center" Background="Honeydew"
    ItemsSource="{Binding Source={StaticResource PlacesData}}"/>
</StackPanel>

Alternatively (or perhaps additionally), you may need to use some sort of converter with your binding. It's possible to have a string converter that replaces all instances of a character with another character and return the transformed string to a binding. See some of Microsoft's documentation on converters here.

  • Related