SharePoint Development from a Documentation Perspective

Andrew May

  • Andrew May's WebLog

    OneNote: An In-Depth Look at the OneNoteImporter Managed Assembly (Part 2 of 5)

    • 4 Comments

    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.

    Read part one here.

    Creating Objects

    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.

    Identifying Objects

    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.

    Committing Objects for Importing

    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.

    Adding Objects to Pages and Outlines

    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.

    Deleting Objects From OneNote

    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.

    Iterating through an import node’s children

    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.

    Read part three here.

  • Andrew May's WebLog

    OneNote: An In-Depth Look at the OneNoteImporter Managed Assembly (Part 1 of 5)

    • 8 Comments

    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.

    Introduction

    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.

    Examining the Classes

    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

    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 PageObject class

    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 OutlineContent 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 Data class

    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.

    Read part two here.

  • Andrew May's WebLog

    SharePoint: Office Developer Conference, Day One

    • 2 Comments

    Man, my brain is full.

    For those of you who don't know, yesterday was the first day of the Office Developer Conference, a three-day extravaganza of technical presentations covering all aspects of Office development, from client applications to server technologies to custom solutions presented by Microsoft partners.

    I have to admit, one of the things I really enjoy about working here is easy access to the people who create the software I use to do so much of my job. Got a question about how Word uses XML? Walk down the hall and ask the guy who wrote the Word SDK. So now, for three days, developers from outside companies can get that same level of access.

    And for me personally, this couldn't have come at a better time. Yesterday I sat through five presentations dealing with developing Windows SharePoint Services. More than just the technical information presented, I now feel like I've got a much better handle on how WSS is structured, why it works the way it does, and what the problems are that it was created to solve. The Q&A's were also a valuable opportunity for a writer-programmer like me to get a peek into what issues are on developers' minds.

    The Opening Keynote was given by Richard McAniff, a Corporate Vice President for Microsoft Office. The two main technologies he stressed were XML and SharePoint. XML being the mechanism by which we enable the user to move their data into and out of the applications they use, and SharePoint as the tool that lets them collaborate on and store that information.

    And here are some random technical issues that seemed to come up in a variety of ways throughout the WSS presentations:

    ·         Seems nobody is a fan of Collaborative Application Markup Language (CAML), which is used to define sites and lists, including fields, views, or forms. The best practice seems to be an iterative approach: make a change in the CAML, then test that change to make sure it functions as you want. Then make another change, re-test, etc. You can't really debug CAML, or apply a schema to it. Yet CAML provides a vital function: it's the reason SharePoint can render hundreds of web sites so quickly without having to have a huge server farm. Call it a necessary evil (or at least, annoyance.)

    (Here's a pointer to more information on CAML: Introduction to CAML.)

    ·         Site definitions: Best practice seems to be, copy an existing site definition, rename it and then modify it. Try not to modify existing site definitions if you can help it. Microsoft reserves the right to make changes to those site definitions, so be aware that if you do modify the default site definitions, we might over-write your changes later.

    ·         Concerning CAML and site definitions in general, I got the impression that the SharePoint team didn't realize so many developers would want to customize them so extensively. It seems like perhaps the SharePoint team thought that Web Parts would be the main avenue by which users developed sites. Certainly the developer story for Web Parts seems a lot more stream-lined and supported. That's just my impression, however.

    ·         How does a site definition differ from a template? A template is actually just the changes a user has made to the properties of a site built based on an existing site definition. A site definition is a complete set of instructions for building a site from the ground up. The template stills needs the site definition it's based on to function; remove that site definition, and the template is broken.

    ·         Customizing pages: When SharePoint renders a web page based on a page definition, it basically reads the user data from the back-end database, then plugs it into the page definition, which resides on the front-end server, and renders the page. That's one of the reasons SharePoint can render pages so quickly. The specific page the user is navigating to doesn't actually exist; it's a union of the specific data and the page definition. However, if you alter the page by editing the aspx, then SharePoint has to store more information in the database. This has three ramifications:

    ·         Database size can increase, because more information about the page, not just the data, must be stored.

    ·         Time to render can increase, since SharePoint has to load more information from the back-end database.

    ·         Inheritance is broken; in other words, if you later make changes to the page definition, those changes do not get propagated to the customized page.

    Because FrontPage alters the aspx of the page, if a user saves the page in FrontPage, that page is now customized. Not that that's necessarily undesirable; but if you let your users edit SharePoint pages in FrontPage, just be aware of what the ramifications might be.

    ·         Mike Fitzmaurice gave a nice shout out to Office Developer content on MSDN during his SharePoint presentation on customizing and branding WSS. Here's the links to articles that discuss pretty much all the technical points I listed above (and probably much more coherently):

    Branding a SharePoint Portal Server 2003 Site (Part 1 of 2)

    Customizing SharePoint Sites and Portals (Part 1 of 3)

    And with that, it's off to the InfoPath sessions…

  • Andrew May's WebLog

    PowerPoint: Launching Other Applications with Command Line Switches

    • 0 Comments

    Here's something I meant to mention a while back: did you know that when you set a shape's action setting to launch a program, you can pass command line switches to that program?

    This is something I stumbled across, and there doesn't seem to be any mention of it in the PowerPoint 2003 VBA Reference, so until the next update I'm posting the information here.

    You can set PowerPoint to perform certain actions, including starting other programs, based on user interaction with a given shape. You do this by specifying a shape's action settings. Each shape can have two actions:

    ·         One PowerPoint performs when the user clicks the shape.

    ·         One PowerPoint performs when the user moves the mouse over the shape.

    If you specify that PowerPoint should launch another application as one of a shape's action settings, you can also include any command line switches you want PowerPoint to pass to the target application.

    Take a look at the code below. It assigns an action setting to the selected shape. The code selects the action PowerPoint performs when the shape is clicked, and sets two of that action's properties. The Action property specifies which action to perform; in this case, run a program. The Run property represents the program name, as well as any command line switches. In this example, we're passing the "/sidenote" command. During a presentation, when the user clicks on the shape, PowerPoint launches OneNote as a new side note.

    Sub OpenSideNote()

      With ActiveWindow.Selection.ShapeRange(1) _

          .ActionSettings.Item(ppMouseClick)

        .Action = ppActionRunProgram

        .Run = "onenote.exe /sidenote"

      End With

     

    End Sub

    (Wondering what other command line switches are available for OneNote? Oh look, I just happened to write an article about that very subject. How's that for cross-promotion?)

    For the code-adverse of you out there, you can also do this through the user interface:

    ·         Right-click the shape, and select Action Settings.

    ·         On the Mouse Click or Mouse Over tab, select the Run program button.

    ·         Enter the application name and command line switches in the text box below the Run program radio button.

    Now all I have to do is update the PowerPoint VBA Reference...

  • Andrew May's WebLog

    Publisher: 'Using Tags in Publisher' Article Now Live

    • 1 Comments

    My latest article on programming Publisher, Using Tags to Store Custom Information in Publisher 2003 and Publisher 2002, has just been published on MSDN. As the name states, the article talks about using the Tags collection of the Document, Page, and Shape objects in Publisher to store information, such as custom variables or state information not normally persisted in a Publisher file. You can use the Tags collection to persist a wide range of custom information within your publication, at the publication, page, or shape level. An interesting aspect of the Tags collection is one of the rare pieces of the Publisher object model that doesn't have a counterpart in the application's user interface.

    If you've been reading my Publisher blog entries, you got a preview of the information in the article here, here, and here. But this article builds on that material and is worth a look even if you've read those entries.

    I'm pretty happy with how this one turned out, especially the in-depth examples using catalog merge. I knew that when you perform a catalog merge, all the shapes in the catalog merge area are copied for each record. What I found out was, this includes any tags you've assigned to those shapes. Which lets you do some very interesting things, like embedding merge data source information within the shapes of the finished merge publication.

    Go take a look.

  • Andrew May's WebLog

    PowerPoint: A Codeless One-Slide Timer (Part 2 of 2)

    • 2 Comments

    Read part one here.

    Next, we need to create custom animation that controls the order in which PowerPoint displays the shapes on the slide.

    1.      From the Slide Show menu, click Custom Animation.

    2.      Select the shape that displays the second-highest time (in our case, the shape displaying 0:50). In the Custom Animation task pane, click Add Effect, select Entrance, and then click on Appear.

    3.      The animation should now be listed in the animation list in the task pane.

    4.      Select the animation, and in the Start drop-down, select After Previous.

    5.      Click on the drop-down arrow next to the animation name, and then click Timing. In the animation dialog box, on the Timing tab, for Delay enter 10.

    Repeat this for each shape, in descending order of the time each shape displays. So now you've got the shapes appearing at 10 seconds intervals, in the order of most time displayed (1:00) to least time (0:00). Note that you don't assign an animation to the first shape, because you want that shape to be visible as soon as PowerPoint displays the slide.

    Now stack all the shapes on top of each other. That way, as each shape appears, it covers the previous shape. Because all the shapes appear the same, except for the time displayed on each, during a slide show they'll appear as a single shape counting down the seconds left on the time.

    So our timer counts down the seconds correctly, but we also want to move to the next slide when the timer is done. For this, we'll use a slide transition, just as Geetesh does in his timer. We'll set the slide transition to occur one second after the timer finishes its count.

    1.      On the Slide Show menu, click Slide Transition.

    2.      In the Slide Transition task pane, select the type of transition you want.

    3.      Under Advance slide, check Automatically after and enter '01:10' in the text box. Make sure On mouse click is not checked.

    We're done. When PowerPoint displays the slide, the animation sequence starts, displaying each shape at ten second intervals. At the same time, the slide transition is counting down. PowerPoint displays the final counter shape (0:00) one second before it executes the transition to the next slide.

    As you can see, if you wanted a longer timer, say a five or ten minutes, or wanted to count off one-second intervals, creating the timer could get tedious real fast. The main advantage to this approach, as well as Geetesh's, is that it doesn't rely on anything other than PowerPoint's native functionality. Which means you can move the presentation to another machine and not have to worry about stuff like whether the security setting will allow code to run.

    The disadvantages, of course, are that it's somewhat laborious to set up, and it's not very flexible. Because it relies on slide transition functionality, the timer has to start as soon as PowerPoint displays the slide. I couldn't think of a way to make the slide transition dependant on user interaction, which would have enabled me to let the user start the timer whenever they wanted. But for short timers where you don't want to (or can't) use VBA code or add-ins, it could come in handy.

    As I mentioned in the last entry, the PowerPoint FAQ lists several timers available as VBA code or add-ins.

    Speaking of time, it's probably past time I got back to my SharePoint research…

  • Andrew May's WebLog

    PowerPoint: A Codeless One-Slide Timer (Part 1 of 2)

    • 3 Comments

    Yesterday, a co-worker asked a few of us in PowerPoint user assistance how to create a timer to use during a presentation break. The only stipulation was that the timer had to use PowerPoint's native capabilities: she wanted something she could give to other people without having to include instructions you'd need if the timer was an ActiveX control or relied on VBA code.

    One of the other team members referred her to the Clocks and Timers section of the PowerPoint FAQ. Sure enough, there was an example of a timer using slide transitions, courtesy of Geetesh Bajaj.

    And it got me thinking: Geetesh uses multiple slides to create a timer, but couldn't you create a timer using shape animation and a single slide? Creating a single-slide timer was primarily an aesthetic preference. The timer functions the same either way. I just preferred to have it on a single slide, so that when I'm in Normal or Slide Sorter view, the timer doesn't appear as a long series of slides. Having everything on a single slide also makes moving the timer, or pasting it into another presentation, easier.

    (Creating it was also a nice distraction from all the SharePoint studying I've been doing. But that's neither here nor there.)

    Anyway, here's how you can create a single-slide, codeless timer:

    As an example, let's say you wanted to create a one-minute timer, with the visual display updated every ten seconds. First, add a new slide to the presentation. Then, create a shape that represents your timer, like so:

     

    Next, copy and paste the shape back onto the same slide. Change the text so it displays ten seconds less. Repeat this process until you have shapes that count down to zero:

    Now, let's make sure the z-order of the shapes is correct. The z-order basically refers to the order in which shapes are layered on the slide. We want the shapes stacked in order of increasing time, with the one-minute shape on the bottom. Think of it this way:

     

    (Tip: Don’t actually stack the shapes on top of each other yet. That'll make the shapes to hard to work with.)

    Here's how we can explicitly set the z-order:

    Right-click the shape with the most time displaying (in our case, that's the one-minute shape), point to Order, and click Bring to Front. This moves that shape to the top of the z-order stack. Repeat this process with the shape displaying the next most time, and so on.

    So now we have our shapes in the proper z-order. Next, we need to animate them so that PowerPoint displays them in the proper sequence, and at the proper intervals. Then we need to add a slide transition for when the timer hits zero. All of which we’ll do in the next entry.

  • Andrew May's WebLog

    OneNote: XML Reference Schema Download Now Live

    • 0 Comments

    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.

  • Andrew May's WebLog

    SharePoint: Brave New World

    • 4 Comments

    So I moved into a new assignment last week. After spending the last two years writing about programming Publisher, PowerPoint, and OneNote (and occasionally Visio), it's only logical that I would move on to writing about something that really plays to my experience programming Windows client applications:

    Windows SharePoint Services.

    Seriously, I’m very excited about digging into the SharePoint products and exploring all the cool things you can do with them. But I have to say, I'm also more than a little intimidated. Not just by having to learn a new product, and the (several) technologies that product is built on and interacts with. Even more than that, I suspect I'll have to learn an entire new way of looking at programming. Having experience exclusively in programming Windows client applications, switching over to an application built on and employing database, server, and web technologies is a sea change. Like it or not, my world is about to get a lot more complicated.

    Which probably puts me in the same camp with a lot of Office developers out there. So I've decided to blog about it. As I ramp up on the SharePoint products and underlying technologies, I'll try and chart my progress here, sharing what I'm learning, listing the resources I've found helpful, expounding on the deep insights I have that are probably blazingly obvious to anyone with a web programming background.

    With any luck, detailing my learning curve with SharePoint technologies here can be of use to other Office developers out there making the same transition.

    Off the top of my head, I'm pretty sure I'll need to learn about the following technologies to various degrees, before even getting to the SharePoint products:

    ·         Windows Server 2003

    ·         SQL Server 2000

    ·         ASP.NET

    ·         XML services

    ·         .NET programming

    Stay tuned. It should be an interesting ride.

  • Andrew May's WebLog

    PowerPoint: Using Tags to Store Custom Data

    • 4 Comments

    So, after blogging several times about the Tags functionality in Publisher, I finally took a look in the PowerPoint object model, and guess what I found? However, the tags functionality is implemented a little differently in PowerPoint than in Publisher, so it deserves some discussion.

    To review: Tags are, in essence, generic holding bins where you can store whatever information you want to persist with the presentation. Each tag is comprised of a name, and a value. Tags are one of the rare pieces of the PowerPoint object model that doesn't have a counterpart in the application's user interface. Tags are a totally programmatic feature: you can't get or set Tags objects except through code. So unless you explicitly expose them in the user interface (by displaying them in a dialog box, for example) or the user writes code to find them, the average user isn't going to be aware of them at all. Which makes them an ideal place to store information you don't want the average (that is, non-programming) user to have easy access to, such as:

    ·         Attribute information that isn’t already persisted in any of the available object model properties.

    ·         Variable data needed for programmatic functionality you’ve written.

    In PowerPoint, the Tags object is a child of the Presentation, Slide, SlideRange, Shape, and ShapeRange objects, so you can use it to store custom information at the presentation, slide, or shape level.

    Each tag has a name, and a value. In Publisher, the tag value took a Variant data type; in PowerPoint, you’re limited to a String data type for the tag value. The biggest difference between how the two application implement tags, however, is in how you create and access individual tags. Unlike in Publisher, the Tags object in PowerPoint is not a collection; there are no individual Tag objects in the PowerPoint object model. Rather, you get and set the name and value of the individual tags by using methods of the Tags object itself. Here’s how tags are structured in the PowerPoint object model:

    As you can see, Name and Value are methods of the Tags object, rather than properties of an individual Tag object (as they are in Publisher). Personally, I find this makes them a little more cumbersome to work with, but the PowerPoint object model has been around a lot longer than the Publisher object model, and the Tags object has been there since at least PowerPoint 2000, so I’m willing to cut it a little slack.

    Here’s another aspect of the Tags object that may be less than intuitive: to update the value of a tag, you call the Tags.Add method, passing the name of the existing tag, and the new value. This effectively overwrites the tag and updates the value.

    So let’s look at a code example. Here’s one I swiped from the PowerPoint Visual Basic Reference. It tests the value of the Region tag for all slides in the active presentation, and hides any slides that don't pertain to the East Coast (denoted by the value "east").

    For Each s In ActivePresentation.Slides

        If s.Tags("region") <> "east" Then

            s.SlideShowTransition.Hidden = True

        End If

    Next

    As I noted in my entries about tags in Publisher, you can come up with some pretty simple, yet powerful solutions by using tags to store custom information where you need it, then writing procedures that use that information. What kind of information, and what do you use it for? Whatever makes the most sense for your business practices.

    Think about it.

  • Andrew May's WebLog

    Publisher: Formatting Text Across Multiple Text Boxes, or in the Overflow Buffer

    • 1 Comments

    Here's an interesting quirk in Publisher's object model. It's by design, and it makes sense if you think about it. But it may lead to some unexpected results if you don't take it into account. I didn't, and it did, so I thought I'd point it out.

    First, start out with a text box that's small enough that not all the text displays:

    Then, programmatically decrease the text font size:

    ThisDocument.Pages(1).Shapes("Example"). _

      TextFrame.TextRange.Font.Size = 14

    You'd expect that all the text in the text box would be set to the new font size, right? Instead, you get this:

    All the text that was showing at the time the code ran is set to the new, smaller font size, thereby leaving space in the text box for more text to display. The additional text displayed, however, is still formatted at the previous, larger, font size.

    This is actually by design. Here's why it happens:

    Publisher lets you link multiple text boxes together, and automatically manages how text flows from one text box to the next. If there isn't room to display all the text in the first text box, the text flows into the second, and so on. If there's too much text to display in the final text box, Publisher stores the text in an overflow buffer. That's the case here; there's only one textbox, so any text there isn't room to display is stored in the overflow buffer.

    Now, let's look at that line of code again:

    ThisDocument.Pages(1).Shapes("Example"). _

      TextFrame.TextRange.Font.Size = 14

    In this code, TextFrame.TextRange returns a TextRange object that represents the text currently displaying in the text box. Which does not include any of the text stored in the overflow buffer. So only the text showing in the text box at the time the code runs is resized.

    To change the font size on all the text in the example above, you'd want to use the Story object. The Story object represents:

    ·         All the text in an unlinked text frame, including the overflow buffer.

    ·         All the text flowing between linked text frames, including the overflow buffer.

    ·         The text in a table cell.

    So, whereas TextFrame.TextRange represents the text currently displayed in a specific text box, Story.TextRange represents the entire text of that story, no matter how many text boxes (and the overflow buffer) it currently resides in. Each TextFrame and TextRange object has a Story property, which lets you access the Story object the text in it belongs to.

    Here's the code again, rewritten to change the font size of all the text, both in the text box and overflow buffer:

    ThisDocument.Pages(1).Shapes("Example"). _

      TextFrame.TextRange.Story.TextRange.Font.Size = 14

    Note   Just be aware when you set story properties, you set them for the entire story, no matter which text box it appears in. If the Story object in the above example flowed through several text boxes, the font size in each would be set to 14.

    There is no object in the Publisher object model that represents the overflow buffer. Each TextFrame does have an Overflowing property, however, that indicates whether text is overflowing from that text box into the overflow buffer. (For linked text frames, of course, only the final text frame could be overflowing.)

    Also, each TextFrame object also has an AutoFitText property, which lets you set how you want Publisher to deal with overflowing text:

    ·         Allow the text to overflow

    ·         Adjust the text frame size to fit the text

    ·         Reduce the text size so that the it fits in the text frame

  • Andrew May's WebLog

    PowerPoint: No Action Settings for Group Shapes

    • 0 Comments

    Here's something I just found out the other day: did you know that you can't assign action settings to grouped shapes? But there is a fairly easy work-around.

    I discovered as I was using PowerPoint's drawing tools to create a grouped shape that I intended to use as a custom button to launch a macro. Turns out there is no way, either programmatically or through the user interface, for me to assign an action setting to the finished group shape. This includes actions like jumps to other slides, other presentations, launching programs, running macros, or hyperlinks.

    So I talked with a few people here that have been working on PowerPoint far longer (and most likely, far more productively) than I, and it seems this has always been the case, and was by design. But no one could tell me what the thinking behind this design choice. However, one of our wonderful and resourceful support people was able to suggest a work-around (thanks, John!), which I now impart to you. Basically, you create the grouped shape, and then cut and paste it back into the slide as an embedded enhanced metafile:

    1.      Create the grouped shape as you want it. (To group the shape, select all the shapes, right-click and point to Grouping, and then select Group.)

    2.      With the grouped shape selected, from the Edit, menu click Cut.

    3.      From the Edit menu, select Paste Special. In the Paste Special dialog box, make sure the Paste radio button is selected, and then select Picture (Enhanced Metafile), and click OK.

    4.      Assign action settings to the metafile shape.

    One nice thing about this approach is that the resulting enhanced metafile is a vector graphic. So you can scale it (resize it larger or smaller) without it looking pixilated or jagged.

    Here are some other random thoughts about this approach:

    If you're working with small shapes (as I was), you might find that the shapes don't move exactly where you want to position them, but instead act like they're snapping to some invisible underlying grid. This is because they are snapping to an invisible underlying grid. If this is the case, zoom in closer to make fine adjustments easier to control, and do one of the following:

    ·         Disable objects snapping to the grid. From the View menu, click Grids and Guides, deselect Snap objects to grid, and then click OK.

    ·         Temporarily disable objects snapping to the grid. Hold down the ALT key as you use the mouse to drag the edge or corner of the shape one display pixel at a time.

    One drawback of the metafile work-around is that, once you've turned your grouped shape into a metafile, you can't edit the individual shapes. So if you later decide you wanted one of the shapes to be red and not blue, you have to start all over again. So you might want to copy the shape, instead of cutting it. That way, you still have the original grouped shape to edit if you want to make changes later on. You can always position the grouped shape on the work area around the slide, so it doesn't show up when you put the presentation in slide show mode.

    You could also turn the grouped shape into a jpg, gif or bmp, using a program like Paint. Just take a screen shot of the slide, paste it into your image program, and crop it down to just the grouped shape. However, if you do it this way, the resulting image is not a vector image. So it may not be as sharp, and will likely become pixilated if you expand its size.

  • Andrew May's WebLog

    Publisher: Creating a Linked Picture from an Embedded Picture (Part 2 of 2)

    • 1 Comments

    Read Part 1 here.

    In the last entry, I showed you how to write a function that creates a separate .jpg file from a picture embedded in a publication. Now, let's finish the job by replacing the embedded picture with a picture linked to the new .jpg file.

    Below is an example of a procedure that may call this function. It specifies the shape from which to create a linked picture, and the location to which to save the resulting picture file. It takes the file path returned from the function and uses it to replace the embedded picture with the newly-created linked picture.

    Sub ReplaceEmbeddedPictureWithLinked()

    Dim pubShape As Shape

    Dim PubFileName As String

    Dim PicFileName As String

     

    'Select the embedded picture to create a linked picture from

    Set pubShape = ActiveDocument.Pages(3).Shapes("Picture 5")

    'Specify the file path to save picture

    PubFileName = ActiveDocument.FullName

    'Call function

    PicFileName = CreatePictureAsFile(pubShape, PubFileName)

    'Replace embedded picture with linked picture

    pubShape.PictureFormat.Replace pathname:=PicFileName, _

        insertas:=pbPictureInsertAsLinked

     

    End Sub

  • Andrew May's WebLog

    Publisher: Creating a Linked Picture from an Embedded Picture (Part 1 of 2)

    • 1 Comments

    A lot of the time, entries that I post here end up inspiring me so that I expand them into articles that end up on MSDN. This time, I'm doing things the other way around: the next two posts are going to be excerpts from an article I actually published about 8 months ago.

    Here's why:

    The other day a user emailed me, asking how he could 'pull out' an embedded picture from a publication; i.e., he wanted to save the embedded picture as a separate file. This is something that comes up, in email to me and the Publisher newsgroups, fairly regularly. And I've written about how to do it; it's not that tough. Problem is, I wrote about it as a (very minor) part of a 34-page article, which was itself the second in a series of three articles—aimed at commercial printers, no less. Given all that, the chances of him finding the relevant code sample by doing a Web search was pretty small.

    So I'm posting the relevant portions here, in the hopes they'll be a little more 'visible'. These are excerpted from the thrillingly-titled Automating Commercial Printing Prepress Tasks in Publisher 2003, Part 2. This article really contains a lot of useful information about how to write code that manages the graphics you include in your publications, including code sample on how to:

    ·         Identify embedded and linked pictures in the publication.

    ·         Generate a list of all pictures that were modified or whose links are broken.

    ·         Update linked pictures whose originals were modified.

    ·         Convert linked pictures to embedded pictures.

    ·         Save embedded pictures as linked pictures.

    ·         Replace one picture with another picture.

    ·         Replace an empty picture frame with a picture.

    ·         Gather detailed information about a specific picture.

    So if any of those tasks fit with what you need to do, check it out.

    End of commercial. Back to the task: how do you turn an embedded picture back into its original, separate file?

    The short answer is, unfortunately, that you can't. However, what you can do is create a new, linked picture file from the embedded picture. There's a number of reasons why you'd want to do this: Using linked pictures instead of embedded pictures not only decreases the file size of a publication but also provides a separate image file that can be edited if necessary, and then updated quickly in Publisher.

    Just realize that the resulting linked picture may not be the same format or resolution as the original picture embedded in the publication. This is because Publisher converted the picture twice: once when it embedded the picture, and again when it saved the picture as a separate file. Also, you cannot save an embedded EPS picture as an EPS file.

    The following example creates a linked picture from an embedded one. Currently in the Publisher object model, the SaveAsPicture method applies only to the Page object, not individual shapes on a page. The function below creates a publication of the exact same dimensions as the shape passed it. It then pastes the shape onto the publication's single page, and saves the page as a .jpg file. It passes the full file path of the saved JPG file back to the calling procedure.

     

    Function CreatePictureAsFile(shpEmbeddedPicture As Shape, _

    strFileName As String) As String

    Dim pubDocument As Document

     

    'Create the new publication

    Set pubDocument = NewDocument

        'Set the page dimensions to equal

        'the picture dimensions

        With pubDocument.PageSetup

            .PageHeight = shpEmbeddedPicture.Height

            .PageWidth = shpEmbeddedPicture.Width

        End With

     

    'Paste the picture into the new publication

    shpEmbeddedPicture.Copy

    pubDocument.Pages(1).Shapes.Paste

     

    'Position the picture correctly

    With pubDocument.Pages(1)

        With .Shapes(1)

            .Top = 0

            .Left = 0

        End With

        'Passes file path of picture back to calling procedure

        CreatePictureAsFile = strFileName & "_" & _

                shpEmbeddedPicture.Name & ".jpg"

        'Saves a picture at the specified file path

        .SaveAsPicture Filename:=CreatePictureAsFile

     

    End With

     

    'Closes Publisher instance without

    'saving publication created

    pubDocument.Application.Quit

     

    End Function

    Read Part 2 here.

  • Andrew May's WebLog

    OneNote SDK Now Live on MSDN

    • 2 Comments

    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.

  • Andrew May's WebLog

    Publisher: Using Tags to Store Custom Properties (Part 3)

    • 2 Comments

    First, I discussed the tags collection, which you can use as generic holding bins for whatever data you want to persists within your publications, pages, or shapes.

    Next, I showed you a few examples using document and page-level tags.

    So now, to round things out, let’s look at a short example using tags stored in shapes.

    Let go back to the book example I mentioned in the first entry: you’ve created a book, and each chapter is a separate publication. Now, let’s further suppose that within each chapter you’re using textbox shapes for a number of different functions: the main chapter text, but also side-bar stories, pull quotes, figure captions, that kind of thing. Programmatically, there’s no way to tell the function of each textbox shape. Except you used tags to do exactly that. When you created each publication, you added a Tag object to each text box that denoted what you’re using it for. Like so:

    Activedocument.Selection.ShapeRange(1).Tags.Add "StoryType", "PullQuote"

    (The above code assigns the tag to the first shape in the ShapeRange you currently have selected in the active document. Obviously, if you’ve only got one shape selected, then that’s shape one.)

    Now, suppose you decide later that you want to change the font you used for all the pull quotes, and bump up the point size while you’re at it. You can quickly create a procedure that identifies all the text boxes containing pull quotes, based on the value of their ‘StoryType’ tag, and makes the appropriate changes.

    Sub FormatPullQuotes()

    Dim d As Document

    Dim p As Page

    Dim s As Shape

    Dim t As Tag

     

    For Each d In Documents

      For Each p In d.Pages

        For Each s In p.Shapes

          If s.Type = pbTextFrame Then

            For Each t In s.Tags

              If (t.Name = "StoryType" And t.Value = "PullQuote") Then

                s.TextFrame.TextRange.Font.Name = "Arial"

                s.TextFrame.TextRange.Font.Size = 20

              End If

            Next t

          End If

        Next s

      Next p

    Next d

    End Sub

    These examples are fairly basic, but I hope you get the idea of just how useful and flexible Tag objects can be. I’m sure, once you start looking at your own business processes, you’ll see situations where using tags will come in handy.

  • Andrew May's WebLog

    Publisher: Using Tags to Store Custom Information (Part 2)

    • 2 Comments

    In my last entry, I talked about the Tags collection, which you can use as generic holding bins for whatever data you want to persists within your publications, pages, or shapes. Now let's look at some actual examples.

    So far, the examples I've been able to think up use tags for storing two general types of information:

    ·         Publication, page, or shape attribute information that isn't already stored in any of the available object model properties.

    ·         Variable data that is needed for running some procedure or add-in functionality.

    Example Using a Document-Level Tag

    Suppose you've created a catalog of your products, including prices that are subject to change over time. You want to be sure none of your employees prints the catalog for a customer once it's more than three months old. So when you've finished creating the catalog, you add a document tag, called Expiration Date, and set its value to a date 90 days in the future.

    ActiveDocument.Tags.Add "ExpirationDate", CStr(Now + 90)

    And then you write an event handler for opening the document. The event handler compares the value of the Expiration Date tag with the current date. If the publication is opened after the expiration date, a dialog box displays advising the user not to print the catalog because the prices may not be accurate anymore.

    (You'd place this event handler in the ThisDocument object module.)

    Private Sub Document_Open()

      If CDate(ActiveDocument.Tags("ExpirationDate").Value) < Now Then

        MsgBox Prompt:="Prices in this catalog may be out of date." & _

          vbLf & "Do not release this catalog to a customer" & vbLf & _

          "without checking that prices are accurate.", _

          Buttons:=vbExclamation, _

          Title:="Expiration Date Passed"

      End If

    End Sub

    Example Using a Page-Level Tag

    By design, when you perform a catalog merge, the publication you create or append with the merge results contains no information about the merge publication or data source used to create it. It has no 'memory' of having been created by a catalog merge.

    But what if you wanted to retain that information in the target publication? Suppose you're building a single publication as a catalog, and you're using catalog merges from several different publication and/or data sources. (Maybe each section of your catalog uses a different data source, or each section has a different layout and so uses a different catalog merge area.) It'd be reasonable to want to know which pages in the publication where created with which data sources and publications, and when.

    The following example uses page-level tags to store this information.

    First, it opens the target publication and records how many pages are in the publication prior to the merge being executed. Then it executes the merge, appending the resulting pages to the target publication. Next, the code adds three tags to each new page created by the merge. These tags contain the following information:

    ·         The data source used in the merge

    ·         The publication that contains the catalog merge area used in the merge

    ·         Date and time of the merge

    The code even creates or increments a document-level tag called "No of Merges", which contains the number of merge results that have been appended to the target publication.

    Function ExecuteMergeAndTagPages(filePath As String)

    Dim d As Document

    Dim p As Page

    Dim intPages As Integer

    Dim i As Integer

    Dim t As Tag

    Dim TagExists As Boolean

     

    TagExists = False

     

    Set d = Application.Open(filePath)

    intPages = d.Pages.Count

    d.Close

     

    Set d = ActiveDocument.MailMerge.Execute(Pause:=False, _

      Destination:=pbMergeToExistingPublication, _

      Filename:=filePath)

     

    For i = (intPages + 1) To d.Pages.Count

      Set p = d.Pages(i)

      With p.Tags

        .Add "Data Source", ActiveDocument.MailMerge.DataSource.Name

        .Add "Merge Pub", ActiveDocument.FullName

        .Add "Page Creation Date", CStr(Now)

      End With

    Next i

     

    For Each t In d.Tags

      If t.Name = "No of Merges" Then

        t.Value = t.Value + 1

        TagExists = True

      End If

    Next t

     

    If TagExists = False Then

      d.Tags.Add Name:="No of Merges", Value:=1

    End If

       

    End Function

    Once you've stored this information, of course, you can write code that programmatically queries the tags and takes action based on the results. For example, you could write a procedure that deletes all the pages created using a certain data source.

    Next time, I'll see if I can't come up with some code samples that use tags at the individual shape level.

  • Andrew May's WebLog

    Publisher: Using Tags to Store Custom Information (Part 1)

    • 4 Comments

    Today I'm going to talk about the Tags collection, a feature in the Publisher object model that hasn't gotten a lot of notice, but is extremely useful when you examine it. (And I'm as guilty as anyone of ignoring it; one of the Publisher testers pointed out tags to me months ago, and it's taken this long for me to start playing around with them.)

    Tags are, in essence, generic holding bins where you can store whatever information you want to persist with the publication. The Tags collection is a child of the Document, Page, and Shape objects, so you can save data at the publication, page, or individual shape level.

    Each Tag object consists of two properties: a name, and a value. The Name property is simply a string that identifies the tag. The Name property of a Tag must be unique within a given Tags collection. For example, you can't have two Tag objects named "Creation Date" in a single Shape object, but multiple Shape objects could each have a single Tag object named that.

    The Value property of a Tag object is where things get interesting. It's data type is Variant, which means it can store any data type except fixed-length strings. (For more information about the Variant data type, see this reference topic.) So you can store pretty much any kind of information you want, including user-defined types.

    Here's another interesting aspect of tags: The Tags collection is one of the rare pieces of the Publisher object model that doesn't have a counterpart in the application's user interface. (The only other instance of this I can think of is WizardTags.) Tags are a totally programmatic feature: you can't get or set Tag objects except through code. So unless you explicitly expose them in the user interface (by displaying them in a dialog box, for example) or the user writes code to find them, the average user isn't going to be aware of them at all. Which makes them an ideal place to store information you don't want the average (that is, non-programming) user to have easy access to.

    To add a Tag object, use the Tags.Add method, which takes the Tag object name and value as parameters:

    ActiveDocument.Tags.Add "CreationDate", CStr(Now)

    So what kind of information can you store in tags? Pretty much anything you want. Seriously, the more I play around with tags, the more ideas I get for how to use them. I'm in the process of implementing some of these ideas in code. I'll post code samples as I complete them. For now, consider the following example to get your mind thinking about how you could use Tags in your business processes:

    Suppose you're creating publications for a number of projects. You might want to add a Tag object to each publication to identify which project it's for. So you add a Tag object, named for the project, that has an initial value of "Pending":

    ActiveDocument.Tags.Add "Farble Project", "InProcess"

    When the project is done, you change the value to "Complete". Then, you write a procedure that goes through your various work folders once a week and moves any publications where Tag.Value="Complete" to a 'Completed Projects' folder.

    Here's the hierarchy of the Tags object model section:

    In my next few entries, we'll look at examples using Tags at the publication, page, and shape levels. See you then.

  • Andrew May's WebLog

    Publisher: Working with Multiple Open Documents

    • 0 Comments

    Here's something I didn't notice until I'd been programming with Publisher for awhile:

    You might have noticed that in Publisher 2003, we added a Documents collection to the Application object. But, you might ask, isn't Publisher a single document interface (SDI) application? If so, shouldn't each open document have its own separate Application object?

    And you'd be right on all counts. Publisher is indeed an SDI application, so each open document has its own unique Application object that you can use in code. However, the Documents collection is our way of providing you the advantages that come with a multiple document interface (MDI) in the developer environment. It gives you a simple, easy way of identifying and iterating through all the publications open on a system. This especially comes in handy when you're doing the following:

    ·         Moving content between publications

    ·         Performing work on several publications at a time

    Here's an example of how using the Documents collection simplifies moving content between publications. The code below cuts a shape from the first page of one publication, and then pastes it onto the second page of another publication.

    Sub MoveFred()

      Documents("Publication1").Pages(1).Shapes("Fred").Cut

      Documents("Publication2").Pages(2).Shapes.Paste

    End Sub

    Handling two separate Publisher application is fairly straight-forward. But things can get tricky when you have multiple instances of Publisher open, and you want to coordinate action between them. That's where the Documents collection comes in handy. For instance, suppose you've created a book, and that each chapter is a separate publication. Using the Documents collection, you can easily selected the publications you want to print, in the order you want to print them to produce a complete copy of your book.

    Sub PrintMyBook()

      With Documents

        .Item("TitlePage").PrintOut

        .Item("Chapter1").PrintOut

        .Item("Chapter2").PrintOut

        'etc etc

      End With

       

    End Sub

    Or suppose you want to create a function that iterates through all the publication you have open, and saves copies of any Web publications as HTML into a specific folder:

    Function SaveOpenPubsAsHTML(FilePath As String)

    Dim d As Document

      For Each d In Application.Documents

        If d.PublicationType = pbTypeWeb Then

          d.SaveAs Filename:=FilePath & d.Name, _

             Format:=pbFileHTMLFiltered, _

            AddToRecentFiles:=False

        End If

      Next d

    End Function

    (By the way, Publisher automatically adds the .htm file extension to the filename when you set the Format parameter to pbFileHTMLFiltered.)

    A lot easier than having to keep track of all the individual application instances of Publisher, isn't it?

  • Andrew May's WebLog

    Publisher: Writing Macros to Save Time and Effort

    • 0 Comments

    Just a quick, shameless plug:

    Do you have things that you do repeatedly in Publisher? Do you find yourself doing a certain set of tasks over and over again? Wouldn't it be great if you could tell Publisher to perform complicated tasks automatically? Guess what? You can. There's a way to give Publisher detailed instructions on what you want it to do to, when you want it to do it, and even under what conditions, and have Publisher carry out all your instructions automatically.

    You can do all that, and more, by writing macros. And the best part is, writing macros is easy. Give me a few minutes of your time and I'll show you how.

    I've written a series of lessons that teach you what you need to know to start writing macros today. Each self-contained lesson introduces core programming concepts, with plenty of examples and hands-on exercises to show you what we're talking about. You don't need any special experience, just a computer with Publisher installed. After each lesson you'll have learned skills you can start using in your macros right away.

    So why wouldn't you give it a try?

    Here's links to the first four lessons:

    Lesson One

    Lesson Two

    Lesson Three

    Lesson Four

  • Andrew May's WebLog

    Publisher: Programming Made Easy Article Now Live

    • 1 Comments

    The first of my five part series, ‘Programming Microsoft Office Publisher Made Easy’ went live yesterday. The series consists of five lessons aimed at users who have never coded before, and want some from-the-ground-up assistance in writing powerful, flexible macros using the Publisher object model.

    Here’s a list of the content and tentative publish dates for the five articles:

    Lesson One (now live)

    ·         Writing your first macro

    ·         Macro security settings

    ·         Sub procedures

    Lesson Two (now live)

    ·         Object-oriented programming

    ·         Object variables

    ·         Method parameters

    ·         Creating event handlers

    Lesson Three (now live)

    ·         Creating dialog boxes

    ·         If…Then statements

    ·         Data variables

    Lesson Four (now live)

    ·         Object collections

    ·         For Each…Next loops

    ·         Nesting code statements

    ·         Indenting code

    Lesson Five (November 2nd)

    ·         Return types

    ·         Using the Object Browser

    ·         Using the online help

    And remember, if you read an article, please take a minute and rate it to let us know what you thought.

    Afterwards, if you want some more background on Visual Basic and object-oriented programming, check out these two previous blog entries:

    Publisher Programming Made Easy: Why Program with Objects, Anyway?

    Publisher Programming Made Easy: Types of Visual Basic Statements

  • Andrew May's WebLog

    PowerPoint: Pause a Sound File During Slide Show

    • 2 Comments

    Here’s one of my rare post concerning things you can do without using code.

    I got an email the other day from a user who had read my blog entries about inserting sound files using code. He had an issue he was looking for help with. He didn’t seem all the comfortable with programming, but was willing to learn some coding if it solved his problem.

    Here’s what he wanted to do: He had recorded narration for a slide as an mp3 file. He knew how to insert the file, and set it to play automatically, but he also wanted to have a Pause/Play button on the slide itself, so he could pause the narration at will during the slide show.

    I knew how I’d set that up in code, but it seemed like a reasonable thing that users would want to be able to do without resorting to automation. So I went to our ever-helpful end-user writers, and sure enough, you can do what he wanted to without writing a single line of code. After they showed me how, I wrote up instructions and mailed them to him. Then I figured I might as well post them on my blog, in case anyone else was trying to do something similar. So here they are.

    When you break it down, here’s what we actually want to do:

    ·        Insert narration that plays automatically when the slide loads.

    ·        Add a button that lets you pause the narration during the slide show.

    So here are the four general tasks we need to set this up:

    1.      Insert the sound file on the slide, and set it to play automatically.

    2.      Add a ‘Pause’ button to the slide.

    3.      Add a custom animation to the slide that pauses the sound file.

    4.      Set the custom animation so that it’s triggered when you click the Pause button.

    First, let’s insert the sound file:

    1.      From the Insert menu, select Movies and Sounds, and then select the option for the sound you want to insert (Sound From File, etc.).

    2.      We want the narration to start when the slide loads, so click Automatically when prompted.

    Next, add the action button:

    1.      From the Slide Show menu, select Action Buttons, and then click the button you want to use.

    2.      Draw the button on your slide.

    3.      When the Action Settings dialog box appears, under Action on Click, select None, and then click OK.

    Next, add a custom animation to your slide:

    1.      From the Slide Show menu, click Custom Animation.

    The Custom Animation pane appears.

    2.      In the Custom Animation pane, select Add Effect, then Sound Actions, and then Pause. This inserts a Pause animation into your animation sequence.

    Finally, set the Pause animation to be triggered when the user clicks your ‘Pause’ action button:

    1.      Select the Pause animation in the Custom Animation pane.

    2.      Right-click and select Timing.

    3.      On the Timing tab, click Triggers. Then click Start effect on click of, and select the action button from the pull-down list. Click OK.

    That should do it. Now, when the sound file plays, clicking the action button will pause the file playback. Clicking the button again starts the sound file from where it was paused.

    The end-user writers were also kind enough to suggest some online articles and trainings that cover these issues in more detail.

    Here’s an article that’s about triggers:

    Use triggers to create an interactive slide show in PowerPoint

    This course covers the new sounds options that were available in 2003:

    Playing Sound

    While the next link is to a course focused on video, its second lesson tells how to set up the button panel to play the video (sound, in this case).

    Playing movies

    And, as always, you can also check out the public PowerPoint newsgroup. It’s a great place to get quick answers for stuff like this.

    PowerPoint General Questions

  • Andrew May's WebLog

    Publisher Programming Made Easy: Types of Visual Basic Statements

    • 1 Comments

    I’m writing a series of articles for beginners on how to program the Publisher object model. These articles focus on the practical side of programming, and only go into enough conceptual detail to explain what the code is doing. So I thought I’d provide some more in-depth technical information for anyone interested.

    In the lessons, we mention that macros are actually Visual Basic procedures. Sub procedures, to be precise. Procedures are made up of code statements that contain the commands for the actions you want Visual Basic to perform. While we use almost all of the different kinds of statements through out the course of the lessons, I didn’t want to unnecessarily complicate things by calling out the types as we used them. So, if you’re interested, here’s the basics:

    VBA distinguishes four kinds of statements: declarations, assignment statements, executable statements, and compiler options.

    Declaration statement

    A statement that tells Visual Basic you intent to use a named item of the following types in your program:

    ·         Variable

    ·         Constant

    ·         User-defined type

    ·         Procedure

    For example:

    Dim MyNumber As Integer

    This declaration statement declares a variable (a named space in computer memory whose value that can change as the code is executed) named MyNumber and specifies it will always contain an integer.

    Constant ProductName = “Publisher”

    This declaration statement declares a constant (a named value that does not changes as the code is executed) named ProductName that consists of the character string “Publisher”.

    Assignment statement

    A statement that sets a variable or property of an object to a specific value. These statement always have three parts:

    Variable or property name = Expression specifying the new value

    For example, this statement:

    MyNextNumber = MyNumber + 12

    Sets the value of the MyNextNumber variable to the sum of the MyNumber variable plus 12.

    Border.Color = Blue

    Sets the Color property of the Border object to blue (blue is a named constant representing the numeric value of a particular color.)

    MySquareRoot = Sqr(MyNumber)

    Sets the value of the SquareRoot variable by calling the Sqr function and passing it the value of the MyNumber variable.

    When you’re assigning values to object variables, use the Set keyword:

    Set MyPage = Document.Pages(1)

    Executable statement

    A statement that executes the program’s instructions. Use executable statements to do the following:

    ·         Call another procedure in your own code

       MyProcedure

    ·         Activate a method belonging to an object

       Shape.Delete

    ·         Control the order in which other statements are executed

       If MyNumber = 5 Then

    ·         Execute one of the built-in VBA statement or functions

       UCase(“lowercase”)

    For example:

    If BorderArt.Exists = True Then
         BorderArt.Delete
    End If

    Two executable statements: The If…Then statement tests to see if the current value of the Exists property is True. If it is, the next method calls the Delete method.

    Compiler options

    Compiler options are commands that control how the compiler compiles your code. The compiler is the application that takes the human-readable code you write and turns it into the 1’s and 0’s the computer can understand. Compiler options go at the top of your project file. You probably won’t mess with compiler options much as you first start to program.

    For example:

    Option Explicit

    This compiler option tells the compiler that you have to declare each variable in your code (using a declaration statement) before you can use it in an assignment statement.

  • Andrew May's WebLog

    Publisher Programming Made Easy: Why Program with Objects, Anyway?

    • 3 Comments

    I’m writing a series of articles for beginners on how to program the Publisher object model. These articles focus on the practical side of programming, and only go into enough conceptual detail to explain what the code is doing.

    So I thought readers of those articles might have some questions about what’s really going on, and why programming is structured the way it is. As it happens, I gave a short presentation to the other Publisher technical writers on this subject a few years ago. So I dusted off my presentation, and I plan on excerpting some of the relevant portions in my next two entries.

    This stuff’ll be old hat to anyone who’s done basic object-oriented programming, but my hope is that it’ll answer some questions for people who haven’t and encourage them to get their feet wet.

    So, here’s today’s topic: why do we program using objects, anyway?

    The short answer is that, as programs get more complicated, objects provide several distinct advantages over the way programming has been done in the past. Object-oriented languages have three main characteristics that make them better to use than procedural programming languages: encapsulation, polymorphism, and inheritance.

    Encapsulation

    Objects package data and procedures into building blocks, based on real-world scenarios, with which to construct complex programs.

    Objects can be defined and maintained independent of one another. A change to one object does not affect another.

    Objects function as black boxes; their internal processes are invisible (and inaccessible) to other objects and code. They only respond to the messages defined for them.

     

     

     

     

     

     

     

     

    PropertyA

     

     

     

     

    ObjectA

    PropertyB

    < 

    Message

     

    ObjectB

     

    MethodA

     

    Return Data

    > 

     

     

    MethodB

     

     

     

     

     

     

     

     

     

     

     

    You cannot access the properties or methods of an object directly. You have to access the properties and methods by sending the object a message.

    A message is the name of an object, followed by the name of a method that the object knows how to perform, or a property the object has. For example:

    Page.Delete

    Note that we have no idea how the Page object performs the Delete method. We just know that when we send the following message, the Page object deletes itself. That’s the essence of encapsulation.

    Signature is the format the message must take in order to be understood by the object. Parameters (also sometimes called arguments) are any additional information the object must know in order to perform the method. Parameters may be required or optional. The message signature defines what information they get back (if any). This is also called the return type.

    A typical signature for an object method might look something like this:

    Object.Method(Parameter1, Parameter2) As ReturnType

    Transport.Travel (DepartureCity, ArrivalCity)

    Technically, parameter refers to the data type, while argument refers to the actual data value passed in a specific instance. But that’s splitting hairs. Most programmers use the terms interchangeably (and incorrectly).

    Programmers sometimes refer to the properties and methods of an object collectively as that object’s interface. That’s because those properties and methods are the only way you can interact with the object. The rest of its operations are hidden from us.

    An object’s interface (or message interface) is the collection of message signatures that object implements (performs). These signatures are the only way for other objects and code to access the object. The inner workings of each object is a ‘black-box’ to other objects; they cannot access the data in an object directly.

    Polymorphism

    Polymorphism refers to the ability of different objects to respond to the same message in different ways.

    Different objects can respond to the same message in different ways. This is because the procedure (i.e., the property or method) for that message has been defined within each object.

    To expand on our earlier example, here’s several objects, each of which implements a Travel method:

    Car.Travel(Chicago, Los Angeles)

    Plane.Travel(Chicago, Los Angeles)

    Boat.Travel(Chicago, Los Angeles)

    Bike.Travel(Chicago, Los Angeles)

    As you can see, each object would implement the Travel method in a very different way. But because the Travel method for each is contained in the object itself, it doesn’t matter. And you could add other objects later with their own Travel methods, and this does not effect the existing objects or procedures using those objects.

    Inheritance

    A class is the template that defines the methods and properties included in a particular type of object. An instance is an example of that object created (or instantiated) when the program is running. One way to think of it is this: the class is the blueprint, while the instance is the actual, specific house.

    A class: Page object

    Three instances: Page 1, Page 2, Page 3

    Classes can be based on other classes. This is called inheritance. The object based on other objects (usually called derived classes, or subclasses) inherit all the properties and methods of the first class (usually called base class, or superclass). However, they can substitute methods of their own for the superclass methods. They can also contain additional properties or methods.

    Classes can be nested to any depth, with subclasses inheriting all the methods and properties of the classes above it.

    The term virtual class refers to a class that other classes will be based on, but which will never actually be instantiated in the program.

    Note that Visual Basic 6.0 is not a completely object-oriented language, because it does not support inheritance. You cannot (really) base classes on other classes in VB or VBA 6.0. You can in VB.Net, however, which is one of the appeals of that language.

    Also, be aware that inheritance has nothing to do with how objects are structured in an object model. In an object model, objects that contain other objects are called parent object, while objects contained in other objects are referred to as children of the containing object. This has nothing to do with inheritance. The child objects do not inherit properties or methods from the parent objects. For example, in the Publisher object model, the Page object inherits none of its members from the Document object, which is one of its parents.

  • Andrew May's WebLog

    OneNote: Positioning Imported Content on Right-To-Left Pages

    • 0 Comments

    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.

Page 3 of 5 (108 items) 12345