Customizing the WPF DatePicker's Watermark
Introduced as part of the WPF Toolkit and added to .NET 4 as a part of the framework, the DatePicker control is a half-decent replacement for Windows Form's DateTimePicker control.
Something that a lot of people (myself included) don't like about the DatePicker, though, is that by default if no date is displayed it shows the text "Select a date" as a watermark, and this text is baked into the control - it's not localized or accessible by any public property. This is particularly frustrating if the date in question is optional and you don't necessarily want to prompt your users to select one.
There are a few posts around the net describing how to customize this text by deriving your own new control from DatePicker and/or by completely replacing the default ControlTemplate, but this is cumbersome, especially if you already have a project with DatePickers throughout.
Here's a much simpler way to customize the watermark text in a DatePicker.
First, in your app's OnStartup method, you'll want to register a global event handler to the control's Loaded event:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
EventManager.RegisterClassHandler(typeof(DatePicker),
DatePicker.LoadedEvent,
new RoutedEventHandler(DatePicker_Loaded));
}
}
That means that any DatePicker in our application will call the DatePicker_Loaded method once it's loaded. That method needs to find the watermark text in the control, so we'll need this helper method defined somewhere too:
public static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
Now that we have that in place, our event handler looks like this:
void DatePicker_Loaded(object sender, RoutedEventArgs e)
{
var dp = sender as DatePicker;
if (dp == null) return;
var tb = GetChildOfType<DatePickerTextBox>(dp);
if (tb == null) return;
var wm = tb.Template.FindName("PART_Watermark", tb) as ContentControl;
if (wm == null) return;
wm.Content = "Your watermark here!";
}
Now all DatePickers will show "Your watermark here!" rather than the default "Select a date" text when no date is selected. If you'd rather not see any watermark at all, simply assign null to wm.Content instead.
Trackbacks
- Customizing the WPF DatePicker’s Watermark « Mas-Tool's Favorites | http://mas-tool.com/?p=2768
Comments
Ian Randall
Nice, Matt. It is a frustration that the control doesn't expose a 'Watermark' property - but this is a nice workround
Juan
Thanks! That's a nice workaround. Still, I could not make it work. I even tried extending DatePicker and editing the template XAML, but could not find the "PART_Watermark" ContentControl. Any ideas?
Olaf Rabbachin
Matt - thanks for posting this. From all the work-arounds I've seen, this is IMHO the best and straightest forward one.
Cheers, Olaf
Geoffrey Adam
Thanks Matt, this really Helped me out for a school project!
Narayana
Great work. Thank you very much Matt.
Leonardo
Didn't work. I have a WPF window that contain a DatePicker. I placed this code on App.xml.cs. But still, my DatePicker gets "Select a date" content. What am I doing wrong?
Matt Hamilton
Leonardo,
Have you stepped through the code in debug mode? Can you see which line it's failing on. For example, is it not finding the PART_Watermark ContentControl?
Leonardo
Sorry Matt. There was an inconsistence among my Namespaces. It worked perfectly! Thanks for the article!
JZ
Is that have anyway to find this watermaker content control in XAML? I am looking a way to bond it with a string which later can be localized in the resource file.
Thanks.
Matt Hamilton
Not that I'm aware of, JZ. That's the big complaint about the DatePicker control (well, one of) - that it doesn't allow for much customization in XAML.
brodny
Thanks, Matt. Really nice workaround, works great. Too bad that we have to use workarounds and .NET 4 developers didn't expose property to modify default watermark. I hope it will change in the next release (or in service pack).
young
you could get around the text by assigning the SelectedDate with a date. code example: myDataPicker.SelectedDate = DateTime.Now.
Matt Hamilton
young,
That doesn't really get around the problem. The watermark is shown whenever there is no text in the DatePicker, so as soon as the user clears out the text, the watermark will show again.
Sebastian
Hi, when the application starts and the datepicker is visibility = collapse its don't have effect because when i do visible the datepicker through one button's event appears the default watermark.. Matt, how to fix this?.., Sorry my English :/...
Matt Hamilton
Sebastian,
That's odd. I don't think the initial visibility of the DatePicker should affect this at all - it all happens before even the windows containing the DatePicker are instantiated. Perhaps there's another problem causing this. Are you using DatePicker directly, or have you derived another control from it?
Sebastian
Hi Matt.. thanks for replying,
Nou.., it's inside the windows directly, and if would put the DatePicker visible inside a Panel (Stackpanel, Grid, Wrappanel, etc..) and the panel is collapsed, after clic on button which put the panel visible appears the default watermark too because he extends visibility from him parent :'(..
Here is one image very clearly from the code actually
http://www.subeimagenes.com/img/datepicker-105906.png
What could be?...
Matt Hamilton
Wow - you're totally right, Sebastian.
It seems that the "Loaded" event isn't firing if the control is initially collapsed.
All I can suggest at this point is to have your DatePicker visible by default, and collapse it in the "Loaded" event handler of your Window. That way the watermark will get changed but the user won't see it until you want them to.
Sebastian
Wow that does the trick, was not what i wanted but it works!!
Excellent code, excellent page, excellent guy... Thanks Man!
Chris
Thank you! That looks to me just the way to do it.
Naveed
Hi Matt,
Thanx for the awesome article. One thing, how can i assign different watermarks to different DatePicker contol. I might want to display "Select DOB" in one and "Select Expiry" in the other.
Matt Hamilton
Hi Naveed,
To assign a custom watermark to an individual DatePicker control is nice and easy - simply handle the
Loadedevent on that control and use the code in theDatePicker_Loadedmethod above, supplying whatever text you like.If you wanted to get tricky you could hijack the
Tagproperty of the DatePicker and assign that as the watermark in the code above. That way you could set the tag to "Select DOB" on one DatePicker but leave it as null on all the others, and have theDatePicker_Loadedmethod say something like:Even more in the spirit of WPF, but a little more work, would be to create a
Watermarkattached property, but I won't try to address that in a comment here. I think the Tag approach is the easiest for your needs.Alvaro Lopez
Excellent tip !!! Thank you Mark !!
Bhuvan Desai
This is one of the best answer so far, I have read online. Just useful information. Very well presented. You made it very simple and understandable. I've found some other good articles on DatePicker control in WPF which also explained very well. Here I'm sharing the link of that helpful post.....
http://www.codeproject.com/Articles/88757/WPF-Calendar-and-Datepicker-Final
and
Date Picker control
These links also helped me to complete my task.
Tony
Nice one Matt - didn't think that would work at all...
Spot on...
andrea
Great job but I'm trying using it in my vb project but I've some problem to converting. Anyone can help me?
Regards Andrea
Matt Hamilton
Post some code, Andrea. Either here, or better still on Stack Overflow.
Camilo Chavarriaga
Thank you very much. I created a DateTimePicker user control in the following way: DatePicker("Not Set" - for null), TextBox(Hours), TextBlock(:), TextBox(Mins), TextBlock(:), TextBox(Secs), ComboBox(AM, PM), and TimeZone. And the Select a date was completely unaceptable for this control.
noir
Thank you very much Matt!!!!!
Garima Saini
Thanks a ton Matt!!!! :)
Alex
Thanks a lot Matt!