The basic idea is to create a Custom View that doesn't interfere with the
Project Guide. See
http://msdn.microsoft.com/library/d...k/html/pjsdkpg101cv07_Building_HV01111342.asp.
You can get the default Project Guide files and a sample Custom View in the
Project 2003 SDK download.
You need to use the Application.LoadWebBrowserControl method with the
correct TargetPage and WrapperPage parameters in a VBA macro. You can then
assign the macro to a custom toolbar button, menu item, or call it from
another macro. The trick is to do this and allow the Project Guide and other
Project views to work in parallel (whether visible or not). The optional
WrapperPage contains the code that allows Project to work that way.
See VBA Help for the LoadWebBrowserControl Method
(
http://www.msdn.microsoft.com/libra...tml/pjmthLoadWebBrowserControl_HV43008557.asp).
Using the WrapperPage and its associated JScript is why the TaskForm custom
view sample in the SDK download looks complicated. However, it is fairly
straightforward to add the funcionality you want and still preserve the
Project Guide. You need to make a one minor modification to the default
Project Guide mainpage.htm file, and a small addition to the mainpage.js file
that mainpage.htm points to – and then add the custom URL frame and
framewrapper HTML and JScript files, which themselves just have minor
modifications to default files.
Use the following snippets to create a sample that shows msdn.microsoft.com
in a custom view. You may need to adjust the line breaks.
1. Copy the default mainpage.htm to another directory, and modify it as
follows, to call a modified mainpage.js (see step 2).
<!-- ProjectGuideMainpage -->
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta http-equiv="MSThemeCompatible" content="Yes">
<title> Microsoft Office Project </title>
<!-- AddedCV: Call a modified mainpage.js -->
<script src="pgmainpage://mainpage.js" language="JScript"></script>
<script src="gbui://util.js" language="JScript"></script>
....
Note, however, the pgmainpage:// custom protocol does not work with WinXP
SP2. In that case, you would need to provide a hard-coded path. See
http://msdn.microsoft.com/library/d...tml/pjsdkpg101a18_Localization_HV01111333.asp
2. Copy the default mainpage.js to the same directory.
--Add the "//AddedCV" section just above the "// Various XML schema tags"
comment:
// AddedCV: A custom view content URL.
var constTestUrlContentPage = "pgmainpage://TestUrlFrame.htm";
var constTestUrlWrapper = "pgmainpage://TestUrlFrameWrapper.htm";
// ------------------------------------------------------
// Various XML schema tags
// ------------------------------------------------------
--Add the following below (*outside of*) the projectActivate() function:
// AddedCV.
// This function gets called when the wrapper for the custom view
// is loaded. It spins until the wrapper's load completes.
// ------------------------------------------------------
// ------------------------------------------------------
function loadFrameWrapperContentTestCV()
{
var rFrame = rscript();
if (rFrame.document.readyState != "complete")
{
nUpdateCounter++;
if (null != pageLoadTimer)
{
window.clearTimeout(pageLoadTimer);
pageLoadTimer = null;
}
pageLoadTimer = window.setTimeout("loadFrameWrapperContentTestCV()",
constWaitTimeOut);
return;
}
nUpdateCounter = 0;
rFrame.viewFrame.location.replace(constTestUrlContentPage);
}
--Add the "// AddedCV" section *within* the function
handle_LoadWebPage(targetPage)
--and *just below* the test "// turn wizard mode off if wizard is active":
// ------------------------------------------------------
// Handle LoadWebPage event
// ------------------------------------------------------
function handle_LoadWebPage(targetPage)
{
lastTargetPage = null;
if (false == wizardAskToExit())
// No button - return and do nothing
return;
// turn wizard mode off if wizard is active
if (true == isWizardMode())
{
setWizardModeOff();
}
// AddedCV.
// First, see if we're going to a page in the custom targetPage values.
That's
// every targetPage outside the Microsoft reserved range of "0" to "999".
switch(targetPage)
{
case "TestURL":
var rFrame = rscript();
rFrame.location.href = constTestUrlWrapper;
loadFrameWrapperContentTestCV();
return;
break;
}
....
3. Create TestUrlFrame.htm in the same directory. It specifies the URL to
load:
<html>
<head>
<title>Microsoft Project </title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta http-equiv="MSThemeCompatible" content="Yes">
<script src="gbui://util.js" language="JScript"></script>
<style type="text/css"> body {font-family: Verdana, Arial, Helvetica;
margin:0px; overflow: hidden;}
table {width:100%; height:3em; border:0px none; font-size:70%}
.activeLink {color: blue; text-decoration:underline; cursor:hand; }
.nonActiveLink {color: blue; text-decoration:none; cursor:auto; }
</style>
</head>
<body onContextMenu="return false">
<!-- This loads the URL test view into the view's content space. -->
<iframe id="fullFrame" name="FullViewFrame" style="LEFT:0px; WIDTH:100%;
POSITION:relative; TOP:0px; HEIGHT:100%"
frameborder="1" marginheight="0" marginwidth="0" tabIndex="2"
src="
http://msdn.microsoft.com/default.aspx"></iframe>
</body>
</html>
4. Create TestUrlFrameWrapper.htm. This is just modified a bit from the
default wrapper.htm.
Again, use a hardwired path instead of the the pgmainpage:// protocol for
WinXP SP2 machines.
<!-- ProjectGuideMainpage -->
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta http-equiv="MSThemeCompatible" content="Yes">
<title> Microsoft Project </title>
<script src="gbui://util.js" language="JScript"></script>
<script src="pgmainpage://TestUrlFrameWrapper.js"></script>
<style type="text/css">
body {font-family: Verdana, Arial, Helvetica; margin:0px;
overflow: hidden;}
table {width:100%; height:3em; border:0px none; font-size:70%}
.activeLink {color: blue; text-decoration:underline; cursor:hand; }
.nonActiveLink {color: blue; text-decoration:none; cursor:auto; }
</style>
<script language="JScript">
initWrapperPageData();
</script>
<!-- Document event handlers -->
<object id="MSPJDocObj"
ALT="Microsoft Project Document Event Handler"
classid="CLSID:494B3458-3EFF-4C66-9C86-D47670D69634"
style="display:none" >
</object>
<!-- Application event handlers -->
<object id="MSPJAppObj"
ALT="Microsoft Project Application Event Handler"
classid="CLSID:04EEB710-3EB7-4B69-9281-E9BBB7B35959"
style="display:none" >
</object>
<script for="MSPJAppObj" event="WindowBeforeViewChange(wnd, prevView,
newView, projectHasViewWindow, EventInfo)" language="JScript">
if (this_Project_Window == wnd.Index)
{
handle_BeforeViewChange();
}
</script>
<script for="MSPJAppObj" event="LoadWebPage(wnd, targetPage)"
language="JScript">
if (this_Project_Window == wnd.Index)
{
handle_LoadWebPage(targetPage);
}
</script>
</head>
<body onContextMenu="return false">
<!-- This spot at the head of the wrapper is a good place for
non-view-specific .
functionality. If you have any UI that should be available to every
view using
this wrapper, regardless of the wrapper's content, put it here. -->
<table id="header" frame="below" bordercolor="buttontext">
<tr>
<td align="right" vAlign="center">
<a class="trigger" href="#" onClick="closeCustomView();">
Click here to close this view
</a>
</td>
</tr>
</table>
<!-- viewFrame is the content section of the wrapper. This frame's source
will be replaced
with whatever content file we need in the view. Users should never see
the
'Content will be loaded here' text, since this wrapper should never
load unless there's
content being put into viewFrame. -->
<iframe id="viewFrame" name="ViewFrame"
style="position:relative; left:0; top:0; width:100%;
height:expression(calFrameHeight())"
frameborder=1 marginheight=0 marginwidth=0 tabIndex=2>
Content will be loaded here
</iframe>
</body>
</html>
5.Create TestUrlFrameWrapper.js for the Custom View (modified from a default
Wrapper.js).
Again, don't use the pgmainpage:// protol for WinXP SP2.
// This error string warns scripters that they've called LoadWebBrowserControl
// with a bad targetPage parameter. In an actual rollout, it should be
changed
// to something friendlier to end users.
var L_BadParameter_Message = "The LoadWebBrowserControl method has been
called with an invalid parameter.";
// Define the values of some needed URLs. The custom view content
// URL goes to the test URL Web site. The other one goes to the
// default Project Guide right pane, which shows the traditional Microsoft
Project
// document UI.
var constTestUrlContentPage = "pgmainpage://TestUrlFrame.htm";
var constProjectView = "gbui://right.htm";
// Create a variable to cache the index of the current project window.
// That way we can see whether document events are meant for our document by
// comparing the cached window index to the one sent as an event parameter.
//***********************************************************************
var this_Project_Window = "";
//***********************************************************************
// NAME: initWrapperPageData
// PURPOSE: Page load, where objects are initialized, calls
initWrapperPageData.
// Here, initWrapperPageData simply caches our document window index.
// PARAMETERS: None
// RETURN: None
//***********************************************************************
function initWrapperPageData()
{
this_Project_Window = window.external.Index;
}
//***********************************************************************
// NAME: calFrameHeight
// PURPOSE: Calculates a size for the custom view body that ensures
// the header will stay visible.
// PARAMETERS: None
// RETURN: None
//***********************************************************************
function calFrameHeight()
{
try
{
var nFrameSize = document.body.offsetHeight;
nFrameSize -= header.offsetHeight;
return nFrameSize;
}
catch(exp)
{
}
return 0;
}
//***********************************************************************
// NAME: unloadWebBrowser
// PURPOSE: Cleans up and closes the custom view using
// Application.UnloadWebBrowserControl. If the Project Guide is up,
// this call tries to restore the current sidepane's default view
// before calling UnloadWebBrowserControl. If there's no Project Guide
// up, unloadWebBrowser directly calls UnloadWebBrowserControl.
// Either way, UnloadWebBrowserControl gets called.
//
// UnloadWebBrowserControl works like LoadWebBrowserControl in reverse. If
// there's a Project Guide up, then it doesn't do anything. It leaves the
// Internet Explorer window up and running so the Project Guide can use it.
// If the Project Guide isn't running, UnloadWebBrowserControl takes Internet
// Explorer out of the view area and brings back the traditional view
// interface.
//
// unloadWebBrowser makes assumptions about the Project Guide mainpage. If
this
// code is executed with a Project Guide that didn't include the method
// rscript(), it would fail. You have to update wrapper code that deals
// with the mainpage layout whenever the Project Guide mainpage changes.
// PARAMETERS: None
// RETURN: None
//***********************************************************************
function closeCustomView()
{
try
{
var parentApp = window.external.Application;
if(parentApp.DisplayProjectGuide)
{
var fRestoreView = false;
var script = pscript();
// First, find out if the Project Guide is showing a task sidepane
// or a goal area menu. If it's showing a task sidepane, then
// we'll use the default mainpage's restoreView function to bring up
// an appropriate view. If it's showing a goal area menu, then
we'll
// refresh the menu by navigating to it, which will trigger any
needed
// view changes.
var tmpTaskID = script.getActiveTaskID();
if ((null == tmpTaskID) || ("" == tmpTaskID))
{
fRestoreView = true;
}
var tmpGoalAreaID = script.getActiveGoalAreaID();
if ((null == tmpGoalAreaID) || ("" == tmpGoalAreaID))
{
tmpGoalAreaID = 1;
}
if (false == fRestoreView)
{
script.navigate(tmpGoalAreaID, -1, null);
}
else
{
script.restoreView(true);
}
rscript().location.href = script.default_view_page;
}
parentApp.UnloadWebBrowserControl;
}
catch(exp)
{
}
}
//***********************************************************************
// NAME: handle_BeforeViewChange
// PURPOSE: This gets called in response to BeforeViewChange events. It
// closes the custom view to insure that the upcoming view change can
// go smoothly.
// PARAMETERS: None
// RETURN: None
//***********************************************************************
function handle_BeforeViewChange()
{
closeCustomView();
}
//***********************************************************************
// NAME: handle_LoadWebPage
// PURPOSE: Users order custom view loads by calling
Application.LoadWebBrowserControl
// in the Project object model. That command loads the IE window, makes
// sure it has a mainpage or wrapper catching events, and then sends that
// page a LoadWebPage event. Here, we're interpreting the targetPage
// parameter of that LoadWebPage event. If it's a code string that we
recognize,
// load the corresponding content into the view frame. If not, put up the
default
// view control. To extend this, just add more cases to the switch statement.
// PARAMETERS: targetPage -- A string indicating which content URL to load.
// RETURN: None
//***********************************************************************
function handle_LoadWebPage(targetPage)
{
try
{
var parentApp = window.external.application;
// Project Guide mainpages handle LoadWebPage events for any custom
views
// meant to work inside the Project Guide. If the Project Guide is up,
// then we'll defer to its mainpage.
if(parentApp.displayProjectGuide)
{
return;
}
var urlToGo = null;
switch(targetPage)
{
case "TestURL":
// This targetPage string maps to the Test URL custom view.
urlToGo = constTestUrlContentPage;
break;
default:
// If we couldn't map to targetPage to a URL, show the
traditional view UI
// as a default.
urlToGo = constProjectView;
try
{
// Warn the user that their targetPage wasn't recognized.
window.external.application.Message(L_BadParameter_Message);
}
catch(Exception)
{
}
break;
}
document.all.viewFrame.src = urlToGo;
handleResize();
}
catch(exp)
{
}
}
6. In Project, click Tools – Options – Interface, and then click Use a
custom page for the Project Guide Functionality and Layout Page. Browse to
the mainpage.htm file you just unzipped.
7. Customize the Project toolbar, to add a button with a macro that calls
the custom view:
Right-click on the Project toolbar, and click Customize. In the Customize
dialog, scroll down the Categories list and click All Commands. Scroll down
the Commands list, click LoadWebBrowserControl, and then drag and drop it to
a location you want in one of the toolbars.
Right-click the LoadWebBrowserControl button you just added to the toolbar
and click Assign Macro. In the Customize Tool dialog, in the Name field type
a name such as Test URL. In the Command field, paste in the text in the file
MacroCommand.txt. That is:
Application.LoadWebBrowserControl "TestURL",
"pgmainpage://TestUrlFrameWrapper.htm"
(And -- you guessed it -- don't use the pgmainpage:// protocol if you're
going to run this on WinXP SP2.)
8. In IE, add the URL to your list of trusted sites (in Tools – Internet
Options – Security – Trusted sites – Sites, add
http://msdn.microsoft.com
to the list)
9. Click your Test URL button in Project, to see the custom view. Try it
both with and without the Project Guide visible.
The "Click here to close this view" control comes from
TestUrlFrameWrapper.htm, and helps the custom view be a good citizen in
Project.