text:
This chapter covers a set of visual tools you can use to simplify database development. The major areas covered are as follows:
These subjects are all related to databases, and to the TField and TDBGrid objects in particular. BCB is a very sophisticated database tool, and it takes time to get a feeling for the breadth and depth of the tools it provides for client/server developers. One of the goals of this chapter is to give you some sense of the powerful TField object and its impact on designing database applications.
One of the most frequently mentioned tools in this chapter is the Fields Editor. By using the Fields Editor, you can create objects that influence the appearance of data shown in visual controls such as TDBEdit and TDBGrid. For instance, you can use the objects made in the Fields Editor to format data so that it appears as currency or as a floating-point number with a defined precision. These same changes can be accomplished through the Data Dictionary in the Database Explorer or through the Database Desktop. These latter tools, however, have a global impact on the field's potential values, whereas the changes made in the Object Inspector affect only the current application.
If you want to dynamically emphasize a particular column or row in a table, this is the chapter you should read to get the information you need. For instance, this chapter covers the Columns property of the TDBGrid control. The Columns property can be used to change the appearance of a grid so that its columns are arranged in a new order or are hidden. You can also use the Columns property to change the color of columns in a grid, or to insert drop-down combo boxes into a grid.
The lessons you learn in this chapter will arm you with key techniques used by most pro- grammers when they present database tables to their users. Much of the material involves manipulating visual tools, but the basic subject matter is fairly technical and assumes a basic understanding of the BCB environment and language. Note that third parties such as TurboPower (www.turbopower.com) and Woll2Woll (www.woll2woll.com) have grid objects that go beyond the capability of the native grids that ship with BCB.
To get things rolling, it might be a good idea to take a close look at the invaluable Fields Editor. This tool lies very much at the heart of BCB database programming. To access it, you double-click or right-click on a TTable or TQuery object.
The Fields Editor helps you associate custom objects with some or all of the fields from a table. By associating a custom object with a field, you can control the way a field displays, formats, validates, and inputs data. The Fields Editor also enables you to add new fields to a table at runtime and then to calculate the values that will be shown in the new fields. This latter procedure is referred to as calculated fields. Another benefit of the Fields Editor is that it helps you create lookup fields. Lookup fields perform a lookup from one table into another based on a foreign key.
It's important to understand that the Fields Editor is just a utility. Everything done inside the Fields Editor could be done in code, although in this particular case the code in question becomes a little complicated, or at least time consuming.
In this section and the next, you will be building a program called MASS, which illustrates both the Fields Editor and calculated fields. This is an important program, so you should try to use your copy of BCB to follow the steps described.
You can access the Fields Editor from either a TTable or TQuery object. To get started, drop a TQuery object on a data module, set up the BCDEMOS alias, enter the SQL statement select * from animals, and make the table active. (The BCDEMOS alias is created automatically when you install BCB. It points at the data in the CBuilder/Examples/Data directory. If you are having trouble with aliases, see the readme file on the CD or visit my Web site.)
Drop down the Object Selector at the top of the Object Inspector. Notice that you currently have two components in use: TDataModule and TQuery.
the TQuery object and select the Fields Editor menu choice
to bring up the Fields Editor. Right-click the Fields Editor and select Add Fields
from the menu to pop up the Add Fields dialog, as shown in Figure 11.1.
FIGURE 11.1. The Add Fields dialog box from the Fields Editor.
By default, all of the fields in the dialog box are selected. Click the OK button to select all five fields, and then close the Fields Editor.
Open the Object Selector a second time; notice that there are now five new objects
on your form, as shown in Figure 11.2.
FIGURE 11.2. The Object Selector lists the objects created in the Fields Editor.
You also can find this list in the TForm1 class definition.
These objects help you hone and define your presentation of the Animals table to the user.
Here's a complete list of the objects you just created, as they appear in your header file:TStringField *AnimalsQueryNAME;
TSmallintField *AnimalsQuerySIZE;
TSmallintField *AnimalsQueryWEIGHT;
TStringField *AnimalsQueryAREA;
TBlobField *AnimalsQueryBMP;
The origins of the names shown here should be fairly obvious. The string AnimalsQuery comes from the name I've given to the TQuery object, and the second half of the name comes from the fields in the Animals table. This naming convention I've adopted here can be very useful if you are working with several tables and want to know at a glance which table and field are being referenced by a particular variable.
NOTE: The names of the fields in the example shown here are capitalized only because the table in question is a dBASE table. dBASE tables automatically capitalize all letters in field names. If I had chosen to work with some other type of table, the capitalization of the letters in the field name would have followed the rules defined by the current database software.
This principle appears again and again in BCB database programming. Whenever possible, the actual naming conventions and other traits of a server are surfaced inside of BCB. The developers of C++Builder did not aim to make all servers appear the same to you, but rather to give you access to their features as transparently as possible. Of course, generic tools such as the TTable object are also common in BCB. The VCL team, however, strove to find the right balance between expediting access to the database and unintentionally crippling the database by wrapping too many of its features inside objects. As a rule, the developers did whatever they could to ease the path, without ever going so far as to actually cut you off from a feature of the server you are accessing.
This same principle applies to the VCL. The developers tried to do everything they could to make Windows programming simple, but they drew the line if a particular feature was so warm and fuzzy that it cut you off from direct access to the OS when and if you needed it.
Each of the objects created in the Fields Editor is a descendant of TField.
The exact type of descendant depends on the type of data in a particular field. For instance, the AnimalsQueryWEIGHT field is of type TSmallIntField, whereas the AnimalsQueryNAME field is of type TStringField. These
are the two field types you will see most often. Other common types include TDateField and TCurrencyField, neither of which are used in this particular table. Remember that these types were selected to correspond with the field types in the table itself.
TStringField, TSmallIntField, and the other objects shown here are all descendants of TField and share its traits. If you want, you can treat these objects exactly as you did the TField objects that you learned about in Chapter 9, "Using TTable and TDataSet." For instance, you can write this:S = MyIntegerField->AsString;
and this:S = MyIntegerField->Name;
However, these descendants of TField are very smart objects and have several traits that go beyond the functionality of their common ancestor.
The most important property you will see is called Value. You can access it like this:void __fastcall TForm1::Button1Click(TObject *Sender)
int i;
AnsiString S;
i = DMod->AnimalsQuerySIZE->Value;
S = DMod->AnimalsQueryNAME->Value;
i += 1;
S = "Foo";
DMod->AnimalsQuerySIZE->Value = i;
DMod->AnimalsQueryNAME->Value = S;
The code shown here first assigns values to the variables i and S. The next two lines change these values, and the last two lines reassign the new values to the objects. It usually wouldn't make much sense to write code exactly like this in a program, but it serves to illustrate the syntax used by TField descendants. (If you bother to try to write code like this as a test, remember that the RequestLive property for a TQuery object is set to False by default. You would
have to set it to True before the code would work.)
The Value property always conforms to the type of field you have instantiated. For instance, TStringFields are strings, whereas TCurrencyFields always return floating-point double values. However, if you show a TCurrencyField in a data-aware control, it will return a string that looks like this: "$5.00". The dollar sign and the rounding to decimal places are simply part and parcel of what a TCurrencyField is all about.
The preceding example might make you think that these variables are declared as Variants, which indeed is the case for the TField object itself. In the actual implementation, however, the TCurrencyField->Value is declared as a Double. If you tried to assign a string to it, you would get a type mismatch. Likewise, TIntegerField.Value is declared as a LongInt, and so on. TSmallIntField and TWordField are both descendants of TIntegerField and inherit the Value declaration as a LongInt. However, they have other internal code that affects the Value field, just as TCurrencyField rings some changes on its Value field to make it look like a monetary value. If you have the source, look up DBTABLES.PAS and DB.PAS to find the details of these constructions. (The Pascal source
ships with some versions of BCB.) At any rate, the point here is that the preceding code is an example of polymorphism; it is not an example of relaxed type-checking. The Value field has a specific type--it's just that it undergoes polymorphic changes.
If you want the names of each field in the current dataset, you should reference the FieldName property through one of the following two methods:S = AnimalsQuery->Fields[0]->FieldName;
S = AnimalsQueryNAME.FieldName;
If you want the name of an object associated with a field, you should use the Name property:S = AnimalsQuery->Fields[0]->Name;
S = AnimalsQueryNAME->Name;
When using the ANIMALS table, the first two examples shown previously yield the string "Name", while the second two lines yield "Query1NAME".
Special properties are associated with most of the major field types. For instance, TIntegerFields have DisplayFormat and DisplayEdit properties, as well as MinValue and MaxValue properties. TStringFields, on the other hand, have none of these properties, but they do have an EditMask property, which works just like the TEditMask component found on the Additional page of the Component Palette. All these properties are used to control the way data is displayed to the user, or the way that input from the user should be handled.
NOTE: I don't want to get ahead of myself, but properties such as MinValue and MaxValue are also used in the Data Dictionary, as will be explained later in this chapter. Changes made in the Data Dictionary will affect these values as seen in the Object Inspector, but changes in the Object Inspector will not affect the Data Dictionary. Don't worry if this doesn't make the slightest bit of sense yet, as I will get to the Data Dictionary in just a little while.
You should be aware of one more thing about the Fields Editor. You can use this tool not only to build objects that encapsulate existing fields, but also to build objects that represent new fields. For instance, suppose you wanted to create a sixth field, Mass, which contains the product of the SIZE and WEIGHT fields, in the Animals table.
To create the
Mass field, open the Fields Editor again, right-click it,
and select the New Field menu choice. In the top part of the New Field dialog, enter
the word Mass. Now set its type to Integer, and leave its field
type as
Calculated, as shown in Figure 11.3.
FIGURE 11.3. Creating the Mass field in the Fields Editor.
If you close the Fields Editor and add a TDataSource and TDBGrid to your project, you will see that the Animals table now appears to have six fields, the last of which is called MASS.
Of course, it's one thing to create a field, and another to fill it in at runtime with an appropriate value. The act of placing a value in the new field you have created involves a concept called calculated fields.
Calculated fields are one of the most valuable features of the TField object and its related architecture. You can use these calculated fields for several different purposes, but two stand out:
The MASS program illustrates one example of the first of the two uses for calculated fields. You got this program started in the last section when you created the field called MASS and displayed it in a grid.
To continue working with the MASS program, highlight the AnimalsQuery object and set the Object Inspector to the Events page. Now create an OnCalcFields event that looks like this:void __fastcall TDMod::AnimalsQueryCalcFields(TDataSet *DataSet)
AnimalsQueryMass->AsInteger =
AnimalsQuerySIZE->AsInteger * AnimalsQueryWEIGHT->AsInteger;
The code shown here assigns the value of the AnimalsQueryMass object to the product of the AnimalsQuerySIZE and sqlWeightWEIGHT fields. This kind of multiplication is legal to do because all of the fields are of the same type. Furthermore, you could have used the Value property instead of AsInteger. I explicitly declared the type in this example to help illustrate precisely what is going on.
OnCalcField methods are called each time a record is displayed to the
user. As a result, all of the Mass fields displayed in the grid
are properly
filled in, as shown in Figure 11.4.
FIGURE 11.4. The MASS field contains the product of the WEIGHT and SIZE fields.
A TDBImage control contains a bitmap from the BMP field of the table.
To get the screen shot shown in Figure 11.4, I opened the Column property in the TDBGrid object and selected Add All Fields. I then deleted the Area and BMP fields and closed the Column property editor. I will talk more about the grid object later in this chapter.
If you choose to never instantiate a particular field in the Fields Editor, the current dataset you are working with no longer contains that field. It can't be accessed programmatically or visually at runtime. Usually, this is exactly the effect you want to achieve, and so this trait will generally be perceived as a strong benefit. However, there are times when it might not serve your purposes, and in those cases you should either create an object for all the fields in a table or stay away from the Fields Editor altogether. Remember that you can hide fields inside a grid by using the Column property, as shown previously. That way, you create objects for all fields, but show only certain ones to the user.
You can use lookup fields to look up a value in one table that you want to use in a second table. For instance, suppose you had two tables, one of which contained a list of books, and the other contained a list of authors. It would be nice if you could automatically view a list of the existing authors whenever you needed to add a new book to the Books table. That way, you could enter the book's name, look up the author in a drop-down list, and presto, you would be done. The Books
table would then automatically contain a reference to the appropriate author in the Authors table. That is, the author number from the Authors table would automatically be inserted in the Books table.
Another way to think about lookup fields is that they provide the ability to perform a powerful kind of pseudo-join using the TTable object. Suppose two tables called Authors and Books are related on a field called AuthNo.
AuthNo is the primary key of the Authors table, and it is a foreign key in the Books table. When you are looking at the Books table, sometimes you would like to be able to include the name of the author of each book
inside the book table. That is, you would like to perform a join on the book and author table. You can't actually perform a join, however, because you are using the TTable object, and not TQuery. The solution to this dilemma is the lookup field. It will use the foreign key in the Books table to reference the name of the author from the Author table. This technique does join one better, however, because it will let you not only view a field from the Authors table as if it were part of the Books table, but also enables you to drop down a list of all the authors in the Authors table while you are still viewing the Books table, as shown in Figure 11.5.
In short, lookup fields give you the same type of benefits you derive from performing a join between two tables. In particular, they let you combine the fields of two tables so that you can create one dataset with fields from multiple tables.
NOTE: Lookup fields are a bit like a combination of a one-to-many relationship and a calculated field. The techniques used to actually implement them, however, have more in common with calculated fields than they do with one-to-many relationships. There are significant differences between the three technologies, but I still tend to think of calculated fields, lookup fields, and one-to-many relationships as being interrelated concepts.
Because lookup fields are so much like one-to-many relationships, it is usually not a good idea to use both techniques simultaneously with the same two TTable objects. For instance, if you have the Authors table related to the books table in a one-to-many, you wouldn't want to simultaneously do a lookup from the Books table to the author table. This problem, and its solution, are addressed in the Lookup example on the CD-ROM that accompanies this book. That program will be discussed throughout the rest of this section of the chapter.
I should perhaps add that lookup fields are a great technique to use with relatively small datasets. If you are from the world of big iron, and work with tables that contain tens of thousands of records or more, you will probably find lookup fields are of only limited use to you.
Needless to say, BCB gives good support for using lookup fields. You can now perform automatic lookups inside grids, list boxes, and combo boxes. In particular, the following controls support lookups: TDBGrid, TDBCtrlGrid, TDBLookupListBox, and TDBLookupComboBox.
The Lookup program shows how to proceed. The code for this application is shown
in Listings 11.1 through 11.3. Two views of the program are shown in Figures 11.5
and 11.6.
FIGURE 11.5. The main form for the Lookup program.
FIGURE 11.6. This form features a combo box that lets you perform lookups from the Books table into the Authors table.
NOTE: The first version of Delphi had a TDBLookupCombo control and a TDBLookupList control that had certain limited capabilities. Both of these controls are still present in some versions of BCB, but they have been moved off the Data Controls page onto the Win 3.1 page. They are being kept around solely for compatibility with legacy Pascal code, and you should not use them in new programs.
The TDBLookupComboBox control and the TDBLookupListBox control now replace the old 16-bit controls, and they outperform them on several fronts. In particular, the TDBLookupComboBox and TDBLookupListBox will be filled up automatically with the data from the lookup table. Don't confuse the old control with the new ones! TDBLookupComboBox is the fancy one; the TDBLookupCombo is the old-fashioned one. You might use the following somewhat whimsical mnemonic: The TDBLookupListBox has a bigger name than the TDBLookupList because it has "bigger" capabilities.
By the way, this is a classic example of why it is important to get things right the first time. In particular, it shows why it is sometimes better to cut a feature rather than trying to put in a hack that you will want to improve in later versions. In particular, the TDBLookupCombo was poorly implemented in the first version of Delphi, which was a 16-bit program. Because Delphi 2.0 promised to compile all your 16-bit programs, this component had to be left in the product even though it was replaced with a far superior tool. Now, this old nemesis lives on even in the C++ version of the product, because BCB advertises the fact that it supports all the legacy Pascal code you might want to bring into a project.
Here's the summary: The original error was made back in Delphi 1.0, but the repercussions still echo even when the 32-bit version of the Delphi is ported to C++! Clearly, it is worthwhile making sure that things are designed right the first time, or else they should be left out of the product altogether. Of course, this is a rule that can be stated fairly easily, but is difficult to live up to.
Listing 11.1. The core functionality for the Lookup program is done in the Object Inspector for the TDMod object and not here in the code for Dmod1.cpp.///////////////////////////////////////
// File: DMod1.cpp
// Project: Lookup
// Copyright (c) 1997 by Charlie Calvert
#include <vcl\vcl.h>
#pragma hdrstop
#include "DMod1.h"
#pragma resource "*.dfm"
TDMod *DMod;
__fastcall TDMod::TDMod(TComponent* Owner)
: TDataModule(Owner)
void __fastcall TDMod::AuthorTableCalcFields(TDataSet *DataSet)
AuthorTableFirstLast->AsString =
AuthorTableFirst->AsString + " " + AuthorTableLast->AsString;
void TDMod::RefreshBookDetail()
AnsiString TDMod::GetCurBook()
return BookDetailTable->FieldByName("Title")->AsString;
AnsiString TDMod::GetCurAuthor(void)
return AuthorTable->FieldByName("FirstLast")->AsString;
void TDMod::FindAuthor(AnsiString S)
AuthorTable->FindNearest(OPENARRAY(TVarRec, (S)));
void TDMod::FindTitle(AnsiString S)
AnsiString Temp(BookLookupTable->IndexName);
BookLookupTable->IndexName = "idxTitle";
BookLookupTable->FindNearest(OPENARRAY(TVarRec, (S)));
BookLookupTable->IndexName = Temp;
void TDMod::BookLookupInsert()
void TDMod::BookLookupPost()
if ((BookLookupTable->State == dsEdit)||(BookLookupTable->State == dsInsert))
void TDMod::BookLookupCancel()
if ((BookLookupTable->State == dsEdit)||(BookLookupTable->State == dsInsert))
void TDMod::BookLookupDelete()
Listing 11.2. Form1 gives you a look at both the Authors table and the Books table. A drop-down in dbGrid2 lets you view the lookup field.///////////////////////////////////////
// File: InsertEdit.cpp
// Project: Lookup
// Copyright (c) 1997 by Charlie Calvert
#include <vcl\vcl.h>
#pragma hdrstop
#include "Main.h"
#include "DMod1.h"
#include "InsertEdit.h"
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
void __fastcall TForm1::Exit1Click(TObject *Sender)
void __fastcall TForm1::EditBook1Click(TObject *Sender)
void __fastcall TForm1::NewBook1Click(TObject *Sender)
Listing 11.3. The InsertEditForm shows how to use DBLookupComboBoxes.///////////////////////////////////////
// File: InsertEdit.cpp
// Project: Lookup
// Copyright (c) 1997 by Charlie Calvert
#include <vcl\vcl.h>
#pragma hdrstop
#include "InsertEdit.h"
#include "DMod1.h"
#pragma resource "*.dfm"
TInsertEditForm *InsertEditForm;
__fastcall TInsertEditForm::TInsertEditForm(TComponent* Owner)
: TForm(Owner)
void __fastcall TInsertEditForm::bbInsertClick(TObject *Sender)
void __fastcall TInsertEditForm::bbPostClick(TObject *Sender)
void TInsertEditForm::ShowEdit(AnsiString S)
void TInsertEditForm::ShowInsert()
DMod->BookLookupTableTitle->AsString = "My New Book";
void __fastcall TInsertEditForm::CancelBtnClick(TObject *Sender)
void __fastcall TInsertEditForm::DeleteBtnClick(TObject *Sender)
if (MessageBox(Handle, "Delete?" , "Delete Dialog", MB_YESNO) == ID_YES)
void __fastcall TInsertEditForm::FormShow(TObject *Sender)
The Lookup program enables you to easily fill in the key fields of the Books table by looking them up in the Authors table. To understand why this capability is important, notice that the only way to tell which author is associated with which book is by placing the appropriate author number in the AuthNo field of the Book table. This is convenient from the point of view of the programmer who wants to construct a well-made relational database. In particular, it saves space by allowing the construction of one-to-many relationships. However, the user isn't
going to want to have to remember that Herman Melville is associated with the number 2, Jack Kerouac with the number x, and so on. The point of a lookup field is that it lets you look up a list of authors in the author table, and then automatically
assigns the chosen author number to the AuthNo field in the Books table.
This program uses two tables called, not surprisingly, Author.db and
Book.db. Both of these tables are found on the CD-ROM that
accompanies this
book. Tables 11.1 and 11.2 show the schema for the tables.
Table 11.1. Author.db table structure.
|Foreign Key
|Character (35)
NOTE: Notice the use of the AutoIncrement fields in the table definitions shown in Tables 11.1 and 11.2. These fields will automatically be filled in when the user adds a new record at runtime. For instance, when you add the first record to the Books table, it will automatically be given a BookNo of 1. The second record will automatically be given a BookNo of 2, and so on. AutoIncrement fields are read-only, and frequently there is no need to show them to the user at runtime.
Furthermore, I use Referential Integrity to ensure that the AuthNo field properly binds to the Author table. In particular, it ensures that you cannot insert records into the Book table that are not properly related to the Authors table through the AuthNo field. Referential Integrity also ensures that the value of the AuthNo field is filled in automatically when you insert a new record into the Books table. To view the Referential Integrity, load the book table into the Database Desktop, choose Table | Info Structure, select Referential Integrity in the Table Properties, highlight the AuthNoRI rule, and press the Detail Info button. There is more on this subject in the next chapter, called "Understanding Relational Databases."
There is little actual work required to construct this program. In particular, look over the source code shown earlier, and you will see that the only significant line of code in the whole program is the one for the OnCalcFields event. Other than that, it's just a matter of manipulating the visual tools.
To get started, create a new
application and add a data module to it. Set up the
Authors and Books tables on the data module. Bring up the Fields
Editor for both tables and create objects for all of their fields. Give the tables
and data sources appropriate
names, such as AuthorTable and BookLookupTable,
as shown in Figure 11.7. Note that later on I will add a second in- stance of the
Book table to the program so that I can simultaneously perform a lookup
and a one-to-many.
FIGURE 11.7. The TDataModule for the Lookup program.
Inside the Author table, create a calculated field called LastFirst. To create the calculated field, first right-click the TTable object, and then right-click the Fields Editor and select New from the menu. After creating the calculated field, assign the following method to the OnCalcFields event:void __fastcall TDMod::AuthorTableCalcFields(TDataSet *DataSet)
AuthorTableFirstLast->AsString =
AuthorTableFirst->AsString + " " + AuthorTableLast->AsString;
This field will be the one that is looked up in the second table. The issue here is that just looking up the last name of an author is not sufficient--you need to look up both first and last names in order to be sure you are finding a unique author. In other words, you can't tell Henry James from William James or Tom Wolfe from Thomas Wolfe unless you have both the first and last name present. It would be wasteful of disk space to permanently add a field to the table that combined the first and last names, but you can create a temporary copy of that field with a calculated field.
Now that you have a calculated field in place, it is time to create a lookup field.
To get started, bring up the Fields Editor for the
Book table. Right-click
it and create a new field called AuthorLookup. Set its Type to
String and its Field Type to Lookup. The KeyField
should be set to AuthNo, the
Dataset to AuthorTable, the
Lookup Key to AuthNo, and the Result field to LastFirst.
Figure 11.8 shows how the New Field dialog should look when you are done. Notice
that you can also fill in this
same information in the Object Inspector if you first
select the BookLookupTable object. (In other words, you could create a new
object and then close the Fields Editor without specifying any of its properties.
Later, you could select the
object and designate its type, its lookup fields, and
so on.)
FIGURE 11.8. Filling in the New Field dialog.
Go back to Form1 and make sure the two TDBGrids are arranged one above the other and are hooked up properly to the tables on the TDataModule. Run the application.
The AuthorLookup field in the TDBGrid object associated with the Books table is now a drop-down combo box. If you click it once, and then drop down its list, you can then perform a lookup into the LastFirst field of the Author table. This lookup will automatically fill in the AuthNo field of the book table. You can use this lookup to insert a new author into a new record or to change the author of an existing record.
Note that lookup fields give you two distinct benefits. They enable you to perform a "join" between the Books table and the Authors table, and they allow you to look up a reference in a drop-down list.
The implementation of this program found on the CD-ROM that accompanies this book actually enables the user to perform the lookup on a second form. I implement things that way because it is probably easiest from the user's perspective, and because I want to support both a one-to-many relationship and a lookup between the Authors and Books tables. However, if you just want to see how lookups work, then you should follow the technique described previously.
NOTE: The capability of having a drop-down list in a grid object comes for free in BCB, even when you are not doing lookups. Go back in design mode and open up the Columns property of a grid object. Add all the fields to the Columns list box. You can now select one of the fields, such as Title, and choose the PickList button in order to create a set of default values available for the field. The user can access these values at runtime by clicking the field and dropping down the combo box, per the lookup example discussed previously. This is the capability supported by the old TDBLookupList and TDBLookupCombo from the old Windows 3.1 days.
Besides the TDBGrid object, there are two other controls in BCB that understand lookup fields. The first of these controls is shown on Form2 of the Lookup program found on the CD-ROM that accompanies this book. The TDBLookupComboBox is the default control you will get if you drag and drop the AuthorLookup field from the Fields Editor onto a form. If you perform the drag-and-drop operation, the control will be hooked up automatically. If you want to hook it up manually, just connect its DataSource to the dsBook object and its DataField to the AuthorLookup field. There is also a TDBLookupListBox, which works exactly the same way as the TDBLookupComboBox.
NOTE: Both the TDBLookupListBox and TDBLookupComboBox have fields that correspond to the ones you filled in with the New Field dialog shown in Figure 11.7. However, there is no need to fill in these fields a second time. Just hook up the DataSource and DataFields properties, and you are ready to go.
When you are working with the Lookup program found on the book's CD-ROM, you should note that Form1 does not contain a lookup. It's meant to help you scan through all the available data so you can grok the significance of the lookup process. The top part of the second form, called the InsertEditForm, is somewhat closer to the type of display you would want to present to the user in a real program. However, I have extended this form to include a TDBGrid object, just so you can see how the lookup combo box is inserted automatically into the grid.
When working with the InsertEditForm, notice how easy it is to simply type in a new book name, select an author in the combo box, and then perform a Post by clicking the OK button. The process is very simple from the user's point of view. In particular, a new BookNo is being assigned automatically by the AutoIncrement field, and the new AuthNo is being filled in automatically by the lookup process.
Here are the two ways to handle the data in InsertEditForm:
It's important to note that lookup controls probably would not be appropriate for use with big datasets because drop-down controls aren't very handy for displaying thousands of items. Even list boxes are fairly limited in these circumstances. You would therefore use lookups mostly with smaller datasets.
It should also be pointed out that not being able to use both one-to-many relationships and lookups between the same two tables is a significant inconvenience. However, the Lookup example discussed in these sections and implemented on the book's CD-ROM shows that the workaround is not that complex.
NOTE: If you are concerned that the solution to the problem in the last paragraph requires using three TTable objects instead of only two, I would ask you to recall that the goal of this book is to show how to get things done, not how to do things in the smallest possible space. If you can get things done in five minutes through a technique that you know is bug free, that is something you should give up only reluctantly, particularly if the alternative is working for days or weeks to implement a solution that is likely to have bugs that will take another week or two to squash. At the very least, you should implement the quick solution for the first draft of your application, and then come back and look for optimizations once you have a working version of the product.
Don't ever try to optimize during your first draft of an application! There is no such thing as getting things right the first time in programming. Instead, you should implement a reasonable solution, critique it, come back and make improvements, critique the improvements, make another pass over the application, and so on. This kind of cycle demands that you not get too hung up on optimizations during early drafts, because you are likely to find that any one part of the application will change in future revisions. If you have the thing implemented correctly, and there is still time left in the project cycle, then you can come back and seek to optimize the code!
The final kicker in this analysis is that contemporary application programmers rarely have time to optimize. Given the success rate of most projects, your customers or managers will usually be ecstatic if you just turn in a working solution to the problem on time. If you release the same application 10 percent faster and 20 percent smaller, but six months later, it's unlikely you will win quite the same number of kudos you think you deserve. I usually leave the minute optimizations up to the development teams at Borland. They know how to reach into the fire without getting burned.
If you want to change the author associated with a particular record, you just click a new item in the list box. The author number will be changed automatically for you by the lookup. It's all very simple and intuitive when viewed from the user's perspective.
TDBGrid objects can be completely reconfigured at runtime. You can hide and show columns, change the order of columns, the color of columns, the color of rows, the color of fields, and the width of columns.
The GridTricks program, shown in Figure 11.9, demonstrates how to take a TDBGrid
through its paces at runtime. The
program is fairly straightforward except for two
brief passages. The first passage involves creating checkbox controls on-the-fly,
and the second shows how to change the traits of columns.
FIGURE 11.9. The main GridTricks program enables you to change the appearance of a grid at runtime.
You need a color monitor to really see what is happening.
When the
user wants to decide which fields are visible, GridTricks pops up a second
form and displays the names of all the fields from the ORDERS table in a
series of checkboxes. The user can then select the fields that he or she wants to
visible. The selected checkboxes designate fields that are visible, whereas
the nonselected ones represent invisible fields. The program also enables you to
set the order and width of fields, as well as to hide and show the titles at the
top of the
grid. (See Listings 11.1 and 11.2.) The code for the GridTricks program
is in the CHAP17 directory on this book's CD-ROM. (See Listings 11.4-11.8.)
Listing 11.4. The main unit for the GridTricks Program.
// File: Main.cpp
// Project: GridTricks
// Copyright (c) 1997 by Charlie Calvert
#include <vcl\vcl.h>
#pragma hdrstop
#include "Main.h"
#include "DMod1.h"
#include "NamesDlg.h"
#include "ColumnEditor1.h"
#include "ShowOptions1.h"
#define NEWCOLOR clGreen
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
void __fastcall TForm1::Exit1Click(TObject *Sender)
void __fastcall TForm1::FieldNames1Click(TObject *Sender)
void __fastcall TForm1::FieldObjectNames1Click(TObject *Sender)
// DBGrid1DrawColumnCell
// Paints ROW different color depending on value of ItemsTotal field
void __fastcall TForm1::DBGrid1DrawColumnCell(TObject *Sender,
const TRect &Rect, Integer DataCol, TColumn *Column,
TGridDrawState State)
if (ColorRows1->Checked)
if (DMod->OrdersTableItemsTotal->Value < 1000)
DBGrid1->Canvas->Font->Color = clRed;
else if (DMod->OrdersTableItemsTotal->Value < 10000)
DBGrid1->Canvas->Font->Color = clBlue;
DBGrid1->Canvas->Font->Color = clGreen;
DBGrid1->DefaultDrawColumnCell(Rect, DataCol, Column, State);
void TForm1::ColorTitles(BOOL UseDefaultColor)
TColor Colors[] = {clRed, clBlue, clGreen, clLime, clWhite, clFuchsia};
int i;
for (i = 0; i < DBGrid1->Columns->Count; i++)
TColumn *Column = DBGrid1->Columns->Items[i];
TColumnTitle *ColumnTitle = Column->Title;
if (UseDefaultColor)
ColumnTitle->Font->Color = FDefaultColor;
ColumnTitle->Font->Color = Colors[random(7)];
void __fastcall TForm1::AnimateTitles1Click(TObject *Sender)
Timer1->Enabled = (!Timer1->Enabled);
AnimateTitles1->Checked = Timer1->Enabled;
if (!AnimateTitles1->Checked)
void __fastcall TForm1::ColorRows1Click(TObject *Sender)
ColorRows1->Checked = (!ColorRows1->Checked);
void __fastcall TForm1::MarkColumnClick(TObject *Sender)
MarkColumn->Checked = (!MarkColumn->Checked);
TColumn *Column = DBGrid1->Columns->Items[DBGrid1->SelectedIndex];
if (MarkColumn->Checked)
Column->Font->Color = NEWCOLOR;
Column->Font->Style = TFontStyles() << fsBold;
Column->Font->Color = FDefaultColor;
Column->Font->Style = TFontStyles();
// Handle Caption
void TForm1::HandleCaption()
TColumn *Column = DBGrid1->Columns->Items[DBGrid1->SelectedIndex];
AnsiString S(DBGrid1->SelectedIndex);
Caption = S;
if (Column->Font->Color == FDefaultColor)
Caption = "Column " + S + " is Default";
Caption = "Column " + S + " is Marked";
void __fastcall TForm1::DBGrid1ColEnter(TObject *Sender)
TColumn *Column = DBGrid1->Columns->Items[DBGrid1->SelectedIndex];
MarkColumn->Checked = (Column->Font->Color == NEWCOLOR);
void __fastcall TForm1::FormCreate(TObject *Sender)
FDefaultColor = DBGrid1->Font->Color;
void __fastcall TForm1::Timer1Timer(TObject *Sender)
void __fastcall TForm1::ShowFieldEditor1Click(TObject *Sender)
void __fastcall TForm1::ToggleTitles1Click(TObject *Sender)
if (DBGrid1->Options.Contains(dgTitles))
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) >> dgTitles;
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) << dgTitles;
void __fastcall TForm1::ToggleIndicator1Click(TObject *Sender)
if (DBGrid1->Options.Contains(dgIndicator))
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) >> dgIndicator;
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) << dgIndicator;
void __fastcall TForm1::ShowTitlesIndicator1Click(TObject *Sender)
ShowTitlesIndicator1->Checked =
!(TDBGridOptions(DBGrid1->Options).Contains(dgIndicator) &&
if (ShowTitlesIndicator1->Checked)
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) << dgIndicator << dgTitles;
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) >> dgIndicator >> dgTitles;
void __fastcall TForm1::ColLines1Click(TObject *Sender)
if (DBGrid1->Options.Contains(dgColLines))
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) >> dgColLines;
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) << dgColLines;
ColLines1->Checked = DBGrid1->Options.Contains(dgColLines);
void __fastcall TForm1::RowLines1Click(TObject *Sender)
if (DBGrid1->Options.Contains(dgRowLines))
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) >> dgRowLines;
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) << dgRowLines;
RowLines1->Checked = DBGrid1->Options.Contains(dgRowLines);
void __fastcall TForm1::ShowAllOptions1Click(TObject *Sender)
void __fastcall TForm1::MustPressF2orEntertoEdit1Click(TObject *Sender)
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) >> dgAlwaysShowEditor;
MustPressF2orEntertoEdit1->Checked =
void __fastcall TForm1::ChangeWidthofField1Click(TObject *Sender)
AnsiString S("");
TColumn *Column = DBGrid1->Columns->Items[DBGrid1->SelectedIndex];
if (InputQuery("Data Needed", "New Width of Selected Field", S))
Column->Width = S.ToInt();
void __fastcall TForm1::HideCurrentColumn1Click(TObject *Sender)
if (MessageBox(Handle, "Hide Column?",
"Hide Info?", MB_YESNO | MB_ICONQUESTION) == ID_YES)
TColumn *Column = DBGrid1->Columns->Items[DBGrid1->SelectedIndex];
void __fastcall TForm1::MoveCurrentColumn1Click(TObject *Sender)
AnsiString S("");
if (InputQuery("Data Needed", "Enter new position of column", S))
DMod->OrdersTable->Fields[DBGrid1->SelectedIndex]->Index = S.ToInt();
Listing 11.5. The data module for the GridTricks program.///////////////////////////////////////
// File: ColumnEditor1.cpp
// Project: GridTricks
// Copyright (c) 1997 by Charlie Calvert
#include <vcl\vcl.h>
#pragma hdrstop
#include "DMod1.h"
#pragma resource "*.dfm"
TDMod *DMod;
__fastcall TDMod::TDMod(TComponent* Owner)
: TDataModule(Owner)
void TDMod::GetFieldNames(TStringList *Items)
int i;
for (i = 0; i < OrdersTable->FieldCount - 1; i++)
void TDMod::GetObjectNames(TStringList *Items)
int i;
for (i = 0; i < OrdersTable->FieldCount - 1; i++)
Listing 11.6. The Column Editor for the GridTricks program.///////////////////////////////////////
// File: ColumnEditor1.cpp
// Project: GridTricks
// Copyright (c) 1997 by Charlie Calvert
// The last build of BCB I checked this on before shipping
// was still broken. BCB was not properly updating the
// data on the grids. Hopefully this program will start
// working properly once there is an update for BCB 1.0.
// The code I have here works fine in Delphi. In short, the
// problem is not in my code, and its not in the VCL. Its a
// BCB problem.
// Check my website for updates: users.aol.com/charliecal
#include <vcl\vcl.h>
#pragma hdrstop
#include "ColumnEditor1.h"
#include "DMod1.h"
#include "Main.h"
#pragma resource "*.dfm"
#define GAP 2
TColumnEditor *ColumnEditor;
__fastcall TColumnEditor::TColumnEditor(TComponent* Owner)
: TForm(Owner)
void TColumnEditor::CreateCheckBox(int Index, AnsiString Name, BOOL Visible)
CheckBoxAry[Index] = (TCheckBox*) new TCustomCheckBox(this);
CheckBoxAry[Index]->Parent = ColumnEditor;
CheckBoxAry[Index]->Caption = Name;
CheckBoxAry[Index]->Left = 10;
CheckBoxAry[Index]->Top = Index * (CheckBoxAry[Index]->Height + GAP);
CheckBoxAry[Index]->Width = 200;
CheckBoxAry[Index]->Checked = Visible;
void TColumnEditor::ShowColumns(void)
int i;
TColumn *Column;
for (i = 0; i < DMod->OrdersTable->FieldCount; i++)
CreateCheckBox(i, DMod->OrdersTable->Fields[i]->Name,
Height = (DMod->OrdersTable->FieldCount - 1) * (CheckBoxAry[0]->Height + GAP);
if (Height > 470)
Height = 470;
for (i = 0; i < DMod->OrdersTable->FieldCount; i++)
DMod->OrdersTable->Fields[i]->Visible = CheckBoxAry[i]->Checked;
Listing 11.7. The NamesDlg for the GridTricks program.///////////////////////////////////////
// File: NamesDlg.cpp
// Project: GridTricks
// Copyright (c) 1997 by Charlie Calvert
#include <vcl\vcl.h>
#pragma hdrstop
#include "NamesDlg.h"
#include "DMod1.h"
#pragma resource "*.dfm"
TNamesDialog *NamesDialog;
__fastcall TNamesDialog::TNamesDialog(TComponent* Owner)
: TForm(Owner)
void TNamesDialog::ShowNames(TFieldData FieldData)
case fdFieldNames:
DMod->GetFieldNames((TStringList *)ListBox1->Items);
case fdObjectNames:
DMod->GetObjectNames((TStringList *)ListBox1->Items);
void __fastcall TNamesDialog::BitBtn1Click(TObject *Sender)
Listing 11.8. The ShowOptions module shows which DBGrid options are currently active.#include <vcl\vcl.h>
#pragma hdrstop
#include "ShowOptions1.h"
#pragma resource "*.dfm"
TShowOptionsForm *ShowOptionsForm;
__fastcall TShowOptionsForm::TShowOptionsForm(TComponent* Owner)
: TForm(Owner)
int i;
int j = 0;
for (i = 0; i < ComponentCount; i++)
if (dynamic_cast<TCheckBox*>(Components[i]))
CheckBox[j] = (TCheckBox*)Components[i];
void TShowOptionsForm::ShowOptions(TDBGridOptions Options)
int i;
for (i = 0; i < 12; i++)
if (Options.Contains(i))
CheckBox[i]->Checked = True;
CheckBox[i]->Checked = False;
In the next few paragraphs you will find descriptions of the key parts of the GridTricks program. Understanding its constituent parts will help you to take control over the grids you display in your programs.
Most of the code in the GridTricks program is fairly simple. However, the program performs a number of separate tasks. To grasp the program, it's necessary to divide and conquer; that is, to take the tasks performed by the program one at a time. Find out how each one works, and then move on to the next one. If you proceed in this fashion, you will find the program easy to understand.
You can use the
Options field of a TDBGrid to change its appearance.
The Options property has the following possible values:
|Set to True by default, it enables the user to edit a grid. You can also set the grid's ReadOnly property to True or False.
|Designates whether titles can be seen.
|Determines whether to show the small icons on the left of the grid.
|Designates whether or not the user can resize columns.
|Determines whether or not to show the lines between columns.
|Designates whether or not to show the lines between rows.
|Enables the user to tab and Shift+tab between columns.
|If you select a field will you be in Edit mode auto-matically?
|Can select rows, mutually exclusive with dgAlwaysShowEditor.
|Selection remains even when grid loses focus.
|Shows message box when user presses Ctrl+Delete.
|Cancels Inserts on exit if no changes were made to row.
|Can select multiple noncontiguous rows with Ctrl+Click.
dgColumnResize, dgColLines, dgRowLines, dgTabs, dgRowSelect,
dgAlwaysShowSelection, dgConfirmDelete, dgCancelOnExit, dgMultiSelect };
For instance, you can set the options at runtime by writing code that looks like this:
DBGrid1->Options = TDBGridOptions() << dgTitles;
This code in effect turns all the options to False except dgTitles. This code turns off all options but dgTitles and dgIndicator.DBGrid1->Options = TDBGridOptions() << dgTitles << dgIndicator;
More specifically, the code sets the DBGrid1 Options property to a set that contains only dgTitles and dgIndicator. This code toggles dgTitles and dgIndicator off and on each time it is called:void __fastcall TForm1::ToggleTitles1Click(TObject *Sender)
if (DBGrid1->Options.Contains(dgTitles))
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) >> dgTitles;
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) << dgTitles;
The set operators shown in ToggleTitles1Click move the dgTitles option in and out of DBGrid->Options.
The following code shows how to toggle back and forth between showing both indicators and titles and hiding both indicators and titles:void __fastcall TForm1::ShowTitlesIndicator1Click(TObject *Sender)
ShowTitlesIndicator1->Checked =
!(TDBGridOptions(DBGrid1->Options).Contains(dgIndicator) &&
if (ShowTitlesIndicator1->Checked)
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) << dgIndicator << dgTitles;
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) >> dgIndicator >> dgTitles;
This code moves both the dgIndicator and dgTitles elements in and out of the Options array as needed. The << operator adds elements to a set, while the >> operator moves things out of the set. If you need to move multiple elements in and out of the set, just use the operator multiple times as shown in the ShowTitlesIndicator1Click method.
NOTE: The following standard set operations will not work with the Options property because the += and -= operators do not work with sets that are properties because of the extra work involved in calling get and set methods: TDBGridOptions Options;
Options << dgTitles << dgIndicator;
DBGrid1->Options += Options;
DBGrid1->Options -= Options;
Here is an example of code showing how to toggle the dgRowLines element of the Options property on and off at runtime.void __fastcall TForm1::RowLines1Click(TObject *Sender)
if (DBGrid1->Options.Contains(dgRowLines))
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) >> dgRowLines;
DBGrid1->Options = TDBGridOptions(DBGrid1->Options) << dgRowLines;
RowLines1->Checked = DBGrid1->Options.Contains(dgRowLines);
The last line of code in the routine toggles the check mark before the RowLines menu item so that it reflects the current state of the grid. In other words, if dgRowLines is part of the set, the menu item will be checked; if it is not part of the set, the menu item will not be checked:RowLines1->Checked = DBGrid1->Options.Contains(dgRowLines);
In this section you have seen how to toggle the elements of the Options set back and forth at runtime. Most of the code for doing this is fairly simple, although you need to have a basic grasp of BCB set operations to understand how it works. If you need to brush up on this material, sets were covered in more depth in Chapter 2, "Basic Facts About C++Builder."
Now that you know how to toggle the Options of a DBGrid, it
might be worthwhile spending a few moments learning how to display the
to the user at runtime. As shown in Figure 11.10, I use a set of 12 CheckBoxes
to depict the current state of 12 DBGrid options. In the next few paragraphs
I will explain how the code that drives this form works.
FIGURE 11.10. Using checkboxes to depict the available DBGrid options to the user at runtime.
In the header file for the unit, I declare an array of checkboxes:TCheckBox *CheckBox[12];
I initialize these checkboxes in the constructor for the form:__fastcall TShowOptionsForm::TShowOptionsForm(TComponent* Owner)
: TForm(Owner)
int i;
int j = 0;
for (i = 0; i < ComponentCount; i++)
if (dynamic_cast<TCheckBox*>(Components[i]))
CheckBox[j] = (TCheckBox*)Components[i];
This code iterates over all the components on the form checking for ones that are of type TCheckBox. When it finds one, it adds it to the array of CheckBoxes. The code uses dynamic_cast to check whether each item in the Components array is of type TCheckBox.
NOTE: A Components array is implemented in TComponent and is maintained automatically for all components that descend from TComponent. The concept of ownership is what governs which items are put in the Components array. All components that are owned by the form are automatically, and by definition, included in the Components array for the form. In other words, if you drop a component on a form, it will be listed in the Components array for the form. You can use the ComponentCount property of the form to determine how many items are in the Components array.
After filling in the array of checkboxes, it is a simple matter to toggle the Checked property of each checkbox depending on the current state of each
DBGrid option:void TShowOptionsForm::ShowOptions(TDBGridOptions Options)
int i;
for (i = 0; i < 12; i++)
if (Options.Contains(TDBGridOption(i)))
CheckBox[i]->Checked = True;
CheckBox[i]->Checked = False;
This code determines which items in the DBGridOptions set are turned on, and then toggles the appropriate checkbox. The code depends, of course, on the fact that the DBGridOptions set is a list of items with values ranging from 0 to 11.
To understand this code, you must grasp that DBGridOption is an enumerated type, and DBGridOptions is a set ranging over the values in that enumerated type, with dgEditing being the minimum value and dgMultiSelect being the maximum value:enum TDBGridOption {dgEditing, dgAlwaysShowEditor, dgTitles, dgIndicator,
dgColumnResize, dgColLines, dgRowLines, dgTabs, dgRowSelect,
dgAlwaysShowSelection, dgConfirmDelete, dgCancelOnExit,
typedef Set<TDBGridOption, dgEditing, dgMultiSelect> TDBGridOptions;
The next three sections of the chapter cover changing the colors of all the titles, columns, rows, and even individual cells in a TDBGrid. This is not something you have to do all that often, but when the need comes around it is fairly pressing. Before reading these sections, you should be sure to run the GridTricks program, because it will be hard to read the code without some understanding of what it does.
Here is how to color the titles in a TDBGrid:void TForm1::ColorTitles(BOOL UseDefaultColor)
TColor Colors[] = {clRed, clBlue, clGreen, clLime, clWhite, clFuchsia};
int i;
for (i = 0; i < DBGrid1->Columns->Count; i++)
TColumn *Column = DBGrid1->Columns->Items[i];
TColumnTitle *ColumnTitle = Column->Title;
if (UseDefaultColor)
ColumnTitle->Font->Color = FDefaultColor;
ColumnTitle->Font->Color = Colors[random(7)];
This code first declares an array of colors. The constants seen here are pre-declared colors of type TColor.
The actual number of colors in the array was chosen at random. I could have added or subtracted colors from the array without changing the rest of code in the routine, with the exception of the number 7, which is passed to random in the routine's last line of code.
The TColumn object defines how a column in a TDBGrid should look. That is, it defines the font, color, and width of the column. The Columns property of a TDBGrid is of type TDBGridColumns, which is a collection of TColumn objects. Each TColumn object has a title. This title is defined in an object of type TColumnTitle. Finally, a TColumnTitle has color, font, and caption properties:TDBGrid Object
Columns Property
TColumn Object
Font, Color, Caption
The preceding list is not an object hierarchy, but just a way of illustrating the relationship between these different entities. In other words, the grid object contains a Columns property, and the Columns property contains
TColumn objects, and the TColumn object contains a TColumnTitle, which in turn contains a Font, Color, and Caption.
To get hold of a TColumn object, you can use the Items property of TDBGridColumns:TColumn *Column = DBGrid1->Columns->Items[i];
To move from there to a TColumnTitle object, you can use the Title property of a TColumn object:TColumnTitle *ColumnTitle = Column->Title;
Once the preceding ColorTitles method has the ColumnTitle in its hands, it can set it to whatever color it wants:if (UseDefaultColor)
ColumnTitle->Font->Color = FDefaultColor;
ColumnTitle->Font->Color = Colors[random(7)];
The FDefaultColor variable is of type TColor. In the OnCreate event for the form, I set it to the default color for the grid's font:FDefaultColor = DBGrid1->Font->Color;
Phew! That was the hard one. If you understand what has happened here, you will have no trouble with the next two sections, which cover changing the color of columns and rows in a grid.
If you understand the code in the last section, it will be easy to understand how to change the look of a single column. Writing this kind of code will enable you to emphasize a certain part of a dataset or to bring the users eye to certain part of your form.
Here is the method that changes the appearance of a column in a TDBGrid:void __fastcall TForm1::MarkColumnClick(TObject *Sender)
MarkColumn->Checked = (!MarkColumn->Checked);
TColumn *Column = DBGrid1->Columns->Items[DBGrid1->SelectedIndex];
if (MarkColumn->Checked)
Column->Font->Color = NEWCOLOR;
Column->Font->Style = TFontStyles() << fsBold;
Column->Font->Color = FDefaultColor;
Column->Font->Style = TFontStyles();
This code first grabs hold of a selected column in a grid:TColumn *Column = DBGrid1->Columns->Items[DBGrid1->SelectedIndex];
If the user has indicated that he wants this column to stand out, it is a simple matter to change its color and set its font to bold:Column->Font->Color = NEWCOLOR;
Column->Font->Style = TFontStyles() << fsBold;
Notice that the Style property is a set, and so you use a template class to manipulate its members. Here, as found in GRAPHICS.HPP, are the different styles you can associate with a font:enum TFontStyle { fsBold, fsItalic, fsUnderline, fsStrikeOut };
There are some parts of the code, such as MarkColumn and HandleCaption, that I don't mention. I ignore these elements because they are merely part of the logic of this program and are not germane to the subject of changing an individual column.
In the last two sections on columns and column titles, you have been working with the TColumn object. You can also change the color of the text in a TDBGrid by working with the font associated with the grid's TCanvas object:void __fastcall TForm1::DBGrid1DrawColumnCell(TObject *Sender,
const TRect &Rect, Integer DataCol, TColumn *Column,
TGridDrawState State)
if (ColorRows1->Checked)
if (DMod->tblOrdersItemsTotal->Value < 1000)
DBGrid1->Canvas->Font->Color = clRed;
else if (DMod->tblOrdersItemsTotal->Value < 10000)
DBGrid1->Canvas->Font->Color = clBlue;
DBGrid1->Canvas->Font->Color = clGreen;
DBGrid1->DefaultDrawColumnCell(Rect, DataCol, Column, State);
If you run the GridTricks program, you can see the effect of this code by choosing Color Rows from the Options menu of that program. Be sure that none of the other special effects are turned on when you choose this option, because they can interfere with your ability to see its results. Be sure to scroll the grid up and down after turning the effect on, because the data at the top of the grid is fairly homogenous.
The data shown in the grid is from the Orders table in the BCDEMOS
database. The code shown here colors each row in the grid according to the amount of money reported in the ItemsTotal field of the Orders table. For instance, if the ItemsTotal field contains a sum less than $1,000 dollars,
that row is painted Red:DBGrid1->Canvas->Font->Color = clRed;
Here the code sets the font of the TCanvas object for the grid to clRed. Nothing could be simpler.
The user can change the width of a column at runtime with the mouse. But how can you do the same thing programmatically without any input from the user?
If you want to change the width of a column at runtime, just change the DisplayWidth property of the appropriate TField object:TblOrders->FieldByName("CustNo")->DisplayWidth = 12;
TblOrdersCustNo->DisplayWidth = 12;
The value 12 refers to the approximate number of characters that can be displayed in the control. Various factors, such as whether or not you are using a fixed-pitch font, affect the interpretation of this value. See the online help for additional information.
Here is how you can change the width of the column in the grid without affecting
the properties of the underlying field:void __fastcall TForm1::ChangeWidthofField1Click(TObject *Sender)
AnsiString S("");
TColumn *Column = DBGrid1->Columns->Items[DBGrid1->SelectedIndex];
if (InputQuery("Data Needed", "New Width of Selected Field", S))
Column->Width = S.ToInt();
This code asks the user for the width he or she wants to assign to the currently selected column. The code then makes the change by retrieving the column and changing its Width property.
The user can change the order of columns in a TDBGrid simply by clicking them and dragging them with a mouse. But how do you proceed if you want to do the same thing at runtime without the user's direct input?
If you want to hide a field at runtime, you can set its Visible property to False:OrdersTable->FieldByName("CustNo")->Visible = False;
OrdersTableCustNo->Visible = False;
NOTE: This code will not work in the first version of BCB. See the top of the ColumnEditor1.cpp file for late-breaking information on this process.
Both lines of code perform identical tasks. To show the fields again, simply set Visible to True.
Alternatively, you can retrieve a TColumn object from the grid, and then quietly dispose of it:void __fastcall TForm1::HideCurrentColumn1Click(TObject *Sender)
if (MessageBox(Handle, "Hide Column?",
"Hide Info?", MB_YESNO | MB_ICONQUESTION) == ID_YES)
TColumn *Column = DBGrid1->Columns->Items[DBGrid1->SelectedIndex];
That'll do it! The column disappears from the grid once it has been freed. Alternatively, you can set the width of a column to 0, which makes the column itself go away, but not the lines between columns.
In order to allow the user to decide which fields are visible, GridTricks pops up a second form with a series of checkboxes on it. The program actually creates each of these checkboxes at runtime. In other words, it doesn't just pop up a form with the correct number of checkboxes on it, but instead iterates through the TblOrders object, finds out how many checkboxes are needed, and then creates them dynamically at runtime.
To perform these tasks, GridTricks calls on a form that is specially designed to display the checkboxes:void __fastcall TForm1::ShowFieldEditor1Click(TObject *Sender)
The ShowColumns method of the VisiForm first calls a routine called CreateCheckBox that creates the checkboxes, displays the form, and
finally sets the state of the checkboxes:void TColumnEditor::ShowColumns(void)
int i;
TColumn *Column;
for (i = 0; i < DMod->OrdersTable->FieldCount; i++)
CreateCheckBox(i, DMod->OrdersTable->Fields[i]->Name,
Height = (DMod->OrdersTable->FieldCount - 1) * (CheckBoxAry[0]->Height + GAP);
if (Height > 470)
Height = 470;
// This is the code that does not work in BCB 1.0
for (i = 0; i < DMod->OrdersTable->FieldCount; i++)
DMod->OrdersTable->Fields[i]->Visible = CheckBoxAry[i]->Checked;
The ShowColumns method iterates through the Query1 object and assigns one checkbox to each field. It also asks TQuery for the names of the fields, and determines whether or not each field is currently hidden or visible. Here is the code that creates a checkbox on-the-fly:void TColumnEditor::CreateCheckBox(int Index, AnsiString Name, BOOL Visible)
CheckBoxAry[Index] = (TCheckBox*) new TCustomCheckBox(this);
CheckBoxAry[Index]->Parent = ColumnEditor;
CheckBoxAry[Index]->Caption = Name;
CheckBoxAry[Index]->Left = 10;
CheckBoxAry[Index]->Top = Index * (CheckBoxAry[Index]->Height + GAP);
CheckBoxAry[Index]->Width = 200;
CheckBoxAry[Index]->Checked = Visible;
Most of the code in this example is performing relatively mundane tasks such as assigning names and locations to the checkboxes. These are the two key lines:CheckBoxAry[Index] = (TCheckBox*) new TCustomCheckBox(this);
CheckBoxAry[Index]->Parent = ColumnEditor;
The first line actually creates the checkbox and gives it an owner. The second line assigns a parent to the checkbox.
NOTE: The difference between a parent and an owner can be confusing at times. A form is always the owner of the components that reside inside it. As such, it is responsible for allocating and deallocating memory for these components. A form might also be the parent of a particular component, which means that Windows will ensure the component will be displayed directly on the form. However, one component might also find that another component is its parent, even though both components are owned by the form. For instance, if you place a TPanel on a form and then two TButtons on the TPanel, all three components will be owned by the form; however, the buttons will have the panel as a parent, whereas the TPanel will have the form as a parent. Ownership has to do with memory allocation. Parenthood usually describes what surface a component will be displayed on. Ownership is a BCB issue--parenthood is mostly a Windows API issue. In particular, it's Windows that cares about parenting, and it's Windows that handles that actual drawing of the controls. If you get confused about this while in the midst of a lengthy programming session, you can look it up in the online help by searching on the topic Parent.
The grids supplied with the first version of BCB are reasonably flexible objects that perform most of the tasks required of them. If you feel you need some additional functionality, check with third-party tool makers such as TurboPower software. A number of third-party grids with extended capabilities are available on the market, and some of them are well worth the purchase price.
New features of the TDuBGrid object not found in BCB 1.0 include the capability to add combo boxes to the grid, and to color the columns of your grids.
To move the location of a column at runtime, you can simply change its index, which is a zero-based number:DMod->OrdersTable->FieldByName("CustNo")->Index = 0;
DMod->OrdersTable->FieldByName("CustNo")->Index = 2;
By default, the CustNo field in the Orders table is at the second position, which means its index is 1. The code in the first example moves it to the first position, whereas the code that reads Query1CustNo.Index = 2; moves it to the third position. Remember, the Index field is zero-based, so moving a field to Index 1 moves it to the second field in a record. The first field is at Index 0.
When you change the index of a field, you do not need to worry about the indexes of the other fields in a record; they will be changed automatically at runtime.
That is all I'm going to say about DBGrid objects. I've gone on at considerable length about this one component, but this is one of the tools that lie at the heart of many database programs, and it's therefore worthy of a fairly serious look.
Another object that deserves mention is the TDBCtrlGrid, shown in Figure 11.11. You can use this object to view multiple records from a single table at one time without using the TDBGrid component. In other words, you can drop down TDBEdit controls onto a TDBCtrlGrid, and these edit controls will automatically be duplicated in a series of rows, where the first set of controls shows the first record, the second set the second record, and so on. You only have to drop down one set of controls--the extra sets are duplicated for you automatically by the DBCtrlGrid.
To get started with this object, drag and drop the
Country table off
the Explorer. Delete the TDBGrid object created for you automatically by
BCB, and add the TDBCtrlGrid object off the Data Controls page of the Component
Palette. Use the Fields Editor to drag and drop all
the fields from Country
table onto the top section of the TDBCtrlGrid. Arrange them as shown in
Figure 11.11. If you need help getting everything arranged properly, notice that
TDBCtrlGrids have RowCount and
ColCount properties that
enable you to define the number of rows and columns in the object. In this case,
I have set the RowCount to 7.
FIGURE 11.11. The TDBCtrlGrid object on the form of the CtrlGrid application.
When displaying a grid on a form, it often helps to add a DBNavigator control to the form so the user can easily iterate through records without using the scrollbar. I like to place the DBNavigator on a panel at the bottom of the form.
When arranging the grid and other components on the form at runtime, it sometimes helps to respond to the OnResize event for the form:void __fastcall TForm1::FormResize(TObject *Sender)
Grid->Left = 0;
Grid->Top = 0;
Grid->Width = ClientWidth;
Grid->Height = ClientHeight - Panel1->Height;
The code shown here arranges the grid so that it reaches from the top left of the form, all the way to the right of the form and down to the top of the panel on which the DBNavigator resides. The preceding few simple lines of code will automatically be called whenever the form is resized by the user, thereby guaranteeing that all the components stay properly arranged.
The DBCtrlGrid component doesn't bring any new functionality to BCB. It's useful, however, because it eliminates the need to have the user slide the scrollbar back and forth on a TDBGrid object. In other words, the TDBGrid object sometimes forces you to use the scrollbar in order to view all the fields of a record. That can be inconvenient, but the ability to view multiple records at once is so valuable that users are willing to put up with the minor annoyance of the scrollbar. The point of the TDBCtrlGrid object is that it lets you view multiple records at one time, while eliminating the need to scroll back and forth when viewing the data. It's hardly earth- shattering in its importance, but it can be very useful under some circumstances. It's a way to make the presentation of your data potentially more viable to the user.
In this chapter you learned some fairly sophisticated methods for displaying the data from multiple tables. In particular, you saw how BCB handles the key features of a relational database.
The tools discussed in this chapter include the following:
The components discussed in this chapter include the following:
The properties discussed in this chapter include the following:
Good database programmers will find that there is a considerable amount of hidden power in the TField object and in the Fields Editor, as well as the other tools and components mentioned in this chapter.


text:
Gear train
|It has been suggested that this article be merged with Gear ratio to Gear train and ratio. (Discuss) Proposed since December 2013.
|This article duplicates the scope of other articles, specifically, Gear ratio. (December 2013)
A gear train is a mechanical system formed by mounting gears on a frame so that the teeth of the gears engage.
Gear teeth are designed to ensure the pitch circles of engaging gears roll on each other without slipping, providing a smooth transmission of rotation from one gear to the next.[1]
The transmission of rotation between contacting toothed wheels can be traced back to the Antikythera mechanism of Greece and the south-pointing chariot of China. Illustrations by the Renaissance scientist Georgius Agricola show gear trains with cylindrical teeth. The implementation of the involute tooth yielded a standard gear design that provides a constant speed ratio.
Features of gears and gear trains include:
- The ratio of the pitch circles of mating gears defines the speed ratio and the mechanical advantage of the gear set.
- A planetary gear train provides high gear reduction in a compact package.
- It is possible to design gear teeth for gears that are non-circular, yet still transmit torque smoothly.
- The speed ratios of chain and belt drives are computed in the same way as gear ratios. See bicycle gearing.
Mechanical advantage[edit]
Gear teeth are designed so that the number of teeth on a gear is proportional to the radius of its pitch circle, and so that the pitch circles of meshing gears roll on each other without slipping. The speed ratio for a pair of meshing gears can be computed from ratio of the radii of the pitch circles and the ratio of the number of teeth on each gear.
The velocity v of the point of contact on the pitch circles is the same on both gears, and is given by
where input gear A has radius rA and meshes with output gear B of radius rB, therefore,
where NA is the number of teeth on the input gear and NB is the number of teeth on the output gear.
The mechanical advantage of a pair of meshing gears for which the input gear has NA teeth and the output gear has NB teeth is given by
This shows that if the output gear GB has more teeth than the input gear GA, then the gear train amplifies the input torque. And, if the output gear has fewer teeth than the input gear, then the gear train reduces the input torque.
If the output gear of a gear train rotates more slowly than the input gear, then the gear train is called a speed reducer. In this case, because the output gear must have more teeth than the input gear, the speed reducer amplifies the input torque.
Analysis using virtual work[edit]
For this analysis, we consider a gear train that has one degree-of-freedom, which means the angular rotation of all the gears in the gear train are defined by the angle of the input gear.
The size of the gears and the sequence in which they engage define the ratio of the angular velocity ωA of the input gear to the angular velocity ωB of the output gear, known as the speed ratio, or gear ratio, of the gear train. Let R be the speed ratio, then
The input torque TA acting on the input gear GA is transformed by the gear train into the output torque TB exerted by the output gear GB. If we assume, that the gears are rigid and that there are no losses in the engagement of the gear teeth, then the principle of virtual work can be used to analyze the static equilibrium of the gear train.
Let the angle θ of the input gear be the generalized coordinate of the gear train, then the speed ratio R of the gear train defines the angular velocity of the output gear in terms of the input gear, that is
The formula for the generalized force obtained from the principle of virtual work with applied torques yields[2]
The mechanical advantage of the gear train is the ratio of the output torque TB to the input torque TA, and the above equation yields
Thus, the speed ratio of a gear train also defines its mechanical advantage. This shows that if the input gear rotates faster than the output gear, then the gear train amplifies the input torque. And, if the input gear rotates slower than the output gear, then the gear train reduces the input torque.
See also[edit]

subset: commoncrawl


text:
Publisher(s): James Irvine Foundation
Author(s): Howard, Don; Patrick Wu
Funder(s): James Irvine Foundation
Related Organization(s): Bridgespan Group
View Report (58 pages; 2.12MB; PDF)
Area of focus: Strengthen Preparation for College and Careers
Based on research by the Bridgespan Group, the report reviews the state of the "multiple pathways" approach to education, which seeks to support both strong vocational and college preparation within the same schools. The assessment is based on a framework of five core qualities that a strong field should maintain: shared identity, standards of practice, knowledge base, leadership and grassroots support, and funding and supporting policy. Conclusions were based on interviews and focus groups with key thought leaders familiar with multiple pathways. The report also offers recommendations for the multiple pathways field to become stronger and more organized and presents profiles of organizations that use a multiple pathways approach.
Key Findings and/or Recommendations
+ Exemplary programs have built awareness of the multiple pathways strategy and buy-in from key constituencies. Many believe the next step is large-scale, system-wide demonstrations.
+ School-level demonstration programs have generated promising evidence of success.
+ District, policy, and business leaders increasingly support the multiple pathways approach.
= Support from parents and students is an important component of bringing multiple pathways to scale.
= Recommendations include:
Develop a clear, precise definition of multiple pathways, messaging aligned with that definition, and a quality-control system to distinguish high-fidelity implementations
Establish large-scale, system-wide demonstrations
Work to increase state funding and create more supportive policies that would facilitate broad adoption
- Members of the field do not agree on terminology or the definition of key concepts surrounding multiple pathways.
- Research gaps are greatest in the development and dissemination of best practices and the measurement of student outcomes.
- Participants have low affiliation with the field.
- The field has few formal mechanisms for sharing knowledge and collaborating.
- The supply of trained teachers, curricula, and technical assistance is insufficient to support growing demand.
Geographic Focus: California
Subjects/Keywords: College prep; school reform; public policy; multiple pathways; college-ready; career-ready
+ Successful strategy
= Observation
– Challenge
© Foundation Center
All Rights Reserved.
Privacy Policy


text:
Event Name- OR - Select an option below
Nestled in the middle of Missouri Wine Country sits Augusta Winery. Known for our Estate Bottled, Award-Winning Wines, Friendly Staff and boutique-style tasting room. Sample wines made from grapes grown in America's First Wine Region. We'll change your mind about Missouri wine. Unless you already love it.. then we'll just confirm.


text:
Thank you. If a matching email exists we will send you an email in a few minutes with instructions on how to reset your password.
Don't forget to check your spam folder if you cannot find the email. For further assistance please contact us at firstname.lastname@example.com .Okay
An entire kitchen's worth of knifing needs--and then some--all assembled in an attractive hardwood storage block.


text:
Mission Statement
Embark Services is committed to providing customers with best-of-class curb appeal through a diverse offering of safe, professional and cost-effective services.
A service company is only as good as its people, and Embark's team members are the deep roots of our company. Our team is made up of people committed to excellence and our environment. We emphasize tree preservation, and conservation of our natural resources. Our Certified Arborists and Professional Landscapers are accredited, degreed, certified and passionate about nature.
We provide your property the necessary nurturing required so that it can thrive and flourish for many years to come. We are confident that our prices are competitive and both our services and quality will prove superior. One of our Service Line Managers will be happy to meet with you, determine services needed and give you an estimate of work to be completed. EMBARK will provide a written contract of work to be done so that there is no confusion ensuring that our clients are knowledgeable of the services to be provided and the expected results. Feel free to contact Embark directly or call 713-462-3261 to find out how our green team can help implement your next project.
Certified Arborists
Our arborists are degreed, accredited and certified to handle all aspects of tree planting, tree repair and tree maintenance. Each certification is regulated and maintained with International Society of Arboriculture. Our Certified Arborists must pass and extensive certification examination sponsored by the International Society of Arboriculture and must remain in good standing with the International Society of Arboriculture. By having Certified Arborists on the Embark Services team, Embark is able to offer higher-quality tree care to our customers, ensuring their trees are healthy and beautiful for years to come.
Landscape Professionals
Embark's Landscape Professionals are involved in analysis, planning, design and creation of stunning exterior spaces using environmentally-friendly plant materials and appropriate hardscape elements to ensure we meet your expectations. When we create a beautiful landscape, we create it to last so your property will look beautiful for years to come. From irrigation systems to specialty gardens, retainer walls to large scale landscape construction projects, Embark has the capacity, experience and resources to meet your needs and exceed your expectations.
Safety Trained Professionals
Through our award-winning training programs, Embark employees are trained to think safety from their first day on the job. Each employee must go through extensive training in safety education (both on-the-job and classroom) and are trained to be personally conscious of potential hazards on each job site. Employees are trained in First Aid/CPR, Work Zone Safety and Decision Driving. The safety of our employees and your property is always top of mind at Embark Tree and Landscape Services. Working safely is a condition of employment!


text:
The Chicago Cubs take on the Milwaukee Brewers Saturday, May 17 and instead of just catching the game and heading back to the hotel why not explore everything Wrigleyville has to offer. Hop on the Red Line, get off at Addison, and get ready to explore Cubs Land.
2014 marks the 100th birthday of one of Chicago's iconic landmarks: Wrigley Field. The ballpark has been commemorating the occasion with throwback uniforms, retro toys, specialty foods, performances, and entertainment. Get a behind the scenes look with their daily tour. On game days you don't get a look at the club houses but you do get to check out the seating bowl, bleachers, indoor batting cage, and the field. Along the way learn cool fun facts you can drop on friends like that until 2003 Wrigley Field held the record for the most NFL games played in a single stadium? If you just said out loud, "Wait, what," then check out the tour to learn more.
Wrigley Field Tour
1060 W. Addison St.
Rockit Burger Bar Venue Showcase from Rockit Ranch Productions on Vimeo.
So you probably only had orange juice before your Wrigley Field tour and at this point you're starving. Head down the block to enjoy a fried egg, applewood smoked bacon, cheddar cheese, tomato, spinach for brunch. Of course, you'll be tempted to have one Rockit Burger Bar's amazing black angus burgers. If you want to continue with the breakfast theme get the Huevos Rancheros burger, which comes with a black angus patty, fried egg, chihuahua cheese, corn tortilla strips, roasted salsa roja, jalapeño aioli all served with curly fries. Why not wash it down at their Bloody Mary bar?
Rockit Burger Bar
3700 N. Clark St.
You got a behind-the-scenes look, and now it's time to see Wrigley Field in its element. With Milwaukee being under two hours away it's inevitable that the Brewers are a rival of the Chicago Cubs. It also doesn't help that the Brewers lead the National League Central Division and the Cubs are in last place. The Cubs are just in a slump and it might take the energy of this rivalry to turn it around, but what has always made Wrigley special is that it doesn't matter how the Cubs are doing because the fans are always passionate. Its hard to beat sitting in history.
Chicago Cubs v. Milwaukee Brewers
1060 W. Addison St.
If you couldn't get tickets to the game don't worry because Cubs games don't just happen at Wrigley Field; they happen all over Wrigleyville. If you're the type that screams at the television and think you can bat better than the professionals then you might want to check out Sluggers. Along with a great beer selection, party atmosphere, and countless televisions, Sluggers houses batting cage upstairs. Yes, alcohol and batting cages don't seem like a good idea until you step into the batters box at Sluggers and ask why more sports bars don't do the same.
If you're looking for a place with an outdoor space then head over to Casey Moran's. Not only do they have an outdoor beer garden but they also have a rooftop Ivy Lounge that overlooks Wrigleyville. With this beautiful weather it's hard to say no to that.
3540 N. Clark St.
Casey Morans
3660 N. Clark St.
The activities aren't over, but pace yourself you party animal. Head back to your hotel, take a shower, close your eyes for a few minutes, and change for the night ahead.
The best way to start up your night after a restful nap is with some laughs and ComedySportz has that with a twist. Instead of the typical improv show, The ComedySportz Theatre makes their show interactive by making it part of a game. The performers are separated into two different teams and the team that gets the most laughs out of the audience wins. The content is friendly for all ages so if you have children 7 years old and up feel free to bring them along.
The ComedySportz Theatre
929 W. Belmont Ave.
After spending all day in a t-shirt with beers, it's nice to get a little gussied up and grab some cocktails at Deuce's & The Diamond Club. Take the escalator up to the Diamond bar and relax on their comfortable couches sipping on a Deuce's Sidecar, and if you need a bite to eat try their pulled pork or Cuban sandwich. Head out to the balcony with your friends and loved ones and take a look at the neighborhood you just conquered.
Deuce's & The Diamond Club
3505 N. Clark St.
Tags: Casey Morans, Chicago Cubs, ComedySportz Theatre, Deuces & The Diamond Club, Sluggers, wrigley field, Wrigley Field Tours, Wrigleyville
Normally Wrigleyville is a haven for all those who bleed "cubbie blue" or anyone wandering down Clark Street looking for a nice adult refreshment. This weekend, however, it's hosting its annual Wrigleyville SummerFest which begins Saturday, August 3rd, in the infamous northern neighborhood.
From rockers to rappers, dancers to deacons, all will be featured at this year's festival. The weekend lineup is filled with a variety of performances from local artists including Too White Crew and Hairbangers Ball as the headliners. On Sunday be sure to join the worshiping sponsored by Bethel Lutheran Church on Seminary Avenue at 11 a.m. (perhaps for the sinning you plan to do at the festival, like indulging in Fannie May candies). The food vendors are set to provide a plethora of food from some of the neighborhood's popular digs like John Barleycorn and Moe's Cantina alongside local vendors. Bring the little ones as well to the Kids' Zone which has 26' slide, obstacle course, face painting and more.
The festival begins on 12 p.m. to 10 p.m. on Saturday and ends on Sunday from 12 p.m. to 9 p.m at 3300 N. Seminary Avenue. There is a $5 suggested donation with proceeds benefitting local organizations and ministries. For more information visit www.wrigleyvillesummerfest.com.
Tags: Chicago Festivals, Wrigleyville, Wrigleyville Summerfest
Looking for a fun distraction that takes you away from the already photographed skyscrapers of Chicago's downtown? If so then find time to head over to SausageFest Chicago in the Lake View neighborhood this Saturday and Sunday (June 15th and 16th), where a catalog of macho meats await.
Returning for its third year, SausageFest Chicago hosts an arrangement of exclusive vendors that continue to display their craftsmanship in an open market setup. Masters in every sense of the word, these retailers will best any stomach.
Known by its association with the historic Wrigleyville area, SausageFest Chicago is accessible by taking the Red Line to the "Addison" stop on the CTA's "L" system. Hopeful attendees/commuters will find themselves enticed by the hints of smoke coming from West Addison Street. Live entertainment is also included, providing a backdrop to the sizzling grills. These sights and sounds, coupled with some tickets to the exciting matchup between the Cubs and Mets this weekend, make for a unique evening in Wrigleyville.
For those feeling a bit more masculine than the rest of us, the title for "Sausage King of Chicago" is up for grabs and is fit with an entire crowning ceremony. The title encourages at both times boasting and appreciation among only the most serious of meat lovers. In some respects it has the ability to rival even the most deserving of awards.
Let it all go once in a while. This weekend allow yourself to indulge in the likes of kielbasa, polish sausages, BBQ pork, hot dogs, roasted duck and the rest that are sure to be as hearty as you are faithful in being a carnivore. The ridiculousness that is sure to ensue and the comfort of taking it easy for a day in Wrigleyville make SausageFest Chicago worthwhile. Running from noon to 10 p.m. the festivities should be convenient and rewarding for families, friends and even the occasional passerby.
Tags: Annual Events, Chicago Cubs, Lake View, New York Mets, Sausage King of Chicago, SausageFest Chicago, Street Festivals, Summer in Chicago, wrigley field, Wrigleyville
Get ready for the first pitch of the season on the North Side on Thursday, April 5, as the Chicago Cubs take on the Washington Nationals at the Friendly Confines. Whether you're looking to pre-game, post-game or just kick back a while watching outside the ballpark, here's a roundup of local spots that bleed Cubbie blue.
The Cubby Bear: This venue, located right across the street from Wrigley Field, is a great spot to hang out or have a post-game party. Enjoy live music or watch the game on one of their five projection screen TVs or 75 plasma TVs throughout the bar. It's ensured that you won't miss any part of the action. And, hey, you're just a long foul ball's distance from the ballpark; so the atmosphere can't be beat.
The Sports Corner: This tavern and grill has been a neighborhood favorite since opening 1976. Enjoy a nice draft and a burger fresh off of the grill while you take in 9-innings and welcome the spring/summer season!
Vines on Clark: For a more intimate spot to watch the game, head up to Vines on Clark. They have an outside patio and a spacious dining room dotted with flat screen TVs, this offers up a less pub-like game-watching experience.
Casey Moran's: They guarantee no bad seat in the house – sounds like a reason to take in the game to me! Watch the game on their 30, 52-inch plasma TVs throughout the entire bar and even out on their garden café. Hungry? Casey Moran's offers up juicy burgers, cold salads, and even sushi!
Rebel: If you're feeling rebellious and want a great nightlife experience after the game, this is the place to be. Rebel Bar and Grill is a year-round destination that features daily food and drink specials, private party rooms, and excellent service! Head there after the game to loosen up and dance!
Benchmark: Located in Old Town, this chic tavern is Chicago's "first convertible beer garden", meaning that the highlight of this venue is its fully-retractable roof. There are 46 TVs, so you won't miss any of Dempster's pitches!
Kirkwood: This is a one-of-a-kind bar offering plenty of seating on their expansive patio (complete with TVs) and cozy, shady spots courtesy of their trees and umbrellas. If you're with a group of friends, you're in luck! Kirkwood has sharable drinks and dishes like fishbowls and skillet cookies!
Schoolyard Bar: Head to Lakeview for upscale bar food and catch the game inside or out in this cozy, small bar. Got the kids in tow? No worries! This is a family-friendly destination, so bring them along!
Tags: Baseball in Chicago, Chicago Cubs, Cubs Bars, wrigley field, Wrigley Field Tours, Wrigleyville
Thank you North Side patrons, media enthusiasts, and restaurant reviewers! It's finally the time where we can all settle our cravings and have more quality burger selections at affordable prices!
Rockit Ranch Productions recently announced the transformation of Rockit Wrigley to Rockit Burger Bar, taking place on the Chicago Cubs Opening Day on April 5.
It's also perfect timing for the expansion of Rockit's burger selection, having recently received best burger praise from "Good Morning America", among others. Chef Amanda Downing welcomes the new season—and new menu—by indulging patrons with mouth-watering gourmet burgers of all kinds!
Sink your teeth into creative burgers like Downing's "The Hottie" made with breaded, cream cheese-stuffed peppers on top of a patty with pepper jack cheese and ghost pepper aioli or the "Johnny Apple Burger" featuring gala apples, melted brie, apple wood-smoked bacon and pecan aiolito.
Rockit Burger Bar's hours: Tuesday-Thursday from 5 p.m. to 12 a.m.; Friday from 5 p.m. to 2 a.m.; Saturday from 10:30 a.m. to 3 a.m.; and Sunday from 10:10 a.m. to close. Weekend brunch will be available Saturday and Sunday from 10:30 a.m. to 4 p.m. Rockit Burger Bar will also open two hours prior to every Cubs home game.
Tags: Best Burgers, Chicago Cubs, Rockit Burger Bar, Rockit Ranch Productions, Rockit Wrigley, Wrigleyville
You know how some people, when asked what their favorite type of music is, answer "anything but country"? Well, those people are missing out.
Casey Moran's, at 3660 N. Clark St., will be hosting Casey's Country Night every Friday starting in November. The tavern will have DJs playing the best country music, and better yet they will take requests. You can sing along to country favorites with everyone at the bar on a Friday night in Chicago's Wrigleyville neighborhood.
Other than the music, Casey Moran's has some deals, too. Pabst Blue Ribbon tall boys are $3, and Jeremiah Weed Lemonades and Wild Turkey Shots are $5.
And you can take part (by either entering or judging) in the "Best Lookin' Cowgirl" contest, where winners will receive free parties and gift certificates. But really, everyone who attends Country Night will be a winner for those contests.
Casey Moran's has a very eclectic menu, with standard pub fare like burgers, wraps and sandwiches, but they also have Panini and sushi. The tavern has drink specials on Thursdays and Saturdays as well, plus they have thirty 52-inch plasma TVs, an outdoor bar and rooftop garden (weather permitting).
Tags: Casey Morans, Chicago Bars, Chicago Drink Deals, Chicago Events, Country Night, Wrigleyville
While it seems that other Major League teams are moving into a new stadium every few decades, the Chicago Cubs have been playing ball at Wrigley Field since 1916. Unlike the other sterile, brand new, state-of-the-art ballparks, Wrigley has an inordinate amount of charm and history.
And you can take it all in with a behind-the-scenes tour.
Wrigley Field Tours will be running through the end of October, virtually every day. Tours are held rain or shine, and whether or not the Cubs have a home game that day. On most September days, sessions mainly occur every hour from around 9 or 10 a.m. to 3 or 4 p.m. October is a little different, as there will only be two or three tours per weekday, mostly in the afternoons. Weekends will still have hourly tours most days from 10 a.m. to 4 p.m. The full schedule is available on Cubs.com.
For $25 (or less, if you attend with a group of 20 or more), you will get to see everything that makes Wrigley special. The 75-minute session lets fans see the Cubs clubhouse, visiting clubhouse, press box, bleachers, dugouts, on-deck circles and a part of the field. Wearing a Cubs jersey and reliving dreams of being Ryne Sandberg is optional.
The tours are a hit (no pun intended). Visitors love stepping on the field, and, as Samantha Morris, the Coordinator of Tour Experiences says, "being able to sit in the same dugout where Ernie Banks did, stepping in the same on-deck circle just like Aramis Ramirez and taking in Wrigley from the best vantage point around is usually a once in a lifetime experience."
Ironically, the most unique part of the tour is going into the visitors' clubhouse, where the likes of Babe Ruth, Hank Aaron, Vince Lombardi's Green Bay Packers, Paul McCartney and Elton John have prepared for their baseball game, football game, or concert.
In fact, it's not impossible for some real major leaguers to be present on game day. Tour groups may see broadcasters getting ready on game days, and you just may see players getting their cuts in the batting cage before the first pitch.
The tour involves a good deal of walking (1.5 miles) with parts of it outdoors, so dress accordingly. Cameras and video cameras are acceptable for personal use.
Guests are encouraged to buy their tickets in advance, and one can do that by ordering from Cubs.com, calling 800-THE-CUBS, or visiting the Wrigley Field Box Office at the ballpark.
Complementary parking is available for tour guests on non-game days in the purple lot on the southeast corner of Clark Street and Waveland Avenue. The best way to get to the park by public transportation is to take the Red Line to the Addison stop, or the Brown Line to the Southport stop and walk a few blocks northeast to the park.
Tags: Baseball, Chicago Cubs, tours, wrigley field, Wrigleyville
One of the latest additions to the Wrigleyville landscape, Club 162 (3551 N. Sheffield Ave.) will be broadcasting the much-anticipated bout as Manny Pacquiao defends his welterweight belt against former champion Shane Mosley on Saturday, May 7 at 8 p.m. CST.
Boasting 53 high definition televisions, including an impressive 10-foot-screen, Club 162 will open their doors at 7:30 p.m. The $15 cover charge includes one complimentary drink and half priced appetizers throughout the night. Club 162 will accept table reservations for groups (there is a $125 minimum per table).
Located just a stone's throw from Wrigley Field, the Chicago Cubs will be playing an afternoon game against the Cincinnati Reds, so make sure to reserve your table now by calling 773-327-7800.
Tags: Boxing, Club 162, Manny Pacquiao, Pacquiao vs. Mosley, Shane Mosley, Wrigleyville
You'll never look at a box of mac & cheee the same way again. In fact after Mac Madness Week at Rockit Bar & Grill, you will gain an entirely new appreciation for this universal comfort food.
Beginning March 14th and coinciding for March Madness Week, the second annual Mac Madness kicks off with Chef James Gottwald and Chef De Cuisine Amanda Downing concocting delicious variations of this baked pasta and cheesy delight for every day of the week.
Expect to see unique variations like French Onion Mac, with caramelized onions and Gruyere cheese; Oyster Mac-a-Fella, with creamed spinach, fried oysters and pecorino; and in lieu of St. Patrick's Day, Rockit will be serving up Corned Beef & Cabbage Mac, with Irish Cheddar and whiskey mustard cream.
Mac Madness runs from March 14 through 18 at Rockit's River North location (22 W. Hubbard Street) and from March 17-19 at the Wrigleyville location (3700 N. Clark Street).
Tags: comfort food, culinary event, mac n' cheese, march madness, restaurant, River North, Rockit Bar & Grill, Wrigleyville
If you thought every bar in Wrigleyville was just about beer and baseball, think again. You don't have to be a wine snob to appreciate a good glass. Mix and mingle with Food and Wine Magazine's "Winemaker of the Year," Charles Smith on April 19 at Rockit Wrigley. The grape guru is will be leading "Taste It," a Monday tradition featuring guest wine makers from around the world and of course, tastes of their favorite vintages. If a taste isn't enough, Rockit Wrigley will be offering 50% off all Smith Wines bottles and glass pours every Monday in April. Cheers!
Tags: Bars, Rockit Bar & Grill, Wine, wine events, Wrigleyville