Erratic behavior of TextBox "TextChanged" event

mauede 221 Reputation points
2022-04-08T12:36:21.107+00:00

I have inserted in the GUI a TextBox. It must hold a number between 0 and 1 that represents a lower threshold for accepting a structure name guessed
by the Dice coefficient. I have set a default but the user can edit the threshold. Therefore I have to check the number entered is between 0 and 1.
The TextBox is implemented as follows:

TextBox TextChanged event:
<TextBox x:Name="DiceTol" Grid.Column="1" HorizontalAlignment="Left" Margin="655,0,0,0" Grid.Row="1" Text="{Binding DiceThreshold , Mode=TwoWay}" VerticalAlignment="Bottom"
Background="Wheat" FontFamily="Arial Black" FontWeight="Bold" FontSize="20" Width="100" Height="46" Grid.RowSpan="1" TextChanged="DiceTol_TextChanged" TargetUpdated="DiceTol_TargetUpdated" TextInput="DiceTol_TextInput"/>

I developed the code that checks the newly entered number using three different pre-existent TextBox events., namely, "TextChanged", "TargetUpdated", TextInput".
The only event that is triggered is "TextChanged" but it does not work all the times a new number is entered in the TextBox. During the debugging session
first, I erase the default value and then I type in a new value that is on purpose greater than 1 but the "TextChange" event is not triggered. Sometimes it is triggered when I click on a GUI button. The other two events are never triggered.

Here is the code I developed to check the number entered in the TextBox:

private void DiceTol_TextChanged(object sender, TextChangedEventArgs e)
{
if(DiceThreshold < 0 | DiceThreshold > 1)
{
MessageBox.Show("Please, enter a number between 0 and 1", "Warning ", MessageBoxButton.OK, MessageBoxImage.Warning);
DiceThreshold = DiceThresholdDefault;
DiceTol.Text = DiceThresholdDefault.ToString();
emptyEditableStructsListBox();
return;
}
}

I would appreciate your help in fixing the above-illustrated problem.
Thank you

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,694 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,481 questions
{count} votes

3 answers

