I know this question has been covered in many posts here.
However, something is still not clear to me, so i wanted to ask you my problem in detail.
I have to develop a java application using Swing and using the MVC model.
The application is mainly divided into two parts:
login part
questionnaire part (after login, a questionnaire is displayed)
So following MVC model i divided my code into 3 packages containing the following classes:
Model
LoginModel
QuestionModel
View
LoginView
QuestionView
Controller
LoginController
QuestionController
After developing these classes, i didn't know how to set the window that the program was current working on (login, questionnaire or other future implementations).
So i thought about implementing 3 other classes that use the Observer pattern:
MainModel - Observable
MainView
MainController - Observer
But now i'm not sure how to change the current window.
For example when login is successful, the window must change from LOGIN to QUESTION, so "MainModel.window = Window.QUESTION" and send it to the View.
Should it be added in LoginModel.login() by extending LoginModel with MainModel?
Or how can I do this?
My code:
public class main {
public static void main(String[] args) {
MainView view = new MainView();
MainModel model = new MainModel();
MainController controller = new MainController(view, model);
}
}
public class MainView {
private JFrame window;
public MainView() {
window = new JFrame();
window.setLayout(new CardLayout(0, 0));
LoginView login = new LoginView(); // init window at opening
QuestionView question = new QuestionView();
window.add(login);
window.add(question);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
public void update(Window window) {
// ??
}
}
public class MainModel {
private List<Observer> observers = new ArrayList<>();
private Window window;
public MainModel() {
window = Window.LOGIN; // init window at opening
}
public void addObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
public void setWindow(Window newWindow) {
newWindow = window;
for (Observer o : observers)
o.update(newWindow);
}
}
public class MainController implements Observer {
private MainView view;
private MainModel model;
public MainController(MainView view, MainModel model) {
this.view = view;
this.model = model;
this.model.addObserver(this);
}
#Override
public void update(Window window) {
this.view.update(window);
}
}
public class LoginView extends JPanel {
private JButton btnLogin;
// ... other attributes
public LoginView() {
btnLogin = new JButton("Login");
new LoginController(this);
}
public JButton getBtnLogin() {
return btnLogin;
}
public void ShowResult(boolean bResult) {
// print result with JOptionPane.showMessageDialog
}
}
public class LoginController {
private LoginView view;
public LoginController(LoginView view) {
this.view = view;
setActionListener();
}
public void setActionListener() {
ActionListener loginButton;
loginButton = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
LoginModel model = new LoginModel();
boolean bResult = model.login(view.getUserNameField(), view.getPasswordField());
view.ShowResult(bResult);
}
};
view.getBtnLogin().addActionListener(loginButton);
}
}
public class LoginModel {
// ... attributes etc
public boolean login(String username, String password) {
boolean bResult;
// ... Some operation etc (useless for this example)
bResult = true; // Simulation login successful
if (bResult)
// ? Change window model to Window.QUESTION.
// But how?
// LoginModel extends MainModel? To call "super.setWindow(Window.QUESTION)?
return bResult;
}
}
// My Observer class
public interface Observer {
public void update(Window window);
}
// My Window class
public enum Window {
LOGIN,
QUESTION,
}
// Questionnaire classes code is very similar to the Login code
public class QuestionView extends JPanel {
private JButton btn;
// ...
new QuestionController(this);
// ...
}
public class QuestionController {
private QuestionView view;
// ...
setActionListener();
// ...
}
So in conclusion is it correct to use this approach? Or how else could i view/update the current window better?
In Swing, the MVC pattern looks like this:
The view reads from the model
The view may not update the model
The controller updates the model and the view
The MVC name implies that you create the model first, then the view, then the controllers.
There's usually not one controller to "rule them all". Each listener is responsible for its own part of the model and the view.
You usually have one application model. An application model is made up of one or more plain Java getter/setter classes. In your case, it looks like a Person class and a Questionaire class. You would probably also have a Question class, to hold one question, several possible answers, and the chosen answer. You may have additional plain Java getter/setter classes I'm not thinking about now.
You would have one JFrame, one JPanel to hold a question and possible answers, and a JDialog for the login and password. You may need multiple JPanels for different types of answers (not different questions), so you might need a main JPanel with a CardLayout.
Your controllers will be the ActionListener for the login JButton, and the "I'm finished answering this question" JButton. You may have other listeners that I'm not thinking about now.
Related
I am implementing a car pooling app. The gui is made with netbeans.
I have my main class Voiture.java
public class Voiture {
public static void main(String[] args) {
View view = new View();
Controller controller = new Controller(view);
controller.run();
}
}
The controller class :
public class Controller {
private buttonHandler butHandler;
private QueriesRunner queriesRunner;
private User user;
private View view;
public Controller(View view) {
this.butHandler = new buttonHandler();
this.queriesRunner = new QueriesRunner();
this.user = new User();
this.view = view;
}
public void run() {
view.getLoginPage().show();
}
}
QueryRunner just runs sql queries by connecting to my model, a database.
The view main class :
public class View {
private LoginPage loginPage;
private RegisterPage registerPage;
private UserWelcomePage userWelcomePage;
public View() {
this.loginPage = new LoginPage();
this.registerPage = new RegisterPage();
this.userWelcomePage = new UserWelcomePage();
}
}
In the LoginPage there is a login button. The function that is run (via a button listener) when the button is clicked is :
private void loginButtonActionPerformed(java.awt.event.ActionEvent evt) {
boolean b = buttonHandler.notifyLogin(enteredEmail.getText(),
enteredPassword.getText());
if (b) {
jLabel1.setText("Login SUCCESS");
} else {
jLabel1.setText("Login Fail, Please Register");
}
}
buttonHandler just run queries by making the correct sql query and then passing it to the queryRunner class.
What seems difficult to do properly, is to destroy the LoginPage instance and show the welcomePage with the correct info (name of the user, city he was born in ...) when the login is successful. Right now it just puts the LOGIN SUCCESS on the screen.
What I tried to do is just put everything in the core of loginButtonActionPerformed by hiding the loginPage, showing the welcome page and passing the info through the ButtonHandler class. But that seems like the beginning of a really hideous code because I don't pass through the controller.
I start doubting if MVC is the best pattern to use for a login page...
I've looked around but nothing seems to help me out. Basically I'm writing a multithreaded chat program with a gui. The user inputs his name in a textfield in a Login class and hits the login button which directs him to a ClientGUI class. In the client GUI class theres a JLabel at the top that says
"Welcome to the ChatSystem (Username)"
. So what the user input in the textfield in the login class should appear in the JLabel after "Welcome to the ChatSystem" but I can't figure out why it doesn't work. Here's my code:
Login Class:
loginB = new JButton("Login");
main.add(loginB);
loginB.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
ClientGUI clientgui = new ClientGUI();
clientgui.setVisible(true);
}
}
ClientGUI class:
public ClientGUI(){
Login login = new Login();
String username = login.usernameTF.getText();
welcome = new JLabel("Welcome to ChatSystem "+username, SwingConstants.CENTER);
}
I understand that username should really by a JLabel and not a String but I have tried many ways to do this and I can't seem to get my head around this.
That is not going to work like that because
login.usernameTF.getText(); is actually a new created object in the ClientGUI constructor...
what I would suggest to do is to overload the constructor and to pass the name as parameter...
Example:
loginB.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
ClientGUI clientgui = new ClientGUI(getTheNameAndpassItHere);
clientgui.setVisible(true);
}
}
and then ClientGUI class:
public ClientGUI(String username){
//Login login = new Login();
// String username = login.usernameTF.getText();
welcome = new JLabel("Welcome to ChatSystem "+username, SwingConstants.CENTER);
}
Basically, you should use a Observer Pattern, which allows ClientGUI to generate events to interested parties when something changes.
This decouples your code and prevents the ClientGUI from doing things it shouldn't (like removing the label or it's parent component for example)
You could use some of the inbuilt listeners if they meet your needs, but for something like this, I'd prefer to use my own
public class LoginEvent extends EventObject {
private Throwable cause;
private String userName;
public LoginEvent(Object source) {
super(source);
}
public Throwable getCause() {
return cause;
}
public String getUserName() {
return userName;
}
}
public interface LoginListener extends EventListener {
public void loginFailed(LoginEvent evt);
public void loginSuccessful(LoginEvent evt);
}
Then you could add an instance of the listener to the ClientGUI...
loginB.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
ClientGUI clientgui = new ClientGUI(getTheNameAndpassItHere);
clientgui.addLoginListener(new LoginListener() {
#Override
public void loginFailed(LoginEvent evt) {
Throwable cause = evt.getCause();
// Show error message
}
#Override
public void loginSuccessful(LoginEvent evt) {
String name = evt.getUserName();
// Update UI
}
});
clientgui.setVisible(true);
}
}
or something simular
the swings component are by default private you need to make them public by just little steps
1.go to the properties of the component and then to the code tab
2.you will find variable modifier as private make them public and as well as static if you want.
you will now be able to use the components with the same code
When I add components to Vaadin's component (such as TabSheet or Tree) , the added components are cached. When user clicks the tab (or tree nodes) , if it contains db data , it shows stale data , not reflecting the latest db state.
I wonder if there is any way to ensure loading latest data ?
I solve the problem by defining my custom interface :
public interface Reloadable {
void reload();
}
And each component implements this Reloadable interface , such as :
#SpringComponent
public class TeachersView extends VerticalLayout implements Reloadable, Serializable {
#Inject
private TeacherDao teacherDao;
private final static int PAGESIZE = 10;
private MTable<Teacher> mTable = new MTable<>(Teacher.class);
#PostConstruct
public void init() {
// mTable settings skip here
reload();
addComponent(mTable);
}
#Override
public void reload() {
mTable.setBeans(new SortableLazyList<>(
sortablePagingProvider ,
() -> (int) teacherDao.count() ,
PAGESIZE
));
}
private SortableLazyList.SortablePagingProvider<Teacher> sortablePagingProvider =
(firstRow, asc, sortProperty) -> {
return teacherDao.findAll(
new PageRequest(
firstRow / PAGESIZE, PAGESIZE,
asc ? Sort.Direction.ASC : Sort.Direction.DESC,
sortProperty == null ? "id" : sortProperty
)
).getContent();
};
}
And this view is injected to UI class :
#SpringUI(path = "/ui")
#Theme("valo")
public class VaadinUI extends UI {
#Inject
private TeacherDao teacherDao;
#Inject
private TeachersView teachersView;
#Override
protected void init(VaadinRequest vaadinRequest) {
Panel panel = new Panel("Admin Panel");
HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
splitPanel.setSplitPosition(15, Unit.PERCENTAGE);
panel.setContent(splitPanel);
Tree tree = new Tree("Menu");
splitPanel.setFirstComponent(tree);
Label home = new Label("Home");
Map<String, Component> map = new HashMap<>();
map.put("Teachers", teachersView);
map.put("Home", home);
map.forEach((k, v) -> tree.addItem(k));
tree.addItemClickListener(event -> {
Component view = map.get(event.getItemId());
if (view instanceof Reloadable) {
((Reloadable) view).reload();
}
splitPanel.setSecondComponent(view);
});
splitPanel.setSecondComponent(home);
setContent(panel);
} // init()
}
Notice the tree.addItemClickListener , I have to check each component if it implements Reloadable , if true , invoke it.
It works . But I don't know if it the standard way achieving this ? I think it should be a common scenario , there should be something like built-in interface for Components to implement , such as onRender like that (but I cannot find one) . Did I miss anything ?
Thanks.
First of all I'm going to suggest this tutorial on Spring & Vaadin that you may have already seen, but I'll be referencing it in a few places and I think it's a good starting point for Vaadin & Spring integration.
Second, out of curiosity, why are you using a tree to build the menu?
In the example provided you seem to be modelling a navigation between some views feature, which is already available in Vaadin, and since you're using Spring, the Vaadin spring & spring-boot extensions makes it really easy to define and navigate between your views. Then you can define some specific behaviour for each view in their own enter() method. I've used the Vaadin dashboard demo as inspiration for the changes below:
#SpringView(name = TeachersView.NAME)
public class TeachersView extends VerticalLayout implements View {
public static final String NAME = "Teachers";
private Label title = new Label("Teachers view");
#PostConstruct
void init() {
addComponent(title);
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// recreate or reload stuff here
title.setValue("Teachers view reloaded # " + new Date());
}
}
#SpringView(name = HomeView.NAME)
public class HomeView extends VerticalLayout implements View {
public static final String NAME = "";
#PostConstruct
void init() {
addComponent(new Label("Home"));
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// meh, nothing special to do here
}
}
public class SpringVaadinUI extends UI {
#Autowired
private SpringViewProvider viewProvider;
#Override
protected void init(VaadinRequest vaadinRequest) {
addStyleName(ValoTheme.UI_WITH_MENU);
Panel panel = new Panel("Admin Panel");
HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
splitPanel.setSplitPosition(15, Unit.PERCENTAGE);
panel.setContent(splitPanel);
VerticalLayout navigationBar = new VerticalLayout();
navigationBar.setPrimaryStyleName(ValoTheme.MENU_ROOT);
navigationBar.addComponent(createNavigationButton("Home", FontAwesome.HOME, HomeView.NAME));
navigationBar.addComponent(createNavigationButton("Teachers", FontAwesome.GROUP, TeachersView.NAME));
splitPanel.setFirstComponent(navigationBar);
CssLayout navigationDisplay = new CssLayout();
splitPanel.setSecondComponent(navigationDisplay);
Navigator navigator = new Navigator(this, navigationDisplay);
navigator.addProvider(viewProvider);
setContent(panel);
}
private Button createNavigationButton(String caption, FontAwesome icon, final String viewName) {
Button button = new Button(caption, icon);
button.setPrimaryStyleName(ValoTheme.MENU_ITEM);
button.addStyleName(ValoTheme.BUTTON_SMALL);
button.addStyleName(ValoTheme.BUTTON_BORDERLESS);
button.addClickListener(event -> getUI().getNavigator().navigateTo(viewName));
return button;
}
}
The result is similar to:
If for some reason you can't or don't want to use the navigator, then your solution looks fine. Nonetheless, whichever solution you chose to use, you should know that by default Spring creates singletons. Except a few such as the UI, you should probably change your components to prototypes so you'll get a new instance each time. Otherwise all your users will get the same instances when accessing the application, which I don't think you want to happen.
I'm working on developing an Vaadin application. When developing a Java desktop application I use AbstractAction to create the buttons of my GUI. How to do that using Vaadin
Here is how I do in Java Desktop application:
// In my view
JButton button = new JButton(new Action(BUTTON_NAME, presenter, "methodToInvoke", Object... arguments));
class Action extends AbstractAction {
public Action(ButtonName name, Presenter presenter, String method, Object... arguments) {
this.name = name;
this.presenter = presenter;
this.method = method;
this.arguments = arguments;
readButtonProperties();
}
public void actionPerformed(ActionEvent e) {
//Call method from presenter with arguments using reflection
}
}
Edit:
I already read this. It's not the way I'm asking for.
From your example it isn't really clear what your requirements are.
Sounds like you insist on using reflection, but I'm not sure why. You could just do this, which is basically the same thing the Button documentation already says.
Presenter presenter = getPresenter();
String with = "with";
int my = 42;
List<Data> = new List<>();
Button vaadinButton = new Button("I'm a button.", clickEvent -> presenter.doStuff(with, my, arguments));
If you need more than a simple method call, you could just as well create a more elaborate ClickListener implementation that has more than the buttonClick() method.
Button vaadinButton = new Button("Button Caption", new Action(...))
class Action extends AbstractAction implements Button.ClickListener {
public Action(ButtonName name, Presenter presenter, String method, Object... arguments) {
this.name = name;
this.presenter = presenter;
this.method = method;
this.arguments = arguments;
readButtonProperties();
}
public void actionPerformed(ActionEvent e) {
//Call method from presenter with arguments using reflection
}
#Override
public void buttonClick(ClickEvent event) {
actionPerformed(null);
}
}
I am developing my GUI according to the MVC pattern:
-GUIview: Swing components (JFrame and several JTables).
-GUIcontroller: listeners (added here, and defined here in Inner classes)
-GUImodel: modify and store data, firing change-events.
Changes in the model are passed to the view through the controller (and not directly), like in this example.
I have also written different customized JTableModels (extending AbstractTableModel) for the different JTables contained in the View class. All the JTableModels are defined in different classes within the package "GUImodel". Each JTableModel defines an ArrayList and a few methods to manipulate the ArrayList.
According to the MVC guidelines, the Model should know nothing about the view. In fact, the main() method is defined as follows:
GUImodel model = new GUImodel();
GUIcontroller controller = new GUIcontroller();
GUIview view = new GUIview(controller, model);
controller.addView(view);
controller.addModel(model);
view.setVisible(true);
controller.addControllerListerners();
My problem is:
When I am executing a method within the GUImodel (for example because a JButton has been pressed and I need to load data from an external file), I need to modify some JTableModels (to add data/rows to its ArrayList) and get the changes reflected in the JTable. My first idea would be:
ArrayList newArrayList = fileLoader(filePath); //create ArrayList and load info
guiView.getTable1Model().updateArrayList(newArrayList); //update JTableModel ArrayList
However, this approach is not valid, since GUImodel should be totally independent of GUIview.
Any idea?
It may be good to realize that MVC is primarily a pattern concerned with data encapsulation, which uses another pattern, Observer, to communicate changes. As data encapsulator, the Model knows nothing of Views and Controllers, but as an Observable it does know that it has Observers, which need to be notified when a change occurs.
A Description of the Model-View-Controller User Interface Paradigm in the Smalltalk-80 System, page 4 explains it well:
To manage change notification, the notion of objects as dependents was developed. Views and
controllers of a model are registered in a list as dependents of the model, to be informed whenever
some aspect of the model is changed. When a model has changed, a message is broadcast to notify
all of its dependents about the change. This message can be parameterized (with arguments), so
that there can be many types of model change messages. Each view or controller responds to the
appropriate model changes in the appropriate manner.
To illustrate the concept, you can start out with your own Observer/Observable classes:
public interface Observer {
public void update(int message);
}
public interface Observable {
public void registerObserver(Observer observer);
}
public class Model implements Observable {
List<Observer> observers;
public void registerObserver(Observer observer) {
observers.add(observer);
}
public void loadFile(String path) {
// load file and change data
foreach (Observer observer: observers)
observer.update(READ_NEW_DATA);
}
public ArrayList getData() { return data; }
}
public class View implements Observer {
public void update(int message) {
doWhateverWith(model.getData());
}
}
public class Controller implements Observer {
public void update(int message) {
doWhateverWith(model.getData());
}
public void onClick() {
model.loadFile("someFile");
}
}
As you can see, the Model knows nothing of the internal workings of the Views and Controllers. It doesn't even know if returning an ArrayList will be particularly useful to them (although in practice you'd like for that to be the case). So in this regard, independence is achieved.
There is no independence in the communication between the Obervable and Observers, but that isn't part of the requirements of the MVC pattern.
If you want your GUI to hitchhike on top of the existing Swing Observer pattern (Listeners), then your classes should inherit from the appropriate classes:
public class Model extends AbstractTableModel...
public class View implements TableModelListener...
public class Controller implements CellEditorListener...
Etcetera. Since JTable implements both TableModelListener and CellEditorListener, it is actually a composite of View and Controller. So you have the choice to either have a combined ViewController class extend JTable, or to have them separately. In the latter case, the View could extend JTable, overriding the control Listeners, so that they pass their events to the Controller class. But that sounds like more work than it's worth.
As discussed here, you are correct to loosely couple the model and view. JTable implements TableModelListener to listen to its own model, and your AbstractTableModel no doubt fires events that cause the listening table to update itself.
In this case, let the dependent TableModel add itself as a TableModelListener to the master TableModel. The dependent model can then fire the events needed to notify it own listeners of changes propagated from the master.
However, this approach is not valid, since GUImodel should be totally independent of GUIview.
The Swing Components themselves use the MVC model. Changes in the model have to trigger changes in the view. The question is how do you do this?
One way is for the model to have access to the view instance(s), as you've illustrated in your question.
ArrayList newArrayList = fileLoader(filePath); //create ArrayList and load info
guiView.getTable1Model().updateArrayList(newArrayList); //update JTableModel ArrayList
Another way is for the controller to update the model and update the view. This is what I usually do in a Swing application.
model.loadArrayList(filePath);
frame.getFrame().getMainPanel().repaint();
Another way is to fire actions. This is how the Swing components update the GUI.
ArrayList newArrayList = fileLoader(filePath); //create ArrayList and load info
fireAction(newArrayLiat);
The fireAction method would work with listeners. Here's a fire method I copied from AbstractListModel.
protected void fireContentsChanged(Object source, int index0, int index1) {
Object[] listeners = listenerList.getListenerList();
ListDataEvent e = null;
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ListDataListener.class) {
if (e == null) {
e = new ListDataEvent(source,
ListDataEvent.CONTENTS_CHANGED, index0, index1);
}
((ListDataListener) listeners[i + 1]).contentsChanged(e);
}
}
}
You would have to write listeners in your model classes that the view classes can write code to change the view.
The Javadoc for the EventListenerList has more information about listeners. Thanks
Catalina Island.
My style of MVC in swing is, the model and the view is oblivious of each other as well as of the controller, but the controller knows the view and the model soo well. This way, I do all of the logic in the controller. I just left the long codes of UI + complex layouts in the view and think of all the data that the application will need for the model & decide whether a certain data should appear in my view. I put the functionality of adding listeners to the buttons, etc. to the controller via view.getBtn().setAction(new ActionForThisOrThatInnerClass()) kind of stuff
In your case, I agree that the data that the table will use should be stored in your main model in the form of, ideally, a List, but I would not bother myself to subclass a new TableModel to handle those data, I think the DefaultTableModel is powerful enough to do a lot.
Here is the runnable example if I am to code your requirements
public class Sample {
public static void main(String[] args){
View view = new View();
Model model = new Model();
Controller controller = new Controller(view, model);
JFrame frame = new JFrame("MVC Demo");
frame.getContentPane().setLayout(new BorderLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(view.getUI());
frame.pack();
frame.setVisible(true);
view.getBtnFileLoader().doClick();
}
}
class View{
private JButton btnFileChooser;
private JButton btnFileLoader;
private JTable tblData;
private JPanel pnlMain;
public View(){
pnlMain = new JPanel(new BorderLayout()){
#Override public Dimension getPreferredSize(){
return new Dimension(300, 400);
}
};
JPanel pnlFileLoader = new JPanel();
pnlFileLoader.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
pnlFileLoader.setLayout(new BoxLayout(pnlFileLoader, BoxLayout.LINE_AXIS));
JTextField txtFileDir = new JTextField();
pnlFileLoader.add(txtFileDir);
btnFileLoader = new JButton();
pnlFileLoader.add(btnFileLoader);
btnFileChooser = new JButton();
pnlFileLoader.add(btnFileChooser);
tblData = new JTable();
JScrollPane pane = new JScrollPane(tblData);
pnlMain.add(pane);
pnlMain.add(pnlFileLoader, BorderLayout.PAGE_START);
}
public JPanel getUI(){
return pnlMain;
}
public JButton getBtnFileLoader(){
return btnFileLoader;
}
public JButton getBtnFileChooser(){
return btnFileChooser;
}
public JTable getTblData(){
return tblData;
}
}
class Controller implements PropertyChangeListener{
private View view;
private Model model;
private DefaultTableModel tmodel;
public Controller(View view, Model model){
this.view = view;
this.model = model;
model.addModelListener(this);
setupViewEvents();
setupTable();
}
private void setupTable(){
tmodel = new DefaultTableModel();
tmodel.addColumn("First Name");
tmodel.addColumn("Last Name");
tmodel.addColumn("Occupation");
view.getTblData().setModel(tmodel);
}
private void setupViewEvents(){
view.getBtnFileChooser().setAction(new AbstractAction("Choose"){
#Override
public void actionPerformed(ActionEvent arg0) {
//choose the file then put the dir
//in the txtfield
}
});
view.getBtnFileLoader().setAction(new AbstractAction("Load"){
#Override
public void actionPerformed(ActionEvent arg0) {
//validate if the dir in the textfield exists and the file is loadable
//load the file specified in the textfield
//assumming the list is already retrieved from the file
//and the list contains the following person
List<Person> list = new ArrayList<Person>();
Person p1 = new Person("Bernardo", "Santos", "Developer");
Person p2 = new Person("Robert", "Erasquin", "Architect");
Person p3 = new Person("Klarrise", "Caparas", "Food Scientist");
list.add(p1);
list.add(p2);
list.add(p3);
//now update the model of the new value for the list
model.setTheList(list);
}
});
}
#Override
#SuppressWarnings("unchecked")
public void propertyChange(PropertyChangeEvent evt) {
if(evt.getPropertyName().equals("theList")){
List<Person> newVal = (List<Person>) evt.getNewValue();
DefaultTableModel tmodel = (DefaultTableModel)view.getTblData().getModel();
for(Person p : newVal){
tmodel.addRow(new Object[]{p.getFirstName(), p.getLastName(), p.getOccupation()});
}
}
}
}
class Model{
private List<Person> theList;
private SwingPropertyChangeSupport propChangeFirer;
public Model(){
propChangeFirer = new SwingPropertyChangeSupport(this);
}
public void setTheList(List<Person> theList){
List<Person> oldVal = this.theList;
this.theList = theList;
//after the model has been updated, notify its listener about
//the update, in our case the controller itself listens to the model
propChangeFirer.firePropertyChange("theList", oldVal, theList);
}
public void addModelListener(PropertyChangeListener prop) {
propChangeFirer.addPropertyChangeListener(prop);
}
}
class Person{
private String firstName;
private String lastName;
private String occupation;
public Person(String firstName, String lastName, String occupation){
this.firstName = firstName;
this.lastName = lastName;
this.occupation = occupation;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getOccupation() {
return occupation;
}
public void setOccupation(String occupation) {
this.occupation = occupation;
}
}