I am new to Eclipse RCP and I am working on an application (Eclipse 4), I have multiple parts within I display data from different sources. I would like to add a menu that displays a Dialog that offers the possibility to select dynamically the data sources that the user wants. When the options are selected I would like to re-instantiate the Part's class using the options as parameters and refresh the view. Is that possible ?
My Part's createComposite method :
#PostConstruct
public void createComposite(Composite parent) {
Composite composite = new Composite(parent, SWT.EMBEDDED);
parent_C = parent;
Frame frame_1 = SWT_AWT.new_Frame(composite);
JPanel mainPanel = new JPanel();
BorderLayout layout = new BorderLayout();
mainPanel.setLayout(layout);
/* Layout Definition */
}
I would like to add another parameter to the createComposite Method that indicates the options :
#PostConstruct
public void createComposite(Composite parent, String[] options) {
/*Code Here*/
}
The Value of the String array changes when the user validate the options from the Menu. When the users validate his options the part's class should be called with the new options.
Is there any way to do this ? Thank You
To do this you need to get the values in the IEclipseContext of the part being created. One way to do this is to subscribe to the UIEvents.Context.TOPIC_CONTEXT event and modify the new part's context in that event.
#Inject
IEventBroker eventBroker;
eventBroker.subscribe(UIEvents.Context.TOPIC_CONTEXT, this::handleContextEvent);
private void handleContextEvent(Event event)
{
Object origin = event.getProperty(UIEvents.EventTags.ELEMENT);
if (!(origin instanceof MPart))
return;
MPart part = (MPart)origin;
// TODO check this is the MPart you want
Object context = event.getProperty(UIEvents.EventTags.NEW_VALUE);
if (!(context instanceof IEclipseContext))
return;
IEclipseContext newContext = (IEclipseContext)context;
newContext.set("nameForOptions", .... options ....);
}
I have used a name for the options here so you would use #Named:
#PostConstruct
public void createComposite(Composite parent, #Named("nameForOptions") String[] options)
Instead of recreating the entire part again, it will be easier to refresh of re-create the content inside the part itself. That should be possible by either disposing the content of the part and recreate the content again under that container, or by refresh mechanism of any table/table viewer.
Related
I am new zk framework. In my zul file there is a notification count indicator, which is a property of view model and indicates the notification count. When I click on notification indicator it opens a toggle window containing notifications. Here I want to implement that when I read a notification , the count will be reduced by one.
<div class="notification_popup"
viewModel="#id('vm') #init('com.zk.viewmodels.ViewAnnouncementViewModel')">
<a sclass="activity-dropdown" id="announcement_notification" iconSclass="z-icon-bell" popup="ann_notification,position=after_end,type=toggle" tooltiptext="Notifications">
<span class="num"><label value="#bind(vm.announcementCount)"/></span></a>
<popup id="ann_notification" class="header-top-dropdown notification-dropdown">
<vlayout id="vl" sclass="notify-popup">
</vlayout>
</popup>
</div>
#VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
public class ViewAnnouncementViewModel {
private long announcementCount = 0;
#NotifyChange({".", "announcementCount"})
public void setAnnouncementCount(long announcementCount) {
this.announcementCount = announcementCount;
}
#SuppressWarnings("unchecked")
#AfterCompose
public void afterCompose(#ContextParam(ContextType.VIEW) Component view) {
super.afterCompose(view);
Selectors.wireComponents(view,this,false);
Html h1 = new Html();
h1.setContent("<h4>Notifications</h4>");
vl.appendChild(h1);
div = new Div();
div.setId("announcementList");
div = updateAnnouncement(div);
vl.appendChild(div);
}
public Div updateAnnouncement(Div div){
private Collection<AnnouncementResultDTO> searchResults =
announcementService.retrieveAnnouncement(instanceInfo);
announcementCount = searchResults.size();
setAnnouncementCount(announcementCount);
postNotifyChange(this,"announcementCount");
for(final AnnouncementResultDTO pasrDTO:searchResults){
A s1 = new A();
Label l1 = new Label();
l1.setValue("Annoncements");
s1.appendChild(l1);
div.appendChild(s1);
s1.addEventListener("onClick", new EventListener() {
public void onEvent(Event event) throws Exception {
Map<String, Object> map = new HashMap<String,Object>();
announcementCount=announcementCount-1;
setAnnouncementCount(announcementCount);
postNotifyChange(this,"*");
Window window = (Window) Executions.getCurrent().createComponents("announcement_popup.zul",null,map);
window.doModal();
}
});
}
}
But announcementCount is reducing when event fires but it is not reflected in view model.
I think the problem is that if we debug and try to find the value 'this', it will contain object with two inner object , one is view model. Here we can understand that postnotify method receives not a view model, but a wrapper class which contain viewmodel as inner class. I dont know my assumption is true. Please help me
Although you have found the correct way to notify a ViewModel's property. But I recommend you not to mix MVVM and MVC pattern in the same scope. Because MVVM is a pattern that let ZK framework manipulate your components, but MVC is a pattern that you (app developers) manipulate components by yourselves by component API. Sometimes your manipulation might interfere ZK framework's MVVM working and produce an unexpected result. Such case is hard to debug for you. One page in one pattern is recommended.
For example, regarding the code snippets:
for(final AnnouncementResultDTO pasrDTO:searchResults){
A s1 = new A();
Label l1 = new Label();
....
If you need to create multiple component according to a collections, please use
<forEach items="#load(vm.searchResults)">
<label value="Annoncements"
...
</forEach>
Please see Shadow components for details.
I got the solution for this . Use postNotifyChange(ViewAnnouncementViewModel.this,""); inside onEvent() method instead of postNotifyChange(this,"");
s1.addEventListener("onClick", new EventListener() {
public void onEvent(Event event) throws Exception {
Map<String, Object> map = new HashMap<String,Object>();
announcementCount=announcementCount-1;
setAnnouncementCount(announcementCount);
postNotifyChange(ViewAnnouncementViewModel.this,"*");
Window window = (Window) Executions.getCurrent().createComponents("announcement_popup.zul",null,map);
window.doModal();
}
});
Thanks https://www.javacodegeeks.com/2012/07/zk-in-action-mvvm-working-together-with.html
I need to update the list of a ListSelect after i click in one button but i dont know how to
Here is my code:
Create List Method
private void createListPanel() {
VerticalLayout listPanel = new VerticalLayout();
listPanel.setWidth(100f, Unit.PERCENTAGE);
queryList= new ListSelect("List Of Querys", getQueryList());
queryList.setWidth(100f, Unit.PERCENTAGE);
queryList.setNullSelectionAllowed(false);
queryList.addValueChangeListener(event -> {
selectedQuery = (String) (queryList.getValue());
String retrievedQuery = repository.getRawQuery(selectedQuery);
});
listPanel.addComponent(queryList);
panelSuperior.addComponent(listPanel);
}
getQueryList()
private List<String> getQueryList() {
return repository.getQueryNames();
}
Create Button Method
private void CreateButton() {
Button buttonRefresh= new Button("Refresh");
buttonRefresh.addClickListener((Button.ClickEvent e) -> {
// i tried this
queryList.setContainerDataSource((Container) getQueryList());
});
buttonPanel.addComponent(button);
}
I tried this line at CreateButton() method:
queryList.setContainerDataSource((Container) getQueryList())
but i get a
java.lang.ClassCastException: java.util.ArrayList cannot be cast to com.vaadin.data.Container
because this method need a Container Object
I was searching in the Vaadin javadoc but I am not able to find a method to set or update the list of the ListSelect
Vaadin Javadoc: Vaadin Javadoc 7.7.9 ListSelect
Thanks in advance
The straightforward way is to remove all items from an underlying container and add new items afterward:
Container container = queryList.getContainerDataSource();
container.removAllItems();
List<String> queries = getQueryList();
for(String query : queries) {
container.addItem(query);
}
ListSelect(String caption, Collection<?> options) constructor creates an IndexedContainer and populates it with items from your collection.
Another option is to bind ListSelect to a container which could possibly enable it to track changes automatically. For more on this, see "Collecting Items in Containers" topic from Vaadin docs.
Inside my Wicket webpage, I have a WebMarkupContainer which contains a ListView:
notifications = new ArrayList<Notification>(...);
ListView listView = new ListView("notification", notifications) {
#Override
protected void populateItem(ListItem item) {
...
}
};
container = new WebMarkupContainer("container");
container.setOutputMarkupId(true);
container.add(listView);
this.add(container);
The WebMarkupContainer is in place in order to let me dynamically update the list of items shown to the user onscreen. This is possible when the user clicks on a link or by adding the container to incoming AjaxRequestTarget.
Now I'm required to update the list without having an Ajax request:
public void refresh() {
List<Notification> newNotifications = ...
notifications.addAll(0, newNotifications);
}
This method is called in a run-time environment and the list of notifications, which is a private field of my webpage (same one as last code), will contain new objects. I want these new items displayed to the user. Is it possible to update (or re-render) the container?
I'm new to Wicket so if you have a better way to achieve the same results, I would appreciate if you could share it with me.
You would have to do it on a timer. Use AjaxSelfUpdatingTimerBehavior to do so. Just set some sensible duration and add your container to target in 'onTimer()' method.
EDIT:
If your 'refresh()' function is only called when new notifications appear, you could set a flag on your page (define boolean variable on page and change it to true when new notification appears and to false once listView is refreshed). Then you can set short duration on the behavior and 'onTimer()' would look something like that:
onTimer(AjaxRequestTarget target) {
if(newNotifications) {
target.add(container);
newNotifications = false;
}
}
And refresh
public void refresh() {
List<Notification> newNotifications = ...
notifications.addAll(0, newNotifications);
newNotifiactions = true;
}
That way container won't be refreshed too often (which might cause strange effects) and will refresh every time new notification appears.
The content of the tab is formed and displayed when the application is loaded. Later the content of the tab may be changed by other actions. I want to show the newer content after each action. And each time when I click the tab sheet, the content should be refresh/updated. But I failed.
//the content of the tab from the "reprintsTab" class
//in the "reprintsTab" it query data from database and print out
//later I update the data in the database from somewhere else, and I want the tab shows the new content
//I want to click the tab sheet to reload the "reprintTab" class and print out the new content
//here is what I did:
public TabSheet sheet;
//add tab and add the content from "reprintTab" into this tab
sheet.addTab(new reprintsTab());
//add the listener
sheet.addListener(new TabSheet.SelectedTabChangeListener() {
#Override
public void selectedTabChange(SelectedTabChangeEvent event) {
//I know it does not work, because it only reload the class. but not put the content under the tab I want
new reprintsTab();
}
});
What should I do? please help me, thanks.
You can use TabSheet.replaceComponent method to do this:
//Field to store current component
private reprintsTab currentComponent;
//during initialization
currentComponent = new reprintsTab();
sheet.addTab(currentComponent);
sheet.addListener(new TabSheet.SelectedTabChangeListener() {
#Override
public void selectedTabChange(SelectedTabChangeEvent event) {
reprintsTab newComponent = new reprintsTab();
sheet.replaceComponent(currentComponent, newComponent);
currentComponent = newComponent;
}
});
Also, you might want to reload this tab only when it's shown:
sheet.addListener(new TabSheet.SelectedTabChangeListener() {
#Override
public void selectedTabChange(SelectedTabChangeEvent event) {
if (event.getTabSheet().getSelectedTab() == currentComponent) {
//here goes the code
}
}
});
This should work for you, but I would suggest a cleaner approach: implement reprintsTab as a container for components, create method reload or buildInterface method to refresh its' state, so you can just call:
currentComponent.reload();
when you need to update interface.
Also, I hope reprintsTab is just an example name, java class names starting with lowercase letter look ugly.
I am currently developing my own minesweeper. Swing follows Model-View-Controller design pattern. In MVC, I learnt whenever there is a change in model, the controller will trigger that change in view too. But In this example, I cannot trace how to make the changes in setTitle and setInfo to get reflected in view.
Here, when I set the title of the Dialog box, the actual content(model) is getting changed, But there is no corresponding change in the output(view).
//InfoDisplayer is inner class of class MenuActionListener
class InfoDisplayer extends JDialog {
JLabel info;
BorderLayout infoBorderLayout = new BorderLayout();
public InfoDisplayer(JFrame ownerFrame) {
super(ownerFrame,true);
info = new JLabel();
setFocusable(false);
setSize(300,400);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setLayout(infoBorderLayout);
add(info,BorderLayout.SOUTH);
setVisible(true);
}
void setInfo(JLabel info) {
this.info = info;
}
public void setTitle(String title) {
super.setTitle(title);
}
}
if ((event.getActionCommand()).equals("HowToPlay")) {
InfoDisplayer instructionsDisplay = new InfoDisplayer(gUIManagerFrame);
//gUIManagerFrame is an object of its outer class,MenuActionListener
instructionsDisplay.setTitle("INSTRUCTIONS");
instructionsDisplay.setInfo(new JLabel("<html><h1><B>INSTRUCTIONS</B></h1></html>"));
} else {// if about is clicked!!
InfoDisplayer aboutDisplay = new InfoDisplayer(gUIManagerFrame);
aboutDisplay.setTitle("MineSweeper v0.1");
aboutDisplay.setInfo(new JLabel("<html><h1><B>MineSweeperv1.0</B></h1> </html>"));
}
Whenever there is a change in model, the controller will trigger that change in view.
In the Model–View–Controller pattern, when the controller updates the model, the model will notify the view, typically using the observer pattern, and the view then updates itself. The view may interrogate the model and process any resulting update. There's a more detailed answer and example here.
You will need to remove the old jlabel and add the new one to the frame.
Though it would make more sense probably to set the text on the existing label rather than a whole new label.
Swing indeed has a model and a view side. For example in a JTable the JTable is the view and the TableModel is the model. When you construct a JTable, you need to pass it a model either during construction or by using the setter. The JTable will then add a listener to model to get informed about any model changes. You can see this listener as the controller.
However, this does not mean that when you use an arbitrary combination of Swing classes they will auto-magically get informed about each other changes. In your case, the label is certainly not 'the model' of your dialog, and there is no such thing as a 'controller' between your label and the dialog. When you make such a change, you need to inform the dialog yourself (and probably add the label to your dialog as well).
Oh, and I would recommend changing your setTitle method into
public void setTitle( String aTitle ){
super.setTitle( aTittle );
}
or remove it completely. This will avoid a StackOverflowException