Sort by: Most helpful
  1. Hui Liu-MSFT 46,961 Reputation points Microsoft Vendor
    2022-04-11T03:13:47.803+00:00

    For the problem of validating the bound data, you could refer to the following code.

    MainWindow.xaml.cs:

    public partial class MainWindow : Window  
      {  
        public double DiceThreshold { get;set;}= 0.3;  
        public MainWindow()  
        {  
          InitializeComponent();  
          DataContext=this;  
        }  
      }  
    public class DoubleRangeRule : ValidationRule  
          {  
            public double Min { get; set; }  
          
            public double Max { get; set; }  
          
            public override ValidationResult Validate(object value,   CultureInfo cultureInfo)  
            {  
              double parameter = 0;  
          
              try  
              {  
                if (((string)value).Length > 0)  
                {  
                  parameter = Double.Parse((String)value);  
                }  
              }  
              catch (Exception e)  
              {  
                return new ValidationResult(false, "Illegal characters or "    + e.Message);  
              }  
          
              if ((parameter < this.Min) || (parameter > this.Max))  
              {  
                return new ValidationResult(false,  
                    "Please enter value in the range: "  
                    + this.Min + " - " + this.Max + ".");  
              }  
              return new ValidationResult(true, null);  
            }  
          }  
    

    MainWindow.xaml:

    <Window.Resources>  
            <local:DoubleRangeRule x:Key="DoubleRangeRule"/>  
            <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)/ErrorContent}"/>  
                    </Trigger>  
                </Style.Triggers>  
            </Style>  
            <ControlTemplate x:Key="validationTemplate">  
                <DockPanel>  
                    <TextBlock Foreground="Red" FontSize="20">!</TextBlock>  
                    <AdornedElementPlaceholder/>  
                </DockPanel>  
            </ControlTemplate>  
        </Window.Resources>  
    
    <Grid>  
        <TextBox x:Name="DiceTol"    Background="Wheat" Width="100" Height="46"  Validation.ErrorTemplate="{StaticResource validationTemplate}"   Style="{StaticResource textBoxInError}">  
                    <TextBox.Text>  
                        <Binding Path="DiceThreshold" StringFormat="N2"  UpdateSourceTrigger="PropertyChanged">  
                            <Binding.ValidationRules>  
                                <local:DoubleRangeRule Min="0.0" Max="1.0"/>  
                            </Binding.ValidationRules>  
                        </Binding>  
                    </TextBox.Text>  
                </TextBox>  
    </Grid>  
    

    The result:

    191590-image.png

    191701-image.png


    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

  2. mauede 221 Reputation points
    2022-04-11T09:05:56.407+00:00

    Thank you for the very sophisticated solution.
    I wonder whether I can use "Mode=TwoWay" in the binding statement for DiceThreshold. The code needs to use the entered value when it is within the established range.


  3. mauede 221 Reputation points
    2022-04-12T21:36:09.363+00:00

    @HiuLiu-MSFT
    Thank you very much. I got that TextBox working on adopting a simpler solution. Basically, I check the TextBox content when a Button is clicked launching the code stub that uses the TextBox content. If the TextBox numerical content falls outside the range a messageBox alerts the user and the TextBox content is set back to a default value.

    However, now I am facing a new challenge and wonder whether the code you sent me could be adapted to my new task.
    In MainWindow.XAML I have a ListBox that is used to display a list of strings that can be edited:

    ** <ListBox x:Name="EditableStructs" Grid.Row="5" Grid.Column="1" Margin="930,62,0,40" SelectionMode="Single" Grid.ColumnSpan="2" Background="PowderBlue" Height="500"
    ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.CanContentScroll="True" ItemsSource="{Binding AutoNames,Mode=TwoWay}" HorizontalAlignment="Left" Width="220" >
    <ListBox.ItemTemplate>
    <DataTemplate>
    <Grid>
    <Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <TextBlock Name="TextInd" Text="{Binding NamInd, Mode=OneWay}" Grid.Column="0" Padding="1,5" HorizontalAlignment="Stretch"/>
    <CheckBox IsChecked="{Binding IsAccepted, Mode=TwoWay}" Grid.Column="1" Padding="5,5" VerticalAlignment="Center" HorizontalAlignment="Center" />
    <TextBox Text="{Binding StrName, Mode=TwoWay}" Grid.Column="2" HorizontalAlignment="Stretch"/>
    </Grid>
    </DataTemplate>
    </ListBox.ItemTemplate>
    </ListBox>**

    The code-behind in MainWindow.XAML.cs I populate the above ListBox upon clicking a Button:

    • public class EditableStructures
      {
      public int NamInd { get; set; } = new int();
      public bool IsAccepted { get; set; } = new bool();
      public string StrName { get; set; } = "";
      }
      public static List<EditableStructures> AutoNames { get; set; } = new List<EditableStructures>();***
      
    • PTAccess pt = new PTAccess();
      List<string> EditedList = new List<string>();
      EditedList = pt.StructuresAutomaticRename(structures);
      int j = 1;
      foreach (string str in EditedList)
      {
      AutoNames.Add(new EditableStructures { StrName = str, IsAccepted = false, NamInd = j});
      j++;
      }
      // string DbgList = "" ; // DEBUG PURPOSE
      // DbgList = String.Join(" , ", AutoNames.Select(x => x.StrName)); // DEBUG PURPOSE
      // MessageBox.Show($"AUTOMATICALLY RENAMED STRUCTURES: \n {DbgList}"); // DEBUG PURPOSE
      //............................ Compare Number of Guessed Structure Names With Number Of Structure Names From Velocity DataBase
      // ............................ Alert User If Less Than 50% Structures Could Not Be Guessed
      int NumGuessed = 0;
      int NumOriginal = 0;
      var Guessed = AutoNames.Where(m => !string.IsNullOrWhiteSpace(m.StrName)).ToList();
      NumGuessed = Guessed.Count;
      NumOriginal = strucNames.Count;
      MessageBox.Show($" {NumGuessed} structures automatically renamed out of {NumOriginal} patient structures\n", "Is this patient belonging to this trial? ", MessageBoxButton.OK, MessageBoxImage.Information);
      UpdateAutoStrucNames();***

    *public void UpdateAutoStrucNames()
    {
    EditableStructs.ItemsSource = (System.Collections.IEnumerable)AutoNames;
    }
    ``***

    Some of the ListBox items contain strings that have been "guessed" through the dice coefficient. The code knows the indexes of the items containing "guessed" strings.
    My questions:
    How can I draw the user's attention to such ListBox elements? Maybe changing their background color or showing the red "!" symbol as you implemented, or some other way.

    I would appreciate some help.
    Thank you in advance.