Tuesday, April 7, 2009

WPF DatePicker: Replace/remove the "Show Calendar" string

Scenario: I am programming in VB.NET using WPF (Windows Presentation Foundation) as the visual platform. The program will among other things also ask the user to select a date. I found and installed the WPF Toolkit from the Microsoft CodePlex website, which includes the DatePicker tool in question.

Problem: This post will probably be obsolete in a few months, but for those of us who needs to use the WPF Toolbox DatePicker tool in its current (April 2009) state in their applications, there's no straightforward way to remove the "Show Calendar" text that shows up on the DatePicker text field when no date is selected. I needed to start out with a blank date - selecting one would be optional for the user.

Solution: On the CodePlex website, they will tell you how to modify the xaml template to achieve just that, but I came up with an (imho) ingenious - or should we say quick and dirty - solution to avoid fiddling with all that. Basically this method consists of two steps:
  1. Put a blank Textbox on top of the DatePicker control so that only the date icon shows
  2. Add a a couple of code lines to control the transfer of the date between the text box and the date picker.
To acheive no. 1, I had a grid control on my xaml where both the DatePicker and the TextBox are aligned in the same position, e.g. Margins 0 of the grid cell in question.

I found 85 to be just the right length to cover the DatePicker's text field, without hiding its icon. I removed tabstop from the DatePicker to avoid ever landing at the DatePicker's text field that is hidden behind the TextBox, when using the keyboard to navigate in the app. The xaml looks like this:

<my:datepicker height="24" verticalalignment="Top" istabstop="False" textbox="" row="0" column="1" margin="0" name="DatePicker1" horizontalalignment="Left" width="85">

<TextBox Grid.Row="0" Grid.Column="1" Margin="0" Name="TextBox1" HorizontalAlignment="Left" Width="85" />

Now, the only thing needed is a couple of event handlers to transfer the dates back and forth:


Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.TextChangedEventArgs) Handles TextBox1.TextChanged
DatePicker1.Text = NormalizeDateString(TextBox1.Text)
End Sub

Private Sub TextBox1_LostFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles TextBox1.LostFocus
TextBox1.Text = DatePicker1.Text
End Sub

Private Sub DatePicker1_SelectedDateChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs) Handles DatePicker1.SelectedDateChanged
TextBox1.Focus()
End Sub

Oh, and finally, the NormalizeDateString function referenced above:

Protected Function NormalizeDateString(ByVal DateIn As String) As String
Dim ReturnDato As String
Try
If DateIn.Substring(DateIn.Length - 1) = "." Then DateIn = DateIn.Substring(0, DateIn.Length - 1)
Dim ConvertedDato As DateTime = Convert.ToDateTime(DateIn)
ReturnDato = ConvertedDato.ToShortDateString
Catch ex As Exception
Return ""
End Try

Return ReturnDato
End Function

And voilla! You have a WPF DatePicker that does not display any text if the date is left blank, and that at any time will hold the date the user selected, regardless if he types the date (in the overlaying TextBox) or he selects the date by clicking the DatePicker calendar icon visible to the right of the textbox.

4 comments:

SpudCZ said...

You can derive from DatePicker and then override method OnRender with this code (written in C#)

protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
{
base.OnRender(drawingContext);
FieldInfo fiTextBox = typeof(DatePicker).GetField("_textBox", BindingFlags.Instance | BindingFlags.NonPublic);
if (fiTextBox != null)
{
DatePickerTextBox dateTextBox = (DatePickerTextBox)fiTextBox.GetValue(this);
PropertyInfo piWatermark = typeof(DatePickerTextBox).GetProperty("Watermark", BindingFlags.Instance | BindingFlags.NonPublic);
if (piWatermark != null)
{
piWatermark.SetValue(dateTextBox, "-", null);
}
}
}

Junior.NET said...

you can just change xaml...

http://stackoverflow.com/questions/1102042/wpf-toolkit-datepicker-template-question-show-calendar-default-value

Michalis said...

Changing xaml as in the stackoverflow does not replace the Show Calendar- when you erase the date Show Calendar reappears...

Abhi said...

Very useful..I was kind of looking for this stuff for a long time. Thanks.