SharePoint Development from a Documentation Perspective
Andrew May
Here's an interesting little "gotcha" that was found by Saul Candib, the programmer-writer who is taking over documenting OneNote programming features, now that I've moved over to SharePoint.
Using my article Importing Content into OneNote 2003 SP1 as a guide, Saul wrote some code that imported content into OneNote. The code seemed to work fine: OneNote would create the page the user specified, and import the specified content no problem.
Except, once you closed OneNote, when you opened it again the page and all its contents were gone.
Turns out Saul wasn't specifying the '.one' file extension for the page. So the import routine worked perfectly; OneNote created the page and imported the content. You could even go in and add or edit the content on the new page, whatever you wanted. OneNote actually was saving the page in the My Notebooks folder, but without an extension. So the next time you launched OneNote, it saw the imported page as an unknown file and so didn't load it.
All of this was news to me. In my XML examples in the article, I used the .one file extension. For example:
<PlaceObjects pagePath="MSN Instant Messenger\Conversations.one" pageGuid="{8FDD3C41-5BB5-4595-B019-7E7E9BC9D38E}">
But I never specifically called out that this was a requirement; to be honest, I don’t remember if I ever realized it was a requirement. I certainly never experimented with what happened if I didn't specify the file extension.
What's worse, in my example in the article, I wrote a really basic application that let the user enter the name of the page they wanted to create. But I didn't write any code that checked the specified page name to make sure it contained the file extension, or add the extension if the page name didn't. Shame on me.
So, I was so annoyed when I realized I had broken one of the basic rules of information design with my diagram of the OneNote 2003 SP1 SimpleImport schema, I had to take a few minutes and see if I could fix it. As I mentioned in my last entry, my diagram uses indenting and boxes to show the element hierarchy in the OneNote schema. But, because I was illustrating the same information (the element hierarchy) two different ways (indenting and boxing), the diagram contained redundant information, and was more complicated than it needed to be.
And I hated those damn boxes anyway. Chopping a diagram up into a grid like that only ends up distracting from the information. I knew it at the time, but I was on a deadline and couldn't come up with a better solution, so…
Below is my latest attempt. The grid lines are gone; element hierarchy is denoted now solely by the indenting of the element names. By formatting the information inside each element (such as data type and attributes) gray, I think I've been able to keep the element names prominent enough I don’t need boxes to denote where one element ends and another starts.
One thing that I still see as problematic is the indenting of the element information, like the data type and attributes. In this small example, I was able to keep all that information at the same left-alignment for all the elements, which again keeps that information from distracting from the element names. But, had the schema hierarchy included a few more levels, I would've had to move the element information even further over, perhaps to the point where it was so far removed from the element names in the top-level elements that it would seem disconnected.
All in all, I think this diagram is quite a bit more successful than the one that currently appears in the article. Looks like it's time to file a bug and get that image swapped out…
In this series of entries, we're taking an in-depth at the OneNoteImporter manage class, which provides an object model interface for the programmability functionality added in OneNote 2003 SP 1.
Read part one here.
Read part two here.
Read part three here.
Read part four here.
The following figures diagram the OneNoteImporter assembly object model, including abstract classes and inheritance. The diagrams mainly document how the objects in the assembly relate to each other. In most cases, when a member takes an object as a parameter, or returns an object, that object is included on the diagram. Value types, such as string or integers, are for the most part not displayed.
For the sake of clarity, the following object information, pertaining to methods that most of the classes have, has been left off the diagrams:
· The Clone method returns the type of object from which you call it.
· The Equals method takes a System.Object as a parameter.
· The GetHashCode method returns a System.Int32 object suitable for use in hashing algorithms and data structures like a hash table.
· Inheritance from System.Object is not shown.
Figure 2. The Application Object (and Legend)
Figure 3. The ImportNode Abstract Class, and Page Class
Figure 4. The PageObject Abstract Class, and Derived Classes
Figure 5. The OutlineContent Abstract Class, and Derived Classes
Figure 6. The Data Abstract Class, and Derived Classes
The OneNoteImporter managed assembly provides a convenient and multi-functional ‘wrapper’ for working with the SimpleImporter and command line functionality in OneNote 2003 SP1. Moreover, using the provided source files for the assembly, a developer can customize and extend the classes as required for his particular application.
Figure 1 diagrams the internal method calls of the Commit method. It shows the OneNoteImporter classes and methods called, and the Import XML elements generated at each step. The various calls to System.Xml methods are not diagrammed.
Figure 1. The Page.Commit Method, and XML Elements Generated
In our final entry, we'll look at some object model maps that detail how the OneNoteImporter class is structured.
Read part five here.
The actual creation of the XML import document, and importing the page contents, takes place when you call the Page.Commit method. This method, in turn, invokes a number of methods in other OneNoteImporter objects. Because of this method’s importance and complexity, it’s worth examining how the method functions.
First, the code checks to see if the page has changed in any way from the last time it was imported. It does this by determining if the Page object’s CommitPending property is set to True. If it is, it calls the SimpleImporter.Import method.
The code calls the Page.ToString method to generate the XML string it passes to the Import method. The ToString method in turn calls the Page.SerializeToXml method.
This begins a series of recursive calls to the SerializeToXml methods of the various objects. Each object’s SerializeToXml method includes instructions to call the SerializeToXml method of any child objects, and append the resulting XML to the parent element. This in turn invokes the SerializeToXml method of any child objects the original child object might have, and so on, until the entire page structure has been serialized to xml in a single xml document.
The Page.SerializeToXml begins by creating a new XmlDocument object, and generating <Import> and <EnsurePage> elements and adding them to the document. Page object property values are used to set the various attributes of the <EnsurePage> element.
Note that the Commit method generates import XML with both <EnsurePage> and <PlaceObjects> elements for that page. Specifying an <EnsurePage> element for a page guarantees that the page exists before OneNote attempts to import objects onto it. So if your application includes a scenario where you only want to import objects onto a page if the page already exists, you’ll need to modify this method, or use another means.
The code then generates a <PlaceObjects> element. For each of the Page object’s children whose CommitPending property is set to True, the code calls the PageObject.SerializeToXml method.
If the page object’s DeletePending property is set to True, the PageObject.SerializeToXml method generates a <Delete> element. If not, the method does three things:
· Generates a <Position> element, whose attributes are set according to Position object property values.
· Calls the SerializeObjectToXml method for the specific PageObject-derived class involved, i.e., ImageObject, InkObject, or OutlineObject.
· Calls the SerializeToXml method for the specific OutlineContent-derived class involved, i.e., HtmlContent, InkContent, or ImageContent.
Executing the SerializeToXml method for each of these content types includes a call to the SerializeToXml method for the Data-derived object they contain: BinaryData, FileData, or StringData. In this way, the entire page structure is serialized to xml in a single xml document.
Note that the HtmlContent.SerializeToXml method includes a call to another internal method of that same object, called CleanHtml. The CleanHtml method reads through the html string or file data and makes sure the HTML is formatted in a way that OneNote accepts. It identifies and replaces problematic formatting with characters which OneNote can process. For example, the CleanHtml method wraps the HTML string with the appropriate <html> and <body> tags if the HTML lacks them.
The serialization of the page nodes is now complete. If the Page object had no children, the <PlaceObjects> element remains empty. In such a case, the Page.SerializeToXml method does not append it to the <Import> element.
Finally, the Page.SerializeToXml method determines the appropriate namespace designation and adds it to the <Import> element.
The ToString method then takes the XmlDocument object, saves it as a text stream, converts it to a string, and passes it back to the SimpleImporter.Import method. This Import method uses the XML string to import the specified content into OneNote.
Now that the content has been imported into OneNote, the Commit method performs some vital housekeeping. Using the RemoveChild method, it removes any of the Page object’s children who have their DeletePending property set to True. It then sets the private committed field to True, thereby making the Date, PreviousPage, RTL, and Title properties read-only. You cannot change these attributes once you import a page into OneNote.
Lastly, it sets the CommitPending property of the Page to False. This it turn sets the CommitPending properties of all the Page object’s remaining children to False as well.
In part four, we'll examine the internal method calls of the Commit method.
In part one, we started to take an in-depth at the OneNoteImporter manage class, which provides an object model interface for the programmability functionality added in OneNote 2003 SP 1.
Creating the page and the object you want to import onto it is relatively straight-forward. The Page, PageObject-derived sub-classes, and OutlineContent-derived sub-classes all have one or more public constructors with which you create new instances. We should, however, briefly touch on a few issues to keep in mind.
Both the Page and PageObject-derived sub-classes have an internal Id property, which gets or sets an ObjectId object. The ObjectId represents the globally unique identifier (GUID) OneNote requires to identify each page and object imported. The data in an ObjectId consists of a private field representing the containing object’s identifier. The ObjectId object is generated when the page or object is constructed.
The ObjectId object cannot be accessed from outside the assembly. The OneNoteImporter assembly generates and maintains the GUIDs for the user. If you are programming for a scenario that requires you to have access to the GUIDs, there are two approaches to consider:
· Override the class, and make the GUID properties publicly accessible.
· Serialize and persist the Page object between sessions, and then deserialize it as necessary.
Note that the ObjectId class stores the object GUID internally as a System.Guid data type, which does not store the GUID in registry format, that is, surrounded by curly braces ({}). OneNote only accepts GUIDs in registry format. Therefore, the ObjectId.ToString method has been overridden so that it wraps the guid value in curly braces before returning the string.
Similarly, if you use the ObjectId(string) overloaded constructor, the constructor code strips off curly braces, if present. As they might be, if you were passing a serialized ObjectId as an argument.
The CommitPending property determines whether or not an import node is included in the XML passed to the SimpleImporter.Import method. Objects with this property set to True represent one of the following:
· an object that hasn’t been imported into OneNote
· an object which as been changed since the last time it was imported; this includes objects marked for deletion from OneNote
· an object that contains a child object that is either of the two cases above
Only objects whose CommitPending property is set to True are serialized and included in the import XML string. By default, whenever an object is constructed from an ImportNode-derived class, this property is set to True. Let’s examine how this property is used, and when they value is changed.
Calling the Page.Commit method imports the page and its contents into OneNote. Once you import a page, the CommitPending property is set to False for that Page object and all the objects contained on it. If you immediately called the Commit method again, neither the page nor any of the objects on it would be included in the XML for the next import.
However, when you make a change to an object, such as changing a property value, the CommitPending property gets set back to True. Not only that, but the CommitPending property of any parent object gets set to True as well. This happens because of the way the import XML is structured. If you want to re-import a specific object, you must also include information about all the objects that contain it, such as the page it appears on. Setting the CommitPending property to True for all the parent objects ensure all the necessary information is included in the import XML string.
Here’s how it actually happens in the code:
You cannot access the CommitPending property from outside the OneNoteImporter class assembly, but if you change any public object properties, the CommitPending property is set to True, so that those object changes are imported into OneNote the next time you call the Page.Commit method. This includes setting the DeletePending property of an object to True.
In addition, the CommitPending property code also sets the CommitPending property of the parent object, if there is one, to True. Which in turns calls the CommitPending property code again, in case that parent object has a parent, and so on up the import node structure to the Page object.
Conversely, when the Commit method sets a Page object’s CommitPending property to False, the property code sets the CommitPending property of any children to False as well, which in turn calls the method again, in case any of those objects have children, and so on.
You can add content directly to a page, and to an outline on a page. The public Page.AddObject and OutlineObject.AddContent methods both call the internal ImportNode.AddChild method, which adds the child object to a private ArrayList object. A convenience constructor enables you to name the object when you add it, if you want.
OutlineContent objects are displayed in the order in which they are added to the OutlineObject object.
To import text into a OneNote page, you add an HtmlContent object to an OutlineObject, and then add the OutlineObject to the desired Page object.
In the OneNoteImporter assembly, there are two levels of deleting objects:
· Marking an object for deletion the next time the page is imported into OneNote
· Removing an object from the OneNoteImporter object structure; for example, as removing an ImageContent object from an OutlineObject object
To delete an object from a OneNote page, use the Page.DeleteObject method. This method does not directly remove the object from the Page object’s private children array. Rather, it sets the DeletePending property of the specified object to True. Therefore, when you execute the Page.Commit method, the object gets deleted from the page in OneNote. Only then is the ImportNode.RemoveChild method called. It is this internal method that removes the object from the Page object’s array, using the ArrayList.Remove method. The RemoveChild method also sets the Page object’s CommitPending property to True; however, the Commit method then sets it to False.
Note You can even call the DeleteObject method for objects that have not been imported into OneNote. While this means the object is serialized and included in the XML string passed to the SimpleImporter.Import method, no error results. OneNote ignores XML elements directing it to delete objects not present on the specific page. This is by design, since the OneNote user may have manually deleted the object themselves.
Conversely, to remove an object from an outline, use the OutlineObject.RemoveContent method, which directly calls the ImportNode.RemoveChild method. So the object is removed from the OutlineObject object’s children array at the time the RemoveContent method executes. The RemoveChild content also sets the CommitPending property of the OutlineObject object to True, so that the entire updated outline (minus the removed object) is imported into OneNote again the next time the Page.Commit method executes.
Both the Page and OutlineObject objects implement the IEnumerable interface. Their respective public GetEnumerator methods provide the ability to iterate through their private children array.
In part three, we'll get to the whole point of the OneNoteImporter class: importing objects into OneNote.
A while back I wrote a series of entries dealing with how you can use Donovan Lange's OneNoteImporter manage class to make importing content into OneNote 2003 SP1 even easier. To recap: Donovan's OneNoteImporter managed class assembly provides an object model interface for the programmability functionality added in OneNote 2003 SP 1. Both the Send to OneNote from Outlook and Send to OneNote from Internet Explorer PowerToy add-ins actually use the OneNoteImporter class in their source code.
You can download the OneNoteImporter class source code from Donovan's blog here.
You can read my initial series of blog entries about the OneNoteImporter class here: part one, and part two.
For the next week or so I'll be running a series of entries that examine the OneNoteImporter class in more detail, and really get 'under the hood' on how the class is structured and functions.
The OneNoteImporter managed class assembly provides an object model interface for the programmability functionality added in OneNote 2003 Service Pack (SP) 1.
The classes in this assembly enable you to:
· Import content into OneNote SP1 using the SimpleImport.Import method without having to explicitly author the XML string this method takes as a parameter. The OneNote managed assembly does this for you.
You do not need to be familiar with the SimpleImporter class, or the XML schema used by its Import method, to use the OneNoteImporter managed assembly. However, for more information about the SimpleImporter class and its operation, see Importing Content into Microsoft Office OneNote 2003 SP1.
· Navigate to a specific page in OneNote using the SimpleImport.NavigateTo method.
This is also covered in the article referenced above.
· Use class methods to invoke some of the more popular command line switches you can use to customize how the OneNote application launches.
For more information on customizing OneNote 2003 using command line switches, see Customizing OneNote 2003 SP 1 Using New Command Line Switches.
This article presents a detailed discussion of the internal design and function of the OneNoteImporter classes, in case a developer wants to modify the classes, or just know more about how they operate internally. For a general discussion of how to use the public members of the OneNoteImporter managed assembly, see OneNote Import Managed Assembly: The Quick Rundown (part one and part two).
Source files for the assembly are available from Donovan Lange's blog here. Developers are encouraged to modify the OneNoteImporter assembly as they desire and redistribute it with their application.
Note To avoid compatibility issues with other versions of the assembly that might be loaded on the user’s computer, include the .dll in your application directory, rather than the system directory.
There are several basic steps in using the OneNoteImporter assembly to import content in to OneNote:
· Create the page onto which you want to import content
· Create and add the content to the page
· Import the page into the desired OneNote location
We’ll discuss the internal operation of the assembly classes during each of these steps.
The OneNoteImporter assembly also enables you to update and delete pages or specific content on them, as long as you know their unique identifier. This is discussed in detail later in this article.
It’s worth noting at this point that the OneNoteImporter assembly is designed to import a single OneNote page at a time. The OneNote.SimpleImporter class can take an XML string that includes content to be imported onto multiple pages. However, when using the OneNoteImporter assembly, you create a separate XML string for each page you want to import.
Before discussing how the classes in the assembly function internally, let’s briefly look at the abstract classes that form the basis of the assembly. There are four such classes, from which almost all of the other assembly classes derive: ImportNode, PageObject, OutlineContent, and Data.
The ImportNode class is the base class for the entire assembly. Almost all the other classes inherit from it, including the other three abstract classes. As mentioned before, the SimpleImporter.Import method takes an XML string comprised of elements that detail the OneNote page and contents you want to import. The ImportNode represents a single node (or element) in this XML structure, such as a page, or an object on a page.
This class provides several important pieces of common functionality:
· Serialization: The ImportNode contains an abstract method, SerializeToXML, that classes derived from it must implement. This ensures that each derived class contains the means to serialize itself into XML for the XML string passed to the SimpleImport.Import method. We discuss how the various classes implement this method later in the article.
· Selection for importing: This class also contains an internal Boolean property, CommitPending, that denotes whether or not to include this object in the XML string passed to the SimpleImport.Import method. By default, all objects based on the ImportNode or its derived classes have their CommitPending property set to True when they are constructed. This denotes that the object has not yet been imported into OneNote. We also discuss how and when an object’s CommitPending property is changed later in this article.
The abstract PageObject class represents an object that can be added, updated, or deleted from the specified OneNote notebook page. There are three classes derived from the PageObject class in the assembly:
· ImageObject, which represents an image, such as a jpeg or gif, on a OneNote page
· InkObject, which represents ink on a OneNote page
· OutlineObject, which represents an outline on a OneNote page. OutlineObject objects are actually comprised of other object, such as images, ink, and text described in html format.
The SerializeToXml method is implemented in the PageObject class. It serializes the properties common to all PageObject classes:
· Whether to delete the object
· The object’s unique identifier
· The object’s position on the page
It then calls the abstract PageObject method SerializedObjectToXml. All classes derived from PageObject must implement this method, which serializes the specific attributes of each derived class.
The abstract OutlineContent class represents an object that is part of an outline. There are three classes derived from the OutlineContent class in the assembly:
· HtmlContent, which represents text on a OneNote page, described in html format.
· InkContent, which represent ink that is part of an outline.
· ImageContent, which represent an image that is part of an outline.
The SerializeToXml method is abstract in this class; each class derived from the OutlineContent class must implement the serialization process.
The final abstract class, Data, represents the actual data of the object to be imported. For example, for an image, this would be either the path to the image file, or the base 64-encoded data of the image itself. There are three concrete derived classes for the different types of data content an object can represent:
· BinaryData, which represents ink or image data that is base-64 encoded.
· FileData, which represents the path to a source file.
· StringData, which represents HTML content.
The SerializeToXml method is abstract in this class; each class derived from the Data class must implement the serialization process.
In the next entry, we'll look at creating objects.
For those of you who have been waiting for an actual copy of the OneNote schema, wait no more. MSDN has just republished the Office 2003: XML Reference Schemas download, and this time it includes OneNote 2003 SP1. The download includes a copy of the OneNote import schema, as well as the complete element, type, and attribute documentation we posted online awhile back. It also include a copy of my article detailing how to use the schema to import content into OneNote.
What more could you ask for?
Oh yeah, it includes the complete schemas and documentation for some applications called Excel, InfoPath, Project, Visio and Word as well.
Here's something that was published on MSDN last week that you might have missed: the OneNote Software Developers Kit (SDK).
Because the developer functionality in OneNote SP1 primarily consists of the SimpleImport interface, the OneNote SDK provides a full schema reference. Each element and type in the schema gets its own topic, complete with a full description and usage example. So if you're looking for detailed definitions of schema elements, this is just the reference you want.
The SDK should be available soon as a download as well. I'll let you know when that happens as well. In the meantime, check out the online version.
Here’s something I found in a code comment in the OneNoteImporter managed assembly source code. It applies to both the OneNoteImporter and the SimpleImporter API itself, and I haven’t seen it documented anywhere else, so I thought I’d pass it on here.
It concerns positioning objects on OneNote pages that read right-to-left. If you’ve used either the OneNoteImporter and the SimpleImporter API itself, you know you can use the RTL attribute of the <Page> element to set whether you want the page to read right-to-left or vice versa. You can then set the x and y attributes of an object’s <Position> element to determine where you want the object placed on the page. (If you’re using the OneNoteImporter assembly, then set the RTL property of the Page object, and the X and Y properties of the Position object, respectively.)
OneNote places the objects it imports onto the page based on absolute coordinates, expressed in points. Turns out that the coordinates differ, based on whether the page reads left-to-right or right-to-left.
Here’s how it works:
If you set the page to read left-to-right, then the origin (that is, the place there both x and y equal 0) is the upper left hand corner of the OneNote page. X coordinates advance in positive numbering from the left to the right; y coordinates advance in positive numbering horizontally down the page. Pretty straightforward.
Things get a little more complicated when you set the page to read right-to-left. In this case, the origin (0,0) is the upper right hand corner. But the x and y coordinates still advance in positive numbering in the same directions: x to the left, y horizontally down the page. So for any objects on a right-to-left OneNote page, the x coordinate will actually be a negative number. Indeed, the x coordinates are increasingly negative as you to the left across the page.
Also remember that the x coordinate refers to the upper left corner of the object, regardless of the direction in which the OneNote page reads.
So let’s look at an example. The figure below represents a OneNote page, set to read right-to-left. For the sake of argument, assume the page is 600 pixels wide by 750 pixels long (I have no idea what the default size of a OneNote page is.) In this case, the upper left corner coordinates are (-600, 0); the lower left corner coordinates are (-600, 750). The (x, y) coordinates of the object A refer to the position of the upper left corner, and so would be (-400, 100), and not (-250, 100), which is the object’s upper right corner.
Make sense?
One last thing: you should also consider page margins when positioning an object on the page. While (0,0) is the absolute origin of the page, depending on user’s preference, their upper left hand corner margin should be at (½”,½”), or (36,36) in points. Which is (-36,36) if the page reads right-to-left.
Here’s still more functionality packed into the OneNoteImporter assembly:
You can also modify how OneNote starts by using the following methods of the Application object. These methods ‘wrap’ command line switches that customize how the OneNote application starts.
The table below lists the Application object methods, and the command line switch each invokes. If a method is overloaded, the number of overloads is noted next to the method name. In most cases, the method is overloaded to account for the fact that the command line switch takes a series of optional string arguments.
All the method parameters take strings, except the openReadonly parameter of the Open method, which takes a Boolean value.
Application method
Parameters
Command line switch
JoinSharedSession (2)
(sessionAddress)
(sessionAddress, password, sectionPath)
/joinsharing
Open (2)
(sectionPath)
(sectionPath, openReadonly)
/openro
PauseRecording
()
/pauserecording
Print
/print
StartAudioNote
/audionote
StartSharedSession (3)
(sectionPath, password)
/startsharing
StartSideNote
/sidenote
StartVideoNote (4)
(recordingProfilepath)
(videoDevice, audioDevice)
(videoDevice, audioDevice, recordingProfilepath)
/videonote
StopRecording
/stoprecording
Be aware that these Application methods have been implemented as asynchronous command line calls. Because of this, your code continues to run after it calls Application method is called, independent of whether or not the OneNote application has yet launched.
In addition, you can use the Activate method to bring the OneNote application to the foreground.
For more information on the command line switches, see Customizing OneNote 2003 SP 1 Using New Command Line Switches.
Is there anything these guys didn’t think of?
In part one, we discussed how to use the OneNote managed class assembly to import content into OneNote. Now let’s actually look at the XML the Page class generates for the SimpleImporter.Import method.
If you want to preview what the XML that’s passed to the SimpleImport.Import method will look like before you run the Commit method, call the Page.ToString method. This is the method that actually serializes the page and its contents to XML prior to importing. Just remember that once you’ve committed a page, none of the object on that page will be included in the generated import XML, unless they’ve subsequently been modified in some way.
Also remember that the Commit method generates import XML with both <EnsurePage> and <PlaceObjects> elements for that page. Specifying an <EnsurePage> element for a page guarantees that the page exists before OneNote attempts to import objects onto it. So if your application includes a scenario where you only want to import objects onto a page if the page already exists, you’ll need to modify this method, or use another means.
So let’s a quick look at the code sample Donovan gives over on his blog (I think he’ll forgive me if I rewrite it in VB). This sample creates a new page, title “General”, with one object on that page: an outline containing the text “Hello world!”
Sub TrivialImportVB()
Dim p As New Page("General")
Dim outline As New OutlineObject
outline.AddContent(New HtmlContent("Hello <i>world</i>!"))
p.AddObject(outline)
p.Commit()
p.NavigateTo()
End Sub
And here’s the resulting XML that gets passed to the SimpleImport.Import method. Notice that the necessary GUIDs are there, and the HTML has even been cleaned up so that OneNote accepts it with no problem. How’s that for service?
<?xml version="1.0" encoding="utf-16"?>
<Import xmlns="http://schemas.microsoft.com/office/onenote/01/2004/import">
<EnsurePage path="General" guid="{e5bf863a-7070-4058-a0f5-
343e4a45dc05}" date="2004-07-23T09:52:17.0963010-07:00" />
<PlaceObjects pagePath="General" pageGuid="{e5bf863a-7070-4058-a0f5-
343e4a45dc05}">
<Object guid="{a5af80e4-8105-4109-b3ac-538851e9e3fa}">
<Position x="36" y="36" />
<Outline>
<Html><Data><![CDATA[<html><body>Hello
<i>world</>!</body></html>]]></Data></Html>
</Outline>
</Object>
</PlaceObjects>
</Import>
And what’s an object model without an object model map? Anyone who’s read this blog has probably realized how fond I am of presenting technical information graphically (which I’m sure has nothing to do with my being the programmer-writer for PowerPoint and Publisher.) So here are some diagrams of how the classes fit together. But first, some caveats:
The following diagrams show only the public members of each class. None of the abstract classes are diagramed, and I’ve left any inheritance information off the diagrams, although I’ve listed any members that a class inherits in the individual classes themselves. For example, ImageObject, InkObject, and OutlineObject all inherit the Height, Position, and Width properties from the abstract class PageObject, which is not diagrammed.
I mainly want to show how the various object relate to one another. For the most part, if a member gets or sets a data type, I haven’t bothered showing that in the diagram. In addition, for the sake of clarity, I’ve left off the following information for basic methods that most classes here have:
· The GetHashCode method returns a System.Int32 suitable for use in hashing algorithms and data structures like a hash table.
Trust me, I’ve got much more detailed diagrams of the internal workings of each class tacked up around my office, and those will most likely make it into the finished article, which discusses the class source code. But these should give you a good at-a-glance idea of how things flow.
Figure 1. Page object (and legend)
Figure 2. ImageObject, InkObject, and OutlineObject
Figure 3. HtmlContent, ImageContent, and InkContent
If you haven’t already seen this, get over to Donovan Lange’s blog. Donovan, Omar, and David have put together an abstracted managed assembly that provides an object model for the SimpleImport functionality OneNote added for SP1. That’s right; you don’t even have to create the XML for the import yourself. The OneNote guys have you covered.
I’m currently putting together an article that highlights this class library and all the functionality it provides. I’ve been pouring over the code for the last week or so, and I think I’ve finally got it. (The assembly itself is written in C#. Did I mention I’m about as fluent in C# as in ancient Armenian?) I have to say, this is some very well-thought-out work.
The OneNote guys have included detailed technical documentation in the download itself, but I thought it might be worth taking a few minutes and putting together a high-level overview of how the classes work. So if you just want to get up and running, and aren’t too concerned about how the assembly does what it does so well, then read on.
I’m going to assume you have a passing familiarity with the OneNote SimpleImport class and the xml schema it uses. (If you don’t, follow the links and then come back. I’ll wait.)
They’ve really made the class library easy to use. Here’s all you need to do:
Not much to say here. Once you’ve created the page, you can set its attributes using various properties such as Date, PreviousPage, RTL, SectionPath, and Title. The PreviousPage property sets the value of the ‘InsertAfter’ XML attribute; the others are pretty self-explanatory.
There are three types of objects you can add to a page: ImageObject, InkObject, or OutlineObject objects. All page objects share common properties, such as Height, Width, and Position.
Note While the OutlineObject inherits the Height property from its base class for consistency, outlines cannot have an explicitly specified height. Therefore, accessing or modifying the Height property of an OutlineObject will result in an error.
Each ImageObject and InkObject contains data representing the actual image or ink content you want to add to the page. You get and set this data by using the ImageData and InkData properties, respectively.
You use these properties to get or set a BinaryData or FileData object. The BinaryData class represents raw binary data in memory. When you add an ImageObject or InkObject object containing BinaryData to a page, the OneNote Import Managed Assembly ensures this data is properly base-64 encoded. The FileData class represents data that is stored in an external file, and is marshaled to OneNote as a file path.
For the most part, you’ll really only need to create BinaryData and FileData objects directly when you want to update existing image or ink objects. The ImageObject and InkObject each provide convenience constructors that create the appropriate data object, when passed a System.IO.FileInfo object or binary array of bytes as a method parameter.
OutlineObject objects are slightly more complex because outlines can be comprised of ImageContent, InkContent and HtmlContent objects. Add content to an OutlineObject using the AddContent method. Outline content objects are displayed within the outline in the same order in which they are added to the OutlineObject object.
The ImageContent and InkContent classes are very similar to the ImageObject and InkObject classes. Again, the actual image or ink content is represented by either a BinaryData or FileData object. This object is also created based on the specified argument passed to the ImageContent or InkContent constructor, and you can subsequently access it using the ImageData or InkData property, respectively. Note that neither the ImageContent or InkContent classes contain properties for setting their position and size on the page, as these properties are set for the outline as a whole.
The HtmlContent object also contains data within the HtmlData property, except it’s data is represented as either a FileData or StringData object. The StringData object represent string data as a System.String. Constructors on the HtmlContent object enable you to create a new HtmlContent object based off of pre-existing HTML in a string object or a FileInfo object containing the path to an HTML file.
Add the objects you’ve created to the Page object using the AddObject method. You can add as many as you want, in any order. How the objects are arranged on the page depends on the coordinates specified in the Position child object for each ImageObject, InkObject, or OutlineObject.
Now that you’ve got your page set up with all your content, it’s time to import it into OneNote. You do this using the Page.Commit method. This method does several vital things, including:
· Creates the XML document to be used for the OneNote SimpleImport class.
· Specifies the correct OneNote namespace, based on whether the OneNote SP1 Preview or SP1 release version is installed on the current machine. (Now, how thoughtful is that?)
· Adds an <EnsurePage> element for the page, using the Page object properties you’ve set.
· If your page contains objects, then the method also creates a <PlaceObjects> element to insert, edit, or delete those objects as specified.
· Serializes the page and all its contents to XML, and adds them to the XML import document. In the course of this, it also does some minor checking of any HTML content to format it property for OneNote.
· Validates the XML it created, to make sure OneNote accepts it.
· Calls the SimpleImporter class and imports the content into OneNote.
Pretty slick, no?
Here’s some things to keep in mind:
The OneNote classes internally generate the necessary GUIDs that OneNote needs to identify each object and page. You cannot access a page or object’s GUID from outside the OneNote class assembly. The GUIDs do appear in the XML passed to the SimpleImporter.Import method, of course.
On a sidenote, Donovan tells me he intends to change this. Originally, the intent was to hide the GUIDs for the benefit of new users. But it turns out that many scenerios require control over the Page and PageObject GUIDs. If you do need access to these GUIDs, here’s two strategies to consider:
Page objects keep track of whether they have been committed (or in other words, imported) and, additionally, keep track of modifications that require an update (that is, another import). So, after you commit a Page for the first time, if you then immediately call the Commit method again, none of the original objects would be re-imported. However, suppose that, after you commit a page, you then added new objects to that page, or changed the content or properties of an existing object on the page. In that case those new or modified objects will be re-imported the next time you execute the Commit method.
The OneNote SimpleImport API does not permit you to change a page’s date, title, insertAfter, or RTL attributes once you’ve imported the page. Because of this, once you’ve called the Commit method for a Page object, the following Page properties become read-only and throw an exception if you try and set them: Date, PreviousPage, RTL, and Title.
You can delete an object from a page using the Page.DeleteObject method. Much like the Page.AddObject method, the deletion does not in fact occur until you execute the Commit method; only then is it deleted within OneNote and removed as a child of the Page object.
Next time, I'll include an example and we'll take a look at the XML actually produced.
The title pretty much says it all. The final versions of the OneNote SP1 Preview documentation I posted here in draft version about a month ago went live on MSDN last night:
Importing Content into OneNote 2003 SP1 Preview
Customizing OneNote 2003 SP 1 Using New Command Line Switches
So we’ve pulled together the information from my assorted blog entries, cleaned it up, and made it ready for prime time. It’s worth taking a look at, even if you’ve already read the original blog entries. And do me a favor: rate the articles. It’s the only way we know if we’re addressing your needs. Thanks.
Here’s a heads-up for developers that are using the SimpleImport interface available in the OneNote 2003 Service Pack 1 (SP1) Preview. Be advised that the namespace for the Import method will be different in the final released SP1 from what it is in the Preview version.
The current namespace for the OneNote SP1 Preview is:
http://schemas.microsoft.com/office/onenote/01/2004/import
While the final namespace for OneNote SP1 will be:
http://schemas.microsoft.com/office/onenote/2004/import
From what I understand, it’s become standard procedure here in Office to use a ‘beta’ namespace for early versions of a product, and then switch to another namespace once the schema has been finalized. Just be advised that if you’re programming against the Preview namespace, you’ll need to update your code for the new namespace in order for it to be compatible with the final OneNote SP1, once it’s released.
I’ve already edited my blog entry detailing how to use the SimpleImport functionality, but I wanted to call this out in a separate entry for anyone who’s already using the documentation.
The last aspect of the OneNote SP1 developer story that we should talk about is the half-dozen or so command line switches that OneNote SP1 has added. Once again, here's a draft version of an article that we'll publish on MSDN once OneNote 2003 SP1 is available. The information has been reviewed by the OneNote team, but this is still draft documentation, and should be considered as such.
Here you go:
Applies To:
Microsoft Office OneNote 2003 SP1 Preview
Summary: OneNote 2003 SP1 Preview features several new command line switches, including switches you can use to record video notes, import content, and collaborate with others.
For SP1 Preview, OneNote 2003 has added six new command line switches that enable you to customize OneNote’s performance. The first three new switches let you automatically start, pause, and stop recording a video note. Two others deal with collaborating with others in a shared work space. The final switch duplicates the content importing functionality of OneNote’s new CSimpleImporter interface.
You can employ these command line switches in scripts, application programming, or even include them in desktop shortcuts.
Note These extensibility features are only available when you upgrade to the OneNote 2003 Service Pack 1 (SP1) Preview.
You can modify how OneNote 2003 starts by adding switches when you launch OneNote from the command line. Several of these switches where available in the initial release of OneNote 2003. For example, you could open OneNote to a specific page, print a specific page, start audio recording, or open a side note. OneNote 2003 SP1 extends this functionality with the new switches.
To use a command line switch to customize how OneNote starts, pass the command line a string composed of:
· The fully qualified path to the OneNote application, and
· The switch you want to use, plus strings for any parameters it requires.
These switches work whether or not OneNote is already open. If you execute a command line switch and OneNote is already running, the switch action executes on the currently running instance of OneNote.
The path of the OneNote exe can be found in the registry key: HKEY_CLASSES_ROOT\CLSID\{22148139-F1FC-4EB0-B237-DFCD8A38EFFC\LocalServer32
You can use four of the new switches to automatically create OneNote video notes:
· /videonote, which starts OneNote recording on the active page, and lets you choose the audio and video devices to use, as well as specify a custom profile.
· /pauserecording, which pauses video or audio recording in the current instance of OneNote. You can resume recording by passing this command line switch again.
· /stoprecording, which stops recording the video or audio note in the current instance of OneNote.
· /recordingprofile, which lets you use an external recording profile, instead of built-in profiles from OneNote.
Note that the pauserecording and stoprecording switches also work with audio notes. You can use audionote, a command line switch present in OneNote 2003, to automatically start recording audio on the active page.
The videonote switch consists of the switch name and three optional string parameters:
/videonote videodevice audiodevice recordingprofilepath
To find the device you’ve specified, OneNote takes each string argument and compares it to the strings it gets from the system enumerating the devices installed. You do not need to pass the entire string for a device; instead, the string should be any unique subsection of that string, so OneNote can correctly determine the intended device. OneNote uses the first device it encounters whose string matches. If the string is not unique to a single device, OneNote may select the wrong device. Note that the class ID by itself is not sufficient to distinguish between devices.
For example, suppose you had the following two video capture devices installed on your system:
@device:pnp:\\?\usb#vid_046d&pid_08b2&mi_00#6&708a54b&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global
@device:pnp:\\?\usb#vid_046d&pid_08f0&mi_00#6&313056d4&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global
The class ID in each case is identical. However, each string does contain information that is unique. So you could pass the following switches to designate the first and second video devices, respectively:
OneNote.exe /videonote vid_046d&pid_08b2
OneNote.exe /videonote vid_046d&pid_08f0
Similarly, suppose you had the following three audio capture devices installed:
@device:cm:{33D9A762-90C8-11D0-BD43-00A0C911CE86}\Intel(r) Integrated Audio@device:cm:{33D9A762-90C8-11D0-BD43-00A0C911CE86}\USB Audio Device@device:cm:{33D9A762-90C8-11D0-BD43-00A0C911CE86}\Camera
Again, the class ID is the same in each case, but each string contains unique information to distinguish which device you want to specify.
So, to start OneNote recording a video note, using the first video and audio device in each list, you would pass the following command line switch:
OneNote.exe /videonote vid_046d&pid_08b2 “Intel(r) Integrated Audio”
Also be aware that, because a space serves as the delimiter between parameters, you cannot pass an argument string that contains a space, unless you enclose the string in quotes. If you pass an argument string that contains a space without enclosing it, OneNote assumes the space signifies the end of that string, and the switch fails.
If you do not specify a video or audio device, OneNote uses the default device, if one is available.
Two of the command line switches enable you to specify a custom recording profile. You can either:
· Use the recordingprofile switch.
· Use the optional recordingprofilepath parameter of the videonote switch.
The recordingprofile switch has the following signature:
/recordingprofile filepath
Filepath is a required parameter that takes a string representing the fully-qualified path to a recording profile file.
Alternately, you can specify a custom recording profile when you use the videonote switch. That switch’s optional recordingprofilepath parameter also takes a string representing the fully-qualified path to a recording profile file.
A recording profile is a text file contains configuration information concerning video and audio recording, such as bit rate, sampling, and decoding. You can generate recording profiles using WMGenProfile, which is included as a sample application in the Media Windows Media Format 9 Software Developer’s Kit (SDK). For more information on recording profiles, see the SDK.
The next two command line switches we’ll discuss involve the important collaboration functionality added to OneNote 2003 SP1. Now, you can share a notebook page with one or more users, with each user able to dynamically add, edit, and delete content in real time to the page, and each user retaining a copy of the notebook page at the end of the session.
One user starts the session by setting up a shared session. The user selects the notebook page they want to share, then specifies a password for the session if desired. He can then invite other users to join the session. Users who want to join the session must specify the address (either IP or network domain) of the computer on which the session was started, and the password if one has been set. The user who started the session also determines whether other users have the ability to edit the shared notebook page.
The command line switches, startsharing and joinsharing, enable you to programmatically initiate or join a shared session, respectively.
The startsharing switch has the following signature:
/startsharing password filename
As its name implies, this switch initiates a shared session from the computer on which it’s executed. The filename string parameter refers to the notebook section to share. The filename string must be a fully-qualified path to the section, including section name and file extension (that is, .one). The page shared is the page most recently selected in that section.
The password parameter is optional. If you specify a password, all other users are required to specify that password in order to join the session. The password can contain any characters; however, strings that contain spaces must be enclosed in quotes. You will need to communicate the computer address and session password to participants in order for them to join the session.
Once the shared session has been started, other users can join and collaborate. The joinsharing switch has the following signature:
/joinsharing sessionaddress password filename
Executing this command line switch adds the computer to the shared session. In order to join a session, you must know the address of the computer that started the session, and the password, if one has been specified. The sessionaddress parameter takes a string that represents the address of the computer that started the session; this address may be the computer’s IP address, or full network domain name. The password and filename parameters represent the password for the session, if one has been set, and the section of the notebook that is being shared, respectively. You will need to receive this information from the session initiator in order to join the session. The filename parameter is optional; if you omit it, OneNote opens to the last section opened. You can only share one notebook section at a time.
To start or join a session using the user interface, from the Tools menu select Shared Session, and then click Start Shared Session or Join Shared Session.
The following function programmatically initiates a shared session in OneNote. The calling application passes string representing the location of the OneNote application and the notebook section to share. The example function generates a random numeric string to use as a password, then starts a new process that initiates a shared session in OneNote. The function then returns the password to the calling application. Note that the auto-generated password offers no extra security, and is only done for convenience.
Public Function ShareOneNote(ByVal appLocation As String, _
ByVal section As String) As String
Dim NoteSwitch As String
Dim OneNoteProcess As Process
Dim Ran As New Random
Dim Password As String
'Generate a random numeric password
'NOT for security, just for convenience
Password = Ran.Next(100000001).ToString
'Compose command line switch
NoteSwitch = " /startsharing " & Password & " " & section
'Pass command line to OneNote application
OneNoteProcess = Process.Start(fileName:=appLocation, _
arguments:=NoteSwitch)
'Return generated session password to calling application
Return Password
End Function
You can also use a command line switch to automatically import images, ink, and HTML into OneNote. You can even create the folders, sections, or pages onto which you want to place your content.
The import switch consists of the switch name and a single required parameter:
/import filename
The filename parameter takes an XML file that specifies:
· The folders, sections, and pages on which you want to place content. If a folder, section, or page does not exist, OneNote creates them.
· Details of the images, ink, and outlines (in HTML form) that you want to place on each specified page.
The file must adhere to the requirements of The OneNote 1.1 SimpleImport XML Schema.
This switch duplicates some of the functionality of OneNote’s CSimpleImporter interface. For more information about this interface, including a discussion of the OneNote SimpleImport schema, see Importing Content into OneNote 2003 SP1 Preview.
Below is a list of the command line switches previously available in OneNote 2003. All parameters take strings as arguments.
/ filename
Opens a file, where filename is a fully qualified file name, including the disk, folder path, and .one file extension.
/new filename
Creates a new section, called New Section 1, in the default folder (such as My Notebook), although filename is a required parameter that includes the disk and folder path and any unique file name with the .one file extension.
Note If a section called New Section 1 already exists in the folder, the number in the section name is increased incrementally, such as New Section 2.
/newfromexisting folder filename
Copies the file that is specified by filename to the folder that is specified by folder and opens the file.
/openro filename
Opens a file as read-only, where filename is a fully qualified file name, including the disk and folder path and .one file extension.
/print filename
Prints a file, where filename is a fully qualified file name, including the disk and folder path and .one file extension.
/forcerepair filename
Runs the file repair feature for a file, where filename is a fully qualified file name, including the disk and folder path. This is the same file repair feature that OneNote automatically runs if it detects file corruption while opening a file.
Starts OneNote in a miniature window, open to the Side Notes section by default.
Starts recording on the active page.
/newtaskpane
Opens the New task pane.
/paste
Pastes the contents of the Clipboard.
Command line switches offer an easy-to-implement way of customizing OneNote not just from the command line, but through scripts, code, or even desktop shortcuts.
One thing I didn’t have time to include in my OneNote SP1 Preview article entry was a simple example that generates GUIDs for elements as necessary, and then creates its own XML string for the Import method. That’s something I definitely want to include in the final MSDN article, and I thought it might be helpful to preview it here first as well. So without further ado:
The following example demonstrates how you can programmatically generate xml strings and object GUIDs for use with the OneNote import functionality. In this sample, the user specifies a location, and optionally, a title, for a new page that the application then creates using the CSimpleImporterClass.Import method.
Once the sample has stored the user input in two variables, it calls the NewGuid method to generate a new GUID for the page to be created. Note that because the NewGuid method is a shared method, you do not have to instantiate a Guid object in order to call it. In order for OneNote to correctly process it, the generated GUID must be wrapped in angle brackets ({}).
The sample then constructs the simple xml document required for the Import method. The code employs an XMLTextWriter object and its various methods to generate an xml stream. The code creates <Import> and <EnsurePage> elements and their required attributes. If the user has specified a page title, the code creates the optional title attribute of the <EnsurePage> element as well. Once it has created the xml stream, it flushes the XmlTextWriter and moves the position back to the beginning of the stream.
At this point, the code uses a StreamReader object to write the contents of the xml stream to the console, to demonstrate what the xml generated looks like. Because the Import method takes a string, not a stream, the code uses the StreamReader.ReadToEnd method to get a string representing the contents of the xml stream. The code then passes this string as the required argument to the Import method, thereby creating the desired page. Finally, the code calls the NavigateToPage method, and passes it the page path and page GUID variables set earlier, in order to navigate to the new page in OneNote.
Imports System.Xml
Imports System.IO
…
Private Sub btnCreate_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnCreate.Click
Dim pagePath As String
Dim pageTitle As String
Dim pageGuid As String
Dim XmlImportStream As MemoryStream = New MemoryStream
Dim XmlImportWriter As XmlTextWriter = _
New XmlTextWriter(XmlImportStream, Nothing)
Dim strEnsurePage As String
Dim OneNoteImporter As OneNote.CSimpleImporterClass
'store user input for path and optional page title
pagePath = Me.txtPagePath.Text.ToString
pageTitle = Me.txtPageTitle.Text.ToString
'generate a new GUID for the page to be created
pageGuid = "{" & Guid.NewGuid.ToString & "}"
'Generate the xml as a stream
With XmlImportWriter
.WriteStartDocument()
'Generate root Import element
'and specify OneNote schema namespace
.WriteStartElement("Import")
.WriteAttributeString("xmlns", _
"http://schemas.microsoft.com/office/onenote/01/2004/import")
.WriteStartElement("EnsurePage")
'Generate required path attribute
.WriteAttributeString("path", pagePath)
'Generate required GUID attribute
.WriteAttributeString("guid", pageGuid)
'Generate opotional title attribute, if title was specified
If Not pageTitle = "" Then
.WriteAttributeString("title", pageTitle)
End If
.WriteEndElement()
.WriteEndDocument()
End With
'Flush the xmltextwriter
XmlImportWriter.Flush()
'Move to the start of the xml stream
XmlImportStream.Position = 0
'Create a streamreader for the xml stream
Dim XmlImportReader As StreamReader = New StreamReader(XmlImportStream)
'Write the xml stream to the console
Console.WriteLine(XmlImportReader.ReadToEnd)
Console.ReadLine()
'Move back to the start of the xml stream
'Store entire xml stream in variable as a string
strEnsurePage = XmlImportReader.ReadToEnd
'Create instance of OneNote simple importer
OneNoteImporter = New OneNote.CSimpleImporterClass
'Pass xml string to import method to create page
OneNoteImporter.Import(strEnsurePage)
'Navigate to new page, based on path and GUID variables
OneNoteImporter.NavigateToPage(pagePath, pageGuid)
For more information on creating and using GUIDs, see GUID Structure in the .NET Framework Class Library.
For more information on creating XML using the .NET framework, see Well-Formed XML Creation with the XMLTextWriter.
The OneNote Service Pack 1 Preview is currently available for download. This Service Pack includes some cool new features that'll be of great interest to developers. We've already been seeing some questions about this new functionality in the newsgroups, so we posting a draft of the article that will appear on MSDN once SP1 is released. Keep in mind that, while we've tried to make sure the information is as accurate and complete as possible, it is a draft document, and provided as such. Void where prohibited.
Anyway, here's the article:
Microsoft Office OneNote 2003 SP1
Summary: Learn about the new extensibility features available for developers in Microsoft Office OneNote 2003 SP1. The new OneNote 1.1 Type Library includes functionality which enables you to programmatically import images, ink, and HTML into OneNote.
For the Service Pack 1 (SP1) Preview, OneNote 2003 has added extensibility functionality that enables applications to interoperate with it in an important, fundamental way—they can add content to OneNote notebooks. You can now push content to OneNote that includes html, images, and even ink (such as from a Tablet PC). You can even create the folder, section, or page onto which you want to place your content.
Note These extensibility features are only available in the OneNote 2003 Service Pack 1 Preview. You can upgrade to the OneNote SP1 Preview here.
OneNote SP1 exposes the OneNote 1.1 Type Library, which consists of a single class, CSimpleImporterClass, which enables you to programmatically add content to a OneNote notebook. You can add text in html form, images, and even ink from a Tablet PC. The CSimpleImporterClass enables you to specify where in the notebook you want to place the content; you can even create new folders, sections, and pages for content, and then programmatically display the desired page. OneNote’s import functionality also lets you later delete the content you import.
The CSimpleImporterClass consists of two methods:
· Import, which enables you to add, update, or delete images, ink, and html content to a specific page in a OneNote folder and section.
· NavigateToPage, which enables you to display a specified page.
To use the CSimpleImportClass, you must add a reference to the OneNote 1.1 Type Library to your project. To add a reference, in Visual Studio .NET, on the Solution Explorer window, right-click References and then click Add Reference. On the COM tab, select OneNote 1.1 Type Library in the list, click Select, and then click OK.
While this article focuses on implementing OneNote’s import functionality using .NET languages, you can also use the OneNote 1.1 Type Library with unmanaged code, such as Visual Basic 6.0 or Visual C++.
The Import method has the following signature:
Import (bstrXml as String)
The method takes an xml string describing the content object(s) you want to import, as well as the location in the notebook where you want them placed. You can also use the import command to delete objects you have previously placed in the notebook.
When called, OneNote executes the Import method with minimal intrusion on the user. OneNote does open if it is not already opened, which means the OneNote splash screen displays, but OneNote does not assume focus. Nor does it change the user’s location in the notebook if OneNote is already running. To change the focus of the OneNote application to the new content, use the NavigateToPage method, discussed later.
If the Import method fails, OneNote does not display an error to the user. However, the COM interface does return an “Unknown Error” to the application making the call.
The figure below outlines the xml schema to which the import file must adhere.
Figure 1. XML Schema Structure of the Import Root Element
Figure 2. Schema Structure of the PlaceObjects Element
The OneNote data import schema can be found at The OneNote 1.1 SimpleImport XML Schema.
Note The namespace for the Import method will be different in the final version of OneNote SP1 from what it is in the SP1 Preview.
Be advised that if you’re programming against the Preview namespace, you must update your code for the new namespace in order for it to be compatible with the final OneNote SP1.
Before you import content, the target pages for that content must exist in the OneNote notebook. Use the <EnsurePage> element to verify or create the target pages for your content. For each page you specify in an <EnsurePage> element, OneNote checks to determine if the page exists, and if not, creates it. You can even create new notebook folders and sections by specifying folders or sections that don’t exist.
You are required to pass OneNote a string representing the path to the desired page, as well as a GUID for that page. If the path is not fully-qualified, OneNote assumes the string starts from the notebook root location for the user. Additionally, you can specify the title, date, reading direction, and page placement relative to other pages in the notebook.
By default, OneNote inserts each new page at the end of the specified section. If you specify a page GUID for the insertAfter attribute, OneNote inserts the new page as a sub-page of the page whose GUID you specified. In such cases, OneNote labels the sub-page with the title and date of the page after which it’s inserted, rather than what you specify in the title and date attributes for the sub-page. If the page you specify does not exist (for example, if it was never added, or the user deleted it), then OneNote ignores the insertAfter attribute and inserts the new page at the end of the specified section, with any specified title and date values.
Consider the following example. This <EnsurePage> element specifies a page in the OneNote section title Negotiations, in the folder Johnson Contract, in the user’s root notebook folder. The page is titled “Budget Concerns”.
<EnsurePage path="Johnson Contract\Negotiations.one"
guid="{8FDD3C41-5BB5-4595-B019-7E7E9BC9D38E}"
title="Budget Concerns"/>
OneNote uses the optional attributes of the <EnsurePage> element if it creates the page you specify. If you specify attributes for an existing page, OneNote leaves the page unchanged. For example, if you use a GUID for an existing page, and specify a title that differs from that page’s current title, OneNote does not change the page title.
Additionally, OneNote only searches the path you specify for the desired page GUID. If the page GUID does not exist in the specified section, OneNote creates it; it does not look for the GUID in other sections of the notebook.
You can use multiple <EnsurePage> elements to create multiple pages within the OneNote notebook. You must verify or create the page before you can place content on it. You are not required to include an <EnsurePage> element for each page on which you want to place content. However, if you use the <PlaceObjects> element to try and place objects on a page that does not exist, the Import method fails. In some cases, this may be the desired outcome; for example, if you only wanted to update content on a page if the page still exists, and not add the content if the page has been deleted by the user.
Once you’ve ensured that the pages onto which you want to import data exist in the OneNote notebook, you can start placing objects on them using the <PlaceObjects> element. Multiple objects can be imported to multiple pages if desired. You create a <PlaceObjects> element for each page on which you want to place content. Same as the <EnsurePage> element, <PlaceObjects> has two required attributes: the path to the page, and the guid assigned to the page. You must include at least one <Object> element in each <PlaceObjects> element.
To create the xml string that describes the content you want to import into OneNote, follow these general steps:
· If you want to make sure the page exists to place content onto, create an <EnsurePage> to verify or create the folder, section, and page as necessary.
· Create a <PlaceObjects> element for the page to which you want to add or delete content.
· Create an <Object> element for the first object you want to alter (add, update, or delete) on the page.
· To delete the object, add the <Delete/> element to that object.
· To import the object, add a <Position> element and use its x and y attributes to specify where on the page to place the object.
· Specify the type of object you’re importing to the page by using the appropriate element, <Image>, <Ink>, or <Outline>, and setting the appropriate optional attributes, if desired.
· If you’re importing an outline object, specify the sub-objects the outline contains in the order in which you want them to appear in the outline. You can specify any number and order of <Image>, <Ink>, and <Html> elements. However, you are required to specify at least one <Image>, <Ink>, or <Html> element for the outline.
· Repeat this procedure for all objects you want to alter on the page. Then repeat this procedure for all pages on which you want to alter content.
Some other technical requirement to keep in mind as you create the xml string:
· OneNote positions objects based on absolute x and y coordinates, where x and y represent measurements in points. Seventy-two points equal one inch.
· Ink objects must be described in Ink Serialized Format (ISF) format, base-64 encoded, or specified by a path to the source file. If you specify a file path, the source file should be a plain file with the byte stream containing the ISF. If you include the ink as data in the XML, then it should be base64 encoded. For more information on programmatically capturing and manipulating ink, see this Ink Serialization Sample.
· Image objects can be specified by a path to a source file, or base-64 encoded.
· Text must be described in html, within a CDATA block, or specified by a path to a source file. In a CDATA block, all characters are treated as a literal part of the element’s character data, rather than as XML markup. XML and HTML use some of the same characters to designate special processing instructions. Using the CDATA block prevents OneNote from misinterpreting HTML content as XML instructions.
· Although the schema does not currently support importing audio or video files, you can include links to these files within the HTML content you import.
To update objects that are already in a notebook, simply re-import the objects to the same page, using the same GUIDs. Be aware, however, that re-importing an object overwrites that object without notifying the user. Any changes made to the content since it was last imported are lost.
To delete an object, place the <Delete/> element within the object element. To delete an object, you must be able to identify it by its GUID and path. In practical terms, this generally means an application can only delete objects it places in OneNote to begin with. However, if the application stores the GUID across sessions, it can delete objects it imported into OneNote in previous sessions. You cannot delete folders, sections, or pages, even those you created.
Below is an example of what a typical xml string for the Import method might resemble. This xml file describes the placement of three new objects onto an existing page, and the deletion of an object already contained on that page.
<?xml version="1.0"?>
<EnsurePage path="MSN Instant Messenger\Conversations.one"
title="Personal"
date="2003-10-16T17:30:00-08:00"
insertAfter="{05239BEA-894E-406d-80AD-8678D1BC1EDD}"/>
<PlaceObjects pagePath="MSN Instant Messenger\Conversations.one"
pageGuid="{8FDD3C41-5BB5-4595-B019-7E7E9BC9D38E}">
<Object guid="{5FCFD7F9-02C2-42fc-B6AF-7A8450D43C2D}">
<Position x="72" y="72"/>
<Image backgroundImage="true">
<File path="c:\image.png"/>
</Image>
<Object guid="{F6FC4149-1092-48ea-806D-0067C8661A18}">
<Ink>
<File path="c:\ink.isf"/>
</Ink>
<Object guid="{7EA551C4-F778-40ce-9181-21A3DB6D33CA}">
<Position x="72" y="432"/>
<Outline width="360">
<Html>
<Data>
<![CDATA[
<html><body><p>Sample text here.</p></body></html>
]]>
</Data>
</Html>
<Object guid="{1A6648BA-D792-48f1-AC6A-43DF6E258851}">
<Delete/>
The following example demonstrates a basic implementation of the OneNote import functionality. The code displays a dialog that enables the user to select an xml file, and then passes the contents of that xml file as an argument for the Import method. This example assumes the xml file conforms to the OneNote data import schema. This example also assumes the project contains a reference to the OneNote 1.0 Type Library.
Dim strFileName As String
Dim XmlFileStream As StreamReader
Dim strImportXml As String
Dim objOneNote As OneNote.CSimpleImporterClass
OpenFileDialog1.Filter = "XML files (*.XML)|*.XML|Text files (*.TXT)|*.TXT"
OpenFileDialog1.ShowDialog()
strFileName = OpenFileDialog1.FileName()
objOneNote = New OneNote.CSimpleImporterClass
XmlFileStream = New StreamReader(strFileName)
strImportXml = XmlFileStream.ReadToEnd
objOneNote.Import(strImportXml)
XmlFileStream.Close()
For the sake of simplicity, so as to highlight how the Import method is implemented, this example assumes that an XML file has already been created to use as the string for the Import method. In most cases, however, the application that calls the Import method will first create the XML string itself. For more information on creating XML using the .NET framework, see Well-Formed XML Creation with the XMLTextWriter.
In addition, most applications will need to create and assign GUIDs to the pages and objects they create. Use the NewGuid method to create a new GUID, and the ToString method to get the string representation of the value of GUID, which the XML string requires. For more information, see GUID Structure in the .NET Framework Class Library.
By design, the Import method executes with minimal focus, so that when you import data, the user is not distracted by OneNote displaying data they might not want to see, or worse, navigate away from a OneNote page the user is currently using. Also, in the cases where you import multiple objects to multiple pages, OneNote does not have to make assumptions about which page the user wants to see, if any.
To display a specific page, use the NavigateToPage method. If OneNote is not open, this method opens OneNote to the specified page. If OneNote is already open, the method navigates to the specified page in the current instance of OneNote.
To select the page to display, you must specify the path to the page, as well as the GUID for that page. If you specify a page that does not exist, OneNote returns an error.
The NavigateToPage method has the following signature:
NavigateToPage(bstrPath as String, bstrGuid as string)
OneNote’s new import functionality opens up exciting possibilities for interacting with other applications. Any application that can save data (either its own or another application’s) as html text, images, or ISF can now push that content into OneNote and place it wherever is desired. And as long as the application retains the GUIDs used, it can update or delete the content it pushed whenever necessary.
The Office 2003: XML Reference Schemas download is now available. The download includes a copy of the OneNote import schema, as well as the complete element, type, and attribute documentation we posted online awhile back. It also include an introduction detailing how to use the schema to import content into OneNote.
To view the OneNote schema reference help online, see the Microsoft Office OneNote 2003 Software Development Kit (SDK) on the Office Developer Center. The SDK contains reference documentation for the OneNote SimpleImport XML Schema, including descriptions of elements and types.