Welcome to MSDN Blogs Sign in | Join | Help

Some opinions on the computer industry

First of all, a disclaimer: These words are the opinion of Mike Fried, a software engineer and internet user, and not the official statements of Microsoft. I don't speak for Microsoft. I speak for myself. However, I think I have an interesting perspective to offer. This posting is provided AS IS with no warranties and confers no rights.

Now that we're clear on this, I was reading the "news" lately. The tagline: "Google CEO Eric Schmidt: Social networks are still too closed". The comment of Google's CEO was that, "If it's not searchable by Google, it's not open, and open is best for the consumer,". Speaking as a consumer, I strongly disagree. I jumped on the social network bandwagon and started accumulating "friends" on Friendster, MySpace, Facebook, Geni, and Linked in. If Google were suddenly given all this social networking data to crawl, without the end users' permission (i.e. my/your personal data without my/your permission), then users like me would be up in arms with Friendster, Facebook, etc. I don't want my private information searchable. (Why don't I keep my private information off the internet in general? Because I like to be able to share it with my friends, and these sites provide a mechanism for me to accept/reject who my friends are/who can see it.) I don't trust people across networks. Moreover, the social networking aspects of Friendster which mostly involved being able to introduce yourself (or your friends) to potential dates through mutual friends conflict sharply with the social networking aspects of Linked in, which involves being able to get/give referrals to jobs or learn more/provide information about companies. I might have a couple family members in my Facebook, and I might have a few family, old school buddies, and friends in Linked in, but Facebook is a social network tool/MMORPG, and MySpace is where I go when my latest rocker friend releases a new blog post or album.

Google's CEO is making the complaint that Facebook et all are closed, and I think they are rightly so. Geni being closed is probably off their radar, because it doesn't have nearly enough market share. Anyone who has used Facebook and a couple other social networks knows that what makes Facebook interesting is that the people who run it understand why we use social networks. They have the best photo sharing experience, with the ability to tag people in pictures. They have the best applications. They are the fastest growing/largest social network because they are better than the rest. And part of what makes any social network useful is the fact that the private user data is locked. Also, Facebook simply has the right features. They let you log in and keep a cookie to indefinately keep yourself connected. Others epic fail here. For example: when MySpace emails me that my friend posted a new blog and I click the link, I get sent to a rejection web page that requires me to log in, even though I've checked the box to keep me logged in forever and am already logged in right now in another web browser window. If I could take "my data" elsewhere, how would I do it, and where would I take it? Seriously, the only way to do it would be to join another social network and try to re-create my network there. Well, the different network would have different sets of experiences and have a different purpose, and not all my friends in one network really belong in the other. Not only that, but what makes Facebook a MMORPG is that people gather there and play, even if that is only "twittering" through it. Facebook recently added IM features through the web page. The way that Facebook is expanding functionality and adding features like IM through the page is quickly making it more of a "life portal". The interesting thing about social networks is that they come and they go. There was a social network in 1997 called SixDegrees. I joined it. It went away. What keeps social networks thriving? Activity. I suspect that Geni and Friendster will eventually fail (though Geni might survive). Facebook is a moving target. They have lots of features, and they keep adding new ones, like the new IM features at the bottom of the pages.

The question that is on my mind is: If Google's platform doesn't add value to Facebook, then why should Facebook consider it?

What Google is trying to do is basically become like the Trillian of social networks - the binding factor. Trillian is a remarkably good IM client because it goes deeper into features than just IM. An IM client, however is not a social network platform. The problem which should be obvious to any developer who uses more than 2 social networks is that the features of each network are similar and yet vastly different. You get the least common denominator in a platform when you bridge multiple platforms. This is ever so obvious if you try to write a program to collect the data from all your social networking sites. You will quickly discover how different each site is. The data in each is on their server in a form which gets rendered into human-readable content. The scripts building that content are subject to change at any time, and so the "protocol" which these web pages are using can change as they are human interfaces, not software contracts.

So basically, lets say that Facebook pioneered this space of social networking apps, and it's at version 3 right now, and Google is in Beta, but Google is trying to get everyone to embrace their platform. What must Google do to make OpenSocial succeed? Well, they must get Facebook onboard. Really? Yes. Facebook is the winning network both for the number of users as well as its "richness" (and I hate that word, but it seems to fit well here). I claim it is in Facebook's best interest to not join OpenSocial. It would be wonderful in theory to bridge the gap between all social networks, but in reality, I like that Facebook is the place I go to to play Texas Hold'em poker and get twitter-like status updates on friends. I like that Geni is the place I go to if I want to look at my family tree. I like that my friends who are in bands put their stuff up on MySpace where I can listen to their music. I like to be able to browse the set of people I went to school with on Friendster by School (I'm not paying classmates.com for this information, when Facebook gives it to me for free). But most of all, I like that most of my friends use Facebook, because Facebook has the best features. It's fun to play "Oregon Trail" with friends or compare movies or political opinions. It's nice to see people's photos and have embedded youtube videos on a friend's wall. Most of my friends are also on one or two other social networking sites, but in my experience Facebook is the one we all update.

So my analysis goes like this: The only way for Google to "win this battle with Facebook" for social networking application development platform is if Facebook has something to lose if they don't join Google. Since Facebook has a lead in terms of time, and is the leader in terms of platform functionality, and the leader in terms of users, I don't see Facebook joining OpenSocial. Can OpenSocial succeed without Facebook? Sure. It's possible. Do I think that Facebook will be the winner in the Social Networking space? Maybe. Maybe not. It is the clear winner at the moment. I have some issues with each social networking site that I am a member of, but "data portability" can be solved at the client side by a developer like myself writing a browser plug-in to harvest, extract, and analyze the data during visits (i.e. copy/paste). Of course, the data extracted is only as valid as the last time it was updated. If I leave Facebook, I miss the opportunity to see friend's photos and twitter-like status updates. Also, crawling is against the rules, and as Scoble discovered, lands your account disabled, but integrating into a web browser and extracting the data as the end user visits can certainly work across networks without detection (still violating the ToS/EULA), but you only get data you've already seen (basically acting as a web cache), and it gets stale as you don't visit. So Facebook has toys to make you come back and visit, and as long as you keep coming back and don't violate their agreement, you bet it will stay.

Will someone build a better social network than Facebook? It's certainly possible, but I doubt it. Overall, I think that Microsoft investing in Facebook was one of the best moves that the company has made in recent memory.

Posted by mikefried | 1 Comments
Filed under: , ,

A shoutout to a good blog.

I have been reading I.M. Wright's writings for a while now, and just recently received an email alerting me that his writings are and have been up on the web for all to see. Go here for good reads about software development.
Posted by mikefried | 0 Comments

Milestones, Cuts, and what you aren't going to ship.

At Microsoft, there is a mantra repeated often by people who have been at the company a long time: "Shipping is a feature."

Like many things in life, what you don't say is often as or more important than what you do say, and there are positive sides to this and negative sides to this mantra. In line with my last post (9 months ago - I know), I wanted to go down into the rabbit hole and take you with me on a tour of some of the indicators that you are on a team which knows how to ship. There is a corollary to "shipping is a feature", and that is:

You don't know what you are shipping unless you know what you are not shipping.

When you build a product, you have a limited number of resources and a limited amount of time from the moment that you formulate your plan to the time you sign off on the gold master to give to marketing and sell to the customer. Building a product suite as large as Office is done much the same way that building a smaller product is done: in milestones. The next several paragraphs explain how the team which builds Office works together to ensure that the right stuff gets into the final product from this perspective and how cuts happen throughout the development cycle.

