"Post-it". Sizable windows without border or title.
Copyright © 2000 Alirio A. Gavidia B..
To see more of my articles
visit Programación
Orientada en Delphi (Site in Spanish)
Translated by Ernesto De Spirito with the author's permission
![]() |
Why a borderless and titleless form?
Four are the fundamental facts in the present essay: The first of them is the existence of programs that mimic Post-it (those little sticky pages that come in notebooks and flood offices with flashy colors). Second, the use of certain undocumented Windows function to drag forms. Third, the problem of how to create an application where the main form is not visible. And fourth, using the Windows task bar.
The programs that mimic Post-it I take as reference are mainly TurboNote in Windows and the one of KDE in Linux. Both programs are easily acquirable and do more than the one I present here. However, I consider this a good teaching example.
It comes to my mind another program that presents itself without title: the round (analog) clock that comes with Power Toys for Windows. It will be left for a future article.
Let's get started
The obvious is to imitate a post-it note in some way. The first thought is a form with a title bar without the usual buttons (minimize, maximize, etc.) and completely with a plain color. This alternative is possible and requires a bit specialized Windows management. In particular I have chosen a more economic alternative regarding code (or at least that's what I think); and this is eliminating the title bar and form borders (like you would do in a "splash-screen") and place a panel and a memo in a form, the first aligned to top and the second to the client area.
Managing a form without borders or title bar
The form with just a panel and a memo will respond without problems to
keyboard actions, but it is obvious it won't respond to mouse drags and size
changes with the mouse (without borders from where to hold it). The drag is
resolved with an undocumented action, at least that's how it was some time
ago, thru the message SysCommand. The line shown here, executed
from a form, places it in drag state.
Perform(wm_SysCommand, $f012, 0)
The matter with the messages doesn't get limited to this. The resize modes are also defined that way. For this purpose we defined a set of constants with the appropriate values starting from $f012.
sc_DragMove = $f012;
sc_Leftsize = $f001;
sc_Rightsize = $f002;
sc_Upsize = $f003;
sc_UpLeftsize = $f004;
sc_UpRightsize = $f005;
sc_Dnsize = $f006;
sc_DnLeftsize = $f007;
sc_DnRightsize = $f008;
| Note: If you wonder where these values came from, well, what can I say... I got to know the first one by a Marco Cantú book called "Delphi Developers' Handbook", the rest came out from trial and error. Let me tell you that $f040 produces a disastrous result. Also $f020 and $f030 minimize and maximize respectively, which is not necessary for our application. I don't know if any of this works outside Windows 98. |
Additionally, in the MouseDown event of the panel and the memo,
where we send the messages, it is necessary to call ReleaseCapture before
sending the indicated messages.
I won't extend much with the code. It's easier to analyze it from Delphi than from an HTML page or a DOC. But notice that it has to be distinguished where a click happened in the panel to know if it is required to resize or just drag the form. Something similar happens with the memo for the resize of the bottom and lateral sides.
procedure TfrmNota.Panel1MouseDown(
Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
ReleaseCapture;
if y<2 then
begin
if x<4 then
Perform(wm_SysCommand, sc_UpLeftsize, 0)
else if x>(Panel1.ClientWidth-4) then
Perform(wm_SysCommand, sc_UpRightsize, 0)
else
Perform(wm_SysCommand, sc_UpSize, 0)
end
else
Perform(wm_SysCommand, sc_DragMove, 0)
end;
The sources are part of this article and you will be able to see the note working in detail.
In the attached example, apart from the mouse event handling we make use of the Windows Registry to store information relative to the note itself (like its position, font type and color). I presume that if you understand what we have done up to this point, you don't need explanations about the use of the Windows Registry (anyway, its use seems to be well documented in the Delphi Help, and there are examples and information about it in the Internet).
An invisible main form
When I made this program I hadn't though of a really silly problem. The user shall be able to create and destroy notes, but he can't destroy the main form because this finishes the application. Well, then simply with not showing the main form it should suffice. If you want to understand this silly problem create an application in which apart from the main there exists a secondary form; then make the application start showing only the secondary form. My attempts resulted in failure. Now I know this gets accomplished with
Application.ShowMainForm := False;
in the main program before creating the forms, but back then I didn't know it and I took advantage of the problem to give it an original solution...
It's not a bug, it's a feature
Thinking around the matter, the solution was converting the main form in a splash screen that lasts a certain amount of time and then hides. This code gets executed in a timer:
procedure TfrmMain.Timer1Timer(Sender: TObject);
begin
SaveNotes(Sender);
if not oculto then // if not hidden
begin
oculto := True;
ShowWindow(Handle, SW_HIDE);
ShowWindow(Application.Handle, SW_HIDE);
SetWindowLong( Application.Handle, GWL_EXSTYLE,
GetWindowLong(Application.Handle, GWL_EXSTYLE)
or WS_EX_TOOLWINDOW and not WS_EX_APPWINDOW);
ShowWindow(Application.Handle, SW_SHOW);
end
end;
With this the main form gets hidden (the next line right after the assignment of the "oculto" (hidden) variable, that by the way is False), and also the presence of the application in the task bar.
The rest of the code is relatively simple and contains some routines to save and restore the content and configuration of the notes (the first to disk and the second to the Registry). I believe it doesn't deserve a deep discussion and therefore I leave to the reader the interpretation of the routine.
The project as such contains five forms:
note, is the note with the panel and the memo.GenConfig, is the configuration form of the application, currently only configures formats and counter of the clock.Config, note configuration (color, alignment and fonts)Main, is the main form, apart from this it works as a splash screen.Acerca(About), program version.
As a separate comment, note that due to the presence of a Timer it is possible to
force an "auto save" for the text in the notes.
This is achieved going thru all the forms of type note and
saving their contents from the memo. The routine named SaveNotes is
executed from the timer (having the precaution of not saving what hasn't
changed). Each note has the public variable Changed that is
modified when saved and in the OnChange event of the memo.
"Pop-up menu"
The application doesn't have a main menu because it doesn't have a main form. All actions different than interaction with the memo field and mouse movements are done by pop-up menus. There are two: one in the note and one in the icon of the task bar (next to the clock). For this one we used the RX. Therefore at least for this version you will need to have the RX to be able to compile these sources. If you don't know them, them you should do it, they are free ("freeware") and very good.
| RX Library Web Site: http://www.rxlib.com Authors: Fedor Kozhevnikov (fkozh@iname.com) Igor Pavluk (igorp@mail.com) Serge Korolev (korolev@usa.net) |
![]() |



