Lit Window Productions programmers logbook
Designing a C++ library to reduce GUI coding times up to 90%...
Wednesday, January 02, 2013
Still alive...
Happy New Year
Hajo
Friday, March 07, 2008
wxVisualSetup 2.0 about to be released
Upgrade is free of charge for existing customers. You can download the release candidate from the download page. Please find the link in the order confirmation email.
Sunday, October 22, 2006
Lit Window Library status
The Lit Window Library project is definitely alive and development continues.
Abstract
Summary: Building a database front end with Lit Window Library…
Audience: Software Developers
Level: Beginner
Introduction
Dear Diary!
I am sitting in my favorite café in Erlangen, sipping a Latte Macchiato and am very happy. Yesterday I visited my customer and showed off the latest version...
Yes, yesterday was a great day for me. After more than a year of development my customer began entering live business data yesterday. I sat a desk away, ready to intervene and fix any last minute crashes and show stoppers, but there were none.
And only today it slowly dawns on me that this is a major, major milestone for the Lit Window Library as well. No, the project is not dead. Far from it. I was simply too busy in the last months to bother with any kind of publicity. Now, as my workload hopefully lessens a little, I intend to publish more again.
What works *today*…
I have written a lot about grand ideas, design schemes and how great all of this is going to be - once the library has actually been implemented. But things have progressed a lot since my first note over at slashdot and in today's blog entry I will write only about what is working here and now.
The program I installed yesterday is a tool to help plan and organize business information, from financial data to individual project management issues. Different bits and pieces of information, each needing a table in a database and a form on a graphical user interface. And many interdependencies.
The application I wrote is built upon a (closed source) framework called ‘Mikado’, which I’ve been developing as the successor for my bug tracking tool BugLister. Mikado is a database front end framework which uses ODBC to connect to a database server. Its a little bit like MS-Access, written in C++ for wxWidgets and the Lit Window Library. A core component of the Mikado framework is the record_form control, a single C++ class that houses an arbitrary form (a wxPanel loaded from XRC), handles the user interface and load/save operations and also calls DLL plugins to allow implementation of specific business logic that goes beyond the capabilities of the framework. (Now imagine Python as a script language and a decent WYSIWYG form editor... But I promised I'd write only about what is working today. ;)
To explain how this works, I am going to walk through the steps to add a new kind of record ‘Project’. My client develops leading-edge communication equipment. A ‘Project’ record shall describe the financial planning aspect of such a development project. It has a unique project name, start date, customer(s), planned cash flow and required development resources. Other records will reference it. Invoices, for example, will be assigned a project, so the project ‘contains’ a list of invoices and we’d like to show all invoices written for a specific project in a list on the project’s form.
CREATE TABLE project
The first step is to define a database table. Here is the shortened version of the SQL statement. We are using postgres, by the way, but I've tested Mikado with MS-SQL, MS-Access and Oracle as well.
CREATE TABLE projects
(
id serial NOT NULL, -- unique id
name varchar(32) NOT NULL, -- project name
start_date date, -- start of the project
customer int4, -- the customer paying for the project
status int2, -- status
notes text, -- some notes
planscript text, -- financial planning information
sys__parent int4, -- a parent project
CONSTRAINT pk_projects_id PRIMARY KEY (id),
-- foreign keys omitted for brevity
);
Create the user input form for projects
Get DialogBlocks, if you haven’t already. It’s the best GUI designer for wxWidgets that I know and is written by Julian Smart. If you are using Visual Studio, get wxVisualSetup as well, as it not only integrates wxWidgets into the Visual Studio IDE. It also helps developing the Lit Window Library, because I am the one who gets the money. Okay, enough advertising ;)
A form is actually a wxPanel with sizers and widgets inside. To create the form, I add a wxPanel to the list of documents and add a wxFlexGridSizer.
id
I first add a wxStaticTextCtrl and set the label to “Id:”. This is the label the user sees so he knows that the number next to it is the id of the project. The id itself is read only. The user should not be able to change it, so I add another wxStaticTextCtrl next to the label. wxWidgets has one HUGE advantage over other GUIs I know, such as the MFC. In wxWidgets, every widget/control can have a name. The Lit Window Library data binding mechanism builds upon this. I give the new wxStaticTextCtrl a name: “id”. Note that the column in the database table has the same name.
start_date, name, notes
The next field I add to my form is the project “start_date”. I add another wxStaticTextCtrl as the label and set its value to “Start date:”. The column “start_date” is of type “date”. The Mikado framework converts this to a wxDateTime object. I add a wxDatePickerCtrl to the form, next to the label, and give it the name “start_date”. Again the control has the same name as the column in the database. I repeat these steps for “name” with a wxTextCtrl singleline and “notes” with a wxTextCtrl multiline.
customer, status
The “customer” column is an enumeration. It contains only a number, not the actual name of the customer. This number references the corresponding entry in a “customers” table in a database. We need a control that lets a user select one entry out of a list, 1 of n. That’s what a wxComboBox is for. I add one and name it “customer”. The Mikado framework does the rest. At program start, it loads the definition of the “projects” table and also resolves foreign key references. When it encounters a wxComboBox in a form, it fills the combo box with names from the “customers” table and binds the selection to the “customer” column of the “project” record. The result: the user sees all customers in the list and the current selection is read from and written to the “customer” column.
I add another wxComboBox for the foreign key column "status", a list with values such as “open”, “completed”, “possible”, “obsolete”.
automatic text completion
Actually I am not using a wxComboBox here, but a custom control "lwAutoCompleteTextCtrl", part of the Mikado framework. This control works like the autocomplete text controls you see when you are using a web browser and start typing an URL in the address bar. The list of customers is much too long to be using a combo box. lwAutoCompleteTextCtrl lets you type a few characters and the list is narrowed down immediately. But the underlying mechanisms – foreign key table and current selection – are exactly the same as for wxComboBox.
sys__parent
“sys__parent” is an interesting one. In Mikado, columns starting with “sys__” always have a special meaning. The “sys__parent” column defines a hierarchy. For table “projects”, “sys__parent” specifies the parent project of the current record, pointing to another row in the “projects” table. I wrote another custom control lwAutoCompleteTreeCtrl, which works like lwAutoCompleteTextCtrl, except that it shows a tree instead of a list. Adding the lwAutoCompleteTreeCtrl to the form lets the user enter the parent project by typing its first few letters. Or, if the user presses F4 to drop down the tree control, he can see the entire project hierarchy and select the parent project.
planscript
This text column contains the actual planning, written in a small scripting language which we designed specifically for that purpose. The parser that handles this is contained in a plugin and reacts to events sent by the framework. I am not going into more details here and simply add a multiline wxTextCtrl for it.
saving the form
The form (wxPanel) itself, like all the controls inside, also has a name property. I name my form “f_projects” and save everything to an .XRC file. The .XRC files are XML files containing the GUI layout. Here is a short code snipped of how you would use them in your C++ code.
// load XRC file into memory
wxXmlResource::Get()->Load(filename);
// create a wxPanel from the “f_projects” definition
wxPanel *panel=wxXmlResource::Get()->LoadPanel(“f_projects”);
This would return a wxPanel object containing all sizers and widgets and all that I designed with DialogBlocks above.
Registering table and form
Now that I have a database table and an XRC form, I need to register them with the framework. To register the table, I add the value “projects” to a special table “l_types”. “l_types” contains the list of the database tables that Mikado knows about. At startup, when Mikado reads it and finds the value “projects”, it will query the database server for the definition of “projects” and build a column and key catalog for it. This is the moment when the framework discovers that column “customer” references another table and that wxComboBoxes later need to be filled with values from “customers”.
I also need to tell Mikado that the form “f_projects” is the right form for “project” records and I also want a human readable name for the form. To do that I add a record “f_projects”, “projects”, “Project folder” to another special table “v_views”. This table contains a list of all forms the user can choose from. The GUI shows this list on the left side of the frame, like the folders list of your email program.
Voíla…
To test it I start Mikado. It shows an entry “Project folder” in the view list, which I click on. Mikado then shows me a list of records from the “projects” table in the database. I select one. Mikado loads the form “f_projects” (a wxPanel containing controls, remember?) from the XRC file, shows it on the screen, loads the record I selected from the database and fills the form with values from the record.
That last part is handled entirely by Lit Window’s RapidUI object, which I wrote so much about already. The framework calls AddWindow() to add the form to the RapidUI object. It then calls AddData() to add the record. After that the RapidUI object creates default rules: “for each data element” - search for a window element of the same name and if one exists, create a simple rule “window.element = data.element”. Together, these default rules bind all columns to their controls.
The code inside Mikado’s “record_form” is pretty simple, thanks to the Lit Window Library:
// load the form, init the RapidUI object
void record_form::prepare_for(wxString type_name, wxString form_name)
{
// m_working_record is of type mikado_record
// create a new, empty record of type ‘type_name’
// also changes the type of m_working_record
m_working_record.create(type_name);// add the data to the RapidUI mediator
m_rapidUI.AddData(m_working_record);
// load the form from the XRC file
wxPanel *m_form=wxXmlResource::Get()->LoadPanel(form_name);
// add the form to the RapidUI object
m_rapidUI.AddWindow(*m_form);
// start the RapidUI object
m_rapidUI.Start();
}
// load a record from the table
void record_form::load(long bookmark)
{
// create a rowset (similar to wxDbTable)
mikado_rowset rows(m_connection);
// bind the current working record to the rowset
rows.bind(m_working_record);
// set the SQL where clause to load a particular record// ([in]bookmark) is OLEDB syntax to specify a named parameter
// ‘bookmark’. Mikado’s ODBC layer uses the same syntax.
rows.set_where(“id=?([in]bookmark)”);
// bind the parameter to the actual variable
rows.bind(bookmark, “bookmark”);
// open the table
rows.open();
// fetch the first entry// this stores the values in the m_working_record
rows.fetch();
// now tell the library that m_working_record has changed// this triggers the RapidUI rules system and will update
// everything in the form
litwindow::NotifyChanged(m_working_record);}
void record_form::test_project_form()
{
// first prepare everything for objects of type
// “projects” and load the form “f_projects”
prepare_for(“projects”, “f_projects”);
// now load record with id==5
load(5);}
That’s all. And the point is, I’ve written this code once and will never have to write it again, because it works with all kinds of records. This is the ultimate goal of the Lit Window Library, a library of building blocks at a significantly higher level of abstraction that other libraries.
High level library of building blocks
The “record_form” is actually a mediator class (search google for mediator design pattern). It connects a record and a form and handles everything in between. A “record_form” can load, create and save arbitrary database records using arbitrary forms. Written once, used many times. If you need to show a single database record, use the “record_form” mediator from the library. Want to display a list of records and handle select/edit/insert/delete? Use a “select/edit/insert/delete” mediator object in combination with the “record_form” mediator. Instantiate them, connect a list control, a form and some action triggers (buttons and menu entries) for the insert/delete/save actions. Everything I have implemented so far is only a means to write generic mediator classes such as “record_form”.
Conclusion
When my customers started entering project information for the first time yesterday, we hit a minor snag immediately. Some projects did not have a start date yet, but the form required an entry. To fix this, I loaded the form in DialogBlocks and set the SP_ALLOWNONE flag for the “start_date” wxDatePickerCtrl. I saved the XRC file and ran Inno Setup to create a new setup.exe. They installed it and the problem was gone. This took less than five minutes and I didn't even have to recompile the application.
They also wanted to categorize projects by region (Asia, Europe, US). While they were waiting I
-
added a new column “region” to the projects table definition
-
added a simple table “regions”
-
added a simple form to be able to add regions and
-
added another lwAutoCompleteTextCtrl to the projects form
and created a new setup.exe. All of it without recompiling the application. I stress this because the chance of breaking something is a lot higher when you change actual C++ code. Mikado gives me a lot more confidence to make changes “on the fly”, while the customer is waiting.
Outlook
I’ve saved the juiciest part for my next blog entry: showing the list of invoices attached to a project in the projects form. In that entry I will add a lwMikadoDBGridCtrl and connect its properties and actions to the main form. These "connections" use the Lit Window Library rules language and are contained in a simple text field in the XRC file. Like above, changes to that connection can be made without recompiling the application.
Here is the code from the “LITWINDOW_RULES” field in the XRC file. This is actual, working code, except that I have changed the identifier names because of Copyright issues.
// set the invoice_grid database connection
// to the form’s database connection
invoice_grid.Connection:=form.connection;
// tell the invoice grid to reload itself
// when the current_bookmark of the form changes
// the invoice will no longer show all invoices,
// it will only show invoices assigned to an object
// ‘current_bookmark’
invoice_grid.ParentBookmark:=form.current_bookmark;
// tell invoice to save changes when the
// form.IsSaving trigger fires
invoice_grid.SaveAction:=form.IsSaving;
// tell form to mark data as dirty when the
// invoice_grid.IsDirty trigger fires
form.DirtyAction:=invoice_grid.IsDirty;
// this is not c++ code, these are lit window
// library rules (or constraints), compiled and
// evaluated at runtime
That’s all for now. I hope you found it interesting reading. If you did, please help spread the word and send the link to my blog to your colleagues. And as always I welcome your comments. Do you want more details? Less? Are these blog entries too long to read? Comments on the library?
Best regards
Hajo Kirchhoff
New layout for my blog...
Thursday, October 12, 2006
Lit Window Library Design Thoughts #1
Abstract
Summary: Explains how to call C++ methods from the Lit Window Library rules system.
Audience: C++ GUI Developer
Difficulty: Advanced
Introduction
Hello, this is the first in a series of articles detailing the ideas behind the Lit Window Library. I want to share my thoughts about the design process and also answer questions I am getting via eMail. If you have not heard of the Lit Window Library yet, you might want to head over to www.litwindow.com/library and browse the official site. This blog keeps you informed about what is going on behind the scenes.
In short: The Lit Window Library extends the decade old 'Method, Property, Event' paradigm in GUI programming with a fourth concept: 'Rules', sometimes also called 'Constraints'. A 'Constraint' is a condition, such as 'frame.title = listbox.selection', which you set at design/compile time. The Lit Window Library constraints solver will ensure that the condition will always be true and update the values - frame.title in this example - whenever a dependent value changes. This greatly reduces the amount of bookkeeping code and together with 'data adapters' (please see www.litwindow.com/library) allows creating a library of frequently used user interface mechanisms. I hope to be able to reduce GUI coding times by up to 90% while at the same time allow tight integration between the new 'rules' mechanism and traditional C++ coding methods.
Which leads me to my first topic, which I received as a question via email:
Q. How do I tell the Lit Window Library to call my C++ code whenever a value changes? I want to play a sound everytime the selection in a list box changes.
Let's assume you have a list box and a frame window and you want to display the value of the current list box selection in the title of the frame. The rule would be 'my_frame.title = my_listbox.selection'. Once set, the Lit Window Library will update the frame's title automatically whenever the list box selection changes. Now let's assume you want to play a sound everytime the selection changes. Or call any other of your own C++ code.
Note: what follows is my design idea, which has not been implemented yet.
There are three different ways at different abstraction level to achive this. Actions, named rules and change notifications.
Actions
Actually I stole the term from Delphi. If you've ever used Delphi, you will be familiar with the concept. An action object is something that is fired by a trigger. A trigger is usually either a button or a menu event. The action calls a method. The advantage of this over other C++ libraries is simple: if you disable the action, all connected triggers will be disabled automatically. If you have, say, a 'Save As...' action and disable it, all toolbar buttons, menu entries, popup menus etc... that are connected to this action will appear disabled without any further work on your part. I like this approach a lot better than the usual OnSaveAsUpdate event where you have to query the SaveAs state for every menu entry, every button and every toolbar separately. A lot of work just to reflect the state of the application in a handful controls.
In the Lit Window Library you create an action object and connect a C++ method to it using a 'function adapter'. A 'function adapter' is a simple piece of code that reflects the C++ method into the rules world, making the method known by name. Think of it as a language binding, much like the language binding between Python and C++. Syntax in the rule system could look like this:
Action SaveAsAction;
SaveAsAction.method = MyCppMethod;
SaveAsAction = SaveAsButton + SaveAsMenu + SaveAsToolbar;
SaveAsAction.Enabled = somedata.state == IsDirty;
You declare the object SaveAsAction, assign the C++ method "MyCppMethod" to it, connect a button, a menu entry and a toolbar button and tell the Lit Window Library system to enable the action - and thus the button, menu and toolbar, whenever somedata.state equals IsDirty.
How does this help to play a sound whenever the list box selection changes? Simple. A changing selection is a trigger, just like a button that is pressed. Here is the code:
Action PlaySoundOnSelectChanges;
PlaySoundOnSelectChanges.method = MyCppPlaySoundMethod;
PlaySoundOnSelectChanges = my_listbox.OnSelectionChanging;
This looks very similar to the 'OnEvent' mechanism used by GUI class libraries. Not suprising, because it is the same concept, spiced up using the action idea. PlaySoundOnSelectChanges is an action object that calls 'MyCppPlaySoundMethod' whenever it is enabled and triggered. my_listbox.OnSelectionChanging is a trigger that fires everytime the selection changes.
Thats the first method to play a sound whenever the selection is changing.
Named rules
Somewhat lower in the hierarchy are named rules and rule evaluation events. The Lit Window Library uses a constraints solver to update values when dependent values change. The constraints solver uses rules as its input. You've seen examples of rules above. All of these rules where unnamed rules. The Lit Window Library rules syntax allows named rules, giving a rule or a set of rules a name by which they can be accessed. Here is the syntax:
selection_changing: frame.title = listbox.selection;
The general syntax is {name :} rule, where the name and colon are optional. If they are omitted you are specifying an unnamed rule. You can also group several rules together and give the whole group a name. { name : } '{' rule '}'.
The constraints solver publishes boost::signals which your code can connect to. Different signals are fired for different actions and events. One signal is the 'value(s) changing' signal. It is fired everytime the value or values of a rule or group of rules are changing. To play a sound whenever the list box selection changes, you need to write your OnCppPlaySoundMethod and connect it to the constraints solver 'value(s) changing' signal.
RapidUI m_rapidUI;
// init rapidui object with
// data and window objects
// now connect the event
m_rapidUI.get_rule("selection_changing").value_changing.connect(boost::bind(&OnCppPlaySoundMethod));
get_rule(string) returns the named rule object. It has a member 'value_changing', which is a boost::signal (see www.boost.org for more info). connect takes a function object which gets called everytime the signal is fired. The rest is taken care of by the constraints solver. Whenever the value of my_listbox.selection changes, the constraints solver will reevaluate the rule and will change the value of the frame.title. Before it does that, it fires the 'values_changing' signal of this rule, giving your C++ code a chance to act. Not shown here is the actual interface of the OnCppPlaySoundMethod, but the interface also allows your C++ code to change the value before it is assigned or otherwise modify the rules behaviour. This allows very complex interactions between the Lit Window Library rules system and your own C++ code and this is also one of the very important and strong points of the system: I wanted to create system that allows a programmer to switch between both world very, very easily - the rules world and the classic C++ sequential world.
Change notifications
Change notifications are at the lowest level of the abstraction. For the rules system to work, it needs to be told whenever a value changes. Values can be C++ members, in which case you - the programmer - must call litwindow::NotifyChanged(value) every time you assign a new value to the member variable. Lit Window Library predefined objects, such as Listbox and other GUI objects already do this behind the scenes everytime you call SetSelection or a user changes the selection via mouse or keyboard for example. litwindow::NotifyChanged sends the information that object 'x' at location 'y' has changed to all active constraints solvers. The constraints solvers in turn search their lists for rules that are affected by this change and reevaluate them, possibly updating other values, sending more change notifications and updating yet other values until all rules have been reevaluated and a new set of values has been assigned - or until a conflict has been found.
These notifications are low level change notifications. They are not sent for rules, they are sent for individual objects. Like with rules you can connect your own C++ code to a change notification.
RapidUI m_rapidUI;
m_rapidUI.get_data("frame.title").value_changing.connect(boost::bind(&OnCppPlaySoundMethod));
Note that I connected the OnCppPlaySoundMethod to the frames title object here. I could have connected it to my_listbox.selection. But since a change in the selection (actually a change in the value of the selection - the string value of the currently selected list box entry) will automatically change the title as well, OnCppPlaySoundMethod will still be called when the selection changes. The change is propagated to the frame.title object by the constraints system.
Thats all from me for tonight. I hope you found my first public rambling on the Lit Window Library ideas interesting and perhaps you want to send me some feedback. I always like to hear other ideas, thoughts, critisism and support.
Best regards
Hajo Kirchhoff (www.litwindow.com)
Sunday, February 05, 2006
wxWidgets Arrays in C++: one more reason to use std::vector<>
wxWidgets offers WX_DEFINE_OBJARRAY and other #defines to help you implement vectors (arrays) of your own types. If you are still using this feature, here is another reason to switch to std::vector<>
Intellisense 2005 will not work with wxArrays of your own type. Classes that use them will not be recognized in most cases. The reason is the need to include arrayimp.cpp multiple times. The wxWidgets documentation briefly mentions std::vector but goes on saying that the wxArray mechanism can be compiled with any dumb C++ compiler in the world. (http://www.wxwidgets.org/manuals/2.6.2/wx_wxarray.html)
Well most C++ compilers by now either come with the C++ Standard Template Library included or you can use STLport, a free port of the STL, with your compiler. Almost all compilers now support templates.
So here is my full list of reasons to throw away all WX_ARRAY_DECLARE macros and use std::vector<> instead.
1. std::vector<> works fine with Intellisense, wxArray does not
2. std::vector<> is in the official C++ standard, wxArray is not
3. wxWidgets has a wxUSE_STL switch already and uses STL internally. I expect that future versions will eventually deprecate the WX_ARRAY... macro mechanism.
4. Learing std::vector<> is a great way to get started with the STL, which includes many other, extremely helpful constructs.
5. And after STL: boost. If you haven't heard about it, check it out at www.boost.org. Boost is like a supercharged Standard Template Library. Many more utilities, some easy, others difficult to learn, but once you get the hang of it, they will amaze you.
Here is a quick starter on how to use std::vector<> for objects of type MyDirectory.
struct MyDirectory {
// include some reasonable definition
};
#include <vector>// note: vector, not vector.h
// now define a 'MyDirectory' array
typedef std::vector ArrayOfDirectories;
ArrayOfDirectories m_my_array;
You can now use
// to append a new element to the array
m_my_array.push_back(aDirectory);
// to access an element
m_my_array[5];
// or with boundary check: will throw an error if vector.size()<5
m_my_array.at(5);
// to check the size of the array
m_my_array.size();
// to iterate over array
for (size_t i=0; i<m_my_array.size(); ++i)
do_something(m_my_array[i]);
// to sort the array
sort(m_my_array.begin(), m_my_array.end());
// to find something in the array
find(m_my_array.begin(), m_my_array.end(), value);
// to clear the array
m_my_array.clear();
I prefer vector and other containers (list, map, deque, queue etc...) over their wx counterparts.
Instead of using WX_DECLARE_OBJARRAY(ElementType, Name), write
typedef std::vector Name;
#include arrayimpl and WX_DEFINE_ARRAY are no longer neccessary.
Note that STL lacks WX_SORTED_ARRAY, but you can use std::set for that instead.
Have fun
Hajo
Tuesday, January 31, 2006
Visual Studio 2005 Intellisense seems unreliable...
I found out that in fact, most of the times it is still working, but it just takes more time to update itself than I'd expect. Here is a tip that helps me keep Intellisense running:
Immediately above the text editor window there are two comboboxes. The left one shows the scopes of the current file and the right one shows the list of methods of the scope. They are called the 'navigation bar'. This has been in Visual Studio for a long time and is very useful to navigate through the classes.
It is a very useful indicator for Intellisense as well. The problem with Intellisense is not so much that it stops working alltogether, but that it is a lot slower than I type.
When a definition changes, Intellisense updates itself in the background, but only during idle time. I am a fast typer. I add a declaration and then switch to the .cpp file to write the definition, typing away continuously. This prevents Intellisense from updating itself, since it is waiting for a small pause in my typing which never comes. How small? This is where the comboboxes of the navigation bar come in handy.
Try this: add a method to one of your classes in your header file, say void my_class::show_something(); Now switch to the end of the corresponding cpp file and type void my_class::show_something() { sometext.... . Do so without a pause. Watch the right combobox. Initially it is empty. About one second after you stopped typing it should change to show_something. This indicates that Intellisense finally caught up and will work inside show_something again. But if you continue typing without a pause, the combo box stays empty or takes a lot longer to fill. This gives the impression that Intellisense does not work, especially when I need it most: when I write new code.
After you've changed or added a definition, stop typing while inside a method and wait until the upper right combobox shows the name of the method again.
Use the upper right combobox as an indicator for Intellisense.
Also I find that Intellisense is extremely sensible to temporary code changes. Consider this:
void my_method()
{
if (something) {
//<>
This happens all the time while you are changing code. When you introduce a new compound statement and type the opening bracket {, for a short time the code is invalid. The closing bracket is missing. This sort of code changes can confuse Intellisense. It has difficulties parsing the code. Since it is rather slow on updating it may take another second after you've added the closing brace } until it is working again.
If you want to know more about this, open the task manager and have an eye on the CPU usage. Experiment a little, get to know Intellisense, adapt your speed and see Intellisense improve.
So usually, when hitting Ctrl+Space does not expand the name rightaway, I pause of for a second and try again.
Note: It is important to close the Intellisense member list combobox if it is open. Intellisense does not update itself while it is open.
Something else I found: Editing a source file in another editor such as DialogBlocks also seems to confuse Intellisense. I haven't yet found out why. Sometimes the entire class definition stops working, somethings it seems unaffected. I'll keep you updated if I find something. If you've got an idea, please leave a comment.