I recently spent a lot of time attempting to solve what ought to have been a relatively simple problem - not because its necessarily a hard problem to solve but rather because I didn't know what the (simple) solution to the problem was.
The problem
I'm using ASP.NET Dynamic data to allow me to create maintenance apps for various databases (or views of databases) that underpin the suite of web applications I'm working on. There are date fields so I need a date picker. Previously I've used Microsoft's AJAX Extender but the css for that doesn't play nicely with the css for dynamic data so its not really useable (and my css skills aren't up to fixing it). Since I've started doing very simple things with jQuery (probably simple because of jQuery!) I though there's bound to be a date picker and indeed there is, as part of jQuery UI.
So far so simple... add jQuery and jQuery UI to the project the solution to providing a date picker becomes a matter of adding a class to the TextBoxes that are used to edit dates and then running 1 line of javascript to cause the necessary magic to occur.
This line in fact:
$(".datepicker").datepicker()
So where to run this? Well that's easy - one $(document).ready(...) later and you're all sorted right? Erm, no - wrong.
Why is there a problem?
That's easy - dynamic data is more than a little bit clever, full of good stuff some of which makes life a bit more interesting than one would like. Specifically - in addressing this problem - it makes use of update panels which are Ajax wrappers that mean that the page is going to get changed dynamically after its been loaded. This in turn means that when you load the page it may well be in a view mode and only when you choose to edit add or a record will it dynamically create the text (input) boxes without fully reloading the page and hence, if I've got this right (!) without firing .ready
How to solve it
Clearly to sort this out you need to find a way to trigger the script when the page is updated after its been initially loaded. So quite a lot of bing and a smidge of google later I found this Simplify ASP.NET AJAX client-side initialization and suddenly things become very easy... setup a pageLoad() function that contains the right incantation and each time the page is updated it goes hunting for the elements decorated with the right css class attribute and there you are, it all works.
Walkthrough the solution
As I said, I want to use this on my dynamic data sites. These use various forms of magic to assemble list and edit pages from a Linq to SQL or Linq to Entities dataset, some page and field templates and some specialist server controls. If we assume that we want to have the date picker on all DateTime fields then the first thing we need to do is edit the user control that's used to edit DateTime fields (or you could copy this to a new user control for plain Dates but that requires a bit more work).
The nice thing about this solution is that the changes to the edit control are almost none...
So first find the Field Templates which are (from the root of the website) in DynamicData\FieldTemplates and open DateTime_Edit.ascx - in there you will find a text box as follows:
<asp:TextBox ID="TextBox1" runat="server" CssClass="droplist" Text='<%# FieldValueEditString %>' Columns="20"></asp:TextBox>
All we need to do is add a new class which we're going to use to indicated to jQuery that this should have a datepicker, I've used "datepicker" so we add that to CssClass and we get:
<asp:TextBox ID="TextBox1" runat="server" CssClass="droplist datepicker" Text='<%# FieldValueEditString %>' Columns="20"></asp:TextBox>
That's it for that (did I mention that I like jQuery!)
Ok, now we need to make some stuff happen, first thing is to go off to jQuery UI and download the required files, the project I did this in already had jQuery added. I used the jQuery UI custom builder, the only required element is the jQuery UI core. Add the downloaded files (a jquery ui .js file, some .css and a pile of images for the theme) into your project in appropriate folders.
I like simple so I'm going to add this to the master page for the site so that it always "just works" when needed. Yes that means a bit of a teeny overhead but not one that you'd notice and Dynamic Data apps are not what you'd call optimised in the first place.
So... in site.master you need to add the following (adjusted appropriately for your site structure).
<link type="text/css" href="/css/ui-lightness/jquery-ui-1.8.1.custom.css" rel="Stylesheet" />
<script type="text/javascript" src="/scripts/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="/scripts/jquery-ui-1.8.1.custom.min.js"></script>
<script type="text/javascript">
function pageLoad() {
$('.datepicker').datepicker({
changeMonth: true,
changeYear: true,
dateFormat: 'dd-M-yy',
gotoCurrent: true
});
}
</script>
First load the stylesheet, then the jQuery file then the custom jQuery UI file then a pageLoad() function that has the one line magic. $('.datepicker') means find me all the elements which have the class datepicker, .datepicker() means add a datepicker to each of those elements and the rest are options which define how we'd like the picker to appear/work - so changeMonth means I'd like a month drop down, changeYear means I'd like a year drop down, date format will give us 22-May-2010 or equivalent (I have users in the UK and the US and that will help avoid errors) and finally gotCurrent means that I want it to default to the date in the text box (assuming there is one) rather than today's date.
So that was half a day or so to work out how to write a function containing one line of code that gives me a really tidy solution to my problem... jQuery is wonderful, Dynamic Data is pretty damned impressive too... either untouched or if you can work out how to make it do what you want (ideally without having to write custome pages)