You know how it is - you start work on one project and halfway through, you find one or two side-projects crop up that have to be solved before you can continue on the original project.

This is one such project with the added twist that it too started its life as a side-project. Here's what happened:

<Cue wavy screen effect>

I can only imagine that the planets must have been in (mis-)alignment or something, because at one point a few months ago, I was suddenly fielding emails on four or five separate articles I had previously submitted to CodeProject, some asking for features and others for bug fixes.

Foolishly or otherwise, I largely agreed with all the points raised, and subsequently found myself with fourteen or fifteen separate issues to resolve.

The situation was also made worse because I was trying to use CodeProject to keep track of all the things I had agreed to do, meaning that I had to continuously trawl the comments section of each article to remind myself of what I was supposed to be working on.

It even got to the stage where I was worrying that I'd fail to deliver on something - silly I know, but there you are!

Keeping a list on paper was a definite step in the right direction, but since I do all my coding on the same machine, it seemed somewhat inelegant, and anyway, we all know what happens to crucial bits of paper left lying around on desks and such.

The next step was to hunt around on the web for a tool to meet the following requirements:

Simple interface

Support for hierarchical data

Numbered items/subitems

Open file format

Freeware

Simple, huh! not!

I will admit that I did not spend weeks searching, but I am still surprised at the general lack of software matching my needs.

On reflection, I think that the reason may be simple: people are so used to commercial software being 'feature-rich' that when they come to design software themselves, they (not unreasonably) think they too need to cram as much in as possible, often leading to software where a lot of essential functionality is hidden away in the menu bar.

So, surprise, surprise, I decided to write something myself.

However, it's fair to say that I did not originally intend to post it on CodeProject and am only really doing so because I had a heap of fun solving some very interesting problems and these are what I think make it worth it.

Using the Software

There's really very little I need to say here since every feature/function is explicitly visible in the interface.

Nevertheless, the following list of basic capabilities and omissions may go someway to answering any questions that arise:

Files are stored in XML format with .xml file extension.

Trying to load a non-tasklist file will generally fail (unless you read the code to see how to circumvent it).

The number of items/subitems is limited only by memory (although performance may be the deciding factor before you exhaust memory).

Marking a parent item as 'done' will also gray-out child items, but they are not disabled or automatically marked as 'done'.

An ellipsis (...) indicates that an item has sub-items.

All items can be expanded or collapsed (by double-clicking).

Top-level items and sub-items are created using different toolbar buttons.

There are task-specific context-menus.

The previously open tasklists are re-opened on startup.

The tasklist is automatically saved when closing the software or minimizing it to the system tray.

The priority of a task is shown as a grayscale box to the left of the item.

Points of Interest

Here's where we come to the side-projects I was talking about, the first two of which I intend to work up into follow-up articles.

They are:

The 'ordered' tree control, which incorporates a non-client gutter for displaying the item numbers.

The idea stemmed from research I did into alternative designs for a tree-list control, which did not solve it by creating a hybrid control incorporating a tree and a list.

The hybrid control seems such an obvious solution that I suspect few people have stopped to question it, but it has still always struck me as looking far too much like hard work to be truly elegant ('square pegs' and 'round holes' spring to mind).

One possible idea is to implement the 'list' portion entirely in the non-client area of the tree. I.e., shift the right hand client edge to the left and then render the list portion in the resulting non-client area.

Whilst I've yet to get round to building a proof of concept, it was nevertheless this ongoing mental debate which prompted me to try to solve the requirement for numbered items and subitems by rendering the item/subitem numbers in the non-client area.

Without going into too much detail (as this will subsequently be an article of its own), this is how I got it to work:

Handle TVM_INSERTITEM and TVM_DELETEITEM to know exactly when items are added and removed.

In these handlers recalculate the width of the gutter required to display the widest 'dotted' item/subitem number. (Note: this is not necessarily simply the deepest subitem.)

Handle WM_NCCALCSIZE when it does, and offset the left border by the required gutter width.

Handle WM_NCPAINT for painting the numbers.

This is necessarily an over-simplification, but it captures the essence of the solution, and all that essentially remains is lots of fiddling about to ensure the non-client area gets redrawn at the the right times to stay synchronized with the client area.

Embedding .RC control definition data directly in a .cpp file to break the dependency on binary resources (a.k.a. 'Runtime Dialogs').