Microsoft has three major divisions of labor in the "development team". These divisions of labor have layers of management reporting to one overall manager (General Manager, PUM, and VP are typical titles), and each of the "disciplines" has peak amounts of work in the beginning ("Program Manager"), middle ("Developer"), and end ("Tester"). This is generally speaking - employees work all the time during the cycle doing different tasks. In general, the PM outlines what the product needs to do, the Developer implements the product/features, and the Tester makes work for the PM/Dev when the product doesn't work as expected. The PM is also tasked with balancing the work, prioritizing features in the schedule and bugs in the **bug database.

In the beginning of the milestone, Program Managers are busy planning, producing, refining, and reviewing specs. Developmers are busy estimating work/entering estimates on work items into the schedule databases, building prototypes to answer important design and implementation questions, fixing up bugs from previous milestones or product releases (service pack and hotfix work included), and reviewing specs. Testers are busy drawing up test plans, testing bug fixes from development, writing more automated tests for previous feature work, learning the latest technology and methods, etc. Before the coding part of the milestone starts, the team meets and cuts features based on the estimates, priorities, and plans to fit the schedule.

In the middle of the milestone, Program Managers are prioritizing bugs, investigating cross-team issues, and taking care of managing the work done by teams such as localization/globalization, documentation, design, etc. Developers are writing code and tracking progress in bug databases and schedule databases. Testers are busy testing, writing automation, and filing bugs against their developers, keeping developers honest about what they actually accomplished and tracking unfinished or unanticipated work (in the bug database).

In the end of the milestone, Program Managers are prioritizing/triaging/cutting work, making tradeoffs for what bugs need potentially destabilizing changes and making decisions about how to deal with bugs which are less likely to affect the customer - these become documented and fall into categories - won't fix (ever) or postponed (maybe to the next milestone if there is one, maybe to a service pack if customers actually run into the problem and there is no readily available workaround, maybe to a next release, etc). Developers are fixing the oldest and/or worst bugs. Testers are preventing developers from checking in fixes that don't meet increasingly "higher standards" and filing bugs directly to Program Managers instead of Developers (for prioritization and pre-triage). They are also running test pass after test pass - security, performace, legacy, accessibility, localization, stress testing, etc.

The final stage of the milestone/product cycle is a code freeze which includes a triage process where the source control for the project is forked and only the very worst bugs get fixed/checked into the deliverable code. Once this stage is entered, all non-milestone blocking bugs will be postponed to the next milestone of the project. The final milestone is like an extra end to a normal milestone except that "The quality bar is raised" until only real show stoppers get fixed - the kind which would cost the company millions to recall the gold master disk. These are what I like to call "billion dollar bugs" they might cost millions to postpone, but postponing them is delaying work that cost billions. When you have no "billion dollar bugs" for your team, then you are said to be "in escrow". In the final milestone, all bugs are either fixed, triaged/postponed, documented, or resolved won't fix. Eventually, when all teams have been in escrow for some given amount of time, management signs off that the quality of the product is good, and you ship it, throw a party, take a break, and begin working on another project (the best possible time to switch teams/jobs is just after you complete a project).

That's a rough outline of what happens in a normal milestone for Office or more generally over a release cycle for a smaller product. The last couple Windows versions of the Office suite (Office XP, Office 2003, Office 2007) all had about 2-3 years of development. Since typically we have something like 6 months in a milestone, we typically had something like 4-6 milestones. At the end of a milestone, we have a "deliverable" product. Between Major Milestones (MM1, MM2, MM3, etc) we have "dogfood builds" that are internally tested and tested by some customers with special relationships. In addition to major milestones, we have Beta testing Milestones, where the deliverable is tested by many customers outside the company (Beta 1, Beta 2, Beta 2TR, etc), and finally there are shipping and maintenance milestones (RC1, RTM, SP1, SP2, etc). The point of a milestone is that it has a "deliverable", be it a dogfood build, a beta build, or a shipping product or patched product (service pack). Each milestone ends with triage. The RTM milestone is special in that it generally begins in "team triage" and ends with multiple stages of triage (multiple stages of triage allow individual teams to synchronize their level of cuts/"quality bar").

Office is a very successful team at producing a product. All of the above background is important because in it I have littered the various ways which define "what you aren't going to ship". When customers feel frustrated with product issues, bugs, design of some feature, etc. They often write with feedback (via several mechanisms) about "Why can't I achieve X by doing Y with product Z?" The answer which is most likely the case and I always feel a little frustrated that I can't give you a direct example is: "We planned to deliver Y in release Z. It was cut with change # for bug # because of reasons R1, R2, R3, and R4 which are documented in the bug database. It is still visible to the team and management."

Remember what I wrote in the beginning of this post: "Shipping is a feature." We often cut your feature Y for shipping. When it comes right down to it, no other features are as important as shipping. That's the downside of what it takes (cutting) to ship a product like Office to about 500 Million paying customers. The upside is that you have a clearer goal - you know for each stage in the product development cycle that you are doing work in a methodical, targeted fashion, and at the end of the cycle there's on the order of more than a billion reasons why you will ship. Imagine for a moment that you have a team of people the size of Office - let's say for argument sake that the numbers are on the order of 5,000 employees on average costing the company $100,000 each per year for a 2 year development cycle. So assume you have a 2 * 5K * $100K = $1B cost to develop Office.The bottom line is that it costs over $1 Billion to develop a single release of Office (today). At the end of all that work, any bug which prevents you from shipping is a billion dollar bug. Teams which cut work earlier ship better features because they spend less time overall on the things that they won't ship and more time on the things that they will. By the time that they are in their last milestone, they have fewer billion dollar bugs. Very rarely does a billion dollar bug slip through the process (when it happens, it makes news). This takes me back to the top of this message:

You don't know what you are shipping until you know what you are not shipping.

Epilogue: It is painful to spend weeks and in some cases months or years of your time working on a feature to have it cut in whole or in part, but in the end, it is better for the customer to have your imperfect product in their hands than to still be working passionately on a product they will never get because it will never be perfect or bug free. Trust me when I say that developers feel customer pain. We probably feel your pain more than you realize - we had to suffer through cuts of some really cool features in order to ship the features that you have in time to make a release date. Key danger signs that your group is not going to ship is when you talk to your senior managers and find that they don't have the ability to say, "No, we're not doing super cool feature #. We spent # time investigating it, but couldn't get it to work right, and it is cut in favor of getting more basic and more important work that we just can't ship without to be high quality and stable." Managing a successful product means knowing how to make the painful cuts necessary to ship the right product.

** Disclaimer: A bug in the context of this post means any feature of a product which works poorly from the perspective of the person who reported it to the product development team or the person in the product development team who filed the bug in the bug database.

This posting is provided "AS IS" with no warranties, and confers no rights.

Posted by mikefried | 5 Comments

The hardest part of writing successful software

The hardest part of writing successful software is legacy. Once your software succeeds, you now have precedents that you must consider. A semi-recent slashdot article points to a blogger who talks about the new OOXML formats. I don't care to get into the merits of his argument. The point is that legacy is hard. Past success is no garuntee of future success. Microsoft has some very successful products and some very unsuccessful products, and there are good people who worked on both kinds of products. In order to be successful, you need to do some things right. In order to continue to be successful, you need to continue to do things right. Is OOXML wrong or right? Time will tell.

For example, you may not have heard of Visact 2000 or Liquid Motion. Both of these products came out of the team I was originally hired into (trivia point: my business cards still say Office Activation although the current name of the team after our most recent re-org is Office Graphics). Fortunately, the technologies/people who built these products made their way into products/teams such as PowerPoint and products/teams with future potential like Expression. Will these products be successful and continue be successful? Only time will tell.

There are some people who criticize the new Office Open XML formats. As I already said, I don't want to debate them. Whether you love them, hate them, or simply don't care about them they are now part of the Office Legacy. The millions of customers already using them is evidence enough of this. I do want to make a quick point that passion about a product is a sign of success. Although you want people to love what you've done, it's hard to make something so universally good that everyone loves it. The fact that some people hate PowerPoint means that in some ways we have succeeded. Maybe in some ways we have failed - some people hate PowerPoint because they hate Microsoft for whatever reasons, and others hate something specific about PowerPoint (myself included - there are some features I just hate for my own reasons).

