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

Developer technologies | Windows Presentation Foundation
Developer technologies | C#
{count} votes

3 answers

Sort by: Most helpful
  1. Hui Liu-MSFT 48,676 Reputation points Microsoft External Staff
    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.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.