This is an idea that has been floating about for quite some time and which has only recently gelled into a workable solution.

The problem, put simply, is that if you want to take advantage of the resource editor in Visual Studio (and who doesn't), then you very quickly find yourself stuck with having to load dialog templates from resources compiled into the binary file.

This further means that if you want to make use of a dialog across multiple projects, then either you need to copy and paste the dialog template between project .RC files, or you need to build the dialog into a DLL from which it can be accessed.

'Runtime Dialogs' (a snappy title I coined myself) is a solution that neatly sidesteps both the nuisance of copying dialog resources between resource files and the extra work (and maintenance) involved in packaging dialogs in DLLs.

And it works like this:

First, you design your dialog template in the resource editor, create a CDialog derived class using class wizard, and wire up all the controls just as you normally would.

Next, you #include "runtimedlg.h" and change all instances of CDialog to CRuntimeDlg.

Then, you cut and paste the control definition section from the appropriate section in the .RC file and embed it directly in the dialog's .cpp file as a static string (with a bit of tweaking to handle double quotes and such like).

Finally, in the constructor of your dialog, you simply call CRuntimeDlg::AddRCControls(...) passing the control definitions as a string.

And CRuntimeDlg takes care of the rest including, if required, auto-sizing the dialog to suit the control layout.

I'm certainly not suggesting that this is a 'win-win' solution for all situations but it certainly has merits in its closer coupling of dialog template to dialog code which makes sharing dialogs across multiple projects a breeze.

P.S.: In case it's not clear here, I used CRuntimeDlg to create CToDoCtrl which encapsulates the ordered tree together with the priority, date and comments controls as a single simple-to-instantiate control.

This is possibly the most satisfying aspect of the whole project because it was completely unexpected.

What I mean is that, until recently, my knowledge of DOM and XMLDOM was virtually non-existent, as it's only since I've become more interested in the presentation of AbstractSpoon that I've been forced to get to grips with the various implementations of DOM and XMLDOM out there.

I'm pleased to say that the code on my site works under IE 6.0, Netscape 7.1, and Mozilla, although custom code was required to achieve this.

Generic MFC Classes that may prove Useful to You

The following table lists a wide range of utility classes written for this project. They can all be included in any MFC project provided you include any class dependencies too. Feel free to ask any questions relating to these specific classes and how to use them.

Adds support for recognizing urls, clicking them and setting custom url callbacks

CWinClasses

Encapsulates the ::GetClassName Win32 functions

CXmlFile, CXmlItem

Non-Unicode class for reading and writing xml files

CXmlFileEx

Adds encryption capabilities to CXmlFile

CXmlFile, IEncryption

* CSubclassWnd was originally written by Paul DiLascia for MSJ magazine. The version I use has been heavily extended to suit my specific needs. The classes that depend on it here need this extended version.

Further Work

Whilst this tool was originally intended for my personal use only, it is now a 'community' project, so if you find it useful and want to make suggestions for enhancements or bug fixes, then post below.

History

7.1.4 (26 Aug 2017)

Fixed filter selection when names contain brackets

Fixed HTML exporting of '&', '<', '>'

Fixed application window resizing on startup

Fixed file encoding of 'Transformed' tasklists when auto-exporting after saving

I created a custom task attribute whose data type is "Date". When I try and filter the task list using this custom task attribute no results are shown. Is this by design or could I be doing something wrong?

Can you provide some specific information about how you are setting up the filter?

Sure. This is what I am doing: I go to Find (Ctrl-F), and select the custom task attribute. I then choose "equals" in the next column and in the next column I set the date using the drop down calendar. I then click on Apply as Filter.

Yes, there is a number of dates in the particular field of the task list, but as I wrote earlier, the filter does not work for me.

EDIT: Further experimentation shows that the date is probably stored internally as a number. If I filter on simply "date is set", then the filter brings the whole list of tasks having the particular attribue, and shows the date under "what matched" as a number with a decimal part.

So now, the practical problem is that in the date selection dialog of the filter set up I can only enter dates in a "dd mm yyyy" format. For any solution, I would suggest to nevertheless preserve the time element in the date field (that is, the fractional part of the date number), as it is now, because it may be useful to somehow find a way to make the analysis of these entries more granular. A practical example of this: I use the Custom Task Attribute as a date to note when I archive a task. If I archive tasks more than once on the same day, the time component would be useful for me to analyse a subset of tasks archived on a particlar day i.e. tasks archived before or after a certain time on the same day.

- Start Date: Earliest time work on the task can be begun (if in future), or when it was started (if in past).
- Due Date: When the task is due. D'oh!
- Completed Date: When the task is done.
- Action Date: When the task is scheduled to be worked on. Between Start Date and Due Date.

We'd like to highlight the tasks based on these dates:

- Green: Start date reached. CAN be worked on today.
- Yellow: Action date reached. SHOULD be worked on today.
- Red: Due date reached. MUST be worked on (and completed) today.

As you see, action date is different from start and due date. But trying to use a custom date field for that doesn't allow us to color and highlight the tasks.

Possible solutions would be to either build in an "Action Date" or, even better, make color highlighting more configurable and compatible with all custom (date) fields. It would be especially nice if the option "Use a task's colour for its title background" could be set independently for each color.

Is something like this already possible or is it planned to be implemented?

With that somehow being possible, we could make much more use out of TDL. In any case, thanks a lot for all your hard work.

I would normally recommend a User Defined Tool[^] for this, but at this time I don't believe there is a command-line[^] option to change the color of the current task. The idea here would be to use a UDT via a toolbar click or key sequence to execute a program (script, macro, whatever) of your own creation which then comes back into ToDoList to set the color.

However, you could still create a UDT which then uses the Action Date to set the Version, Status, Category, AllocatedBy, or ExternalID, all of which can be used to set a specific color based on the value. For example, tags could be CanWork, ShouldWork, or MustWork. If you're not using one of those fields, just re-purpose it and then hide it from the display/edit area.

How to do that? You need code that accepts the ID of a task, parses the XML from the .tdl file for the custom attribute (Action Date), and then executes a command-line to set the tag[^].

I was going to suggest using a different visual indicator too, like an icon or flag. (Tasks can be colored based on flags.) But there isn't a command-line option for changing the icon or flag either. However, this can be done with code which updates the tasklist directly rather than coming back in through a command-line - and in fact, with this kind of code you can directly change a task color, as long as you don't have a conflicting preference to reset the color based on some other criteria.

Since you're using this in a business context, I would consider writing a new UDT for you, or an enhancement to my published UDT[^] which would color a task or set other fields based on the value of a custom attribute. For something like this I would ask for pizza, beer, or other remuneration. You can contact me with a PM from this message, or from the Contact page on my website where the UDT is hosted.

So the answer to your question is that you can do anything you want, but some functions are not built-in ... yet. Dan has provided us with wonderful freeware, it's not beyond reason to consider a bit of custom development around this fine platform for features that may not have a broader scope of interest. Or you can simply wait and hope for new features, but that doesn't help with business you need to conduct today. Life is full of trade offs...

I have been pushing TDL a bit hard with multiple lists open - many items in each list - lots of nesting - file links - icons - colors etc. Total volume of resources ~40mb. But all was well until...

So today I wanted to filter for items that have file links, and couldn't see a filter item for that specifically, so instead I sorted by 'file reference'. This is the first time I've done this and tbh didn't know if 'file reference' is even the same thing as file link...!

Anyway, TDL froze with processor on ~30pc. I left it for a few minutes then decided to end the process in task manager. But then restarting TDL didn't help - and restarting the PC didn't help either...

I can't see any explicit reference to recovery from crashes in the forum, so can anyone suggest the least painful way to recover?

I take very frequent saves with many automatic backups and also external backup with version control via a script - so 'rolling back' is not a problem for me. But I'd rather get the opinion of experts first!

Has the (unwise?) sorting by file-reference corrupted the list I was working on? In which case should I just roll back this list to an earlier version?

I was also wondering if I can 'hack' the .ini file so I keep my preferences but start with just one list. I guess this is kind of a separate issue but would be useful to know.

I tried to restart TDL twice, once straight-away and then again after restarting the PC. I waited a few minutes and observed the processor trace in task manager - and the TDL progress bar was frozen. So I didn't think it would recover by itself and killed the process.

Actually I am on 7.0.11 - and I can confirm that opening the list without the .ini did work! Problem solved!

I will upgrade asap, and edit the .ini file as you describe.

BTW, can you confirm that 'file link' and 'file reference' are the same thing?