MoveFocus() inside UserControl

65 Views Asked by At

I'm building a custom DatePicker in WPF using a UserControl. My control inside it has 3 TextBoxes that take care of indicating day month and year based on the cultureInfo. I am trying to move the focus from one textbox to another to simplify data entry but I always have the same problem, the focus moves but always ends up at the first of the 3 textboxes inside the control. Here is the code of TextChanged event that is one for all three texboxes:

    private void _DateTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        EvolutionTextbox textBox = sender as EvolutionTextbox;
        string tagText = textBox.TagText.ToString();
        int value;
        bool emptyText = false;
        bool isNumber = Int32.TryParse(textBox.InputText, out value);

        if (textBox.InputText == "") {
            emptyText = true;
        }

        _UnregisterEvents();
        int year = 2024; // default value for year
        int month = 1; // default value for month
        int day = 1; // default value for day

        // get current input from textboxes
        if (_YearTextBox.InputText != "" && _YearTextBox.InputText.Count() == 4) {
            year = Int32.Parse(_YearTextBox.InputText);
        }
        if (_MonthTextBox.InputText != "" && Int32.Parse(_MonthTextBox.InputText) < 12 && Int32.Parse(_MonthTextBox.InputText) > 0) {
            month = Int32.Parse(_MonthTextBox.InputText);
        }
        if (_DayTextBox.InputText != "") {
            day = Int32.Parse(_DayTextBox.InputText);
        }

        int maxDay = DateTime.DaysInMonth(year, month);

        switch (tagText) {
            case "d": // Day
                if (value <= 0 || value > maxDay) {
                    ((EvolutionTextbox)sender).InputText = maxDay.ToString();
                }
                break;
            case "M": // Month
                if (value <= 0 || value > 12) {
                    ((EvolutionTextbox)sender).InputText = "12";
                }
                else if (value <= 12) {
                    maxDay = DateTime.DaysInMonth(year, value);
                    if (day > maxDay) {
                        _DayTextBox.InputText = maxDay.ToString();
                    }
                }
                break;
            case "yyyy": // Year
                if (value <= 0 || value > 9999) {
                    ((EvolutionTextbox)sender).InputText = "9999";
                }
                else if (value > 0) {
                    maxDay = DateTime.DaysInMonth(value, month);
                    if (day > maxDay) {
                        _DayTextBox.InputText = maxDay.ToString();
                    }
                }
                break;
        }

        // If empty set empty string
        if (emptyText) {
            ((EvolutionTextbox)sender).InputText = "";
        }

        _RegisterEvents();

        //jump between textboxes

        if ((tagText == "d" || tagText == "M") && textBox.InputText.Length == 2) {
            TraversalRequest req = new TraversalRequest(FocusNavigationDirection.Next);
            MoveFocus(req);
        }
        else if (tagText == "yyyy" && textBox.InputText.Length == 4) {
            TraversalRequest req = new TraversalRequest(FocusNavigationDirection.Next);
            MoveFocus(req);
        }


            
    }

I also tried putting KeyboardNavigation.TabNavigation="Contained" and also KeyboardNavigation.TabNavigation="Local" but they did not have the desired effect. I also tried setting the TabIndex of the various controls by putting TabIndex = 1,2,3 for the respective controls. I do not understand what may be missing or what I am doing wrong!

1

There are 1 best solutions below

4
BionicCode On

The behavior you are observing is correct: you are always calling FrameworkElement.MoveFocus on the parent UserControl to move the focus to the next element. And the next element in the hierarchy is the first TextBox. Unless you move the elements inside the child element tree the next element relative to the MoveFocus caller will never change.

You have to call MoveFocus on the currently focused element to continue focus traversal.

Assuming that you use the same _DateTextBox_TextChanged event handler for all TextBox.TextChanged events of the participating TextBox elements then you must change the focus navigation part as follows:

private void _DateTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
  var textBox = (EvolutionTextbox)sender;
  string tagText = textBox.TagText.ToString();

  ...

  // Move focus to next TextBox 

  bool isInputDayOrMonth = tagText == "d" || tagText == "M";
  int expectedInputLength = isInputDayOrMonth ? 2 : 4;
  if (textBox.InputText.Length == expectedInputLength) 
  {
    TraversalRequest req = new TraversalRequest(FocusNavigationDirection.Next);
    //MoveFocus(req);

    // Move from the currently focused element (TextBox) to the next element
    textBox.MoveFocus(req);
  }
}