1

The .NET DateTimePicker has Text, but I cannot figure out how to get and set the SelectionStart, SelectionLength, and SelectionText properties for the DateTimePicker.

For example, how can I programmatically select the 2 day digits in the date "11/17/2001"? and how can I programmatically determine the currently selection start position?

Essentially, what I would like to do is something like the following code:

// Select the day's text
((TextBox)myDateTimePicker).SelectionStart = 3;
((TextBox)myDateTimePicker).SelectionLength = 2;

// Get the text start position
return ((TextBox)myDateTimePicker).SelectionStart;

enter image description here

gridtrak
  • 731
  • 7
  • 20
  • 1
    Call `SendKeys.Send("{Right}")` in the `DTP.Enter` event to select the next part. –  Apr 29 '20 at 10:44
  • @JQSOFT reads my mind. Actually that is sort of my end goal. I do the part stepping you describe when the Space key is pressed. I would like the Tab to work for stepping as well. Unfortunately, the Tab then gets *stuck* in the text box. I want to know when SelectionStart is in the last or first parts ( so I can set the PreviewKeyDown's `e.IsInputKey = false;` for Tab in the *year* and Shift-Tab in the *month* so the user can Tab through the DTP parts and move on. – gridtrak Apr 29 '20 at 12:58
  • I also want to be able to Select the DTP's *month* when entering the DTP from an external control from the Left side of the DTP; or Select the DTP's *year* when entering the DTP from an external control from the Right side of the DTP. – gridtrak Apr 29 '20 at 13:10
  • 1
    Nice trick [here](https://stackoverflow.com/a/38385827/10216583). –  Apr 29 '20 at 19:00
  • 1
    @JQSOFT's nice trick there works for me. – gridtrak Apr 29 '20 at 20:50

2 Answers2

1

The following code is the answer to my question. It is derived from the DateTimePicker get focused field question's answer.

public event System.EventHandler<EventArgs> Parent_ValueChanged;

public enum DatePart
{
  YEAR,
  MONTH,
  DAY
}

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public DatePart SelectedPart { get; set; }

private DateTime m_PreviousValue = TimeTools.UnixBaseTime;
private bool m_CheckSelectedPart = false;

public void DateTimeField_GotFocus(object sender, EventArgs e)
{
  m_PreviousValue = this.Value;
  m_CheckSelectedPart = true;
  SendKeys.SendWait("{UP}");
  SendKeys.SendWait("{DOWN}");
  m_CheckSelectedPart = false;
}


public void DateTimeField_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
  switch (e.KeyCode)
  {
    case Keys.Space: 
      e.IsInputKey = true;
      break;
    case Keys.Tab:
      m_PreviousValue = this.Value;
      m_CheckSelectedPart = true;
      SendKeys.SendWait("{UP}");
      SendKeys.SendWait("{DOWN}");
      m_CheckSelectedPart = false;

      // Set e.IsInputKey to false to let Windows use the Tab 
      // to go to the previous or next component with TabStop = true
      if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift)
      {
        // false = exit to the left or up
        e.IsInputKey = !(SelectedPart == DatePart.MONTH);
      }
      else
      { 
        // false = exit to the right or down
        e.IsInputKey = !(SelectedPart == DatePart.YEAR);
      }
      break;
    default: break;
  }
}


public void DateTimeField_KeyDown(object sender, KeyEventArgs e)
{
  // Mimic Delphi's DateTimePicker behavior by advancing to the next part in the format
  // using Space or Tab
  if (e.KeyCode.Equals(Keys.Space) || e.KeyCode.Equals(Keys.Tab))
  {
    if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift)
    {
      SendKeys.Send("{left}");
    }
    else
    {
      SendKeys.Send("{right}");
    }
  }
}


private void DateTimeField_ValueChanged(object sender, EventArgs e)
{
  if ((m_CheckSelectedPart) && (sender is DateTimePicker dtp))
  {
    TimeSpan change = (dtp.Value - m_PreviousValue);
    var dayChange = Math.Abs(change.Days);
    if (dayChange == 1)
    {
      SelectedPart = DatePart.DAY;
    }
    else if (dayChange >= 365)
    {
      SelectedPart = DatePart.YEAR;
    }
    else
    {
      SelectedPart = DatePart.MONTH;
    }
    m_PreviousValue = dtp.Value;
  }

  // parent's ValueChanged event handler
  Parent_ValueChanged?.Invoke(sender, e);
}

gridtrak
  • 731
  • 7
  • 20
-1

I know that the question is asked for a long time but it will be useful for the next ones who are still looking for the solution which is for the moment unresolved, the asking again should be a contentment solution with less code written and to manage.

After the focus leaves a DateTimerPicker control and then at a later time returns to it, the control remembers which part (day, month or year) had the focus. Focus is returned to that part.

How can force the focus back to the first per of the control?

To force the control to forget which part had the focus you can change the Format and then change it back again. So if it is set to Short set it to Long and then back to Short.

The trick is to do this after the focus returns via the Enter event

So the best place to do this is in the Enter event:

Private Sub DateTimePicker_Enter(sender As Object, e As System.EventArgs) Handles DateTimePicker.Enter

        DateTimePicker.Format = DateTimePickerFormat.Long
        DateTimePicker.Format = DateTimePickerFormat.Short
End Sub
Tyler2P
  • 2,324
  • 26
  • 22
  • 31