Start off by making a new project, and dropping a tab control onto the form... edit its property sheet to give it three tab pages (you can make their captions: Test Tab 1, 2nd Test and Last One). Now double click on the first tab form and change its foreground colour to white and its background colour to black. You can place a label with some text on the tab form just to see how things look. Do the same for the other two tab forms. Now everything looks nice except that the tabs themselves are still using the standard Windows colours. bring up the property sheet of the tab control itself (right-click on a tab and choose properties) and change the foreground colour there to white and the background colour to black. Hmm... That's odd. No change in the tabs. :(
As it turns out, Windows doesn't support changing the colours of certain controls, so if you want to do it, you're going to have to do the dirty work yourself. Fear not... it's not that hard. The idea is to make the control owner-drawn. In this way, Windows will bug you whenever it wants to repaint a tab, and you'll be left with the job of doing the actual painting. This gives you a lot of flexibility in what your controls look like and saves you from actually having to rewrite a control from scratch just to give it a non-standard look.
To make the tab control owner-drawn, bring up its property sheet (right-click on a tab and choose Properties) and check OwnerDrawnFixed from the Advanced Style page. When Windows first displays the tabs on the screen, the tab control's MeasureItem event will get fired for each tab... This event gives you the chance to tell Windows what size the tabs should be when they are displayed by changing the itemWidth and itemHeight members of the event parameter. In this case we don't need to handle this event since Windows is kind enough to default to correct normal size, which is fine for us since we aren't changing what the tabs contain, but rather what they look like.
The event we do need to handle is the DrawItem event. So right-click on a tab, choose Events/More and find the DrawItem event. Make the code in the event look something like this:
// event->rcItem : The rectangle that bounds the control to
// event->canvas : Points to the display canvas on which to redraw the control.
// When you have finished redrawing the control make sure that you leave
// the canvas is in it default state.
// Get the text of the current item to be drawn:
WString itemText = tabctrl->GetText( event->itemID );
// Set the colours based on the tab control's ForeColor specified on the Property Sheet
event->canvas->SetBackColor( tabctrl->GetBackColor() );
event->canvas->SetTextColor( tabctrl->GetForeColor() );
// Now draw the stuff!
WBrush brush = WBrush( tabctrl->GetBackColor() );
event->canvas->FillRectangle( event->rcItem, &brush );
event->canvas->DrawText( event->rcItem, itemText, itemText.GetLength(),
WDrawTextCenter | WDrawTextVCenter | WDrawTextSingleLine );
As you can see, it's pretty straight-forward once you know how to do it, and with a little more work you can get cool effects like combining pictures and text, etc... A question I see asked every now-and-again on the Power++ discussion groups is, "is there a button control that lets you use both a picture and some text?" And the usual answer is, "no; make it yourself." :) Well, now you know how... The techniques above can easily be applied to command buttons. This sounds like a great project for someone who wants to contribute to the Power++ community. Why not create a native component based on the command button which does all the same things except that it is owner-drawn, and adds properties to allow changing of foreground and background colour, and placement of a picture, along with the caption, on the button in different arrangements... Have fun!
The DataWindow print function prints to the default printer. To change this in your Power++ application is not documented well (if at all) by Powersoft. To solve this, I place a printer option on the main form's menu with the following code:
#define wm_broadcast 0xffff WBool Form1::menu_1_printsetup_Click( WObject * source, WEventData
* event ) prevArray = WAppObject.GetProfileString("windows","device",WString("")).Parse(",");
#define wm_wininichange 0x001a
WStringArray profArray, prevArray;
if(printdlg_1->Prompt(this, "Print") )
printerData = printdlg_1->GetPrinterData();
profileString = WString(printerData.GetDeviceName()+","+ \
profArray = profileString.Parse(",");
if( (profArray != prevArray) || (profArray != prevArray) )
wm_broadcast, (WUInt) _wininichange,
0L , (WUInt)(char *) "windows"));
WBool Form1::menu_1_printsetup_Click( WObject * source, WEventData
* event )
prevArray = WAppObject.GetProfileString("windows","device",WString("")).Parse(",");
Note: This changes the default printer at the registry level. Thus, after exiting your Power++ application, your default printer will be set to whatever you had it in the Power++ program.
WForm is the base for for all others; both WDialog and WModelessDialog inherit from it. WForm is a modeless form, meaning that if one Form1 in your program brings up Form2 which is based on WForm, Form1 is still accessible to the user. Generally, using WForm tends to be less useful than the other types since it has no advantage over them (except perhaps a smaller memory footprint) and lacks some features such as the ability to tab between controls on the form automatically - if you want to tab between controls on a WForm, you'll have to write in the tabbing code by yourself. That's why most people tend to stay away from WForm unless they have a specific reason to use it.
WModelessDialog is similar to WForm, except it supports other dialog-specific features such as the ability to automatically tab between controls. When you create a WModelessDialog (say Form2) from another form (say Form1) using code such as form_2->Create( this );, you will notice that although Form1 is still accessible to the user, Form2 always remains 'on top' of From1. This is the expected result. If you don't want this behaviour, you'd instead have to create Form2 using code such as form_2->Create( NULL ); where NULL specifies that Form2 should be created with the desktop as its parent rather than with Form1 as its parent. If you choose this latter situation and you require that Form2 communicates with Form1 while it's active, you will need to find some way to get a pointer to Form1 other than calling GetParent() (which would have been fine in the former case) since the parent of Form2 is the desktop, and not Form1. It seems to me that despite involving the G-Word, the use of form GlobalVariables in Power++ (see the General property sheet of your form) is a clean and simple way around this problem if you only intend to have one instance of those forms that have GlobalVariables.
The final type of form is the WDialog. As you might expect, the WDialog has all the same properties as WForm, and all the dialog-specific properties like WModelessDialog. The difference is that WDialog is model to your program, meaning that once you create a WDialog, the program is frozen to the user (except of course for the WDialog itself) until the user closes or finishes with the WDialog. This is true even if the WDialog is created with the desktop, as opposed to another form, as its parent.
There's no easy way to use a WDialog as a WModelessDialog, or a WModelessDialog as a WDialog, so if you choose the wrong one to begin with you're going to have to change it. To do this, you'll need to close your project and open up the desired form's WXF file in a text editor. Look for a line like (at)begin Object "Powersoft_WCM::WDialog" and change it to (at)begin Object "Powersoft_WCM::WModelessDialog" (or the other way around...). Make backups before you do this in case this little trick doesn't work for some reason!