When new versions of the file formats come out, customers need to work with them. Unlike binary formats where the data structures and extensibility measures are locked down, XML formats can be harder to maintain. Indeed, with 6000+ pages of specs for Office Open XML there are a lot of areas to maintain. If customers can add their own extensibility tags willy nilly then there's nothing to prevent them from picking a tag name which conflicts with a tag name that we will use in the future for something else. If the conversion between binary and XML isn't perfect then loading and saving through the converters can change document content (and it happens to some extent). Also, in the future, there will undoubtedly be new features that need to be added. How well the XML changes to meet these new features without breaking and requiring rewrites will be lessons learned from the designers that came before. And that's not even mentioning bugs. I don't think we're perfect - there are bugs in those millions of lines of code that will result in someone either loving or hating the product in one version or the next. Sometimes we fix some feature in one version and break other features. I'm guilty of doing that to other people's features just as much as some other people are guilty of doing that to my features.

Another aspect to legacy is that not all of your users are modern in all ways. If your organization uses older applications and older documents written in older applications you would require new software you purchase to work with your old/existing formats. Office applications have a long history of file formats and converters. It's pretty cool how many formats Office can consume that are for products which no longer have active development but that you know someone out there depends on. People have good reason to support only the formats that they do and there is a cost-benefits analysis anyone writing converters understands (Office supports a lot of really old formats). A common theme in my recent messages is that you should do what is most valuable in your time on this earth because the opportunity cost of doing something else is not getting to do what is most valuable to you. For me that means that I have recently been spending a lot of time with my family and in particular, my 11 month old son.

The trick to deal with legacy in software is to give it respect and strive to serve as a better example in the future, because what you do leaves a legacy. Sometimes legacy is an ugly and hard problem, but without the past there can be no future. Some hacks will live on in infamy, but we learn valuable lessons from the past, and we leave valuable lessons for those who come to continue in our footsteps. My new years resolution for 2007 is to learn from past mistakes and strive to catch myself before I repeat them.

This posting is provided "AS IS" with no warranties, and confers no rights.

Posted by mikefried | 0 Comments
Filed under: ,

Good stuff in many blogs all around

I was recently looking around at some good old blog posts and some of their comments by other people.

Chris Becker in his post here links to this old post by Chris Pratley, which has a comment by Rick Schaut which begins:

   I've often referred to bugs whose ease of reproduction appears to run inversely proportional to the rigor with which one attempts to observe the cause of the bug as "Heisenberg" bugs.

I've heard them called "Heisenbugs". Heisenbugs are the reason why whenever I debug anything UI related I do it on a second console. It is the reason that unlike many other developers at Microsoft, each of my computers has its own keyboard, video, and mouse (and why I don't use KVM switches any more). My typical example of a Heisenbug goes something like this:

   "Do {some complicated operation} in the UI and {something} doesn't redraw/update properly."

This is one class of UI interaction bug that is very difficult to debug on the same console as the debugger. The reason these bugs are Heisenbugs always goes something like: you switch windows to the debugger to investigate some piece of the problem and the window switching process causes an invalidation or other messages to be pumped to the application which fixes or changes the problem. Visual Studio has the wonderful ability to Remote Debug a second machine, and that's what I use to debug just about all the time. It lets me see on one computer what the application running on the other computer is doing at a frozen moment in time. But enough on Heisenbugs (for now), I just thought I'd throw out one way of looking at the problem.

So anyway, back to my train of web links, I was looking at Rick Schaut's blog post here, and then I read this post from David Weiss which reminds me that nobody's perfect. So here I am reading Chris Pratley's latest post about why there is no OneNote viewer, which references a rather old post of his that he answers in an interesting way a few weeks later with another post about design.

So I went from reading posts about why we put the brakes on taking fixes late, to posts about features which get cut, to the design of a product. Development has a lot to do with a principle I learned in a freshman economics class called opportunity cost. Our time on this planet is limited, and what we do with that time we can only do once. There is an opportunity cost of spending that time working on one of many different challenges/problems. That cost is that you can't spend time working on anything else. If the people at Microsoft do our jobs best, we spend that time working on the most important challenges/problems first.

In simpler words: we ship with bugs (any feature you don't like is a bug). If we did our jobs right, then we have addressed the most important bugs and the only bugs left over that we ship with are the ones that you care least about. That is the theme of the blog blog posts I have been reading. I wanted to end by saying that there are lots of passionate people at Microsoft.

This posting is provided "AS IS" with no warranties, and confers no rights.

Posted by mikefried | 1 Comments
Filed under:

Future thoughts

First of all, I want to wish everyone happy holidays. I also want to give a shoutout to my friend/coworker Chris Becker who just started his own blog this week. This blog post was written from a hotel room - like many people in the greater Seattle area, my home lost power. Unlike as many, power hasn't been restored to my home yet after 3 days... most of my friends got their power back today (Sunday). We lost power at 10PM on Thursday. My 10 month old son, Benjamin got himself a cold a few days ago, so we couldn't stay in our cold, powerless house.

It has been a busy time of year for me, and I haven't been posting much. One reason is that I haven't seen any comments on my postings before the comment period expires (the comment period is designed to cut down on spam comments to seemingly forgotten posts). I have seen some people write about Office and Microsoft and what it's like shipping a product, and also about what people do after a product ships. So what does a developer do after spending the better part of a year fixing up all the issues in a shipping product for 10-20+ hours a day after several years of working on a product? Well, this developer is changing teams! Mind you, it's not a big change. It's part of a re-org. :)

I'm still involved in the development of PowerPoint, just from a slightly more shared perspective. I'm now an Office "shared team" Developer. One needs to wear more hats in a shared team than being a PowerPoint developer; Excel, Word, Outlook, and the other Office applications each have their own rich history, functionality, and requirements which make writing code that runs well across many/all of them more challenging.

So it's the holiday season, and I'm making a new years resolution to be more communicative, but I need your help! What do you want me to write about? Do you like my previous posts? It has been a long time since I had any ideas about what to post. Here's to the future.

This posting is provided "AS IS" with no warranties, and confers no rights.

Posted by mikefried | 1 Comments
Filed under:

Creating a Custom Task Pane in PowerPoint 2007

To start off a series of blog posts, I'm going to talk about creating a Custom Task Pane using PowerPoint 2007 and Visual Studio Tools for Office. Before I get into any code, I wanted to point you to the resources you can use to do this and some screencasts which you should follow along with for more detailed information of how to do some of the nitty gritty details that I don't want to cover because they are well presented. Before we begin you should already have Visual Studio 2005 and Visual Studio Tools for Office, the current beta can be downloaded for free - see this blog post (dated 9/13/2006) for details.

There are 2 screencasts demonstrating how to add a Custom Task Pane to Office applications, specifically Word and Excel. You can find them here:

1. Extending the Office 2007 UI with a Custom Task Pane
2. Creating Custom Task Panes in Visual Studio Tools for Office v3 June CTP

Later in this series I will also refer to the screencasts which show how you customize the Ribbon and demonstrate how you can make a custom XML Part and load/save it from your add-in when loading/saving PowerPoint files. This series will be in C#. You can do everything I describe here in other .NET languages as well as from any language using COM, but I personally find C# to be the easiest because it is supported by wonderful tools like Visual Studio Tools for Office. I have no reason to believe that you can't do everything below in Visual Studio Express editions and by locating the appropriate places to plug in your add-in using the Registry. Having said this, I tried it at home and decided it wasn't worth the effort. I wanted to stop for a moment to plug Collin Coller's Copy Source as HTML add-in for Visual Studio 2005. If you are a blogger looking to show example code listings, this is a great tool.

So follow the Tim Patterson's screencast (1 in the list above) and pick PowerPoint instead of Word. I named my shared add-in project "StoredSelectionPane", and I inserted a custom control into the project (rather than creating a new project) called "TaskpaneControl".

Listing 1: Connect.cs: C# code to show the "Stored Selections" Custom Task Pane in this example.

    1 namespace StoredSelectionPane

    2 {

    3    using System;

    4    using Extensibility;

    5    using System.Runtime.InteropServices;

    6    using MSO = Microsoft.Office.Core;

    7    using PPT = Microsoft.Office.Interop.PowerPoint;

    8 

    9    [GuidAttribute("549D3E43-47D0-468D-91E0-EE3690BF53A2"), ProgId("StoredSelectionPane.Connect")]

   10    public class Connect : Object, Extensibility.IDTExtensibility2, MSO.ICustomTaskPaneConsumer

   11    {

   12       private PPT.Application pptApplication;

   13       private object addInInstance;

   14 

   15       public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)

   16       {

   17          pptApplication = (PPT.Application) application;

   18          addInInstance = addInInst;

   19       }

   20 

   21       public void CTPFactoryAvailable(MSO.ICTPFactory CTPFactoryInst)

   22       {

   23          MSO.CustomTaskPane ctp =

   24             CTPFactoryInst.CreateCTP("StoredSelectionPane.TaskpaneControl", "Stored Selections", Type.Missing);

   25          ctp.Visible = true;

   26       }

   27 

   28       // Unimplemented interface members

   29       public void OnDisconnection(Extensibility.ext_DisconnectMode disconnectMode, ref System.Array custom) {}

   30       public void OnAddInsUpdate(ref System.Array custom) {}

   31       public void OnStartupComplete(ref System.Array custom) {}

   32       public void OnBeginShutdown(ref System.Array custom) {}

   33    }

   34 }

