Setting Custom Context Variables In A Part's Context Before Part Creation [message #912414] |
Thu, 13 September 2012 10:35  |
Eclipse User |
|
|
|
I have a situation where I'd like to establish a detail object of interest for some part before the part and its view/contribution are created. The part's associated context seemed like a logical place to do this since the object of interest could be injected into the detail view contribution and be available to its @PostConstruct method.
However... it appears that the part's context is not created until the part is already in the renderer, so I don't get an opportunity to set the object of interest in the part's associated context until after the @PostConstruct method is called. This has led to some slightly clumsy code in the view contributions for the parts. Things I could have gotten over with in the @PostConstruct method now have to be delayed until some later @Inject setter is called (that approach works fine BTW, but it feels kind of out-of-the-loop).
I tried creating a child context of the perspective's context and setting it on the new part prior to creation/activation; when I did this the framework created the part and associated view contribution but the contribution's @PostConstruct method was never called. Maybe this would work and I'm just missing some additional set-up calls (?).
In my case at least it would be smoother if there were a way to get custom context variables established - in the part's associated context and not upstream of it - before the part is created/activated through the part service.
Is there a way to do this?
|
|
|
|
Re: Setting Custom Context Variables In A Part's Context Before Part Creation [message #913040 is a reply to message #912427] |
Fri, 14 September 2012 12:43   |
Eclipse User |
|
|
|
Thanks Sopot - that worked perfectly.
In summary, when a new part is created - e.g. through MBasicFactory.INSTANCE.createPart(), or partService.createPart(someDescriptorId), or probably any other way - the context won't be non-null until the part is shown though the rendering machinery as noted by Sopot above. By the time partService.showPart or partService.activate returns the part contribution's @PostConstruct method has already been called.
To set extra data in the context and make it available for injection prior to the part contribution's @PostConstruct method being called we can use Sopot's suggestion. The method below was swiped from a project we have in development, and demonstrates how we borrowed from the code in the link noted above. There are a few things in here that prevent it from being used as-is, but with just a bit of tailoring some of this might be useful to somebody.
Some of this content was borrowed from tutorials by Lars Vogel, Jonas Helming, and others.
The "View" class used in this snippet is a relatively simple convenience that sits above all our part contribution classes, and IContainedObject is the thing we wanted to stuff in the new part's context. One could easily modify this to take a more general set of data values to stuff.
public void showView(
View requestingView,
Class<? extends View> viewClass,
String targetPartStackId,
final IContainedObject containedObject
)
{
IEclipseContext context = requestingView.getPart().getContext() ;
MApplication app = context.get( MApplication.class ) ;
final EModelService modelService = context.get( EModelService.class ) ;
MUIElement uiElement = modelService.find( targetPartStackId, app ) ;
if( uiElement != null &&
uiElement instanceof MPartStack )
{
MPartStack partStack = (MPartStack)uiElement ;
// Either of these works. Not sure which is best practice.
// It probably is a tradeoff between programmatic convenience
// and having to add stuff to some Application or fragment
// e4xmi file. Note that is part descriptors are used then
// some of the calls made below are not necessary because the
// property values are loaded from the e4xmi file.
final MPart part = MBasicFactory.INSTANCE.createPart() ;
//final MPart part = partService.createPart( "com.zedasoft.viewer4.fc.view.e4.testpartdescriptor" ) ;
part.setLabel( containedObject.getDisplayName().toString() ) ;
part.setCloseable( true ) ;
part.setToBeRendered( true ) ;
part.setVisible( true ) ;
//part.setElementId( viewClass.getName() + ":" + _secondaryId++ ) ;
part.setContributionURI( viewClass.getName() ) ;
// Set up a handler to set some data in the part's context so
// it can be available to inject into the part's contribution
// and be there when the @PostConstruct method is called. Most
// of the code in this block was borrowed from:
//
// https://bugs.eclipse.org/bugs/attachment.cgi?id=219843
{
IEventBroker eventBroker = context.get( IEventBroker.class ) ;
eventBroker.subscribe( UIEvents.Context.TOPIC_CONTEXT, new EventHandler() {
public void handleEvent( Event event )
{
Object origin = event.getProperty( UIEvents.EventTags.ELEMENT ) ;
Object context = event.getProperty( UIEvents.EventTags.NEW_VALUE ) ;
if( ( origin instanceof MHandlerContainer ) &&
( UIEvents.EventTypes.SET.equals( event.getProperty( UIEvents.EventTags.TYPE ) ) &&
context instanceof IEclipseContext ) )
{
IEclipseContext eclipseContext = (IEclipseContext)context ;
MPart contextPart = eclipseContext.get( MPart.class ) ;
if( contextPart == part )
{
// Set extra context data here.
eclipseContext.set( View.CONTEXT_CONTAINED_OBJECT, containedObject ) ;
}
}
}
} ) ;
}
partStack.getChildren().add( part ) ;
{
EPartService partService = context.get( EPartService.class ) ;
// Either of these works. The latter one probably is best practice.
//partService.showPart( part, EPartService.PartState.ACTIVATE ) ;
partService.activate( part ) ;
}
}
else
{
Logger logger = context.get( Logger.class ) ;
logger.error( "Couldn't find a part stack with id \"" + targetPartStackId + "\"." ) ;
}
}
All that aside, this general topic probably is worth further discussion. It seems like it would be common in drill-down presentations to want a value present in a new part's context before the contribution springs to life. If true, then one needs to wonder why it isn't possible to simply do this before the part is shown:
newPart.getContext().set(...whatever...)
This obviously is far simpler than the event handler workaround. I can imagine reasons why context creation is deferred, but it definitely led to some hoop-jumping in this situation. Knowing little about the rationale behind E4 internals this may be a naive suggestion, but the getContext() method feels like a candidate for an "if you have it then return it, else create it then return it" approach. Then the user can put whatever they want in there, and the framework can take care of the remainder of the setup when the part is finally added to UI object graph and the rest of the context ancestry can be determined.
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.04639 seconds