Another way this can be done is by overriding the ApplicationPage such as the VLDockingApplicationPage and adding a public #showView() method like this one (Cut/Pasted from the private one in AbstractApplicationPage):
Code:
public View showView(ViewDescriptor viewDescriptor, boolean setInput, Object input) {
Assert.notNull(viewDescriptor, "viewDescriptor cannot be null");
View view = (View)findPageComponent(viewDescriptor.getId());
if(view == null) {
view = (View)createPageComponent(viewDescriptor);
if(setInput) {
// trigger control creation before input is set to avoid npe
view.getControl();
view.setInput(input);
}
addPageComponent(view);
} else {
if(setInput) {
view.setInput(input);
}
}
setActiveComponent(view);
return view;
}
Then create a custom ViewDescriptorRegistry like this one:
Code:
/**
* <p>
* A <code>ModifiableBeanFactoryViewDescriptorRegistry</code> is a ViewDescriptorRegistry
* that stores registered view descriptors in a map for later lookup. A LinkedHashMap was used to
* ensure the entire list of beans is returned in the same order in which they came from the
* application context.
* </p>
*
* @author aarmistead
*/
public class ModifiableBeanFactoryViewDescriptorRegistry extends ApplicationObjectSupport implements ViewDescriptorRegistry {
/**
* The map of view descriptor id's to view descriptors.
*/
private Map<String, ViewDescriptor> descriptors;
/**
* Creates a new instance of <code>ModifiableBeanFactoryViewDescriptorRegistry</code>.
*/
public ModifiableBeanFactoryViewDescriptorRegistry() {
descriptors = new LinkedHashMap<String, ViewDescriptor>();
}
/**
* Creates a new instance of <code>ModifiableBeanFactoryViewDescriptorRegistry</code>
* with the specified list of view <code>descriptors</code> to initially register.
*
* @param descriptors The list of view descriptors to register.
*/
public ModifiableBeanFactoryViewDescriptorRegistry(List<ViewDescriptor> descriptors) {
Assert.notNull(descriptors, "descriptors cannot be null");
for(ViewDescriptor descriptor : descriptors) {
addViewDescriptor(descriptor);
}
}
/**
* Add the specified view <code>descriptor</code> to this registry.
*
* @param descriptor The view descriptor to register.
*/
public void addViewDescriptor(ViewDescriptor descriptor) {
Assert.notNull(descriptor, "descriptor cannot be null");
descriptors.put(descriptor.getId(), descriptor);
}
/**
* Gets the view descriptor with the specified <code>viewDescriptorId</code>, returns null if
* a view descriptor has not been registered with the specified id.
*
* @param viewDescriptorId The id of the view descriptor to return.
* @return The view descriptor with the specified id, or null if one is not found.
*/
@Override
public ViewDescriptor getViewDescriptor(String viewDescriptorId) {
Assert.hasText(viewDescriptorId, "viewDescriptorId cannot be empty");
ViewDescriptor descriptor = descriptors.get(viewDescriptorId);
// If a ViewDescriptor was not found, see if we can find a ViewDescriptor bean to add and return from the application context.
if(descriptor == null) {
try {
ViewDescriptor bean = (ViewDescriptor)getApplicationContext().getBean(viewDescriptorId, ViewDescriptor.class);
addViewDescriptor(bean);
descriptor = bean;
} catch(NoSuchBeanDefinitionException e) {
// Do nothing, as a ViewDescriptor bean was not found.
}
}
return descriptor;
}
/**
* Gets the list of all registered view descriptors. Loads all beans of type ViewDescriptor from
* the application context and registers them if they are not found in the registry before
* returning the list.
*
* @return The list of all registered view descriptors, including all ViewDescriptor beans in the
* application context.
*/
@Override
public ViewDescriptor[] getViewDescriptors() {
Map<String, ViewDescriptor> beans = getApplicationContext().getBeansOfType(ViewDescriptor.class, false, false);
// Registers any unregistered ViewDescriptor beans.
for(String beanName : beans.keySet()) {
if(!descriptors.containsKey(beanName)) {
addViewDescriptor(beans.get(beanName));
}
}
return descriptors.values().toArray(new ViewDescriptor[descriptors.size()]);
}
/**
* Removes the specified view <code>descriptor</code> from this registry.
*
* @param descriptor The view descriptor to unregister.
*/
public void removeViewDescriptor(ViewDescriptor descriptor) {
Assert.notNull(descriptor, "descriptor cannot be null");
descriptors.remove(descriptor.getId());
}
}
Don't forget to register the new ViewDescriptorRegistry with the application services in the application context:
Code:
<bean id="viewDescriptorRegistry" class="org.blah.myapp.ModifiableBeanFactoryViewDescriptorRegistry"/>
<bean id="applicationServices"
class="org.springframework.richclient.application.support.DefaultApplicationServices">
... other services ...
<property name="viewDescriptorRegistryId">
<idref bean="viewDescriptorRegistry"/>
</property>
</bean>
Now you can create prototype view descriptors for views you want to dynamically display:
Code:
<bean id="PrototypeView"
class="org.springframework.richclient.application.docking.vldocking.VLDockingViewDescriptor" scope="prototype">
<property name="viewClass" value="org.blah.myapp.view.ClientView" />
<property name="autoHideEnabled" value="true" />
<property name="closeEnabled" value="true" />
<property name="floatEnabled" value="true"/>
<property name="maximizeEnabled" value="true"/>
<property name="dockGroup" ref="Forms"/>
<property name="resizeWeight" value="1"/>
<!--<property name="securityControllerId" value="adminController"/>-->
</bean>
and pass them data when shown with a command like this:
Code:
public class ShowClientViewCommand extends ApplicationWindowAwareCommand {
/**
* The id value used to access the prototype client view.
*/
private String prototypeViewId;
/**
* Sets the id value used to access the prototype client view to <code>prototypeViewId</code>.
*
* @param prototypeViewId The new prototype client view id.
*/
public void setClientViewId(String prototypeViewId) {
this.prototypeViewId= prototypeViewId;
}
/**
* Creates a new instance of <code>ShowClientViewCommand</code>.
*/
public ShowClientViewCommand() {
super("showClientViewCommand");
}
@Override
protected void doExecuteCommand() {
Client client = // Get the currently selected client, however you do that in your application...
if(client != null) {
ApplicationPage page = getApplicationWindow().getPage();
if(page instanceof VLDockingApplicationPageEx) {
VLDockingApplicationPageEx vlpage = (VLDockingApplicationPageEx)page;
ModifiableBeanFactoryViewDescriptorRegistry registry = (ModifiableBeanFactoryViewDescriptorRegistry)vlpage.getViewDescriptorRegistry();
// Create a new id for the new view instance.
String newId = prototypeViewId + " - " + client.getClientCode();
// Check for a prototype view for the selected client. This allows me
// to open a view for a client, but if I try to open it again for the same
// client, everything finds the first one I opened, so a new one isn't
// created, this avoids opening duplicate windows.
DefaultViewDescriptor viewDescriptor = (DefaultViewDescriptor)registry.getViewDescriptor(newId);
if(viewDescriptor == null && Application.instance().getApplicationContext().isPrototype(prototypeViewId)) {
// Create a new instance of the prototype view if one with the new name wasn't found.
viewDescriptor = (DefaultViewDescriptor)Application.instance().getApplicationContext().getBean(prototypeViewId);
viewDescriptor.setId(newId);
registry.addViewDescriptor(viewDescriptor);
}
// Show the prototype view, passing it the selected client.
View view = vlpage.showView(viewDescriptor, true, client);
}
}
}
}
Now you can register the command in your commands context and use it in menus/toolbar/other forms.
Code:
<bean id="showClientViewCommand"
class="org.blah.myapp.command.showClientViewCommand">
<property name="clientViewId" value="PrototypeView"/>
<!--<property name="securityControllerId" value="adminController"/>-->
</bean>
<bean id="toolBar"
class="org.springframework.richclient.command.CommandGroupFactoryBean">
<property name="members">
<list>
<value>newCommand</value>
<value>saveCommand</value>
<value>printCommand</value>
<value>separator</value>
<value>propertiesCommand</value>
<value>showClientViewCommand</value>
</list>
</property>
</bean>
This of course does not handle loading/saving these prototype views. If you are using VLDocking support and saving your ApplicationPage layouts to xml, this will make them fail when they try to load the dynamic view id's you created. To fix this, you'll need to override the #loadInitialLayout() method in your custom VLDockingApplicationPage and pre-register dockables for your prototype views BEFORE reading in the xml file.
Anyway, this kind of thing is why Spring Rich Rocks, hope this helps someone.