I just wanted to remind people that GUIDs are unique - don't copy/paste the code on line 9 of listing 1. Instead, follow the Wizards as demonstrated in the screencasts above. The important things to note above are on lines 12 and 17, where I changed the type from object to PPT.Application (line 7 aliases PPT to Microsoft.Office.Interop.PowerPoint both to keep the namespace explicit and for convenience). Note that in order to get access to Microsoft.Office.Interop.PowerPoint, you will need to add a reference to the PowerPoint 12 COM DLL to your project. Note that line 24 tells the Custom Task Pane factory to create a Custom Task Pane with StoredSelectionPane.TaskPaneControl as the ProgId. You need to create the control and name it appropriately if you want this method call to work... The ProgId for your control goes into a special place in the Registry when you install the project. Visual Studio and Visual Studio Tools for Office handle connecting all of this for you in your install project. When you create a new user control in C#, Visual Studio generates you a class to hold all your interactions. In this case, I named my custom control TaskpaneControl and added it to the StoredSelectionPane project. The generated code for my taskpane control starts like this (I added my aliases to this file as well. We will use them throughout):

Listing 2: TaskpaneControl.cs (code editor view): The user control generated code.

    1 using System;

    2 using System.Collections.Generic;

    3 using System.ComponentModel;

    4 using System.Drawing;

    5 using System.Data;

    6 using System.Text;

    7 using System.Windows.Forms;

    8 using MSO = Microsoft.Office.Core;

    9 using PPT = Microsoft.Office.Interop.PowerPoint;

   10 

   11 namespace StoredSelectionPane

   12 {

   13    public partial class TaskpaneControl : UserControl

   14    {

   15       public TaskpaneControl()

   16       {

   17          InitializeComponent();

   18       }

We currently have a plain vanilla control. The idea I had for this control was to allow the user to save and recall shape selections. When working with large and complicated slides with hundreds of shapes, I find it useful to be able to quickly select a set of shapes which have some meaningful relationship. So with that concept in mind, we're going to add a ListView control, and some buttons. The buttons will be inside of a FlowLayoutPanel that is docked to the top of the pane and set to auto size to ensure that it is tall enough to show all controls when the user resizes the pane. The ListView control will be docked to fill the remaining space below the buttons. For buttons, I'm going to add "Add", "Remove", "Select", "Show", and "Hide". For each button, I have set AutoSize to true, AutoSizeMode to GrowAndShrink, and I have renamed the buttons from the generated names to match their text. For the ListView, I set the View type to be List (the default is Large Icon) I have turned off MultiSelect so that you can only work with one selection at a time. I have also turned off HideSelection so that when the control is out of focus you can still see the selected item. Double clicking each button creates callback methods and attaches them to their appropriate controls in the generated code. At this point, it is important to think about what we are going to do. Each entry in our ListView will be associated with a "Stored Selection". For convenience, I have written a small class to facilitate this. We will expand its functionality later, but first we need to talk about the constructor and members:

Listing 3: The StoredSelection helper class constructor.

    1    public class StoredSelection : ListViewItem

    2    {

    3       private int m_slideId = -1;

    4       private List<int> m_ids = new List<int>();

    5       public StoredSelection(PPT.Selection selection) : base("Empty Selection")

    6       {

    7          if(selection.Type != PPT.PpSelectionType.ppSelectionShapes)

    8             return;

    9          m_slideId = selection.SlideRange.SlideID;

   10          PPT.ShapeRange range = selection.HasChildShapeRange ? selection.ChildShapeRange

   11                                                              : selection.ShapeRange;

   12 

   13          string selectionName = "Shape Selection (";

   14          bool fFirst = true;

   15          foreach (PPT.Shape shape in range)

   16          {

   17             m_ids.Add(shape.Id);

   18             selectionName += fFirst ? shape.Name : ", " + shape.Name;

   19             fFirst = false;

   20          }

   21          selectionName += ")";

   22          base.Text = selectionName;

   23       }

   24    }

The StoredSelection class is called with a PowerPoint selection. Rather than associating each stored selection with an item in our list view, we will subclass the ListViewItem to store these selections inside the ListView. We set the default string to "Empty Selection". If the user of this class constructs us with a non-shape selection, this is an error case, but since we will need a valid m_slideId in order to do anything, the defaultly initialized m_slideId = -1 will ensure that in this case we won't be usable. Line 10 demonstrates that you have 2 kinds of shape selections, and we need to handle them both separately throughout to support working with children of group shapes. In order to consume our StoredSelection class so far, let's implement the AddButton callback.

Listing 4: Getting the PPT.Application and implementing the AddButton callback

    1       private PPT.Application m_app = null;

    2       public void SetApp( PPT.Application app )

    3       {

    4          m_app = app;

    5       }

    6 

    7       private void AddButton_Click(object sender, EventArgs e)

    8       {

    9          PPT.Selection selection = m_app.ActiveWindow.Selection;

   10          if (selection.Type != PPT.PpSelectionType.ppSelectionShapes)

   11             return;

   12          SelectionListView.Items.Add(new StoredSelection(selection));

   13       }

Note that for convenience, we will add a pointer to an instance of the PPT.Application interface to our Taskpane. We need to be very careful here. Do not be tempted to set m_app to "new PPT.ApplicationClass()". That will create a new instance of PowerPoint, which will keep alive your add-in after closing the instance of PowerPoint which you connected to. Go back to Connect.cs and add the following between lines 24 and 25 in listing 1 above:

         ((TaskpaneControl)ctp.ContentControl).SetApp(pptApplication);

This will let us use the application class without having to worry about new instance lifetimes keeping alive PowerPoint DLL instances on your machine after closing. Now lets implement the other buttons, starting with Remove:

Listing 5: Get the currently selected item and do something with it (part 1)

    1       private StoredSelection GetSelection()

    2       {

    3          if (SelectionListView.SelectedItems == null ||

    4             SelectionListView.SelectedItems.Count != 1)

    5             return null;

    6          return (StoredSelection)SelectionListView.SelectedItems[0];

    7       }

    8 

    9       private void RemoveButton_Click(object sender, EventArgs e)

   10       {

   11          StoredSelection selection = GetSelection();

   12          if( selection != null )

   13             SelectionListView.Items.Remove(selection);

   14       }

And now you can add and remove stored selections from the list. The rest of the buttons we will delegate the action down to some new methods on the StoredSelection class.

Listing 6: Get the currently selected item and do something with it (part 2)

    1       private void SelectButton_Click(object sender, EventArgs e)

    2       {

    3          StoredSelection selection = GetSelection();

    4          if (selection != null)

    5             selection.Select(m_app.ActiveWindow);

    6       }

    7 

    8       private void ShowButton_Click(object sender, EventArgs e)

    9       {

   10          StoredSelection selection = GetSelection();

   11          if (selection != null)

   12             selection.SetVisible(m_app.ActiveWindow, true);

   13       }

   14 

   15       private void HideButton_Click(object sender, EventArgs e)

   16       {

   17          StoredSelection selection = GetSelection();

   18          if (selection != null)

   19             selection.SetVisible(m_app.ActiveWindow, false);

   20       }

So now we just need to implement Select and SetVisible. Note that we pass along the active window to these methods. We will use the active window to search through the shapes on the slide, and do something for the shapes whose ids match those in the list. Remember that we stored the Shape.Id in the List<int> in the StoredSelection class (see listing 3 lines 4 and 17).

Listing 7: Implement StoredSelection.Select

    1       public void Select(PPT.DocumentWindow activeWindow)

    2       {

    3          if (m_slideId < 0 || activeWindow.Selection.SlideRange.SlideID != m_slideId)

    4             return;

    5 

    6          // Iterate over all shapes in the current slide

    7          PPT.Shapes shapes = activeWindow.Selection.SlideRange.Shapes;

    8          activeWindow.Selection.Unselect();

    9          foreach (int id in m_ids)

   10             foreach (PPT.Shape shape in shapes)

   11                Select(shape, id);

   12       }

   13 

   14       private void Select(PPT.Shape shape, int id)

   15       {

   16          if (shape.Id == id)

   17          {

   18             try

   19             {

   20                shape.Select(MSO.MsoTriState.msoFalse /*Replace*/);

   21             }

   22             catch (Exception e)

   23             {

   24             }

   25          }

   26          else if (shape.Type == MSO.MsoShapeType.msoGroup)

   27          {

   28             // Recurse over sub-shapes. id could be the Shape.Id of a child.

   29             foreach (PPT.Shape childShape in shape.GroupItems)

   30                Select(childShape, id);

   31          }

   32       }

There are a few important issues to consider when selecting. This implementation replaces the shape selection with a new selection built up. Note that PPT.Shape.Select will throw an exception if you attempt to select a shape which is not selectable. This is undersireable. If for example, you have a selection which includes shapes that are hidden, it would be good to select just the shapes you can select. On line 8, we clear the selection. On line 20, we add the shape whose ID matches. lines 9-11 and 29-30 ensure that we search recursively through the whole tree of shapes including iterating through groups. Finally, we implement SetVisible.

Listing 8: Implement SetVisible

    1       public void SetVisible(PPT.DocumentWindow activeWindow, bool fVisible)

    2       {

    3          if (m_slideId < 0 || activeWindow.Selection.SlideRange.SlideID != m_slideId)

    4             return;

    5 

    6          // Iterate over all shapes in the current slide

    7          PPT.Shapes shapes = activeWindow.Selection.SlideRange.Shapes;

    8          foreach (int id in m_ids)

    9             foreach (PPT.Shape shape in shapes)

   10                SetVisible(shape, id, fVisible);

   11       }

   12 

   13       private void SetVisible(PPT.Shape shape, int id, bool fVisible)

   14       {

   15          if (id == shape.Id)

   16          {

   17             try

   18             {

   19                shape.Visible = fVisible ? MSO.MsoTriState.msoTrue : MSO.MsoTriState.msoFalse;

   20             }

   21             catch (Exception e)

   22             {

   23             }

   24          }

   25          else if (shape.Type == MSO.MsoShapeType.msoGroup)

   26          {

   27             // Recurse over sub-shapes. id could be the Shape.Id of a child.

   28             foreach (PPT.Shape childShape in shape.GroupItems)

   29                SetVisible(childShape, id, fVisible);

   30          }

   31       }

This is almost identical to the previous code listing. We have one line less because we didn't need to reset the selection. The signature of these methods is slightly different so we can pass down the value to set the visibility to for all shapes in the stored selection. Note that if you have nested shapes, and you set their visibility to true, that operation does not affect the visibility of their parent group shape, but if you set the visibility of a group shape, it does set the visibility of its children (and their children).

Next time, we will address some flaws in this approach and add support to save and load custom XML for this add-in.

This posting is provided AS-IS with no warranties expressed or implied.

Posted by mikefried | 0 Comments

On account of not posting for a while...

One of the reasons I started this blog was to address what I felt are the needs of the Office technical community to get technical information that wasn't available on the other blogs. Unfortunately, most of the other blogs have been doing a better job at keeping up with producing information than I have been lately. I just wanted to reassure my readers that the time I've spent not blogging recently means that you will get a better PowerPoint 2007. I hope to resume posting useful content more regularly soon.

Posted by mikefried | 0 Comments

PowerPoint Table Styles XML (Part 2)

This time we will look at the table style portion of PresentationML in the Office Open XML Format and we will go through many of the finer details of how to make one. To get PowerPoint to generate the XML for a built-in table style, you simply insert a Table and Save as PPTX. Rename your PPTX file to .ZIP, and look under ppt\tableStyles.xml. You will see something resembling the following:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<a:tblStyleLst xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" def="{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}">
   <a:tblStyle styleId="{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}" styleName="Medium Style 2 - Accent 1">

Colors and formatting here are from Visual Studio, but you can use any old text editor such as notepad to edit these XML files. The default table style here is specified with the GUID {5C22544A-7EE6-4342-85BDC9FD1C3A}. It is the only style listed in the list above. Let’s start by removing it from the list and making a new custom style. Create a new GUID (either using a GUID Generating tool or an online GUID Generator). For example, I used the online GUID Generator and got: {90651C3A-4460-11DB-9652-00E08161165F}. You do not need to capitalize all the hexadecimal letters (but I do so because earlier beta versions required that your GUIDs be in capital hexadecimal). Let’s start with a minimal style. Here it is:

<a:tblStyle styleId="{90651C3A-4460-11DB-9652-00E08161165F}" styleName="Empty Custom Style" />

It doesn’t do much, but it gets the point across. Save your modified tableStyles.xml, copy your ZIP file to a new PPTX, apply your custom style to the table, and PowerPoint will show you the fruits of your labor:

What is the point of customizing a style if you don’t do anything with it? Well, it lets you see what the default values are. Generate a new GUID and we can start a real example:

<a:tblStyle styleId="{put your GUID here}" styleName="Example Custom Style 1">


We will start out by setting the background:

   <a:tblBg>
      <a:fillRef idx=
"3">
         <a:schemeClr val="accent1">
            <a:tint val="50000"/>
         </a:schemeClr>
      </a:fillRef
>
      <a:effectRef idx=
"2">
         <a:schemeClr val="accent2"/>
      </a:effectRef>
   </a:tblBg>

Note that we are setting the fill to refer to the style matrix with index = 3 (intense fill) and the effect to refer to the style matrix with index = 2 (moderate effect). This means that our custom style will appear different when the user changes themes. See Howard’s post on the topic for more info. These indices refer to the columns in “Style Matrix” from Howard’s post (0 = no line/fill/effect, 1 = Subtle, 2 = Moderate, 3 = Intense):

Note: If you wanted to give a specific appearance to your tables, you do not have to use themed elements (fonts, fills, lines, and effects) and instead can specify your own fonts, fills, lines, and effects. You are not limited to the colors from the color scheme. Scheme colors are one of 6 ways to specify colors.

Now that we have set the background fill/effect for the whole table, we will set properties which apply to all cells in the table (whole table part style). This part defines the default appearance for all of the cells in the table in their default state. The first thing we set is the table cell text style (tcTxStyle):

   <a:wholeTbl>
      <a:tcTxStyle
>
         <a:fontRef idx=
"minor"/>
         <a:schemeClr val="dk1"/>
      </a:tcTxStyle>

We pick the minor font (used for text – the major font would be used for headings – this is discussed a few times in Howard’s posts). We use the dk1 (Dark  1) color for our text. Notice that we tinted our table background color by 50% above. Tint makes the color lighter. Shade makes the color darker. By tinting the background color by 50%, we make it contrast more with our dark 1 colored text. There are other good choices here, and I wanted to highlight that tx1 (Text 1) maps to either dk1 (Dark 1) or lt1 (Light 1), and bg1 (Background 1) maps to the contrasting color (same for tx2, bg2, dk2, and lt2). You set which light/dark pair are used with the background style gallery in the design tab. If your table style has no fill, then using tx1 or tx2 will make your text more likely to contrast against the slide background (but this is not a guarantee – the slide background may have images, there may be shapes on the master, or you can have other shapes under your table). If you wanted to always ensure that your text will contrast and still show design elements under it, you can use a semi-transparent fill (see below) of bg1 or bg2 to mute the background or other slide content under your table. I recommend using an un-themed solid fill in this case.

Next we will set the line styles for the outer edges of the table with accent 3 and the “subtle” line style from the theme. Note that these lines are the lines around and inside the “whole table” part. Top here is a continuous single line from the top left to the top right (in Left-to-Right language tables). We empty out the inner line styles (the diagonals are empty by default, but the inner horizontal/vertical lines are set by default). I have collapsed these XML elements on individual lines to save space.

      <a:tcStyle>
         <a:tcBdr
>
            <a:left><a:lnRef idx=
"1"><a:schemeClr val="accent3"/></a:lnRef></a:left>
            <a:right><a:lnRef idx="1"><a:schemeClr val="accent3"/></a:lnRef></a:right>
            <a:top><a:lnRef idx="1"><a:schemeClr val="accent3"/></a:lnRef></a:top>
            <a:bottom><a:lnRef idx="1"><a:schemeClr val="accent3"/></a:lnRef></a:bottom>
            <a:insideH><a:ln><a:noFill/></a:ln></a:insideH>
            <a:insideV><a:ln><a:noFill/></a:ln></a:insideV
>
         </a:tcBdr>

After specifying the border line styles, we define a fill for all the cells in the table. Note that cells are drawn on top of the table background. Since we already have a contrasting set of colors for the table background and the default text color, we really don’t need to tweak this part. The default is “no fill”, but I’m going to set it explicitly just to indicate how it is done:

         <a:fill>
            <a:noFill
/>
         </a:fill
>
      </a:tcStyle>

And that’s it for the whole table part. Let’s close the table style tags now.

   </a:wholeTbl>
</a:tblStyle>

Save the XML file, copy the ZIP to a new PPTX file, and load it into PowerPoint. After applying the new table style, we see should see something like this:

Notice that there are grid lines showing in the table in the document, but no gridlines showing in the thumbnail. The default setting in the Table Tools/Layout tab is to show gridlines when editing the table. This setting is not table or document specific.

We haven’t specified the top row, bottom row, or any other part. Users of our table style will want to be able to express subtle or not-so-subtle structure in the data that they enter into their tables. Our design is not yet done. We don’t need to change every part, but at the moment we have a very uninteresting style.

The next set of parts defined in the table are the horizontal and vertical bands. “band1H” applies to every “odd” row starting after the first (also called header) row and before the last (also called total) row (if the header or total row parts are applied) and “band2H” applies to the “even” rows. Similarly, “band1V” applies to every “odd” column after the first column (left column in left-to-right languages) and before the last column (right column in left-to-right languages) and “band2V” applies to the “even” columns.

For this example table style, remember that the background fill is using dark 1 for the text color and accent 1 tinted to be 50% brighter as color for the intense theme filled background. Let’s change the background fill and text color for odd rows and lets make the text italic for odd columns. Insert the following before the close tag for our table style:

   <a:band1H>
      <a:tcTxStyle
>
         <a:schemeClr val=
"lt1"/>
      </a:tcTxStyle>
      <a:tcStyle
>
         <a:tcBdr
/>
         <a:fill
>
            <a:solidFill
>
               <a:schemeClr val=
"dk1">
                  <a:alpha val="33333"/>
               </a:schemeClr>
            </a:solidFill
>
         </a:fill
>
      </a:tcStyle
>
   </a:band1H
>
   <a:band2H
/>
   <a:band1V
>
      <a:tcTxStyle i=
"on"/>
   </a:band1V>
   <a:band2V/>

Rather than make the background a solid fill using an opaque color, I set the “alpha transparency” on the color to 33.333% here (the alpha value is the opacity – the opposite of transparency. The 33.333% value here means it is 2/3rds transparent). Note that this is not a themed fill. By choosing a solid color fill with a mostly transparent color, we are effectively darkening the whole table background fill underneath the cells affected by this part. This means that if the “intense” fill effect used by our table background has a picture in it (or some cool gradient fill, pattern fill, etc) then we don’t completely cover it up, but we still make sure that the text color will contrast with it. Here is what the effect now looks like with a theme with a picture fill (from the built-in paper theme):

For the last column, lets give our style a gradient filled line from Red to Blue on the left. This isn’t going to look good with all styles or color schemes. The point is to demonstrate that it can be done and how you would do it.

   <a:lastCol>
      <a:tcStyle
>
         <a:tcBdr
>
            <a:left
>
               <a:ln w=
"127000">
                  <a:gradFill>
                     <a:gsLst
>
                        <a:gs pos=
"0">
                           <a:prstClr val="red"/>
                        </a:gs>
                        <a:gs pos=
"100000">
                           <a:prstClr val="blue"/>
                        </a:gs>
                     </a:gsLst
>
                     <a:lin ang=
"5400000"/>
                  </a:gradFill>
               </a:ln
>
            </a:left
>
         </a:tcBdr
>
      </a:tcStyle
>
   </a:lastCol>

We express the thickness of the line in EMUs (English Metric Units). Note that 914,400 EMUs make up an inch. 360,000 EMUs make up a centimeter, and 12,700 EMUs make up a “point” (1/72nd of an inch). Note that the gradient fill is a linear gradient fill, and the angle is at 90 degrees. The angle is expressed in units where 60,000 = 1 degree. The stops of the gradient fill are at 0% and 100.000%, and we are using preset colors this time. You can have as many stops as you want. I set the thickness to 10 “points” here.

If you merge cells horizontally in your table so that the last column runs through your merged cell, you will notice something interesting: the line which we have styled will move around your merged cells. There is a complicated bit of logic in the table styling engine which handles these special cases, and I just thought I would point it out because it is cool. A gradient fill is useful to demonstrate this edge case as it shows how the line is broken down into segments. I chose this example to demonstrate some features of table styles.

That’s a lot of material to cover for one post. Feel free to play with this and leave comments and questions. Let us know what you think. The XML format is very powerful, but there are no fine-grained error messages to tell you what you broke when hand-editing the XML. Most Office users are not expected to do this, and PowerPoint will recover your data even in the case that your table styles are invalid. Be careful not to do too much work on an XML file without saving and making sure that PowerPoint loads it and your table appears to be styled correctly. Don’t forget that the Table Styles XML elements are in the “DrawingML / Office Art” (a:) namespace. Other elements in the XML file in a PowerPoint presentation are in the “PresentationML / PPT” (p:) namespace. I want to thank Jason Schneekloth (the Tables/Table Styles PM) for his help editing this post and for his post on the topic as well as the rest of the team that implemented this feature.

This posting is provided “AS IS” with no warranties and confers no rights.

Posted by mikefried | 1 Comments
Filed under: ,

No post today

My life has been a little extra busy this week and I've had no time to blog.

PowerPoint Table Styles XML (Part 1)

One of the coolest parts of the new tables feature in PowerPoint is that you can style tables. The design of the table styles feature mixes some of the flexibility of Word's table styles with the power of the new OfficeArt platform. Like Word, we have several parts to a table style, and these parts are applied in a very similar way. A part is defined as a group of cells which we will apply a style to in certain circumstances. There are 6 flags which determine when we apply parts to the given cells:

Flag Description
Header Row Applies styling to the top row. In conjunction with the First/Last Column options also applies corner styling.
Total Row Applies styling to the bottom row. In conjunction with the First/Last Column options also applies corner styling.
Banded Rows Applies 2 alternating styles to all rows between the top and bottom.
First Column Applies styling to the Left (Right in Right-To-Left languages) column. In conjunction with the Header/Total Row options also applies corner styling.
Last Column Applies styling to the Right (Left in Right-To-Left languages) column. In conjunction with the Header/Total Row options also applies corner styling.
Banded Columns Applies 2 alternating styles to all columns between the left and right.

A Table Style has a background style and 13 part styles. They are listed in the XML and applied in the order listed from back to front in the following table. Source: Office Open XML Draft Spec 1.4 - Markup Language Reference Section 5.1.5.9 - DrawingML Reference Material -> Tables -> tableStlye element on page 3135. Also found in the Table Styles section (5.1.3.2 on pages 2918-2951)

<complexType name="CT_TableStyle">
 <sequence>
  <element name="tblBg"    type="CT_TableBackgroundStyle" minOccurs="0" maxOccurs="1"/>
  <element name="wholeTbl" type="CT_TablePartStyle"       minOccurs="0" maxOccurs="1"/>
  <element name="band1H"   type="CT_TablePartStyle"       minOccurs="0" maxOccurs="1"/>
  <element name="band2H"   type="CT_TablePartStyle"       minOccurs="0" maxOccurs="1"/>
  <element name="band1V"   type="CT_TablePartStyle"       minOccurs="0" maxOccurs="1"/>
  <element name="band2V"   type="CT_TablePartStyle"       minOccurs="0" maxOccurs="1"/>
  <element name="lastCol"  type="CT_TablePartStyle"       minOccurs="0" maxOccurs="1"/>
  <element name="firstCol" type="CT_TablePartStyle"       minOccurs="0" maxOccurs="1"/>
  <element name="lastRow"  type="CT_TablePartStyle"       minOccurs="0" maxOccurs="1"/>
  <element name="seCell"   type="CT_TablePartStyle"       minOccurs="0" maxOccurs="1"/>
  <element name="swCell"   type="CT_TablePartStyle"       minOccurs="0" maxOccurs="1"/>
  <element name="firstRow" type="CT_TablePartStyle"       minOccurs="0" maxOccurs="1"/>
  <element name="neCell"   type="CT_TablePartStyle"       minOccurs="0" maxOccurs="1"/>
  <element name="nwCell"   type="CT_TablePartStyle"       minOccurs="0" maxOccurs="1"/>
  <element name="extLst" type="CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1"/>
 </sequence>
 <attribute name="styleId" type="ST_Guid" use="required"/>
 <attribute name="styleName" type="xsd:string" use="required"/>
</complexType>

Note that because the list of table part styles is specified in a sequence, you must list the parts in the order specified above in order to make valid XML that Office can read. Having them appear in order from back to front helps designers to tell what part styles override what other part styles.

Another thing to note: in order to work correctly, you must generate GUIDs with actual GUID generating tools or an online GUID generator. GUIDs work best if they are unique. That should be enough information to wet your appetite. Next time, we'll go over some samples and show off the power of what you can do with them and the OfficeArt platform underneath.

This posting is provided "AS-IS" with no warranties and confers no rights.

Posted by mikefried | 1 Comments
Filed under: ,

Happy Labor Day Weekend

I joined Microsoft the day after Labor Day (September 4th) in 2001. For those of you reading this who work at MS, there will be 5 pounds of candy outside my office door this Tuesday for you to come and enjoy...

This post was pre-recorded. Mike is out taking pictures... Next week's topic will probably be the new and improved Photo Album feature.

Posted by mikefried | 0 Comments

The Selection and Visibility Pane (Part 2 - Using the Task Pane)

Today we will look at how you use the Selection and Visibility Pane to edit the contents of a presentation. The first thing we need to do is to show the Selection Pane. You can do this in PowerPoint and Excel from the Select Dropdown in the Editing Group on the Home Tab:

Screenshot showing how to launch the pane.

I recommend right clicking the button and adding a shortcut to the Quick Access Toolbar. It will save you time if you use the feature a lot. Now that we have the pane up, I wanted to point out a few things:

Screenshot demonstrating Selection Pane usage
  1. The pane starts with Keyboard Focus. This means that keyboard presses are interpreted by the pane as commands. The pane indicates this with the highlight color in the title.
  2. The pane allows you to edit the name of the selected or focused shape. Click on a selected shape to edit its name. Press F2 to edit the name of the currently focused shape. Keyboard focus is indicated with a dotted box around a non-selected shape and with an additional highlight on a selected shape.
  3. The selected shapes do not change when editing. In this example, the Title shape was selected and I clicked on it in the pane to edit its name.
  4. This is the Selection Pane icon in the ribbon when added to the QAT. Note that when the Selection Pane is visible, it is highlighted.

Using the pane, we can add helpful names to otherwise meaningless shape names and groups. This helps give us an alternate way to communicate about a drawing. The shapes in the pane appear on top of the shapes listed below them. Groups may contain other groups. You may not select a group which is not top-level, but you may rename it by setting keyboard focus to the inner shape and pressing F2. In the following drawing, we have a lightning bolt and a house. The house is made up of a Roof, a Chimney, a Wall, and a Door.

Screenshot demonstrating using the Selection Pane to add meaning to shapes

In addition to giving meaning to shapes in a drawing, the Selection Pane also helps you work with complex drawing consisting of overlapping shapes. It enables you to hide the shapes and all of their attached animation UI so that you can work on shapes underneath with the mouse. In the screenshot below, we have used the Selection Pane in addition to the Custom Animation Pane to create an animation which has several portions of a "Curtain" slide off the screen to reveal a hidden message.

Screenshot showing how to select buried shapes.

Now we'll hide a few shapes (Note that Beta 2 had some bugs in this area. This screenshot was produced in a later build.):

Screenshot showing how to hide shapes and animations.
  1. The I shape has keyboard focus in the Selection Pane and is selected as well.
  2. The list of effects in the timeline does not show hidden shapes. Animations on hidden shapes will not play back in slide show or in preview.
  3. The OOUI as well as the Motion Paths are not shown for hidden shapes.

The full list of keyboard shortcuts is:

Keys Action
Up / Down Arrows Change keyboard focus to next / previous shape

Shift + Up / Down Arrows

Change focus and select new focused shape (if possible)
Space / Enter Select focused shape (if possible)
Shift + Space / Enter Add focused shape to selection (if possible)
Ctrl + Shift + S (for "Show") Toggle Visibility
Ctrl + Shift + F (for "Forward") Move selected shapes forward
Ctrl + Shift + B (for "Backward") Move selected shapes backward
F2 Enter rename shape / editing mode
Enter (in editing mode) Accept new shape name (if valid)
Escape (in editing mode) Cancel editing shape name
Alt + Shift + 1 Collapse all groups
Alt + Shift + 9 Exapnd all groups
Left / Right Arrows Collapse/Expand focused group
Tab / Shift + Tab Cycle through Selection Pane controls
Escape Return keyboard focus to document editing window
F6 / Shift + F6 Cycle through next / previous pane
Any key not handled in the Selection Pane Return keyboard focus to document editing window and press the key.

Finally, I just wanted to say that this feature is fully accessible to screen readers and Braille readers via Microsoft's Active Accessibility (see also here), and it replaces the functionality of the Select Multiple Objects dialog as the preferred way for visually impaired people to interact with multiple shapes in PowerPoint and Excel documents. Exposing these shape names to seeing users and encouraging them to add meanings to these names helps everyone to communicate more effectively.

This posting is provided "AS-IS" with no warranties, and confers no rights.

Posted by mikefried | 2 Comments
Filed under: ,

The Selection and Visibility Pane (Part 1 - Design)

The Selection and Visibility Pane (called "Selection Pane" for short) is a new feature in Office 2007. It is an OfficeArt shared feature and appears in PowerPoint and Excel. Its main purpose is to help you manage complex drawings and especially complex animations. Unlike a posting to the PowerPoint/OfficeArt blog which is run by program management, I'm going to give you some of the problems we had to solve and our design decisions first before showing the screenshots and giving you pointers for using the feature. There were a lot of discussion here by a number of passionate people in various disciplines. My recollection of some points is a little fuzzy, so bear that in mind.

The Selection Pane exposes two properties of shapes to the end user via the UI for the first time: The shape’s Name and the shape’s Visibility.

The name and visibility properties existed in previous versions of the Office product. In previous versions, these properties were visible to the Office development tools, and developers used these properties as they saw fit without it affecting the user in any way. With the introduction of UI to give users access to these properties, we needed to make a few additional changes. There were many difficult tradeoffs which needed to be made between behavior of legacy functionality, potential privacy concerns, international/multi-language documents, and programmatic access. I'll discuss the tradeoffs briefly and how the final design of the pane and supporting features accommodate the issues. It is my personal belief that the resulting design gives the most power to the users while still protecting privacy and making sure that developers can still work with these features with a minimal amount of breaking scripts and plug-ins.

Shape Name:
The name property in previous versions of Office consisted of a "Friendly Name" which was generated in the UI language and shown to the user in places like the "Custom Animation Pane" and in the "Select Multiple Objects Dialog". Each shape also had an "Object Model Name" which if not set defaulted to the "Friendly Name" except that it was generated in US English instead of the UI language. Some scripts/code may depend on these names, but these names have no explicit guarantees, and we changed the way that we generate them in the Office 2007 release.

After many discussions about uniqueness and guaranteeing uniqueness, and using one name vs. two, it was decided that making this work the way we did before would cause the user needless headaches. In Office 2007, we reduced this down to only one name property. So Office developers take note: If you wrote code looking for "Rectangle 1", you really meant to be looking for the shape whose ID is 1 and whose geometry is a rectangle, because your users might not be using English as their language or they might have changed the name in the Selection Pane. It is not robust to rely on a user-adjustable setting to stay the same. It is also not robust to rely on a shape remaining in the z-order which it was positioned in. If you look at the list of shapes in a drawing, they are listed in “drawing order” (reverse z-order), which is not guaranteed to be the same as their ID order (because you can reorder the shapes with Send Backward/Bring Forward). Shape IDs are unique within their containing drawing.

Shape Visibility:
In Office 2003, the Visibility property of a shape was only available via the Object Model. Now that the hidden properties are accessible to the end user without breaking out a development environment, we had to make some tough choices. Several designs presented themselves, and there was a great amount of concern that people would use the Selection Pane to hide sensitive data within documents. There were many alternative ideas floated to combat these concerns with several variations on each, and each had tradeoffs. Here were some of the ideas (to give you a sense of the variety of ways that we looked at solving the potential problems):

  1. An extreme option which was discussed early is to delete all hidden shapes when opening or maybe when saving. This approach has obvious drawbacks because it deletes user data without asking and would break older Office scripts and plug-ins as well as some collaboration features if you opened a binary with hidden shapes and resaved it in Office 2007. It also would not be consistent with auto-save. Something like a power failure would cause you to lose hidden shapes that should be saved.
  2. A less extreme option was to have a temporary/view-dependent visibility property. The theory behind this idea was that users hide shapes because they are working on a complex drawing and when the user is finished and enters slide show, the users still want to see and animate those shapes. The original Visibility property (it is still needed for certain legacy functionality such as "Send for Review") here would remain truly hidden, and those shapes would not be shown in the Selection Pane. A downside to an approach like this is that you now have two invisibility properties and they do two different things (just like having two names that do different things added too much complexity).
  3. Another option was to continue to use the Visibility property as-is, but to only hide invisible shapes when the Selection Pane was up. Several variants of this idea were discussed, including showing the invisible shapes as ghost shapes with some adjustable amount of transparency slider in the Selection Pane. This is a nice idea, but just like option 2 above, you can get into a state where you have problems working between versions of the product. One version shows the hidden shapes in slide show, and the other one doesn't.
  4. There were many other potential designs each with their own set of tradeoffs.

The final design was to stick with the previous visibility property behavior (which makes the behavior consistent between versions of the product) and make an additional tool available to work with and remove hidden shapes. The Selection and Visibility Pane itself is a tool to view hidden shapes and change their visibility state, so by having this feature in the product, we are providing a way to "see" and work with these hidden shapes. In the Selection Pane, there are buttons to show or hide all shapes in the current drawing (PowerPoint Slide/Excel Worksheet). The tool to remove hidden shapes is part of the Document Inspector which can be found in what I have to call the "File Menu".

So that's a little bit on the design tradeoffs of these two properties. Next time, we'll walk through the UI, go over keyboard shortcuts, give you some screen shots, and talk about how it works with the Custom Animation Pane in PowerPoint to make it easy to tell a story or how it works by itself in PowerPoint or Excel to help you work with a lot of overlapping shapes.

This posting is provided "AS-IS" with no warranties, and confers no rights.

Posted by mikefried | 3 Comments

Topics for future posts

Here are some topics which I intend to discuss in the future in one or more posts. When these topics have posts, I will link them to this page.

  • Selection and Visibility Pane
    • Part 1: Differences in design between shape Name and Visibility properties in OfficeArt that developers should note.
    • Part 2: How to use it, What it helps you do.
  • Under the hood with Table Styles - We'll delve into the XML and show you how to make your own Custom Table Style. There are lots of options here, and the built-in styles scratch the surface of the functionality.
  • Presenter View tips/tricks - Ric went over the Presenter View on the PowerPoint/OfficeArt Blog. I wanted to delve into a bunch of cool/small sub-features he didn't mention and how to use them.
  • Custom Animations - How to tell stories with shapes and the timeline. How to spruce up Diagrams and Charts with custom subtle animations that add character without distracting the audience from your message. Some advanced timeline techniques and some code to interact with the timeline. This will be a multi-part series, and there may be guest posters...
  • 3D - Text and Shapes in OfficeArt now have 3D properties. I will show you some cool things you can do with them.
  • Outline Pane - Differences between PowerPoint 2003 and 2007, and some of the hidden parts of this feature that few people know.
  • Features you might not know about - High Contrast, High DPI, Accessibility, Multiple Languages, Right-To-Left, and other features of Office/PowerPoint that are crucial to some people and invisible to everyone else.

These are some of the topics I have in mind to write about over the next few months. As Ric recently noted, we're awfully busy in the final stages of bug fixing and polishing Office 2007. This is your opportunity to give me suggestions, post a comment and tell me if there are any feature areas you want me and others to talk about.

This posting is provided "AS IS" with no warranties and confers no rights.

Posted by mikefried | 0 Comments
Filed under:
More Posts Next page »
 
Page view tracker