mvc : http://leepoint.net/notes-java/GUI/structure/40mvc.html
Delegate - Model :
Model View Controller Pattern
Table 1. Common Swing Event Listener Methods |
Design :
When to Use Dialog Boxes or Windows :
| Type | Description | Use | Example |
|---|---|---|---|
| Modal | Dialog box | Presentation of a finite task | File Open dialog box, Save As dialog box |
| Modeless | Dialog box | Presentation of an ongoing task | Search dialog box, History List dialog box, Task List dialog box |
| Application window | Window frame with document (child) windows contained within | Presentation of multiple instances of an object, Comparison of data within two or more windows | Word processor, Spreadsheet |
| Document window | Modeless dialog box or document window contained within and managed by the application window | Presentation of multiple parts of an application | Multiple views of data (sheets) |
| Secondary window | Primary window of a secondary application | Presentation of another application called from the parent window | Help window in an application |
List of Reserved Words
| Meaning And Behavior | Appears on Button | Appears on Menu | Mnemonic Keystrokes | Shortcut Keystrokes | |
|---|---|---|---|---|---|
| OK | Accept the data entered or acknowledge the information presented and remove the window | Yes | No | None | Return or Enter |
| Cancel | Do not accept the data entered and remove the window | Yes | No | None | Esc |
| Close | Close the current task and continue working with the application; close the view of the data | Yes | Yes | Alt+C | None |
| Exit | Quit theapplication | No | Yes | Alt+X | Alt+F4 |
| Help | Invoke the application's help facility | Yes | Yes | Alt+H | F1 |
| Save | Save the data entered and stay in the current window | Yes | Yes | Alt+S | Shift+F12 |
| Save As | Save the data with a new name | No | Yes | Alt+A | F12 |
| Undo | Undo the latest action | No | Yes | Alt+U | Ctrl+Z |
| Cut | Cut the highlighted information | No | Yes | Alt+T | Ctrl+X |
| Copy | Copy highlighted information | No | Yes | Alt+C | Ctrl+C |
| Paste | Paste the copied or cut information at the insertion point | No | Yes | Alt+P | Ctrl+V |
Reserved Icons
| Meaning and Behavior | Used to Identify an Application | Used to Identify a Function | Reserved Word Text Label |
|---|---|---|---|
| Information message | No | Yes (identifies an information message box) | None |
| Warning message | No | Yes (identifies a warning message box) | None |
| Question message | No | Yes (identifies a question message box) | None |
| Error message | No | Yes (identifies an error message box) | None |
Guidelines for Using Controls
| Control | Number of Choices in the Domain Shown | Type of Control |
|---|---|---|
| Menu bar | Maximum of 10 items | Static action |
| Pull-down menu | Maximum of 12 items | Static action |
| Cascading menu | Maximum of 5 items, 1 cascade deep | Static action |
| Pop-up menu | Maximum of 10 items | Static action |
| Push button | 1 for each button, maximum of 6 per dialog box | Static action |
| Check box | 1 for each box, maximum of 10 to 12 per group | Static set/select value |
| Radio button | 1 for each button, maximum of 6 per group box | Static set/select value |
| List box | Maximum of 50 in the list, display 8 to 10 rows | Dynamic set/select value |
| Drop-down list box | Display 1 selection in the control at a time, up to 20 in a drop-down box | Dynamic set/select single value |
| Combination list box | Display 1 selection in the control at a time in standard format, up to 20 in a drop-down box | Dynamic set/select single value; add a value to the list |
| Spin button | Maximum of 10 values | Static set/select value |
| Slider | Dependent on the data displayed | Static set/select value in range |
Applying Design Principles
Understanding the principles behind good GUI design and applying them to your applications can be a challenge. Let's examine an application to see how these principles can result in an improved interface.
Part 1: Exploring a GUI in Need of Redesign The interface in Figure 5 is used by an ambulance-dispatching company to maintain customer data, provide billing information, and dispatch ambulances. The application was a port from a character-based system and contains several design errors that affect the user's performance with this mission-critical application. Keep in mind that GUI ease of use and clarity is especially important in a critical application such as this where the rapid handling of a request can make the difference between life and death. Here is what is wrong with this screen:
Figure
- Too many functions at the top level. The users requested that the new application provide all of the information at their fingertips. This results in the screen being used for both customer maintenance and ambulance dispatching. If you enter extensive customer information and then press the Update button, the record is updated. However, if you enter minimal customer information, such as social security number, diagnosis, from-location, and to-location, and then press the Trans button, an ambulance will be dispatched. Updating and dispatching functions need to be in separate dialog boxes.
- Too many buttons. The buttons along the right should be on the application's parent window, possibly in a toolbar, but not on this child window.
- Poor navigational assistance. GUI controls should be positioned according to frequency of use. The most important field should be in the upper left; the least important field should be in the lower right. It's hard to imagine how the company and invoice number could be the most important fields when dispatching an ambulance.
- Inappropriate use of controls. The designer chose to use text labels rather than group boxes to identify which groups of data would be placed in the boxes. This many group boxes with text labels in these positions makes the screen appear convoluted and makes it difficult to distinguish the data from the labels. Also, the editable fields should be identified with a box around them, so that it is intuitively obvious which fields can be changed.
- Lack of symmetry. Just lining up fields, group boxes, and buttons will make this GUI much easier to use. Our brains like order, not chaos.
AWT Figure :
Two groups of classes are involved in a GUI program:
- Component classes: Components are elementary UI entities such as buttons, labels, text fields, and check boxes.
- Container classes: Containers, such as frames and panels, are used to hold UI components. A container can also hold containers.
Frame is a "window", which has a title bar (containing program icon, title, minimize, maximize/restore-down and close buttons), an optional menu bar and program display area.An AWT
Dialog is a pop-up window used for interacting with the users. A Dialog has a title-bar (containing program icon, title and close button) and a display area.AWT also provide a secondary container called
Panel. A Panel is an invisible retangular box under a higher-level container, used to group and layout a set of related UI components.Event-Driven Programming
In an event-driven programming model, a piece of event handling codes will be executed whenever an event is triggerred (such as mouse-click and enter-pressed).Three objects are involved in event-handling:
- a source object,
- a listener object, and
- an event object.
Steps for Events :
1- Firstly, we need to declare an interface called
XxxListener (e.g., MouseListener) containing the signature of the handler methods for all the operational modes. Recall that an interface contains only abstract methods.2 - Secondly, all the listeners interested in the
XxxEvent must implement the XxxListener interface. That is, the listeners must provide their own implementations (or programmed responses) to all the abstract methods declared in the XxxListener interface.3 - Source object
to maintain the list of listener objects, and define two methods: addXxxListener() and removeXxxListener() to add and remove a listener from this list.Event Concept Link :
http://javadude.com/vaj/general/understandingGeneratedCode.html
JTable
1. JTable: Controls the visual presentation of the data, however, it has limited control over where the data comes from. In the simplest of circumstances, the JTable can populate itself with data only if the data is static and doesn't come from a database. In the above case, it can be used without any supporting classes.
Usually the data to be populated into a JTable comes from a database. In this case, the JTable must work with the JTableModel class; rarely does it work alone.
2. AbstractTableModel: Controls where the data comes from and how it behaves within the JTable. Although the data can come from just about anywhere, this class is almost always used when the data comes from (via JDBC) a database. AbstractTableModel is used by an extension class that you define and is never used directly. It has some interesting default methods, similar to listeners, that automatically fire when data is altered. Knowing when and how these methods are fired is important to understanding how the AbstractTableModel works. I'll discuss them later.
3. TableModelListener: Not really a class but an interface that's used when you want some action to be performed while the user is interfacing with the data in the JTable. It's misnamed - in general, listeners are automatically fired when some action is performed on a control. A programmer-defined class that implements the TableModelListener isn't really a listener at all as it's never automatically fired. It must be manually fired by the programmer via the fireTableDataChanged method for the TableModel. Since TableModelListeners must be fired manually, they can't be considered a true listener. Not that they aren't valuable. I think the powers that be at Sun purposely made it this way so the programmer controls when the listener is fired.
The code placed in the listener can vary. Popular uses include adding code to update the database. Note: The use of this interface is optional. If you don't really care when data is changed, you don't have to use it.
Figure 1 illustrates the relationship between the three major players.
Part 1 focused on the use and implementation of the JTable. Part 2 covers the use of the table model in depth.
The AbstractTableModel Class
As stated earlier, the job of the AbstractTableModel is to provide the source for the data in the JTable and some built-in methods that are automatically fired when certain things happen within the Table (more on this later). This class isn't used directly but as an ancestor in a programmer-created class. When a programmer-defined class extends the AbstractTableModel class, in Javaspeak we're using a Table Model.
The Table Model can get the data from several places. I'll start with a simple Table Model example and get more complex as we go. Your Table Model might obtain its data in an array, vector, or hashtable, or it might get the data from an outside source such as a database (the most flexible and most complex). If needed, it can even generate data at runtime. In Listing 1 (Listings 1-6 can be found on the JDJ Web site, www.JavaDevelopersJournal.com), the Table Model populates itself with an array of strings. The resulting frame window is displayed in Figure 2.
Table Model Dissected
There are a few immediate advantages to using a Table Model instead of using the JTable alone. In addition to the data, the Table Model informs the JTable of the data type of the column, which leads to some desired formatting behavior. For example, since the JTable knows that a column is numeric, it'll right-justify it within the cell. Also, Boolean values will display as a checkbox (since there are only two possible values). Since the JTable is using a Table Model, it'll display the data in a user-friendly way. In Listing 2 the application doesn't use a Table Model. Notice the difference in how the data is represented (see Figure 3).
Table Model Methods
The methods shown in Listing 1 are used internally by the Table Model. Similar to listeners, these methods are fired when needed, and the programmer can choose to implement them at will. You can use all or none of them; Java doesn't care. The important point is that these methods are fired automatically and usually never by the programmer. Similar to other automatically fired methods, Java lets you decide what kind of code to execute. Java provides the method, but you (surprise!) decide the functionality. The automatic table methods are described below.
getColumnCount() This method is fired once - at the time the JTable is created. Its sole purpose is to tell the JTable how many columns it has. If the data to be passed to the JTable is contained in an array, simply return its length (see Listing 1). If the data comes from a database, the ResultSetMetaData class can determine how many columns exist by using the following code:
// myResultSet is of the Java type ResultSet and has already been
// filled with data in the program.
int li_cols;
ResultSetMetaData myMetaData = myResultSet.getMetaData();
li_cols = myMetaData.getColumnCount();
return li_cols;
getRowCount()
As with getColumnCount(), this method is fired once - at the time the JTable is created. Its sole purpose is to tell the JTable how many rows it has. If the data to be passed to the JTable is contained in an array, simply return its length (see Listing 1). If the data comes from a database, there are a few options. Later I'll show how to populate database data into a vector. The following code can be used to return the number of rows in a vector. I'll show you how to populate it later.
// Get the number of rows that will be used to build the JTable. allRows if of the Java type vector
return allRows.size();
getColumnName(int col)
After the Table Model fires off the getColumnCount() method, it calls this method for each column that's been found. For example, if the getColumnCount() method returned a seven (for seven columns), the Table Model will fire this method seven times, passing it the number of the column that it must resolve the column name for each time. The method returns a string that represents the column name. For example:
// colnames is an array of Strings representing the column names for
// the JTable.
return colNames[col];
getValueAt(int row int col)
After the Table Model determines the number of rows and columns, it needs to populate them with data. This is the job of the getValueAt function, which is called for every column and row intersection (called a cell) that exists in the Table Model. If there are 30 rows with five columns, this method will automatically be fired 150 times - one for each cell. As in the previous methods, you determine which code is executed in this method. Remember, Java provides the method, you provide the functionality. Listing 1 returns objects from a two-dimensional array. The following code can be used if you're returning data from a vector.
// row and allrows are vectors
row = (Vector) allRows.elementAt(row);
return row.elementAt(col);
getColumnClass(int col)
This method is automatically fired and gets the class for each column of the Table Model. The following code gets the data type of each column.
// Return the class for this column
return getValueAt(0, col).getClass();
Next, the table compares the column's data type with a list of data types for which cell renders (more about renders in Part 3) are registered. The default classes are:
- Object: Left-aligned label that displays the object's string value
- Boolean: Displayed as a check box
- mageIcon: Displayed by a centered label
- Number: Displayed as a right-aligned label
isCellEditable(int row, int col)
This method determines which rows and columns the user is allowed to modify. Since this method returns a Boolean, if all cells are editable it simply returns a true. To prevent a JTable from editing a particular column or row value, it returns a false from this method. The following code enables only column one to display while allowing the rest of the columns to be modified.
// Make column one noneditable
while allowing the user to edit at
all // other columns.
If (col == 1){
return false;
}
else{
return true;
}
public void setValueAt(Object value, int row, int col)
When the user makes changes to an editable cell, the Table Model is notified via this method. The new value, as well as the row and column it occurred in, is passed as arguments to this method. If the original data is coming from a database, this method becomes important. As you'll see, data retrieved from a database is held locally within the Table Model, usually as vectors. When the user changes a cell value in a JTable, the corresponding data in the Table Model isn't automatically changed. It's your responsibility to add code in this event to ensure that the data in the Table Model is the same as the data in the JTable. This becomes important when code is added to update the database. The following code updates the data (held in an array of objects) in the Table Model with the new value that the user just entered in the JTable.
// Update the array of objects with
the changes the user has just entered in a cell.
Then notify all listeners (if any) what column
and row has changed. Further processing may take place there.
rowData[row][col] = value;
fireTableDataChanged();
Taking the Model Further
I have a promising example of Table Models that use JDBC as their data source. By far the most powerful implementation of the JTable with a Table Model uses data from a database (via JDBC). The good news is that adding the JDBC element doesn't significantly change what we've already learned. Admittedly, it does add more complexity to our task, but it doesn't change the way we treat the JTable/Table Model relationship.
Where Does the Data Come From?
Any program written in Java uses JDBC to connect to a database. JDBC is a complex topic in its own right so I'll save the comprehensive overview for a later date. Here I'll explain the relationship between the Table Model and JDBC. The examples will use a Microsoft Access database that can be reached via a locally configured ODBC data source. For simplicity, the data contained within the database will be the same as the data in the previous examples that used arrays as the data source.
Creating the Table Model
The first step is to declare the Table Model and all instance variables that will be needed. All classes needed as well as a description of the instance variables are included in the code below:
// Table Model
import javax.swing.table.AbstractTableModel;
import java.sql.*;
import java.util.Vector;
public class MyTableModel extends AbstractTableModel {
Connection myConnect; // Hold the JDBC Connection
Statement myStatement; // Will contain the SQL statement
ResultSet myResultSet; // Contains the result of my SQL statement
int li_cols; // Number of database columns
Vector allRows; // Contains all rows of the result set
Vector row; // Individual row for a result set
The next step is to add a constructor to the class. The code in the example constructor is broken into two functions. One secures a connection to the database while the other performs the retrieval. This division of functionality is intentional. When this class is instantiated we want to perform the database connection and the data retrieval. However, during the life cycle of this object, we're only interested in the data retrieval since the connection needs to be performed only once. That's why the connection logic is separate from the retrieval logic. For the sake of simplicity, this example is only interested in retrieving data. The actual update of the data will be a topic in Part 3. The code for the constructor is:
public MyTableModel() {
//connect to database
try{
connect();
getData();
}
catch(Exception ex){
System.out.println("Error!");
}
}
The first method called within the constructor provides the connection to the database. The primary purpose of this method is to provide a connection to our ODBC data source. Since our connection (myConnect) is an instance variable, the database can be accessed as long as it's instantiated.
void connect() throws SQLException{
try{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}
catch(ClassNotFoundException cnf){
System.out.println("Driver does not exist!");
}
String url = "jdbc:odbc:BradyGirls";
myConnect = DriverManager.getConnection(url);
}
The second method called in the constructor retrieves data from the database and puts it into a vector (see Listing 3). I won't go into detail about vectors as they're a topic in their own right. Vectors are basically "growable" arrays and can also hold any type of object. The function uses two vectors: one holds an individual row of the result set (result of the SQL statement), the other contains all the "row" vectors and is essentially the "vector of vectors." When we connect a JTable with this table model, the vectors will be used to provide the data to be displayed within the table.
The last portion of the Table Model involves the automatic Table Model methods. The code placed in these methods is important. It informs the JTable about the contents of the data and what the data looks like. The automatic Table Model methods are provided in Listing 4.
Building the JTable
If you followed the steps provided, you have a Table Model ready to be used. The next (and last) step is to write a Java program that will use it. Listing 5 instantiates a JTable based on the Table Model that's just been built. Listing 6 contains the completed Table Model.
Conclusion
You should now have a basic understanding of how custom Table Models work and how to use JDBC within a Table Model. We're still far away from what I'd consider a robust application, however. Next month I'll focus on listeners and updating the database with changes to the data.
You have to provide your own TableCellRenderer implementation if you want to display buttons in a JTable. The TableCellRenderer interface contains only one method, getTableCellRendererComponent(...), which returns a java.awt.Component that knows how to draw the contents of a specific cell. Usually, getTableCellRendererComponent() will return the same component for every cell of a column, to avoid the unnecessary use of extra memory. But when the contents of a cell is itself a component, it is all right to return that component as the renderer. Therefore, the first step towards having JButtons display correctly in a JTable is to create a TableCellRenderer implementation that returns the JButton contained in the cell being rendered. In the accompanying code listing, JTableButtonRenderer demonstrates how to do this.
Even after creating a custom TableCellRenderer, you're still not done. The TableModel associated with a given JTable does not only keep track of the contents of each cell, but it also keeps track of the class of data stored in each column. DefaultTableModel is designed to work with DefaultTableCellRenderer and will return java.lang.String.class for columns containing data types that it does not specifically handle. The exact method that does this is getColumnClass(int column). Your second step is to create a TableModel implementation that returns JButton.class for cells that contain JButtons. JTableButtonModel shows one way to do this. It just returns the result of getClass() for each piece of cell data.
At this point, you're almost done, but not quite. What's the use of putting a JButton in a JTable if you can't press the darn thing? By default, JTable will not forward mouse events to components contained in its cells. If you want to be able to press the buttons you add to JTable, you have to create your own MouseListener that forwards events to the JButton cells. JTableButtonMouseListener demonstrates how you could do this.
Yep, you're done now.
Listing A contains the code for a simple example that demonstrates common JTable behavior. The user can change JTable’s layout, dragging and dropping columns or resizing them by dragging the line that separates them in the header.
The columns are stored in a String array:
String[] columnNames = {"Product","Number of Boxes","Price"};
The data is initialized and stored in a two-dimensional object array:
Object[][] data = {
{"Apples", new Integer(5),"5.00"},
{"Oranges", new Integer(3),"6.00"},
{"Pears", new Integer(2),"4.00"},
{"Grapes", new Integer(3),"2.00"},
};
The JTable is constructed using data and columnNames:
JTable table = new JTable(data, columnNames);
Viewing the JTable
The height and width of the JTable are set as follows:
table.setPreferredScrollableViewportSize(new Dimension(300, 80));
If one of the JTable columns or the JTable window itself is resized, the other columns might react by shrinking or enlarging to fill the new dimensions. It's possible to control this behavior using the setAutoResizeMode() method:
table.setAutoResizeMode(int mode);
Possible values of the mode integer field are:
AUTO_RESIZE_OFF
AUTO_RESIZE_NEXT_COLUMN
AUTO_RESIZE_SUBSEQUENT_COLUMNS
AUTO_RESIZE_LAST_COLUMN
AUTO_RESIZE_ALL_COLUMNS
Table defaults
The default color for the cells’ gridlines is Color.gray. To change the color of these gridlines, use:
table.setGridColor(Color.black);
You can change row heights with:
table.setRowHeight(int pixelHeight);
The height of the individual cells will be equal to the row height minus the height of the row margins.
By default, the selection background and foreground colors are determined by Swing's look-and-feel implementation. You can change selection colors using:
table.setSelectionBackground(Color.black);
table.setSelectionForeground(Color.white);
You can also hide the cells’ gridlines, like this:
table.setShowHorizontalLines(false);
table.setShowVerticalLines(false);
Figure A depicts a JTable that has its horizontal gridlines hidden.
Figure A
Column widths
The JTable component has several classes and interfaces that represent the characteristics of the table. TableColumn keeps track of the column’s width and manages the resizing of columns, including maximum and minimum widths.
TableColumnModel manages collections of TableColumns as well as column selections. To set a particular column’s width, get a reference to the table’s column model. Then, get the desired TableColumn and call its setPreferredWidth() method:
TableColumn column = table.getColumnModel().getColumn(0);
column.setPreferredWidth(100);
When the user drags and drops columns, the column’s index is not changed. The getColumn(0) method will always return the correct column, no matter where it appears on the screen.
Headers
JTableHeader handles the display of JTable headers. You can subclass JTableHeader to get a customized layout. For example, if your application needs a header that spans multiple columns, simply subclass JTableHeader and incorporate it into your JTable.
You can specify whether the reordering of headers is allowed by getting a reference to the current JTable’s JTableHeader and calling its setReorderingAllowed() method:
table.getTableHeader().setReorderingAllowed(false);
Similarly, you can ensure that columns cannot be resized by dragging between the column headers. To do this, you use the setResizingAllowed() method:
table.getTableHeader().setResizingAllowed(false);
Selection modes
By default, when a user selects a cell in a JTable, the entire row is selected. There are several techniques for customizing the way a user can make selections. You can allow the user to select individual or multiple rows using the ListSelectionModel interface:
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
ListSelectionModel has the following fields:
SINGLE_SELECTION allows the selection of one row at a time.
SINGLE_INTERVAL_SELECTION allows the selection of a contiguous set of rows.
MULTIPLE_INTERVAL_SELECTION also allows selections of contiguous rows, but with extended functionality. It lets the user create multiple interval selections (noncontiguous rows) by using the [Ctrl] key.
The setCellSelectionEnabled() method lets users select both individual cells and whole rows simultaneously:
table.setCellSelectionEnabled(true);
If set to true, the setCellSelectionEnabled() method will also allow the selection of columns, as shown in Figure B, in conjunction with rows and individual cells.
Figure B
Editing cells
Our simple table allows the user to edit any of the cells in the table. Listing B shows a table that lets the programmer decide which cells can be edited. The first step is to create a custom
TableModel:
class SimpleTableModel extends AbstractTableModel {}
The data is encapsulated in the TableModel, and when the JTable is initialized, the custom TableModel is passed as a parameter to the JTable constructor instead of the two-dimensional object array:
SimpleTableModel myModel = new SimpleTableModel();
JTable table = new JTable(myModel);
If you wanted to make the second and third columns editable and make the first column constant, you would override the TableModel’s isCellEditable() method:
public boolean isCellEditable(int row, int col){
if (col == 0) {
return false;
}
else {
return true;
}
Simple cell validation
To make sure that the user can type only integer values into, say, the second column (Number of Boxes), override the setValueAt() method and include the validation logic inside the new method. First, check to see whether a column is an integer and should thus contain only integer values:
if (data[0][col] instanceof Integer && !(value instanceof Integer))
{… }
else { data[row][col] = value;}
Next, check that the value being inserted is an integer. If it isn't, the field should not be updated and an error message should be displayed:
try {
data[row][col] = new Integer(value.toString());
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(SimpleTable.this, "Please enter only integer values.");
}
Background colors
Listing C contains the code for ColorTable.java, which demonstrates how to add color to a JTable. You can add background colors to a JTable by overriding its prepareRenderer() method:
JTable table = new JTable(data, columnNames){
public Component prepareRenderer(TableCellRenderer r, int row, int col){}
};
Next, insert logic that decides which columns should have colors and what they should be:
if (col == 2 && !isCellSelected(row, col)) {
Color bg = new Color(200, 100, 30);
c.setBackground(bg);
c.setForeground(Color.white);
}
Note that when you change the cell’s background color, you should also change the color of the text displayed in the cell to make the text more readable. Figure C displays a JTable with colors added to its first and third columns.
Figure C
Take control
Our examples are merely the basic foundation upon which JTables rest. Using these tools, you can quickly and easily rein in the possible formatting of tables generated by your Java applications and thereby keep your users from tripping over themselves during standard usage.
JTable - Vector can also be used.
Eg:
Vector
rowsParamTable = new Vector();
Vector columnsParamTable = new Vector();
Vector v = new Vector();DefaultTableModel tabModel = new DefaultTableModel();
tabModel.setDataVector(rowsParamTable, columnsParamTable);
JTable ParamTable = new JTable(tabModel);
ParamTable.getModel().addTableModelListener(this);//for adding a particular data to the JTable
v.add((Object)"data1");
v.add((Object)"data1");rowsParamTable.addElement(v);
ParamTable.addNotify();
//for removing a particular row use removeElement() function
of the rows Vector and invoke removeNotify() function
No comments:
Post a Comment