Summary: Exploring the code of the Office solution that shows all the buttons available for the Microsoft Office System Command Bar. Learn how to use the command bar object model in the managed code while enabling the solution across version. The final solution shows all available Office Command Bar buttons and FaceIDs (a tool that is very useful to develop custom command columns).
This page
Introduction Creating an external connection Prepare an external program for encoding and deploying an external program to add support for other applications
Introduction
One of the most difficult things is one of the most difficult things to use the command bar and the command bar button. From simple to complex solutions, you may require the use of conventional text on a small button as a title, but there are some solutions to identify features using attractive independent small icons or small icons with text. Figure 1 here shows an example of all three ways.
Figure 1. Button can have images, text or both
With the button function icon to create a command bar, there are thousands of possible icons available. Try to find the most common practice of the surface of the appropriate command bar to write a process with a macro, which uses the loop structure to pass the numbers and specify them as the FaceId property of the test button to see the appearance of the generated icon.
The solutions of this article are built in this same concept, but add more elegant appearance (as shown in Figure 2). Moreover, it can be used in different Microsoft Office versions of different applications, such as Microsoft Office PowerPoint ® 2003, Microsoft Office Word 2003 or Microsoft Office Excel 2003.
Figure 2. External program displays the button surface and provides surface ID in the Tool Tips text
Create an external program
The add-in is a Microsoft .NET assembly that uses Office object models through COM interoperability. In this example, the .NET assembly is written in C #, but if necessary, it can be easily converted to another language compatible with the .NET. To create a project, follow the steps below to start and use Extensibility Wizard:
1. In Microsoft Visual Studio ® .NET, create an item and select Extensibility Projects in the New Project dialog. 2. In the Templates window of the dialog, click Shared Add-I, as shown in Figure 3.
Figure 3. Select "Shared Add-in" project type in Visual Studio .NET
3. Specify a name (such as ButtonFaces) for the project, then specify the project location, and then click OK. 4. In the next step (Figure 4), select the application to host the external program. Click Microsoft Word and Microsoft Excel, then click Next.
Figure 4. To select the application you want to host the program with EXTENSIBILITY WIZARD
5. This dialog allows you to type names and descriptions for your external connections. Configure as shown in Figure 5.
Figure 5. Configuring the name and description of the add
6. In the next window (Fig. 6), select two check boxes to load the add-in, and any user can use the add-in when the host application starts. Click Next.
Figure 6. Select when to load the add-in and which users should access it
7. Click Finish to complete the steps in ExtensIlity Wizard.
Prepare external procedures for use
After the project is set, Visual Studio .NET creates a class named Connect. This class contains methods automatically generated by ExtensIlity Wizard to implement interfaces of the external program (IDTEXTENSIBILITY2 interface). The method you need is started by the following: public void onConnection (Object Application,
Extensibility.ext_ConnectMode ConnectMode,
Object AddinInst, Ref System.Array CustomAn
{
ApplicationObject = Application;
AddinInstance = AddinInst;
IF (ConnectMode! = EXTENSIBILITY.EXT_CONNECTMODE.EXT_STARTUP)
{
OnStartupComplete (Ref Custom);
}
}
This method is excited when an add program is enabled in the host application. The reference to the host application will be passed as a parameter to the method. This will allow the code to set the internal variable to the host application. The code calls another wizard to generate an ONSTARTUPCOMPLETE from which to begin to prepare the add-in. The code's code uses certain variables to access the command bar, add a new button to the menu in the host application, and understand the type of host application. The code and the onstartupComplete method of these claims are as follows:
Private office.commandbarbutton getButtonface;
Private Office.commandbars Bars;
Private Type ApplicationType;
Public Void OnStartupComplete (Ref System.Array Custom)
{
Office.commandbar bar = NULL;
String buttonname = "get button faces";
Try
{
ApplicationType = ApplicationObject.gettype ();
Bars =
(Office.commandbars) ApplicationType.InvokeMember
"Commandbars", BindingFlags.getProperty, NULL,
ApplicationObject, NULL);
Bar = BARS ["Tools"];
Object missing = missing.value;
Office.commandbarButton Button = (Office.commandbarButton)
Bar.FindControl (Office.msoControlType.msocontrolbutton,
Missing, ButtonName, True, TRUE
IF (Button == Null)
{
GetButtonFaces = (office.commandbarbutton) bar.controls.add (
Office.msocontrolType.msocontrolbutton,
Missing, Missing, missing, missing;
GetButtonFaces.caption = buttonname;
GetButtonFaces.Style = office.msobuttonstyle.msobuttoncaption;
GetButtonFaces.tag = buttonname;
}
Else
{
Getbuttonfaces = button;
}
GetButtonFaces.click =
New office._commandbarbuttonevents_clickeventhandler
GetButtonFaces_Click);
}
Catch (Exception EX)
{
Messagebox.show (ex.Message);
}
}
The purpose of this code is to add buttons to the host application's Tools menu, as shown in Figure 7.
Figure 7. External program starts from the Tools menu of the host application
Using the InvokeMember method, the code can get a reference to the hostbars collection of the host application and then retrieve the Tools command bar. InvokeMember methods are used when using the .NET Framework in part of the System.Reflection NameSpace. Reflection can allow code to discover and check the members of the program set. Use the reflection, you can also use the specified binding constraint and match the specified parameter list to call these types of members. Because the code contains references to the type of host application, we can use this type of InvokeMember method to perform operations such as the COMMANDBARS collection in the host application.
Bars = (office.commandbars) ApplicationType.InvokeMember
"Commandbars", BindingFlags.getProperty, NULL,
ApplicationObject, NULL);
Bar = BARS ["Tools"];
By using the FindControl method, the code can see if the desired button has been installed. In Excel, trying to add an existing button to cause an exception, but Word allows the same button to add multiple times. Capture an exception in the code cannot be applied to both, so before trying to add a button, it is best to check if the button already exists.
Bar.FindControl (Office.msoControlType.msocontrolbutton,
Missing, ButtonName, True, TRUE
If this button is not installed, the code adds the button to the target command bar.
IF (Button == Null)
{
GetButtonFaces = (office.commandbarbutton) bar.controls.add (
Office.msocontrolType.msocontrolbutton,
Missing, Missing, missing, missing;
GetButtonFaces.caption = buttonname;
GetButtonFaces.Style = office.msobuttonstyle.msobuttoncaption;
GetButtonFaces.tag = buttonname;
}
Otherwise, you will get a reference to the existing button.
Else
{
Getbuttonfaces = button;
}
In either case, the main function of the application is started by the button getButtonFaces. Because the button is a method of building a command bar (that is, most of the external programs), we need an event process of this button. You can add an event process with the following method:
GetButtonFaces.click =
New office._commandbarbuttonevents_clickeventhandler
GetButtonFaces_Click; encode interface
The button event handler added by the code to the menu (Figure 7) is primarily used to start the main functionality of the external program. The code for the event handler is as follows:
Private void getButtonFaces_Click
Office.commandbarbutton cmdbarbutton, Ref Bool CancelButton
{
IF (ButtonfaceBar == Null)
{
Buildcommandbar ();
SetupnaVigationButtons ();
AttachButtonfaces (0);
}
}
The event handler code calls the other three processes, where the first one contains a large number of codes in the rest of the external program. The BuildCommandbar process is responsible for building a command bar of the icon and stores a reference to the new command bar to the ButtonFaceBar variable. SetupnavigationButtons procedure puts the navigation button on the interface so that the user can view the button surface in each group. AttachButtonFaces The process actually displays the button surface on the newly created command bar. View these processes in the execution order, the BuildCommandbar code is as follows:
Private const short buttonsinrow = 20;
PRIVATE CONST Short buttonswithnaVigation = 120;
Private office.commandbar buttonfacebar;
Public void buildcommandbar ()
{
Try
{
Buttonfacebar = Bars.Add ("Button Faces",
Office.msobarposition.msobarfloating, false, true
ButtonfaceBar.Protection =
Office.msobarprotection.msobarnochangevisible
| Office.msobarprotection.msobarnoreSize
| Office.msobarprotection.msobarnochangedock;
INT AppTop = 0;
INT Appleft = 0;
String Appname = (String) ApplicationType.InvokeMember
"Name", bindingflags.getProperty, NULL,
ApplicationObject, NULL);
Switch (appname)
{
Case "Microsoft Excel":
Applet = (int) (Double) ApplicationType.InvokeMember
"TOP", BindingFlags.getProperty,
NULL, ApplicationObject, NULL;
Appleft = (int) (Double) ApplicationType.InvokeMember
"Left", bindingflags.getproperty,
NULL, ApplicationObject, NULL;
Break;
Case "Microsoft Word":
Applet = (int) ApplicationType.InvokeMember
"TOP", BindingFlags.getProperty,
NULL, ApplicationObject, NULL;
Appleft = (int) ApplicationType.InvokeMember ("Left", BindingFlags.getProperty,
NULL, ApplicationObject, NULL;
Break;
DEFAULT:
Break;
}
ButtonfaceBar.top = (int) (AppTOP 50);
ButtonfaceBar.Left = (int) (AppleFT 50);
Office.commandbarbutton button = NULL;
For (int i = 1; i <= buttonswithnavigation; i )
{
Object missing = missing.value;
Button = (office.commandbarbutton)
ButtonfaceBar.Controls.add (
Office.msocontrolType.msoControlButton, 1,
Missing, Missing, Missing;
Button.faceId = 1;
}
GethighestFaceIDS
(Office.commandbarbutton) ButtonfaceBar.Controls [1]);
Buttonfacebar.visible = true;
Buttonfacebar.width = button.width * ButtonsinRow 6;
}
Catch (Exception EX)
{
Messagebox.show (ex.Message);
}
}
The main process of this process is to first add a new command bar to the CommandBars collection of the host application.
Buttonfacebar = Bars.Add ("Button Faces",
Office.msobarposition.msobarfloating, false, true
ButtonfaceBar.Protection =
Office.msobarprotection.msobarnochangevisible
| Office.msobarprotection.msobarnoreSize
| Office.msobarprotection.msobarnochangedock;
The Add method of the Commandbars collection accepts four parameters. The first is the name of the target command column. The second is the location of the command column, in this example, the floating command bar. The third with false values, which specifies the new command bar to replace the active menu bar. The last parameter tells the host application, the command bar should not be deleted when the application is turned off.
After adding a new command bar, the code will set the TOP and LEFT attribute offset to locate the newly created command bar in a specific location relative to the host application's top and LEFT properties. The command column itself includes a set of buttons, which is exactly 120 buttons, which are reused when the user views thousands of buttons. When requesting each new button surface group, the new button image is copied to the existing button. Even if there is more than 120 buttons, only one row button is used to make navigation, so the user can view the button surface in each group. The interface button includes buttons for navigation and a cancel button.
Office.CommandBarButton button = null; for (int i = 1; i <= buttonsWithNavigation; i ) {object missing = Missing.Value; button = (Office.CommandBarButton) buttonFaceBar.Controls.Add (Office.MsoControlType.msoControlButton, 1, Missing, missing, missing; button.faceID = 1;} In the loop operation of adding a new button, add a new blank button using the controls of the primary command bar. Call the Add method to add and return to the new button. By passing the MSoControlButton parameter to this method, you can specify a new button should be a simple button, not a drop button or a long string possible button type. The second parameter (value here is 1) Specifies the addition of blank controls for the specified type to the command bar. Then, set its FaceID attribute to 1 to make it a blank control or no image thereof. This is different from the specified control type, which only specifies which button surface (if any) should be displayed on the button.
After the interface is ready, the code will find the starting point of the last set of buttons by calling the custom procedure gethighestFaceIDS. Then it makes the command bar visible and sets its width. This width is obtained by multiplying the width of these buttons. In addition, an arbitrary number is added to the calculation. This number (6 in the following) may not be suitable for all screen resolutions or styles. Of course, it should be fully tested. It is also possible to design a more distinguished context calculation.
GethighestFaceIDS
(Office.commandbarbutton) ButtonfaceBar.Controls [1]);
Buttonfacebar.visible = true;
Buttonfacebar.width = button.width * ButtonsinRow 6;
In order to find the starting point of the last set of buttons, the gethiGhestFaceids process passes through each number in an increase in 1000, up to the high limit (10 million), although the code is long enough, and in it reaches the number fashion Not ended. Setting the FaceID property (TestButton.FaceID = i) will cause an error in the number excessive. When the abnormality is triggered, the code will pass the number by the smaller increment before finalizing the starting point of the last set of buttons. Before exiting, the code continues to find the last available surface ID and store it in the FinalID variable. The GethigHestFaceids process stores the maximum surface ID value of the button that starts in the current Office application version in the HIGHESTSTARTID variable.
Private int higheststartid;
PRIVATE INT FINALID;
Public void gethighestfaceids (office.commandbarbutton testbutton)
{
INT INCREMENT = 1000;
INT loopStart = 1000;
For (INT i = loopstart; i <= 10000000; i = increment)
{
Try
{
TestButton.FaceId = i;
}
Catch (Exception)
{
IF (increment == 1000)
{
I - = 1000 100;
Increment = 100;
}
Else IF (increment == 100)
{
HIGHESTSTARTID = I - ButtonSingRoup;
I - = 100 1;
Increment = 1;
}
Else
{
FinalId = I - 1;
Break;
}
}
}
}
After the command bar is prepared by calling BuildCommandbar, the GetButtonFaces_Click process (when the user decides to use the event procedure that can be raised when an external program from the host application Tools menu) can set the navigation button, then start with the visible image to populate its buttons. Setting the navigation button is done during the setupnavigationButtons process. Although the setting process is a bit lengthy, the code is very simple, just adds the navigation button with their own fixed button surface and the corresponding event handler. The first button (appropriately named firstButton) includes a property setting that is significantly different from the other buttons, ie, its BeGingROUP property is set to TRUE. This ensures that the button is located in front of all other buttons on the command bar. The following code excerpts also contains values for the surface of the buttons suitable for each navigation button. Here, use the value of the existing button surface. This code also includes a constant (firstnavigationButton) that sets the firstButton navigation button ID, which is the seventh button of the bottom row. Because there is a hundred buttons, the ID must be 100 plus 7.
Private office.commandbarbutton firstbutton;
Private Office.commandbarbutton BigPreviousButton;
Private office.commandbartbutton previousbutton;
Private office.commandbarbutton nextbutton;
Private Office.commandbarbutton BignextButton;
Private office.commandbarbutton lastbutton;
Private office.commandbarbutton canbutton;
PRIVATE SHORT FIRSTNAVIGATIONBUTTON = 107;
Private const short first firstface = 154;
Private const short bigpreviousface = 41;
Private const short previousface = 155;
Private const short nextface = 156;
PRIVATE CONST SHORT BIGNEXTFA Part = 39
Private const short lastface = 157;
Private const short cancelfact = 478;
Public void setupnaVigationButtons ()
{
Try
{
FirstButton =
(Office.commandbarbutton) ButtonfaceBar.Controls
[firstnavigationbutton];
FirstButton.begingRoup = true;
FirstButton.FaceId = firstface;
FirstButton.click =
New office._commandbarbuttonevents_clickeventhandler (firsthundred_click);
FirstButton.Tooltiptext = "first 100";
BigPreviousButton =
(Office.commandbarbutton) ButtonfaceBar.Controls
[firstnavigationButton 1];
BigPreviousButton.FaceId = BigPreviousFace;
BigPreviousButton.Click =
New office._commandbarbuttonevents_clickeventhandler
BigPreviousHundred_Click);
BigPreviousButton.Tooltiptext = "Previous 1000";
PreviousButton =
(Office.commandbarbutton) ButtonfaceBar.Controls
[firstnavigationButton 2];
PreviousButton.FaceId = previousface;
PreviousButton.click =
New office._commandbarbuttonevents_clickeventhandler
Previoushundred_click;
PreviousButton.Tooltiptext = "previous 100";
NextButton =
(Office.commandbarbutton) ButtonfaceBar.Controls
[FirstnavigationButton 3];
NextButton.faceId = nextface;
NextButton.click =
New office._commandbarbuttonevents_clickeventhandler
Nexthundred_click;
NextButton.Tooltiptext = "Next 100";
BIGNEXTBUTTON =
(Office.commandbarbutton) ButtonfaceBar.Controls
[FirstnavigationButton 4];
BIGNEXTBUTTON.FACEID = BIGNEXTFACE;
BignextButton.click =
New office._commandbarbuttonevents_clickeventhandler
BIGNEXTHUNDRED_CLICK;
BIGNEXTBUTTON.TOOLTIPTEXT = "Next 1000";
LastButton =
(Office.commandbarbutton) ButtonfaceBar.Controls
[FirstnavigationButton 5];
LastButton.faceId = lastface;
LastButton.click =
New office._commandbarbuttonevents_clickeventhandler
Lasthundred_click;
LastButton.Tooltiptext = "Last 100";
CancelButton =
(Office.commandbarbutton) ButtonfaceBar.Controls
[FirstnavigationButton 6]; CancelButton.FaceId = Cancelface;
CancelButton.click =
New office._commandbarbuttonevents_clickeventhandler
Cancelfacebar_click;
CancelButton.TooltExt = "close";
Office.commandbarButton Button =
(Office.commandbarbutton) ButtonfaceBar.Controls
[FirstnavigationButton 7];
Button.begingroup = true;
}
Catch (Exception EX)
{
Messagebox.show (ex.Message);
}
}
The list of buttons and their uses are as follows:
Button Name event procedure uses firstButton firstHundred_Click first 100 buttons bigPreviousButton bigPreviousHundred_Click displayed before the current set of 1000 buttons nextButton nextHundred_Click display 100 buttons previousHundred previousHundred_Click after the current group is displayed before the current set of 100 buttons bigNextButton bigNextHundred_Click display 1000 after the current group Button LastButton LastHundred_Click Displays the last 100 buttons CancelButton CancelfaceBar_Click Close Custom Command Bar
The corresponding code of these event processes is as follows:
Private void firsthundred_click
Office.commandbarbutton cmdbarbutton, Ref Bool CancelButton
{
AttachButtonfaces (0);
}
Private void bigprevioushundred_click
Office.commandbarbutton cmdbarbutton, Ref Bool CancelButton
{
IF (LatestStartID> = ButtonSingRou)
{
AttachButtonfaces (Math.max (0, LatestStartID - 1000);
}
}
Private void previoushundred_click
Office.commandbarbutton cmdbarbutton, Ref Bool CancelButton
{
IF (LatestStartID> = ButtonSingRou)
{
AttachButtonfaces (LateststartID - ButtonsingRou);
}
}
Private void nexthundred_click
Office.commandbarbutton cmdbarbutton, Ref Bool CancelButton
{
LatestStartid { AttachButtonFaces (LatestStartid ButtonSingRou); } } Private void bignexthundred_click Office.commandbarbutton cmdbarbutton, Ref Bool CancelButton { LatestStartid { AttachButtonfaces (Math.min (HighestStartID, LatestStartID 1000); } Private void Lasthundred_Click Office.commandbarbutton cmdbarbutton, Ref Bool CancelButton { AttachButtonfaces (HigheststartID); } Private void CanceceBar_Click Office.commandbarbutton cmdbarbutton, Ref Bool CancelButton { Closefacebar (); } Canceling the event procedure using a custom command bar will call a ClosefaceBar process. This process can easily delete the custom command bar. Private void closefacebar () { Try { Buttonfacebar.delete (); Buttonfacebar = NULL; } Catch (Exception EX) { Messagebox.show (ex.Message); } } AttachButtonFaces procedure uses the starting point and fill the button from the starting point with the image surface. This process uses two variables (LatestStartID and FinalID) to record the ID of the button at the beginning of the current group. This ID will increment or decrease as the user moves on the button surface group. Private const short butt "" Private Int LatestStartId; Public Void AttachButtonFaces (int StartID) { INT i = 1; LatestStartId = StartID; UpdatenaVigationButtons (); INT Realingfinalid = FinalID - LatestStartId; Try { For (; i <= buttonsingroup&& i <= relativefinalid; i ) { Office.commandbarButton Button = (Office.commandbarbutton) ButtonFaceBar.Controls [i]; INT ID = startID i; Button.faceid = ID; Button.tooltiptext = id.toString (); } Buttonfacebar.name = string.format Button Faces ({0} .. {1} ", StartID 1, StartID I - 1); ClearunusedFaces (i); } Catch (Exception EX) { Messagebox.show (ex.Message); } } In order to let the user understand which set button is displayed, the title with two placeholders should be used in series to set the display name of the command bar, one for the starting faceid used in the group, and the other for ending the FaceId: Buttonfacebar.name = string.format Button Faces ({0} .. {1} ", StartID 1, StartID I - 1); By using the starting point of the entire group, the code can sequentially through the new button surface until group limit ButtonSingRoup. For (; i <= buttonsingroup && i <= relativeFinalId; i ) { Office.commandbarButton Button = (Office.commandbarbutton) ButtonFaceBar.Controls [i]; INT ID = startID i; Button.faceid = ID; Button.tooltiptext = id.toString (); } Through this method, the AttachButtonFaces process performs two more important operations. First, it updates the navigation button to disable it when the button cannot be used. For example, if the user is at the end of the list, let the user view more buttons on the button surface should be disabled. Similarly, if the user is at the beginning of the list, it should not be able to return back, so the corresponding button should be disabled. This logic is processed during the UpdatenavigationButtons process. Public void updatenavigationButtons () { Try { ButtonfaceBar.Controls [firstnavigationButton] .enabled = LatestStartID! = 0; ButtonfaceBar.Controls [firstnavigationButton 1] .enabled = LatestStartID> = 1000; ButtonfaceBar.Controls [firstnavigationbutton 2] .enabled = LatestStartID! = 0; ButtonfaceBar.Controls [firstnavigationButton 3] .enabled = LatestStartID! = HIGHESTSTARTID; ButtonfaceBar.Controls [firstnavigationButton 4] .enabled = LatestStartID <= HIGHESTSTARTID - 1000; ButtonfaceBar.Controls [firstnavigationButton 5] .enabled = LatestStartID! = HIGHESTSTARTID; } Catch (Exception EX) { Messagebox.show (ex.Message); } } Second, the AttachButtonFaces procedure calls the ClearunusedFaces process so that if there is a unused button at the end of the button list, clear any pre-existing content of these buttons. Public void ClearunusedFaces (int firstunusedbutton) { Try { For (Int i = firstunusedbutton; i <= buttonsingroup; i ) { Office.commandbarButton Button = (Office.commandbarbutton) ButtonFaceBar.Controls [i]; Button.faceId = 1; Button.tooltiptext = "" } } Catch (Exception EX) { Messagebox.show (ex.Message); } } The entire application is encoded, in addition to the routines that run when an external program is uninstalled from the host application. There is also a built-in event process as part of the IDTextensibility2 interface, which allows you to run code based on the condition, depending on the reason why the add-on-connections are triggered. Public void ondisconnection Extensibility.ext_disconnectMode DisconnectMode, Ref System.Array Custom) { IF (disconnectmode! = EXTENSIBILITY.EXT_DISCONNECTMODE.EXT_DM_HOSTSHUTDOWN) { Onbeginshutdown (Ref Custom); } ApplicationObject = NULL; } Public void Onbeginshutdown (Ref System.Array Custom) { Object missing = missing.value; GetButtonFaces.delete (MISSING); GetButtonFaces = NULL; } This code will check if the host application is closed, which will cause the add-in to be uninstalled. If the uninstallation of the add-in is not the result of the host application shutdown, the code will allow the ONBEGINSHUTDOWN process to run. The code in this event will only delete the button from the Tools menu of the host application. Establish and deploy an external program The last step is to establish and deploy an add-in. When using the EXTENSIBILITY WIZARD in Visual Studio .Net creates the entire solution, not only, the actual add-in, but also creates a second project and add it to the solution. This second project is the installation item of the external program itself. Project documents (see Figure 8) Includes dependencies detected for external programs, once a project is established, it will become a Microsoft Installer package that can be activated to install an add-in. Figure 8. Overall solutions include projects and add-in settings files To build an add-in in Visual Studio .NET, click Build Solution on the Build menu. This will create a .NET assembly and create the necessary infrastructure for COM interoperability. One way to generate the installation project is that right-click the project in the Solution Explorer window, and then click Build. This will generate an installation item and create a .msi file. To find the generated Microsoft Installer package, review the same directory for the solution and application project file. There, you can find the ButtonFaceSsetup directory. In this directory, you can take a look at the completed build type directory, publish, or debug, and find the .msi file. Double-click the file to start the installation, but this step may not be executed on the development computer because it has been registered and can be used. To use an add-on, start Word or Excel, and click Get Button Faces on the Tools menu. Add support for other applications So far, the add-on only Word and Excel are supported. It is necessary to remember that each application (but part of the Microsoft Office system) may have different behavior and properties that are different from other applications in the same series. For example, the slight differences supported by the command bar in Word and Excel, which means that the code is to be changed to accommodate them. Similarly, adding support for other applications (such as PowerPoint) means that you must add the conditional code at the other location of the code. Fully understand object models and strict tests, ensuring that you select these differences and adjust your code to develop stable and reliable add-in. As for projects in Visual Studio, you only need to make an adjustment, you can make the add-in in other applications. Figure 9 shows the Registry Editor interface of an external program installation item. Figure 9. Configuring registry settings on the target computer using the Registry Editor The Registry Editor in the installation project allows you to configure the project run, you want to configure the registry settings on the target computer. For example, in Figure 9, you can see the registry key of Excel, PowerPoint, and Word. The ButtonFaces.Connect item has three values, two of which are string values, one is a value, which can tell when the host application should load an add-in. In this example, these three values specify that the add-in should be loaded when the host application is started. When the extension wizard is first run to create an initial project, the add-in registration item in the PowerPoint item does not exist. This can be added manually later so that the add-in can be used for PowerPoint. Running the installation project again can add the registry key to the registry of the target machine. In addition, you may need to write conditional code to accommodate differences in the object model of the host application. summary The command bar is usually used when designing a user-friendly Office application. However, images are often used on the command bar button instead of text. Before you create a custom image for the button, you should first check if there is an image that is appropriate for you. This possibility is present in the surface of nearly thousands of buttons, there is always a meeting that meets your application requirements. The code in this article makes the search button surface library to find the desired button surface easier. The code is written in C #, which created an add-in, which can be used for Office 2000, Office XP, and Office 2003. It is somewhat possible for portability of different OFFICE versions, because it does not assume the number of buttons provided in each version. These quantities are indeed different in different versions, but the external program code is designed to be intelligently identified and respond accordingly.